Python列表减法运算
我想做类似的事情:Python列表减法运算,python,list,Python,List,我想做类似的事情: >>> x = [1,2,3,4,5,6,7,8,9,0] >>> x [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] >>> y = [1,3,5,7,9] >>> y [1, 3, 5, 7, 9] >>> y - x # (should return [2,4,6,8,0]) 但是python列表不支持这一点 最好的方法是什么?使用 或者
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])
但是python列表不支持这一点
最好的方法是什么?使用
或者,您可以只设置x和y,这样就不必进行任何转换。这是一种“集减法”操作。为此使用设置的数据结构
在Python 2.7中:
x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y
输出:
>>> print x - y
set([0, 8, 2, 4, 6])
使用列表理解:
[item for item in x if item not in y]
如果要使用-
中缀语法,只需执行以下操作:
class MyList(list):
def __init__(self, *args):
super(MyList, self).__init__(args)
def __sub__(self, other):
return self.__class__(*[item for item in self if item not in other])
然后,您可以像这样使用它:
x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y
但是,如果您不一定需要列表属性(例如,排序),只需按照其他答案的建议使用集合。试试这个
def subtract_lists(a, b):
""" Subtracts two lists. Throws ValueError if b contains items not in a """
# Terminate if b is empty, otherwise remove b[0] from a and recurse
return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:])
for i in [a.index(b[0])]][0]
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0]
>>> x = [1,2,3,4,5,6,7,8,9,0,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0, 9] #9 is only deleted once
>>>
如果存在重复和订购项目的问题:
[i代表a中的i,如果不是b中的i或b中的i.remove(i)]
对于许多用例,您想要的答案是:
ys = set(y)
[item for item in x if item not in ys]
这是和之间的混合
aaronasterling的版本对x
中的每个元素进行len(y)
项比较,因此需要二次时间。quantumSoup的版本使用集合,因此它对x
中的每个元素执行单一的固定时间集合查找-但是,由于它将x
和y
转换为集合,因此它会丢失元素的顺序
通过仅将y
转换为一个集合,并按顺序迭代x
,您可以获得最佳的线性时间和顺序保持*
但是,quantumSoup的版本仍然存在一个问题:它要求元素是可散列的。这几乎是集合的本质。**如果你试图,例如,从另一个dict列表中减去一个dict列表,但是要减去的列表很大,你会怎么做 如果你可以用某种方式来修饰你的值,使它们可以散列,那么问题就解决了。例如,对于值本身可散列的平面字典:
ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]
如果您的类型稍微复杂一些(例如,您经常处理JSON兼容的值,这些值是可散列的,或者列表或dict的值是递归的相同类型),您仍然可以使用此解决方案。但是有些类型不能转换成任何可散列的内容
如果您的项目不可散列,也无法进行散列,但它们是可比较的,那么您至少可以通过排序和使用对分法,获得对数线性时间(
O(N*logm)
,这比列表解决方案的O(N*M)
时间好得多,但不如设置解决方案的O(N+M)
时间好:
ys = sorted(y)
def bisect_contains(seq, item):
index = bisect.bisect(seq, item)
return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]
ys=已排序(y)
def对分包含(序号,项目):
索引=二等分。二等分(序号,项目)
返回索引
如果您的项既不可散列也不可比较,那么您就只能使用二次解
*请注意,您也可以通过使用一对
OrderedSet
对象来实现这一点,您可以为这些对象找到配方和第三方模块。但我认为这更简单
**设置查找是固定时间的原因是,它所要做的就是散列值,并查看是否有该散列的条目。如果无法对值进行散列,则无法执行此操作。在集合中查找值比在列表中查找值快:
[item for item in x if item not in set(y)]
我相信这将比以下几点更好:
[item for item in x if item not in y]
两者都保留列表的顺序。此示例减去两个列表:
# List of pairs of points
list = []
list.append([(602, 336), (624, 365)])
list.append([(635, 336), (654, 365)])
list.append([(642, 342), (648, 358)])
list.append([(644, 344), (646, 356)])
list.append([(653, 337), (671, 365)])
list.append([(728, 13), (739, 32)])
list.append([(756, 59), (767, 79)])
itens_to_remove = []
itens_to_remove.append([(642, 342), (648, 358)])
itens_to_remove.append([(644, 344), (646, 356)])
print("Initial List Size: ", len(list))
for a in itens_to_remove:
for b in list:
if a == b :
list.remove(b)
print("Final List Size: ", len(list))
@aaronasterling提供的答案看起来不错,但是它与list的默认界面不兼容:
x=MyList(1,2,3,4)
vsx=MyList([1,2,3,4])
。因此,以下代码可以用作更友好的python列表:
class MyList(list):
def __init__(self, *args):
super(MyList, self).__init__(*args)
def __sub__(self, other):
return self.__class__([item for item in self if item not in other])
例如:
x = MyList([1, 2, 3, 4])
y = MyList([2, 5, 2])
z = x - y
如果列表允许重复元素,则可以使用集合中的计数器:
from collections import Counter
result = list((Counter(x)-Counter(y)).elements())
如果需要保留元素从x开始的顺序:
result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]
我认为实现这一点最简单的方法是使用set()
其他解决方案有以下几个问题之一:
x=[1,2,2]
和y=[2,2]
它们将y
转换为集
,或者删除所有匹配元素(只保留[1]
),或者删除每个唯一元素中的一个(保留[1,2]
),正确的行为是删除2
两次,留下[1,2]
,或O(m*n)
工作,而最优解可以O(m+n)
工作值列表中n
重复的每个值的第一个n
副本)是:
要使其删除每个元素的最后副本,只需将for
循环更改为for val in reversed(x):
并在退出for
循环后立即添加out.reverse()
就y
长度而言,构造计数器是O(n)
,就x
长度而言,迭代x
是O(n)
,就x
长度而言,计数器成员测试和变异是O(1)
,而列表.append
是摊销O(1)
(一个给定的append
可以是O(n)
,但是对于许多append
s,总的big-O平均值O(1)
,因为它们中越来越少需要重新分配),所以所做的总功是O(m+n)
您还可以通过测试来确定y
中是否有未从x
中删除的元素:
remaining = +remaining # Removes all keys with zero counts from Counter
if remaining:
# remaining contained elements with non-zero counts
这将丢失任何顺序。这可能会也可能不重要,取决于上下文。这还将丢失任何可能需要/想要维护的重复项。我得到TypeError:unhable type:“dict”
如果要比较的列表顺序较大,并且列表中的项目重复,则这会更快
result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> list(set(x)- set(y))
[0, 2, 4, 6, 8]
from collections import Counter
x = [1,2,3,4,3,2,1]
y = [1,2,2]
remaining = Counter(y)
out = []
for val in x:
if remaining[val]:
remaining[val] -= 1
else:
out.append(val)
# out is now [3, 4, 3, 1], having removed the first 1 and both 2s.
remaining = +remaining # Removes all keys with zero counts from Counter
if remaining:
# remaining contained elements with non-zero counts