对原始python列表中的元素子集进行原位修改

时间:2015-08-14 00:06:45

标签: python mapping list-comprehension

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

这是一个简单的例子,

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

如何修改vt的元素以映射到新的值,例如'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)
...
>>>

4 个答案:

答案 0 :(得分:2)

您可以使用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']

答案 1 :(得分:1)

如果您尝试更改任何匹配值v in 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 set中的元素时,您将使用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"

答案 2 :(得分:1)

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']

答案 3 :(得分:0)

这应该这样做:

>>> 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']
>>>