【问题标题】:Comparing first element of the consecutive lists of tuples in Python比较Python中连续元组列表的第一个元素
【发布时间】:2015-12-16 12:09:00
【问题描述】:

我有一个元组列表,每个元组包含两个元素。少数子列表的第一个元素很常见。我想比较这些子列表的第一个元素并将第二个元素附加到一个列表中。这是我的清单:

myList=[(1,2),(1,3),(1,4),(1,5),(2,6),(2,7),(2,8),(3,9),(3,10)]

我想从中列出一个列表,看起来像这样:`

NewList=[(2,3,4,5),(6,7,8),(9,10)]

我希望有什么有效的方法。

【问题讨论】:

  • 如果子元组的第一个元素不常见怎么办?你想要一个单一元素的元组?
  • 那不是列表列表,而是元组列表;这对您的问题没有影响,但您仍然应该意识到差异
  • 感谢指正。

标签: python list python-2.7 compare append


【解决方案1】:

您可以使用OrderedDict 按每个元组的第一个子元素对元素进行分组:

myList=[(1,2),(1,3),(1,4),(1,5),(2,6),(2,7),(2,8),(3,9),(3,10)]

from collections import OrderedDict

od  = OrderedDict()

for a,b in myList:
    od.setdefault(a,[]).append(b)

print(list(od.values()))
[[2, 3, 4, 5], [6, 7, 8], [9, 10]]

如果你真的想要元组:

print(list(map(tuple,od.values())))
[(2, 3, 4, 5), (6, 7, 8), (9, 10)]

如果您不关心元素出现的顺序,而只是想要最有效的分组方式,您可以使用collections.defaultdict

from collections import defaultdict

od  = defaultdict(list)

for a,b in myList:
    od[a].append(b)

print(list(od.values()))

最后,如果您的数据按照您的输入示例排序,即已排序,您可以简单地使用 itertools.groupby 按每个元组中的第一个子元素分组,然后从分组元组中提取第二个元素:

from itertools import groupby
from operator import itemgetter
print([tuple(t[1] for t in v) for k,v in groupby(myList,key=itemgetter(0))])

输出:

[(2, 3, 4, 5), (6, 7, 8), (9, 10)]

同样,groupby 仅在您的数据至少按第一个元素排序时才有效。

合理大小列表中的一些时间安排:

In [33]: myList = [(randint(1,10000),randint(1,10000)) for _ in range(100000)]

In [34]: myList.sort()

In [35]: timeit ([tuple(t[1] for t in v) for k,v in groupby(myList,key=itemgetter(0))])
10 loops, best of 3: 44.5 ms per loop

In [36]: %%timeit                                                               od = defaultdict(list)
for a,b in myList:
    od[a].append(b)
   ....: 
10 loops, best of 3: 33.8 ms per loop

In [37]: %%timeit
dictionary = OrderedDict()
for x, y in myList:
     if x not in dictionary:
        dictionary[x] = [] # new empty list
    dictionary[x].append(y)
   ....: 
10 loops, best of 3: 63.3 ms per loop

In [38]: %%timeit   
od = OrderedDict()
for a,b in myList:
    od.setdefault(a,[]).append(b)
   ....: 
10 loops, best of 3: 80.3 ms per loop

如果顺序很重要并且数据已排序,请使用groupby,如果需要将所有元素映射到,它会更接近defaultdict方法默认字典中的元组。

如果数据没有排序或者您不关心任何顺序,您将找不到比使用 defaultdict 方法更快的分组方式。

【讨论】:

  • 感谢 Padraic,提供代码。这帮助我解决了我幼稚的问题。
  • 同意,这可能是最有效的方法
  • @PadraicCunningham 我收回我的观点,即这可能是最有效的方法;见my second answer
  • @MarcusMüller,您的任何方法都不能保持顺序,如果顺序无关紧要,我的 defaultdict 方法将比您的两个答案中的任何一个都更有效。不确定 groupby,因为我没有计时
  • @PadraicCunningham 好点,但使用 OrderedDict() 而不是 {} 可以解决这个问题,所以我在更新中参考了你的答案。当然,假设“有序”并不意味着“按第一个元组成员的第一次出现的顺序”,而是“按数字排序”。
【解决方案2】:

这感觉像是字典的任务(如果您还不知道字典,请在 python.org 上查找它们)。这是一个非常冗长的例子,所以这不是我在日常编码中会写的,但最好是冗长而不是不清楚:

dictionary = collections.OrderedDict()
for x, y in myList:
    if not dictionary.has_key(x):
        dictionary[x] = [] # new empty list
    # append y to that list
    dictionary[x].append(y)

【讨论】:

  • 感谢马库斯的建议。我想了解 Python 的不同模块,这将使我能够熟练使用 Python。如果您有任何更好的建议,请告诉我。
【解决方案3】:

考虑到这一点,最有效的方法可能是这个单行(假设dictionary 是一个空的dict,即dictionary = {}dictionary = OrderedDict() 就像Padraic' excellent answer ):

for x,y in myList: dictionary.setdefault(x,[]).append(y)

我并不是说这是最容易阅读的方法,但我喜欢它:)

编辑哈!基准测试证明我错了; setdefault 方法比 if not dictionary.has_key(x): dictionary[x]=[] 方法慢:

>>> timeit.timeit("for x,y in myList:\n    if not dictionary.has_key(x):\n        dictionary[x]=[]\n    dictionary[x].append(y)", "from collections import OrderedDict\nmyList=[(1,2),(1,3),(
1,4),(1,5),(2,6),(2,7),(2,8),(3,9),(3,10)]\ndictionary=OrderedDict()")
2.2573769092559814
>>> timeit.timeit("for x,y in myList: dictionary.setdefault(x,[]).append(y)", "from collections import OrderedDict\nmyList=[(1,2),(1,3),(1,4),(1,5),(2,6),(2,7),(2,8),(3,9),(3,10)]\ndictiona
ry=OrderedDict()")
3.3534231185913086

当然,Padraic 仍然是对的:他的 defaultdict 方法在我的机器上使用了 0.82 秒,所以它快了 3 倍。

另外,正如 Padraic 指出的那样:dict.has_key(x) 已被弃用,应该改用x in dict;但是,我无法测量速度差异。

【讨论】:

  • Having thought about this 你也测量了吗?顺便说一句:你为​​什么要“复制”一个答案?
  • @Wolf:我指的是,不是抄袭:)
  • 是的,但要说这可能最有效的方法,评论就足够了。
  • 这是一个自给自足的答案。我不同意你对此的批评。另外,基准测试!
  • 在python2中已弃用,in也比has_key快
【解决方案4】:

以下应该有效:

import itertools

myList = [(1,2),(1,3),(1,4),(1,5),(2,6),(2,7),(2,8),(3,9),(3,10)]
print [tuple(x[1] for x in g) for k, g in itertools.groupby(myList, key=lambda x: x[0])]

其中显示:

[(2, 3, 4, 5), (6, 7, 8), (9, 10)]

【讨论】:

  • 抱歉,完全巧合。
  • 可能要提到数据必须按排序顺序排列,至少就第一个元素而言
猜你喜欢
  • 2020-02-28
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-08
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
相关资源
最近更新 更多