我有一大堆元组,例如[ (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
尝试使用defaultdict
,我认为这是一种自然的解决方案而不重新发明轮子。它运行良好,但它不保留键的顺序。一种解决方案是将有序defaultdict
用作explained here。
无论如何,我想知道这个问题的语言独立和有效的解决方案。我目前的解决方案需要两次通过,并在列表中调用一次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
脚本在这里http://github.com/dilawar/playground/raw/master/Python/so_group_tuple.py
PM 2Ring
解决方案更快(大约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 。
答案 0 :(得分:9)
您可以使用itertools.groupby
执行此操作,并使用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])]
那个列表comp有点密集。这是使用传统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, 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))]
答案 1 :(得分:7)
您可以使用collections.OrderedDict
(import collections
优先):
In [983]: o = collections.OrderedDict()
In [984]: for x in t:
...: o.setdefault(x[0], []).append(x[1])
...:
现在,将o.items()
转换为列表:
In [985]: list(o.items())
Out[985]: [(1, [2, 3, 4]), (2, [1, 3])]
答案 2 :(得分:1)
如果输入列表已经订购,则可能不需要使用任何其他订购功能或功能来重新排序列表。 下面的代码将自动显示您显示的输出。
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]}