【问题标题】:Inplace modification for subset of elements within original python list就地修改原始python列表中的元素子集
【发布时间】:2015-11-07 03:19:31
【问题描述】:

我看到列表推导如何擅长返回列表的子集,甚至在没有设置条件的情况下也能全面重新映射。但是,过滤条件将返回列表中的值,该列表可能小于过滤后的列表。如何获取返回值的位置并有条件地更改这些元素?

这是一个简单的例子,

>>> v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
>>> t = ['a', 'k']

如何修改tv 的元素以映射到新值,例如'z',以产生类似

>>> v = ['z' , 'b' , 'c' , 'd' , 'f' , 'z' , 'g' , 'z', 'd']

有很多关于 SO 的问题,来自 Matlab 的人们希望做相当于逻辑索引的操作,但主要只是为了选择元素而不是修改它们如我所愿。为了修改过滤的元素,我想做相当于逻辑索引的操作。

我也刚刚发现我不能同时为 Python 列表提供多个索引。我正在使用 Python 3。

>>> v[ [1,3] ] = 'z'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not list
>>> v[ 1:3 ] = 'z'
>>> v
['a', 'z', 'd', 'f', 'k', 'g', 'a', 'd']

filter 可以为我提供一个可迭代的实际对象,但我不知道如何修改它们指向的元素。

>>> v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
>>> t = ['a', 'k']
>>> p = filter(lambda x: x in t, v)
>>>
>>> for elem in p :
...    print(elem)
...
a
k
a
>>>
>>> for elem in p :
...    p = 'z'
...
>>>
>>> for elem in p :
...    print(elem)
...
>>>

【问题讨论】:

    标签: python mapping list-comprehension


    【解决方案1】:

    您可以使用mapv 中的值映射到固定值z(如果它们也在t 中),否则保持不变:

    >>> print list(map(lambda i: 'z' if i in t else i, v))
    ['z', 'b', 'c', 'd', 'f', 'z', 'g', 'z', 'd']
    

    或者,您仍然可以使用推导:

    >>> v[:] = [i if not (i in t) else 'z' for i in v]
    >>> print v
    ['z', 'b', 'c', 'd', 'f', 'z', 'g', 'z', 'd']
    

    就个人而言,我会发现定义函数更明显,特别是如果您正在使用带有多个参数的函数。因为通过functools.partial很容易“冻结”固定参数:

    >>> def f(value, lookup, mapping_value):
    ...     if value in lookup:
    ...         return mapping_value
    ...     return value
    

    --

    >>> from functools import partial
    
    >>> v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
    >>> t = ['a', 'k']
    
    >>> func = partial(f, lookup=t, mapping_value='z')
    >>> v[:] = [func(i) for i in v]
    >>> print v
    ['z', 'b', 'c', 'd', 'f', 'z', 'g', 'z', 'd']
    

    【讨论】:

      【解决方案2】:

      应该这样做:

      >>> v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
      >>> t = {'a', 'k'}
      >>> new_v = [item if (item not in t) else 'z' for item in v]
      >>> new_v
      ['z', 'b', 'c', 'd', 'f', 'z', 'g', 'z', 'd']
      >>> 
      

      【讨论】:

        【解决方案3】:

        如果您尝试更改任何匹配的值 v 在 t 中,最有效的方法是使用 make t a set 如果元素是可散列的并且只使用 in

        v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
        st = {'a', 'k'}
        
        v[:] = ["z" if s in st else s for s in v]
        

        或者将它与生成器表达式结合起来:

        v[:] = ("z" if s in st else s for s in v)
        

        使用v[:] 将修改原始对象/列表v

        如果您要使用 for 循环,当您在 v 中找到一个也在 out 集中的元素时,您将使用 enumerate 使用索引更新列表:

        v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
        st = {'a', 'k'}
        
        for ind,ele in enumerate(v):
            if ele in st:
                v[ind] = "z"
        

        【讨论】:

          【解决方案4】:

          Python 中 MATLAB 数组的等价物是 numpy 数组,而不是列表。您不能对列表进行逻辑索引,但可以在 numpy 数组上进行。所以对于你的任务,一个 numpy 数组会很好用:

          >>> import numpy as np
          >>>
          >>> v = np.array(['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd'])
          >>>
          >>> v[v == 'a'] = 'z'
          >>> v[v == 'k'] = 'z'
          >>> print(v)
          ['z' 'b' 'c' 'd' 'f' 'z' 'g' 'z' 'd']
          

          当您有更大的序列时,这会变得复杂。在这种情况下,您可以使用 np.in1d ,它为第一个序列中存在于第二个序列中的任何元素返回 True,而 False 则返回任何不存在的元素。这也可以用于逻辑索引:

          >>> t = ['a', 'c', 'f', 'k']
          >>> v[np.in1d(v, t)] = 'z'
          >>> print(v)
          ['z' 'b' 'z' 'd' 'z' 'z' 'g' 'z' 'd']
          

          在此处使用集合 (t = {'a', 'c', 'f', 'k'}) 会更快,但我试图让事情接近您的示例。

          这种方法大致相当于:

          >>> t = ['a', 'c', 'f', 'k']
          >>> for ti in t:
          ...     v[v == ti] = 'z'
          ...
          

          numpy 数组也支持多个索引,尽管索引序列本身必须是一个 numpy 数组。

          正如其他人所指出的,您可以使用列表推导和成员测试进行简单替换。但是,您也可以使用 dict,这可能会更简单,并且在我看来更清晰:

          >>> t = ['a', 'k']
          >>> tdict = dict.fromkeys(t, 'z')
          >>> v2 = [tdict.get(vi, vi) for vi in v]
          >>> print(v2)
          ['z', 'b', 'c', 'd', 'f', 'z', 'g', 'z', 'd']
          

          dict.fromkeys 方法创建一个dict,其中键是t 的元素,值都是z。相当于{ti: 'z' for ti in t}

          dict.get(x, y)获取与键y对应的字典的值,如果没有这样的键则返回x。我的代码所做的是遍历列表中的每个元素。如果该元素在tdict 中,它将用tdict 中的相应值替换该值。如果不是,它用它自己替换这个值(也就是说,它什么也不做)。

          这并不比成员资格测试示例简单得多。但是,如果您需要使用多个目标和多个替换进行更复杂的替换,则 dict 方法会变得更加更简洁:

          >>> repdict = {'a': 'z', 'k': 'z', 'i': 'y', 'd': 'y', 'b': 't'}
          >>> v2 = [repdict.get(ti, ti) for ti in v]
          >>> print(v2)
          ['z', 't', 'c', 'y', 'f', 'z', 'g', 'z', 'y']
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-03-06
            • 2013-03-27
            • 1970-01-01
            • 1970-01-01
            • 2012-11-11
            • 2019-11-10
            • 1970-01-01
            相关资源
            最近更新 更多