Python列表减法运算

我想要这样的东西:

>>> x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1, 3, 5, 7, 9]
>>> y - x
# should return [2,4,6,8,0]
587675 次浏览

使用设置不同

>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]

或者你可以让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 for i in a if not i in b or b.remove(i)]

a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]

对于许多用例,您想要的答案是:

ys = set(y)
[item for item in x if item not in ys]

这是aaronasterling的回答quantumSoup的回答的混合。

aaronasterling的版本对x中的每个元素进行len(y)项比较,因此需要二次时间。quantumSoup的版本使用集合,所以它对x中的每个元素执行一个常量时间集合查找,但是,因为它将这两个 xy转换为集合,所以它失去了元素的顺序。

通过只将y转换为一个集合,并按顺序迭代x,您可以获得两者的最佳结果——线性时间和顺序保存


然而,这仍然存在一个问题:它要求你的元素是可哈希的。这是集合的本质。**如果你试图,例如,从另一个字典列表中减去一个字典列表,但要减去的列表很大,你会怎么做?

如果你能以某种方式装饰你的值使它们是可哈希的,这就解决了问题。例如,对于一个值本身是可哈希的平面字典:

ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]

如果你的类型有点复杂(例如,你经常处理json兼容的值,它们是可哈希的,或者列表或字典,它们的值递归是相同的类型),你仍然可以使用这个解决方案。但是有些类型就是不能转换成任何可哈希的类型。


如果你的项目不是,也不能是可哈希的,但它们具有可比性,你至少可以通过排序和使用bisect获得对数线性时间(O(N*log M),这比列表解决方案的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)]

如果你的项目既不是可哈希的也不是可比较的,那么你就只能用二次解了。


*注意,你也可以通过使用一对OrderedSet对象来做到这一点,你可以为它们找到食谱和第三方模块。但我认为这样更简单。

**设置查找是常量时间的原因是,它所要做的就是散列值,并查看是否有该散列的条目。如果它不能散列值,这将不起作用。

在set中查找值比在list中查找值更快:

[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) vs x = 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

如果列表允许重复元素,你可以使用Counter from collections:

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,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> list(set(x)- set(y))
[0, 2, 4, 6, 8]

其他解决方案都存在以下几个问题之一:

  1. 它们不能维持秩序,或者
  2. 它们不删除精确的元素计数,例如,对于x = [1, 2, 2, 2]y = [2, 2],它们将y转换为set,并删除所有匹配的元素(只留下[1])或删除每个唯一元素中的一个(留下[1, 2, 2]),当正确的行为是删除2两次,留下[1, 2],或
  3. 它们做O(m * n)工作,其中最优解决方案可以做O(m + n)工作

Alain的Counter是正确的来解决#2和#3,但该解决方案将失去顺序。保持顺序的解决方案(删除每个值的n重复值的list的第一个n副本)是:

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.

试一下在线!< / >

要使它删除每个元素的最后的副本,只需将for循环更改为for val in reversed(x):,并在退出for循环后立即添加out.reverse()

根据y的长度构造CounterO(n),根据x的长度迭代xO(n),而Counter成员测试和突变是O(1),而list.appendO(1)的平方根(给定的O(n)0可以是O(n),但对于许多O(n)0,整体大o平均值是O(1),因为它们越来越少需要重新分配),因此完成的总体工作是O(n)4。

你也可以通过测试来确定y中是否有任何元素没有从x中删除:

remaining = +remaining  # Removes all keys with zero counts from Counter
if remaining:
# remaining contained elements with non-zero counts

我们也可以使用set方法来查找两个列表之间的差异

x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
y = [1, 3, 5, 7, 9]
list(set(x).difference(y))
[0, 2, 4, 6, 8]

如果值是唯一的,你也可以尝试这样做:

list(set(x) - set(y))
from collections import Counter


y = Counter(y)
x = Counter(x)


print(list(x-y))
list1 = ['a', 'c', 'a', 'b', 'k']
list2 = ['a', 'a', 'a', 'a', 'b', 'c', 'c', 'd', 'e', 'f']
for e in list1:
try:
list2.remove(e)
except ValueError:
print(f'{e} not in list')
list2
# ['a', 'a', 'c', 'd', 'e', 'f']

这将改变list2。如果你想保护list2,只需复制它,并在这段代码中使用list2的副本。

def listsubtraction(parent,child):
answer=[]
for element in parent:
if element not in child:
answer.append(element)
return answer

我认为这应该可行。我是初学者,所以请原谅我的错误