对原始 python 列表中的元素子集进行就地修改
Inplace modification for subset of elements within original python list
我看到列表理解如何擅长 returning 列表的子集,甚至在没有设置条件的情况下进行全面重新映射。但是,过滤条件将 return 列表中的值可能小于过滤列表。如何获取 returned 值的位置并有条件地更改这些元素?
这是一个简单的例子,
>>> v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
>>> t = ['a', 'k']
如何修改 t
中 v
的元素以映射到新的新值,例如 '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)
...
>>>
您可以使用 map
将 v
中的值映射到固定值 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
:
很容易 "freeze" 固定参数
>>> 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']
应该这样做:
>>> 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']
>>>
如果您尝试更改 t 中的任何匹配值 v,最有效的方法是使用 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[:]
会修改原来的object/listv
.
如果您要使用 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"
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
,其中 returns 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 数组。
正如其他人所指出的,您可以使用列表理解和成员测试进行简单的替换。但是,你也可以使用字典,这可能会更简单,而且在我看来更清晰:
>>> 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)
获取keyy
对应的字典的值,如果没有这样的key则returnsx
。我的代码所做的是遍历列表中的每个元素。如果该元素在 tdict
中,它会用 tdict
中的相应值替换该值。如果不是,它会用自己替换该值(也就是说,它什么都不做)。
这并不比成员测试示例简单多少。但是,如果您需要具有多个目标和多个替换的更复杂的替换,dict 方法将变得 much cleaner:
>>> 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']
我看到列表理解如何擅长 returning 列表的子集,甚至在没有设置条件的情况下进行全面重新映射。但是,过滤条件将 return 列表中的值可能小于过滤列表。如何获取 returned 值的位置并有条件地更改这些元素?
这是一个简单的例子,
>>> v = ['a' , 'b' , 'c' , 'd' , 'f' , 'k' , 'g' , 'a', 'd']
>>> t = ['a', 'k']
如何修改 t
中 v
的元素以映射到新的新值,例如 '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)
...
>>>
您可以使用 map
将 v
中的值映射到固定值 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']
应该这样做:
>>> 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']
>>>
如果您尝试更改 t 中的任何匹配值 v,最有效的方法是使用 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[:]
会修改原来的object/listv
.
如果您要使用 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"
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
,其中 returns 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 数组。
正如其他人所指出的,您可以使用列表理解和成员测试进行简单的替换。但是,你也可以使用字典,这可能会更简单,而且在我看来更清晰:
>>> 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)
获取keyy
对应的字典的值,如果没有这样的key则returnsx
。我的代码所做的是遍历列表中的每个元素。如果该元素在 tdict
中,它会用 tdict
中的相应值替换该值。如果不是,它会用自己替换该值(也就是说,它什么都不做)。
这并不比成员测试示例简单多少。但是,如果您需要具有多个目标和多个替换的更复杂的替换,dict 方法将变得 much cleaner:
>>> 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']