我可以对 python 中的列表执行减法或加入操作,包括重复条目
Can I perform minus or join operations on lists in python including duplicate entries
我有两个或更多大列表(每个列表包含 20 到 25 GB 数据)。我想执行减号并加入 operations.For 示例我想找出列表 1 中存在但列表 2 中不存在的项目以下列表。
list1=[1,2,1,3,4,4,5,6,2,8]
list2=[3,5,3,8,1,9,9]
结果应该是:
result_list1minuslist2 =[1,2,2,4,4,6]
result_list2minuslist1 =[3,9,9]
加入操作:
result_list1joinlist2 =[1,1,3,5,8]
result_list2joinlist1 =[3,5,3,8,1]
对于列表减法,您可以尝试使用包含列表的字典对源列表中的值进行分组,并提供快速查找操作。假设列表中的项目是可哈希的,因此可以用作字典键。
这可能是合理的内存效率,因为对象引用应该在数据结构中使用,所以原始列表中的数据重复应该 最小化。但是,如果原始列表包含许多小对象,那么在构建数据结构的开销中仍然会消耗大量内存。取决于你的数据。
我建议使用 defaultdict
列表,因为很容易将原始列表中的值分组,但您也可以使用标准字典。
因此,将要减去的列表转换为 defaultdict
个列表。原始列表中的每一项都是此字典中的一个键,对应的值是包含相同键的列表,原始列表中每个条目一个条目。
然后遍历第二个列表,从字典的值中删除条目(如果存在)。这个位应该比直接在列表上操作更快,因为在字典上的 in
操作平均是 O(1),而在列表上的 in
操作是 O(N)。
from collections import defaultdict
def list_sub(list1, list2):
'''Subtract list2 from list1'''
dd = defaultdict(list)
for i in list1:
dd[i].append(i)
# now remove items in list2 from the defaultdict
for i in list2:
if dd[i]:
dd[i].pop()
return (x for v in dd.itervalues() for x in v)
list1=[1,2,1,3,4,4,5,6,2,8]
list2=[3,5,3,8,1,9,9]
>>> list_sub(list1, list2)
[1, 2, 2, 4, 4, 6]
>>> list_sub(list2, list1)
[3, 9, 9]
备选方案
使用一个 defaultdict
的整数作为计数器:
from collections import defaultdict
def list_sub_ddi(list1, list2):
dd = defaultdict(int)
for i in list1:
dd[i] += 1
for i in list2:
dd[i] -= 1
return (x for l in ([k]*n for k,n in dd.iteritems() if n>0) for x in l)
from collections import Counter
def list_sub_counter(list1, list2):
c = Counter(list1) - Counter(list2)
return (x for l in ([k]*n for k,n in c.iteritems() if n>0) for x in l)
执行次数
使用timeit
模块:
# test.py
from random import randint
from collections import defaultdict
from collections import Counter
list1 = [randint(1, 10000) for i in range(1000000)]
list2 = [randint(1, 5000) for i in range(10000)]
def list_sub_ddl(list1, list2):
dd = defaultdict(list)
for i in list1:
dd[i].append(i)
for i in list2:
if dd[i]:
dd[i].pop()
return (x for v in dd.itervalues() for x in v)
def list_sub_ddi(list1, list2):
dd = defaultdict(int)
for i in list1:
dd[i] += 1
for i in list2:
dd[i] -= 1
return (x for l in ([k]*n for k,n in dd.iteritems() if n>0) for x in l)
def list_sub_counter(list1, list2):
c = Counter(list1) - Counter(list2)
return (x for l in ([k]*n for k,n in c.iteritems() if n>0) for x in l)
请注意,每个函数 return 都是一个生成器,它最大限度地减少了预先完成的工作量,并允许调用代码迭代值或根据需要转换为列表。如果愿意,每个函数都可以 return 一个完全实现的列表。下面的测试一次性消耗了生成器中的所有项目。
Python 2
$ python -m timeit -s 'import test' 'list(test.list_sub_ddl(test.list1, test.list2))'
10 loops, best of 3: 362 msec per loop
$ python -m timeit -s 'import test' 'list(test.list_sub_ddi(test.list1, test.list2))'
10 loops, best of 3: 223 msec per loop
$ python -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 476 msec per loop
Python 3
代码与Python2相同,但itervalues()
和iteritems()
改为values()
和items()
。
$ python3 -m timeit -s 'import test' 'list(test.list_sub_ddl(test.list1, test.list2))'
10 loops, best of 3: 386 msec per loop
$ python3 -m timeit -s 'import test' 'list(test.list_sub_ddi(test.list1, test.list2))'
10 loops, best of 3: 267 msec per loop
$ python3 -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 214 msec per loop
结果
如果您使用 Python 2,请使用 defaultdict
整数。对于 Python 3 使用 Counter
.
您的里程会有所不同,具体取决于使用的实际数据。此测试数据远小于 20GB,包含小对象的长列表可能与包含较大对象的较短列表的行为不同。
此测试还忽略了每种方法在内存使用方面的差异,因为我不知道有什么简单的方法可以测量它,而且我的测试数据可能不具有代表性。不过,列表的 defaultdict 可能会消耗更多。
这里有一些有趣的 pythonic 版本,用于减号:
from heapq import heapify, heappop
def sort_gen(l, StopException=StopIteration):
heapify(l)
for i in xrange(len(l)):
yield heappop(l)
raise StopException
class YStopIteration(StopIteration):
pass
def diff_gen(l1, l2):
xgen = sort_gen(l1)
ygen = sort_gen(l2, YStopIteration)
try:
x = next(xgen)
y = next(ygen)
while True:
if x < y:
yield x
x = next(xgen)
elif x == y:
x = next(xgen)
y = next(ygen)
else:
y = next(ygen)
except YStopIteration:
# 2nd generator exhausted: yield all the rest
yield x
for x in xgen:
yield x
list1=[1,2,1,3,4,4,5,6,2,8]
list2=[3,5,3,8,1,9,9]
dg = diff_gen(list1, list2)
d = list(dg) # [1, 2, 2, 4, 4, 6]
在 python 中,多重集以 counter 的形式提供,因为您有多个相同的可哈希且因此不可变对象的实例,您应该考虑使用它。
这里是减号和加入列表
def list_sub(list1, list2):
result = Counter(list1)
result.subtract(list2)
return result.elements()
def list_join(list1,list2):
test = set(list2)
return filter(lambda x: x in test,list1)
#return (x for x in list1 if x in test)
两者都是 return 结果的迭代器,在 python 中具有相同的效果 2 使用 itertools.ifilter 或生成器表达式
>>> list(list_sub(list1,list2))
[1, 2, 2, 4, 4, 6]
>>> list(list_sub(list2,list1))
[3, 9, 9]
>>> list(list_join(list1,list2))
[1, 1, 3, 5, 8]
>>> list(list_join(list2,list1))
[3, 5, 3, 8, 1]
>>>
现在将此版本与@mhawke 版本进行比较(使用与 mhawke 相同的脚本)
在python3
>python3 -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 274 msec per loop
>python3 -m timeit -s 'import test' 'list(test.list_sub(test.list1, test.list2))'
10 loops, best of 3: 199 msec per loop
在python2
>python2 -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 627 msec per loop
>python2 -m timeit -s 'import test' 'list(test.list_sub(test.list1, test.list2))'
10 loops, best of 3: 558 msec per loop
在这两种情况下,这个版本都更好,除了我在 defaultdict 和 Counter 中得到相同的结果,所以使用 [= python2 中的 14=] 或 python3
中的 Counter
我有两个或更多大列表(每个列表包含 20 到 25 GB 数据)。我想执行减号并加入 operations.For 示例我想找出列表 1 中存在但列表 2 中不存在的项目以下列表。
list1=[1,2,1,3,4,4,5,6,2,8]
list2=[3,5,3,8,1,9,9]
结果应该是:
result_list1minuslist2 =[1,2,2,4,4,6]
result_list2minuslist1 =[3,9,9]
加入操作:
result_list1joinlist2 =[1,1,3,5,8]
result_list2joinlist1 =[3,5,3,8,1]
对于列表减法,您可以尝试使用包含列表的字典对源列表中的值进行分组,并提供快速查找操作。假设列表中的项目是可哈希的,因此可以用作字典键。
这可能是合理的内存效率,因为对象引用应该在数据结构中使用,所以原始列表中的数据重复应该 最小化。但是,如果原始列表包含许多小对象,那么在构建数据结构的开销中仍然会消耗大量内存。取决于你的数据。
我建议使用 defaultdict
列表,因为很容易将原始列表中的值分组,但您也可以使用标准字典。
因此,将要减去的列表转换为 defaultdict
个列表。原始列表中的每一项都是此字典中的一个键,对应的值是包含相同键的列表,原始列表中每个条目一个条目。
然后遍历第二个列表,从字典的值中删除条目(如果存在)。这个位应该比直接在列表上操作更快,因为在字典上的 in
操作平均是 O(1),而在列表上的 in
操作是 O(N)。
from collections import defaultdict
def list_sub(list1, list2):
'''Subtract list2 from list1'''
dd = defaultdict(list)
for i in list1:
dd[i].append(i)
# now remove items in list2 from the defaultdict
for i in list2:
if dd[i]:
dd[i].pop()
return (x for v in dd.itervalues() for x in v)
list1=[1,2,1,3,4,4,5,6,2,8]
list2=[3,5,3,8,1,9,9]
>>> list_sub(list1, list2)
[1, 2, 2, 4, 4, 6]
>>> list_sub(list2, list1)
[3, 9, 9]
备选方案
使用一个 defaultdict
的整数作为计数器:
from collections import defaultdict
def list_sub_ddi(list1, list2):
dd = defaultdict(int)
for i in list1:
dd[i] += 1
for i in list2:
dd[i] -= 1
return (x for l in ([k]*n for k,n in dd.iteritems() if n>0) for x in l)
from collections import Counter
def list_sub_counter(list1, list2):
c = Counter(list1) - Counter(list2)
return (x for l in ([k]*n for k,n in c.iteritems() if n>0) for x in l)
执行次数
使用timeit
模块:
# test.py
from random import randint
from collections import defaultdict
from collections import Counter
list1 = [randint(1, 10000) for i in range(1000000)]
list2 = [randint(1, 5000) for i in range(10000)]
def list_sub_ddl(list1, list2):
dd = defaultdict(list)
for i in list1:
dd[i].append(i)
for i in list2:
if dd[i]:
dd[i].pop()
return (x for v in dd.itervalues() for x in v)
def list_sub_ddi(list1, list2):
dd = defaultdict(int)
for i in list1:
dd[i] += 1
for i in list2:
dd[i] -= 1
return (x for l in ([k]*n for k,n in dd.iteritems() if n>0) for x in l)
def list_sub_counter(list1, list2):
c = Counter(list1) - Counter(list2)
return (x for l in ([k]*n for k,n in c.iteritems() if n>0) for x in l)
请注意,每个函数 return 都是一个生成器,它最大限度地减少了预先完成的工作量,并允许调用代码迭代值或根据需要转换为列表。如果愿意,每个函数都可以 return 一个完全实现的列表。下面的测试一次性消耗了生成器中的所有项目。
Python 2
$ python -m timeit -s 'import test' 'list(test.list_sub_ddl(test.list1, test.list2))'
10 loops, best of 3: 362 msec per loop
$ python -m timeit -s 'import test' 'list(test.list_sub_ddi(test.list1, test.list2))'
10 loops, best of 3: 223 msec per loop
$ python -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 476 msec per loop
Python 3
代码与Python2相同,但itervalues()
和iteritems()
改为values()
和items()
。
$ python3 -m timeit -s 'import test' 'list(test.list_sub_ddl(test.list1, test.list2))'
10 loops, best of 3: 386 msec per loop
$ python3 -m timeit -s 'import test' 'list(test.list_sub_ddi(test.list1, test.list2))'
10 loops, best of 3: 267 msec per loop
$ python3 -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 214 msec per loop
结果
如果您使用 Python 2,请使用 defaultdict
整数。对于 Python 3 使用 Counter
.
您的里程会有所不同,具体取决于使用的实际数据。此测试数据远小于 20GB,包含小对象的长列表可能与包含较大对象的较短列表的行为不同。
此测试还忽略了每种方法在内存使用方面的差异,因为我不知道有什么简单的方法可以测量它,而且我的测试数据可能不具有代表性。不过,列表的 defaultdict 可能会消耗更多。
这里有一些有趣的 pythonic 版本,用于减号:
from heapq import heapify, heappop
def sort_gen(l, StopException=StopIteration):
heapify(l)
for i in xrange(len(l)):
yield heappop(l)
raise StopException
class YStopIteration(StopIteration):
pass
def diff_gen(l1, l2):
xgen = sort_gen(l1)
ygen = sort_gen(l2, YStopIteration)
try:
x = next(xgen)
y = next(ygen)
while True:
if x < y:
yield x
x = next(xgen)
elif x == y:
x = next(xgen)
y = next(ygen)
else:
y = next(ygen)
except YStopIteration:
# 2nd generator exhausted: yield all the rest
yield x
for x in xgen:
yield x
list1=[1,2,1,3,4,4,5,6,2,8]
list2=[3,5,3,8,1,9,9]
dg = diff_gen(list1, list2)
d = list(dg) # [1, 2, 2, 4, 4, 6]
在 python 中,多重集以 counter 的形式提供,因为您有多个相同的可哈希且因此不可变对象的实例,您应该考虑使用它。
这里是减号和加入列表
def list_sub(list1, list2):
result = Counter(list1)
result.subtract(list2)
return result.elements()
def list_join(list1,list2):
test = set(list2)
return filter(lambda x: x in test,list1)
#return (x for x in list1 if x in test)
两者都是 return 结果的迭代器,在 python 中具有相同的效果 2 使用 itertools.ifilter 或生成器表达式
>>> list(list_sub(list1,list2))
[1, 2, 2, 4, 4, 6]
>>> list(list_sub(list2,list1))
[3, 9, 9]
>>> list(list_join(list1,list2))
[1, 1, 3, 5, 8]
>>> list(list_join(list2,list1))
[3, 5, 3, 8, 1]
>>>
现在将此版本与@mhawke 版本进行比较(使用与 mhawke 相同的脚本)
在python3
>python3 -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 274 msec per loop
>python3 -m timeit -s 'import test' 'list(test.list_sub(test.list1, test.list2))'
10 loops, best of 3: 199 msec per loop
在python2
>python2 -m timeit -s 'import test' 'list(test.list_sub_counter(test.list1, test.list2))'
10 loops, best of 3: 627 msec per loop
>python2 -m timeit -s 'import test' 'list(test.list_sub(test.list1, test.list2))'
10 loops, best of 3: 558 msec per loop
在这两种情况下,这个版本都更好,除了我在 defaultdict 和 Counter 中得到相同的结果,所以使用 [= python2 中的 14=] 或 python3
中的Counter