我有一个列表列表 A
和一个列表列表 B
其中 A
和 B
有很多相同的子列表。
将唯一子列表从 B
中取出并放入 A
的最佳方法是什么?
A = [['foo', 123], ['bar', np.array(range(10))], ['baz', 345]]
B = [['foo', 123], ['bar', np.array(range(10))], ['meow', 456]]
=> A = [['foo', 123], ['bar', np.array(range(10))], ['baz', 345], ['meow', 456]]
我试过:
A += [b for b in B if b not in A]
但这给了我一个 ValueError
提示使用 any()
或 all()
。我真的必须逐个元素地测试 A
中每个子列表的 B
中的每个子列表吗?
ERROR: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
最佳答案
通常,您会使用多种方法中的一种来按顺序或不按顺序唯一化一个列表或多个列表。
这里有一个唯一化两个不保持顺序的列表的方法:
>>> A=[1,3,5,'a','c',7]
>>> B=[1,2,3,'c','b','a',6]
>>> set(A+B)
set(['a', 1, 'c', 3, 5, 6, 7, 2, 'b'])
这是一种维持秩序的方法:
>>> seen=set()
>>> [e for e in A+B if e not in seen and (seen.add(e) or True)]
[1, 3, 5, 'a', 'c', 7, 2, 'b', 6]
问题是所有元素都必须是可散列的才能使用这些方法:
>>> set([np.array(range(10)), 22])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'numpy.ndarray'
解决这个问题的一种方法是使用每个元素的 repr:
>>> set([repr(e) for e in [np.array(range(10)), 22]])
set(['22', 'array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])'])
或者使用 frozenset :
>>> set(frozenset(e) for e in [np.array(range(10)), np.array(range(2))])
set([frozenset([0, 1]), frozenset([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])])
在您的情况下,frozenset 方法不适用于列表列表:
>>> set(frozenset(e) for e in [[np.array(range(10)), np.array(range(2))],[np.array(range(5))
]])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
TypeError: unhashable type: 'numpy.ndarray'
因此您需要使用扁平化列表。
如果子列表的 repr 是其唯一性的明确证据,您可以这样做:
from collections import OrderedDict
import numpy as np
A = [['foo', 123], ['bar', np.array(range(10))], ['baz', 345]]
B = [['foo', 123], ['bar', np.array(range(10))], ['meow', 456]]
seen=OrderedDict((repr(e),0) for e in B)
newA=[]
for e in A+B:
key=repr(e)
if key in seen:
if seen[key]==0:
newA.append(e)
seen[key]=1
else:
seen[key]=1
newA.append(e)
print newA
# [['foo', 123], ['bar', array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])], ['baz', 345], ['meow', 456]]
由于 repr
函数返回一个字符串,eval
函数可以使用该字符串重新创建列表,这是非常确定的测试,但我不能说绝对肯定。这取决于您的列表中的内容。
例如,lambda 的 repr 不能重新创建 lambda:
>>> repr(lambda x:x)
'<function <lambda> at 0x10710ec08>'
但是 '<function <lambda> at 0x10710ec08>'
的字符串值仍然是绝对唯一的,因为 0x10710ec08
部分是 lambda 内存中的地址(无论如何在 cPython 中)。
您也可以执行我上面提到的操作——在 frozenset 中使用扁平化列表作为您所见或未见事物的签名:
def flatten(LoL):
for el in LoL:
if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
for sub in flatten(el):
yield sub
else:
yield el
newA=[]
seen=set()
for e in A+B:
fset=frozenset(flatten(e))
if fset not in seen:
newA.append(e)
seen.add(fset)
print newA
# [['foo', 123], ['bar', array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])], ['baz', 345], ['meow', 456]]
因此,如果您在 A 和 B 中有既不可散列又怪异、非唯一的 repr
字符串对象的奇怪对象——您就不走运了。根据您的示例,其中一种方法应该可行。
关于Python:两个列表列表的交集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21920598/