Python 有效地分组元组列表

Python 有效地分组元组列表,python,algorithm,list,group-by,Python,Algorithm,List,Group By,我有一个很大的元组列表,例如[(1,2),(1,3),(1,4),(2,1),(2,3)]等。我想有效地将其转换为[(1,[1,2,3,4]),(2,[1,3])。我根据每个元组的第一个元素对元组进行分组,即(1,2)、(1,3)、(1,4)变成(1、[2,3,4])(另请参见下面的Haskell版本)。我怀疑这能一次完成吗输入列表始终按顺序排列。 在pythonIn中,我尝试使用defaultdict,我认为这是一个自然的解决方案,无需重新发明轮子。它工作得很好,但不能保持键的顺序。一种解决方

我有一个很大的元组列表,例如
[(1,2),(1,3),(1,4),(2,1),(2,3)]
等。我想有效地将其转换为
[(1,[1,2,3,4]),(2,[1,3])
。我根据每个元组的第一个元素对元组进行分组,即
(1,2)、(1,3)、(1,4)
变成
(1、[2,3,4])
(另请参见下面的Haskell版本)。我怀疑这能一次完成吗输入列表始终按顺序排列。

python
In中,我尝试使用
defaultdict
,我认为这是一个自然的解决方案,无需重新发明轮子。它工作得很好,但不能保持键的顺序。一种解决方案是使用ordered
defaultdict
as

无论如何,我想知道这个问题的独立于语言且有效的解决方案。我当前的解决方案需要两次传递和一次调用列表上的
set()

更新

我正在考虑实施以下Haskell版本:

a = [ (1,2), (1,3), (1,4), (2,1), (2,3) ] 
b = groupBy (\ x y -> fst x == fst y ) 
b 
[[(1,2),(1,3),(1,4)],[(2,1),(2,3)]]  
map (\x -> (fst .head $ x, map snd x ) ) b 
[(1,[2,3,4]),(2,[1,3])]
答覆的表现 我实现了两个答案(coldspeed和pm2ring)。在中等尺寸列表(最多10^4个元素)上,PM2环解决方案速度更快;在10^5码的时候,两者都需要同样的时间,在更大的名单上,COLDSPEED开始获胜。下面是数字(使用python3)

第一列是列表中的条目数,第二列是
coldspeed
所用的时间,第三列是
pm2-ring
解决方案所用的时间。所有的时间都是秒

10 0.0001 0.0000
100 0.0001 0.0000
1000 0.0005 0.0001
10000 0.0044 0.0014
100000 0.0517 0.0452
1000000 0.5579 1.5249
脚本在这里

使用Ashwini优化
PM 2Ring
根据Ashwini的建议,解决方案的速度更快(大约是3x-5x)

10 4.887580871582031e-05 1.2636184692382812e-05
100 0.00010132789611816406 2.0742416381835938e-05
1000 0.0005109310150146484 0.000110626220703125
10000 0.004467487335205078 0.0009067058563232422
100000 0.05056118965148926 0.017516136169433594
1000000 0.6100358963012695 0.26450490951538086
10000000 6.092756509780884 2.8253660202026367
和派比 结果有些复杂。最后一列是第2列和第3列的比率

pypy so_group_tuple.py 
(10, [1.6927719116210938e-05, 3.409385681152344e-05], 0.4965034965034965)
(100, [4.601478576660156e-05, 8.296966552734375e-05], 0.5545977011494253)
(1000, [0.010258913040161133, 0.0019040107727050781], 5.388054094665665)
(10000, [0.0002448558807373047, 0.00021600723266601562], 1.1335540838852096)
(100000, [0.002658843994140625, 0.0018231868743896484], 1.45834967961292)
(1000000, [0.0833890438079834, 0.02979302406311035], 2.7989452709245284)
(10000000, [1.0556740760803223, 0.6789278984069824], 1.5549133841124023)

我选择的是
PM 2Ring
解决方案,因为在列表大小为10^5之前,它的速度要快得多

您可以使用
集合。OrderedDict
首先导入集合
):

现在,将
o.items()
转换为列表:

list(o.items())
# [(1, [2, 3, 4]), (2, [1, 3])]
您可以使用并使用
zip
重新排列收集的组中的数据:

from itertools import groupby
from operator import itemgetter

a = [(1, 2), (1, 3), (1, 4), (2, 1), (2, 3)]
b = [(k, list(list(zip(*g))[1])) for k, g in groupby(a, itemgetter(0))]
print(b)
输出

[(1, [2, 3, 4]), (2, [1, 3])]
[(1, 1, 1), (2, 3, 4)]
[(2, 2), (1, 3)]
Output [[2, 3, 4], [1, 3]]

那个列表比较密集。这里有一个变体,它使用传统的
for
循环打印中间结果,以便更容易看到发生了什么

b = []
for k, g in groupby(a, itemgetter(0)):
    t = list(zip(*g))
    print(t)
    b.append(list(t[1]))

print('Output', b)
输出

[(1, [2, 3, 4]), (2, [1, 3])]
[(1, 1, 1), (2, 3, 4)]
[(2, 2), (1, 3)]
Output [[2, 3, 4], [1, 3]]

正如Ashwini Chaudhary在评论中提到的,在其中嵌套另一个列表comp可以使代码更具可读性,也可能更高效,因为它避免了几个调用

b = [(k, [x for _, x in g]) for k, g in groupby(a, itemgetter(0))]

可能是,如果输入列表已排序,则无需使用任何其他排序函数或功能来再次对列表进行排序。 下面的代码将自动给出如您所示的输出

mylistarr = ((1, 2), (1, 3), (1, 4), (2, 1), (2, 3))
output = dict()
for tuple in mylistarr:
    if tuple[0] not in anotherlist:
        output[tuple[0]] = list()
        output[tuple[0]].append(tuple[0])
    output[tuple[0]].append(tuple[1])
print output
输出:
{1:[1,2,3,4],2:[2,1,3]}

请包括您当前的解决方案,并澄清问题所在-我不清楚您是如何从第一个列表到第二个列表的。输入列表总是这样排序的吗?顺便说一句,您在该列表中有一个输入错误。您的预期输出实际上是
[(1[2,3,4]),(2[1,3])]
?我不知道第一个元组列表中的
1
是从哪里来的。感谢您添加该计时信息。您应该看一看,它比使用
时间
模块手动执行更准确(更方便)。
anotherlist=dict()
是一个不好的命名。@AshwiniChaudhary确实是!谢谢。@AshwiniChaudhary您的建议使这个实现更快了。我添加了一些基准测试。虽然这很容易阅读,但它比10^5-10^6大小的列表中的
pm2ring
解决方案要慢一些。我在问题主体中添加了一些基准。@Dilawar性能不是唯一考虑的因素。如果您想提高速度,请使用C;)你应该选择最简单、最清晰、最容易阅读和理解的内容。可以理解的是,pm2ring的解决方案是有效的,看起来也不错,但我想知道我的代码在做什么。最后由你决定。干杯