【问题标题】:do list subtraction in python [duplicate]在python中做列表减法[重复]
【发布时间】:2012-09-26 13:06:26
【问题描述】:

可能重复:
Python list subtraction operation

在 Python 中,您可以像这样连接列表:

print([3,4,5]+[4,5])

给出这个输出:

[3,4,5,4,5]

但我正在寻找的是一个等效的“减法”操作,所以做这样的事情:

print([3,4,5]-[4,5])

会输出这个:

[3]

但是,没有为列表定义减法运算符。我试过这个:

a = [3,4,5]
b = [4,5]
print(list(filter(lambda x : x not in b,a)))

哪个有效,但我不确定这是否是最好的方法。我也想保留原来的物品位置

【问题讨论】:

  • 把列表变成集合然后做减法?用列表做减法不是很明确,特别是如果你有重复。
  • a = [3,4,5]b = [5,4] 的情况应该发生什么,因为您希望订单很重要...
  • 添加这样的列表称为“连接”。减法不是相反的过程。
  • 如果第一个列表包含重复项会怎样?如果a = [3, 4, 4, 5]b = [4, 5],是否只有一个 4 被删除?答案是[3, 4] 还是[3]
  • 我也不清楚 OP 想要什么。 [3, 4, 5]-[4] 应该产生 [3, 5] 还是不是一个有效的操作(如,这是否只适用于尾部 - 正如我的回答所假设的那样)?

标签: python python-3.x


【解决方案1】:

您可以通过列表推导轻松做到这一点:

nl = [elem for elem in a if elem not in b]

编辑

最好使用set 进行测试。这将从您的列表中删除重复项。

bb= set(b)
nl = [elem for elem in a if elem not in bb]

【讨论】:

  • 如果ab是大列表,使用前最好先转换b设置
  • 是的,这是 n^2,最好使用一组
  • @larsvegas -- 我不肯定,但我很确定(Cpython)解释器不够聪明,无法知道避免为 a 中的每个元素创建一个集合。最好把bb = set(b)放在单独一行,然后测试是否elem in bb
  • @mgilson 似乎不是——或者至少,它为我的测试中的每个项目运行打印语句。 [_ for _ in [1, 2, 3] if not print("Run")]
  • @mgilson,还是编辑了我的答案。
【解决方案2】:

这是一个定义不明确的问题。我可以想到列表“减法”的几个非等效定义,其中两个已经被表示:截断(通过切片)——真正的串联逆;和过滤,类似于集合的“减法”(真正的相对互补)的定义。对于过滤,使用a 上的列表理解并将b 转换为集合是最好的方法。 (即larsvegas的回答。)

但尚未考虑的一个版本是multiset 对减法的定义。 Python 的Counter 类型为我们提供了一个多重集:

>>> from collections import Counter
>>> a = [3, 4, 5]
>>> b = [4, 5]
>>> a_counter = Counter(a)
>>> b_counter = Counter(b)
>>> a_counter
Counter({3: 1, 4: 1, 5: 1})
>>> b_counter
Counter({4: 1, 5: 1})
>>> a_counter - b_counter
Counter({3: 1})

当然,这不会保留顺序,但我们可以通过根据 a_counter - b_counter 的结果过滤 a 来解决这个问题:

def subtract_lists(a, b):
    multiset_difference = Counter(a) - Counter(b)
    result = []
    for i in a:
        if i in multiset_difference:
            result.append(i)
            multiset_difference -= Counter((i,))
    return result

这有几个不错的属性。它保持秩序;它的作用是串联的真正逆;它在可以包含重复项的数据类型上实现了直观一致的减法版本;它在线性时间内工作。

>>> subtract_lists(a, b)
[3]
>>> subtract_lists([1, 2, 3, 4], [2, 3, 4])
[1]
>>> subtract_lists([1, 2, 3, 4], [2, 4])
[1, 3]
>>> subtract_lists([1, 2, 3, 4, 4, 4], [2, 4])
[1, 3, 4, 4]

【讨论】:

    【解决方案3】:
    a = [3,4,5]
    b = [4,5]
    
    list(set(a) -  set(b))
    [3]
    

    【讨论】:

    • 这不会按照 OP 的要求保留 a 中的值顺序。
    【解决方案4】:

    如果你的意思是从列表中删除最后一个元素的减法,那么使用列表切片是一个非常简单的操作:

    def list_subtraction(seq, remove):
        l = len(remove)
        if seq[-l:] == remove:
            return seq[:-l]
        else:
            raise ValueError("Subtraction not possible, "
                             "{} is not a tail of {}.".format(remove, seq))
    

    【讨论】:

      【解决方案5】:

      这当然是因为它只是追加,这就是为什么根本不会删除或影响重复项的原因。

      减法只是切掉结尾:

      a = [3, 4, 5]
      b = [4, 5]
      c = a + b
      
      d = c[:-len(b)]
      

      这将使d 等于a,即[3, 4, 5]

      【讨论】:

        【解决方案6】:

        给定:

        a = [3, 4, 5]
        b = [4, 5]
        

        然后根据您的需要,以下其中一项应该可以工作。

        # remove 'b' from the end of 'a' if it's there (strict de-concatenation)
        if a[-len(b):] == b:
            a = a[:-len(b)]
        
        # remove any elements from 'a' that are in `b` (including multiples)
        bset = set(b)
        a = [x for x in a if x not in bset]
        
        # faster version of above but doesn't preserve order
        a = list(set(a) - set(b))
        
        # remove elements from 'a' that are in 'b' (one leftmost item only)
        bset = set(b)
        a = [x for x in a if x not in bset or bset.remove(x)]
        
        # remove elements from 'a' that are in 'b' (one rightmost item only)
        bset = set(b)
        a = list(reversed([x for x in reversed(a) if x not in bset or bset.remove(x)]))
        

        【讨论】:

          【解决方案7】:

          如果您希望它从列表中的任何位置删除内容,并且只删除它们出现在第二个列表中的次数(这样sub([1, 2, 3, 3, 4, 4, 5], [3, 4, 5]) == [1, 2, 3, 4]),您需要稍微复杂一点,并从 (使用时的右侧列表的副本:

          def sub(l, r):
              '''
              Remove all elements in r from l
              '''
              r = r[:]
              res = []
              for a in l:
                  try:
                      i = r.index(a)
                  except ValueError:
                      res.append(a)
                  else:
                      del r[i]
              return res
          

          如果您希望 [1, 2, 3] - [4] 成为错误,您可以在循环后检查 r 是否为非空。

          【讨论】:

            猜你喜欢
            • 2014-08-11
            • 2012-01-01
            • 2014-12-14
            • 1970-01-01
            • 2013-03-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-26
            相关资源
            最近更新 更多