列表推导可以分组吗?

0 投票
2 回答
950 浏览
提问于 2025-04-18 04:52

我有一个列表,长得像这样:

[(1,2,5),(2,10,13),(5,24,56),(1,8,10),(2,3,11)]

我想通过把元组的第一个元素分组,来生成一个字典,并且在第二个元素中找到最小值,在第三个元素中找到最大值:

{1:(2,10),2:(3,13),5:{24,56}]

2 个回答

0
In [9]: ll = [(1,2,5),(2,10,13),(5,24,56),(1,8,10),(2,3,11)]

In [10]: {k[0]: ( min([kk[1] for kk in ll if kk[0] == k[0]]), max([kk[2] for kk in ll if kk[0] == k[0]]) ) for k in ll}
Out[10]: {1: (2, 10), 2: (3, 13), 5: (24, 56)}

或者

In [11]: {k[0]: (v[0], v[-1]) for k in ll for v in [sorted([x for y in [[kk[1], kk[2]] for kk in ll if kk[0] == k[0] ] for  x in y])] }
Out[11]: {1: (2, 10), 2: (3, 13), 5: (24, 56)}
6

你可以先对分组的元素进行排序,然后使用itertools.groupby()来对这些元素进行分组,并测试每组的最小值和最大值。因为这些组是生成器,所以你需要多做一步,把它们转换成一个可以重复使用的列表,以便使用min()max()函数:

from itertools import groupby
from operator import itemgetter

result = {k: (min(item[1] for item in gv), max(item[2] for item in gv))
         for k, g in groupby(sorted(inputlist, key=itemgetter(0)), itemgetter(0))
         for gv in (list(g),)}

需要注意的是,这个过程先是排序(复杂度是O(NlogN)),然后对每个组循环两次,以找到每组的最小值和最大值,这样总的复杂度又增加了2N。

额外的for gv in (list(g),)循环是把g组中的所有元素放到一个列表中,赋值给gv

简单的循环版本是:

result = {}
for key, v1, v2 in inputlist:
    minimum, maximum = result.get(key, (float('inf'), float('-inf')))
    if v1 < minimum:
        minimum = v1
    if v2 > maximum:
        maximum = v2
    result[key] = (minimum, maximum)

这是一个简单的O(N)循环,而且更容易理解。

这两种方法的演示:

>>> from itertools import groupby
>>> from operator import itemgetter
>>> inputlist = [(1,2,5),(2,10,13),(5,24,56),(1,8,10),(2,3,11)]
>>> {k: (min(item[1] for item in gv), max(item[2] for item in gv))
...          for k, g in groupby(sorted(inputlist, key=itemgetter(0)), itemgetter(0))
...          for gv in (list(g),)}
{1: (2, 10), 2: (3, 13), 5: (24, 56)}

还有

>>> result = {}
>>> for key, v1, v2 in inputlist:
...     minimum, maximum = result.get(key, (float('inf'), float('-inf')))
...     if v1 < minimum:
...         minimum = v1
...     if v2 > maximum:
...         maximum = v2
...     result[key] = (minimum, maximum)
... 
>>> result
{1: (2, 10), 2: (3, 13), 5: (24, 56)}

撰写回答