【问题标题】:python array intersection efficientlypython数组交叉有效
【发布时间】:2013-08-24 01:43:18
【问题描述】:

我不知道如何使这两个数组相交:

a = [[125, 1], [193, 1], [288, 23]]
b = [[108, 1], [288, 1], [193, 11]]

result = [[288,24], [193, 12]]

所以交集是第一个元素,数组的第二个元素相加,有什么想法可以有效地做到这一点吗?

好吧,我犯了一个错误,没有解释我所说的高效,对不起。考虑以下简单的实现:

a = [[125, 1], [193, 1], [288, 23]]
b = [[108, 1], [288, 1], [193, 11]]
result = {}
for i, j in a:
    for k, l in b:
        if i == k:
            result[i] = j + l
print result

所以我试图找到一种方法来更有效地解决我的问题,在某种程度上更pythonic。所以这就是为什么我需要你的帮助。

试试这个测试用例(我的代码也在上面):

Test Case

运行时间:28.6980509758

【问题讨论】:

  • 如果a中有两个匹配的元素应该怎么做?它们也应该相加吗?例如,a = [[100, 1], [100, 2]]b = [[50, 1]]
  • 我不知道我会将这些数据保留为列表列表。也许字典或更好的计数器会更有意义。
  • 如果您认真使用“高效”这个词,您尝试过哪些替代方案,为什么它们的性能不足?另外,非常重要的是,数据集的大小是多少?
  • @badc0re 关于使用表达性(并且易于编写、测试和理解)的语言特性与效率(也非常重要)似乎存在争议。在这种特殊情况下,你为什么不帮助我们解决这个问题?您能否对不同的答案进行测试并计时并发布?
  • @badc0re 您的期望是什么?为什么29秒不好?这个非常重要。大多数有经验的开发人员会逐步提高性能(并降低可读性),直到性能可以接受并且一点也不。他们重视自己的时间以及可能遇到他们代码的其他开发人员的时间。

标签: python


【解决方案1】:

这些数据似乎最好存储为字典

da = dict(a)
db = dict(b)

一旦你拥有它,你就可以:

result = [[k, da[k] + db[k]] for k in set(da.keys()).intersection(db.keys())]

或者在 python 2.7 中你也可以只使用 viewkeys 而不是一个集合

result = [[k, da[k] + db[k]] for k in da.viewkeys() & db]

【讨论】:

  • 我没有投反对票,您的解决方案迄今为止的最佳性能以 0.0124080181122 计算。
  • 不错! pythonic和高效
【解决方案2】:
result = []
ms, mb = (dict(a),dict(b)) if len(a)<len(b) else (dict(b),dict(a))
for k in ms:
  if k in mb:
    result.append([k,ms[k]+mb[k]])

【讨论】:

  • 对不起,我意识到我错过了最后一条语句中的 []。
  • 还添加了一些不错的优化,以防您的数据非常小,只有我的 2 美分
  • 在我的笔记本电脑上 0.0067 秒的速度非常快,结果验证与原始结果一致。我认为它也很好读。干得好!
【解决方案3】:

使用计数器:

c_sum = Counter()
c_len = Counter()
for elt in a:
    c_sum[elt[0]] += elt[1]
    c_len[elt[0]] += 1

for elt in b:
    c_sum[elt[0]] += elt[1]
    c_len[elt[0]] += 1

print [[k, c_sum[k]] for k, v in c_len.iteritems() if v > 1]

【讨论】:

    【解决方案4】:

    给你

    a = [[125, 1], [193, 1], [288, 23]]
    b = [[108, 1], [288, 1], [193, 11]]
    for e in a:
        for e2 in b:
            if e[0] == e2[0]:
                inter.append([e[0], e[1]+e2[1]])
    print inter
    

    输出

    [[193, 12], [288, 24]]
    

    【讨论】:

    • 也许您错过了问题中的“高效”一词?我认为 O(n^2) 算法不合格。
    • 我认为 OP 使用“有效”一词只是出于习惯。或者无知。如果他是认真的,他会指定列表的大小、一般的时间限制、他尝试过的其他时间信息解决方案,甚至他为什么使用 Python 而不是其他语言。
    • 很可能 OP 不太关心解决方案的时间复杂度。因为他们没有表现出任何改进基本解决方案的尝试,而只是针对他们的问题提出一般性问题。
    【解决方案5】:

    如果您还希望计算列表中的重复项,则此解决方案有效。

    from collections import defaultdict
    
    a = [[125, 1], [193, 1], [288, 23]]
    b = [[108, 1], [288, 1], [193, 11]]
    
    d = defaultdict(int)
    
    for value, num in a+b:
        d[value] += num
    result = filter(lambda x:x[1]>1, d.items())
    result = map(list, result)  # If it's important that the result be a list of lists rather than a list of tuples
    print result
    # [[288, 24], [193, 12]]
    

    【讨论】:

      【解决方案6】:

      首先,Python 没有数组。它有列表。只是名字的问题,但它可能会令人困惑。单线是:

      [ [ae[0],ae[1]+be[1]] for be in b for ae in a if be[0] == ae[0] ]
      

      PS:正如你所说的“交叉点”,我假设列表是集合式的(实际上是“袋子”),并且作为袋子,它们被适当地标准化(即它们没有重复的元素/键) .

      【讨论】:

      • 一个班轮被高估了,这特别低效
      • 当您了解它们时,它们就有了重要的地位。如果你如此担心性能,你在用 Python 开发什么?与其他语言相比,它的主要优势是表现力,而不是性能。请不要到处诋毁语言的重要部分并阻止人们学习它。让他们自己得出结论。在这种特殊情况下,我认为性能差异不大。你给他们计时了吗?
      • 这无法回答 OP 提出的问题。这效率不高。提出反对语言的论点并不能减轻您的解决方案是一种低效的算法。
      • 效率与问题有关。如果对你来说“富有表现力”和“不如其他语言高效”是反对 Python 的论据,那么你是对的。对我来说,第一个支持 Python,第二个只是陈述事实。
      • 我同意,对于小型数据集,您的解决方案可以正常工作并且具有表现力。然而,OP 多次写道,需要一种有效的算法。让我相信这些列表可能非常大。您的算法的大 O 表示法是 n*m。可以做得更好,但仍然保持表现力。我也不同意仅仅因为有人在 python 中编码他们不关心性能的说法。
      【解决方案7】:

      假设 a 和 b 具有唯一性,我将采用以下方法:

      k = {} # store totals
      its = {} # store intersections
      for i in a + b:
          if i[0] in k:
              its[i[0]] = True
              k[i[0]] += i[1]
          else:
              k[i[0]] = i[1]
      # then loop through intersections for results
      result = [[i, k[i]] for i in its]
      

      【讨论】:

      • 表现也很棒(0.0174601078033),但我没有得到 its[i[0]] = True 部分。
      • 当它第二次点击key/id时,它意味着它相交,然后我们存储它而不是做另一个循环来找出它是否是一个相交。
      【解决方案8】:

      我明白了:

      from collections import defaultdict
      d = defaultdict(list)
      for series in a, b:
          for key, value in series:
              d[key].append(value)
      result2 = [(key, sum(values)) for key, values in d.iteritems() if len(values) > 1]
      

      在 O(len(a)+len(b)) 中运行,或者在我的笔记本电脑上运行大约 0.02 秒,而在你的笔记本电脑上运行 18.79 秒。我还确认它返回的结果与您的算法中的 result.items() 相同。

      【讨论】:

        【解决方案9】:

        这个解决方案可能不是最快的,但它可能是最简单的实现,所以为了完整起见,我决定发布它。

        aa = Counter(dict(a))
        bb = Counter(dict(b))
        cc = aa + bb
        cc
        => Counter({288: 24, 193: 12, 108: 1, 125: 1})
        
        list(cc.items())
        => [(288, 24), (193, 12), (108, 1), (125, 1)]
        

        如果您必须只包含公共键:

        [ (k, cc[k]) for k in set(aa).intersection(bb) ]
        => [(288, 24), (193, 12)]
        

        【讨论】:

          【解决方案10】:

          numpy serachsorted()argsort()intersect1d() 是可能的替代方案,而且速度非常快。此示例还应注意非唯一的第一个元素问题。

          >>> import numpy as np
          >>> a=np.array([[125, 1], [193, 1], [288, 23]])
          >>> b=np.array([[108, 1], [288, 1], [193, 11]])
          >>> aT=a.T
          >>> bT=b.T
          >>> aS=aT[0][np.argsort(aT[0])]
          >>> bS=bT[0][np.argsort(bT[0])]
          >>> i=np.intersect1d(aT[0], bT[0])
          >>> cT=np.hstack((aT[:,np.searchsorted(aS, i)], bT[:,np.searchsorted(bS, i)]))
          >>> [[item,np.sum(cT[1,np.argwhere(cT[0]==item).flatten()])] for item in i]
          [[193, 12], [288, 24]] #not quite happy about this, can someone comes up with a vectorized way of doing it?
          

          【讨论】:

          猜你喜欢
          • 2020-10-31
          • 2012-07-14
          • 1970-01-01
          • 1970-01-01
          • 2016-09-09
          • 2019-01-11
          • 2018-10-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多