在 Python 中对嵌套列表进行排序和分组

Posted

技术标签:

【中文标题】在 Python 中对嵌套列表进行排序和分组【英文标题】:Sorting and Grouping Nested Lists in Python 【发布时间】:2009-01-03 17:05:30 【问题描述】:

我有以下数据结构(列表列表)

[
 ['4', '21', '1', '14', '2008-10-24 15:42:58'], 
 ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
 ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
 ['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
 ['7', '22', '3', '2somename', '2008-10-24 15:45:51']
]

我希望能够

    使用函数重新排序列表,以便我可以按列表中的每个项目进行分组。例如,我希望能够按第二列进行分组(以便所有 21 都在一起)

    使用函数仅显示每个内部列表中的某些值。例如,我想将此列表减少为仅包含“2somename”的第四个字段值

所以列表看起来像这样

[
     ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
     ['7', '22', '3', '2somename', '2008-10-24 15:45:51']
]

【问题讨论】:

次要问题,但您可能应该使用元组而不是内部列表 【参考方案1】:

对于第一个问题,您应该做的第一件事是使用操作员模块中的itemgetter 按第二个字段对列表进行排序:

x = [
 ['4', '21', '1', '14', '2008-10-24 15:42:58'], 
 ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
 ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
 ['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
 ['7', '22', '3', '2somename', '2008-10-24 15:45:51']
]

from operator import itemgetter

x.sort(key=itemgetter(1))

然后就可以使用itertools的groupby函数了:

from itertools import groupby
y = groupby(x, itemgetter(1))

现在 y 是一个包含 (element, item iterator) 元组的迭代器。解释这些元组比显示代码更令人困惑:

for elt, items in groupby(x, itemgetter(1)):
    print(elt, items)
    for i in items:
        print(i)

哪些打印:

21 <itertools._grouper object at 0x511a0>
['4', '21', '1', '14', '2008-10-24 15:42:58']
['5', '21', '3', '19', '2008-10-24 15:45:45']
['6', '21', '1', '1somename', '2008-10-24 15:45:49']
22 <itertools._grouper object at 0x51170>
['3', '22', '4', '2somename', '2008-10-24 15:22:03']
['7', '22', '3', '2somename', '2008-10-24 15:45:51']

对于第二部分,您应该使用这里已经提到的列表推导:

from pprint import pprint as pp
pp([y for y in x if y[3] == '2somename'])

哪些打印:

[['3', '22', '4', '2somename', '2008-10-24 15:22:03'],
 ['7', '22', '3', '2somename', '2008-10-24 15:45:51']]

【讨论】:

我添加了列表理解示例。 这个答案是很久以前写的,现在你应该使用生成器表达式而不是列表理解:pp(y for y in x if y[3] == '2somename') 没有。这是错误的。 genexpr 在这里不合适。尝试运行代码。【参考方案2】:

如果你将它分配给 var "a"...

python 2.x:

#1:

a.sort(lambda x,y: cmp(x[1], y[1]))

#2:

filter(lambda x: x[3]=="2somename", a)

蟒蛇3:

#1:

a.sort(key=lambda x: x[1])

【讨论】:

比 itemgetter 更简单、更干净的方法 lambda 为胜利。我真的很喜欢这个解决方案【参考方案3】:

如果我正确理解您的问题,以下代码应该可以完成这项工作:

l = [
 ['4', '21', '1', '14', '2008-10-24 15:42:58'], 
 ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
 ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
 ['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
 ['7', '22', '3', '2somename', '2008-10-24 15:45:51']
]

def compareField(field):
   def c(l1,l2):
      return cmp(l1[field], l2[field])
   return c

# Use compareField(1) as the ordering criterion, i.e. sort only with
# respect to the 2nd field
l.sort(compareField(1))
for row in l: print row

print
# Select only those sublists for which 4th field=='2somename'
l2somename = [row for row in l if row[3]=='2somename']
for row in l2somename: print row

输出:

['4', '21', '1', '14', '2008-10-24 15:42:58']
['5', '21', '3', '19', '2008-10-24 15:45:45']
['6', '21', '1', '1somename', '2008-10-24 15:45:49']
['3', '22', '4', '2somename', '2008-10-24 15:22:03']
['7', '22', '3', '2somename', '2008-10-24 15:45:51']

['3', '22', '4', '2somename', '2008-10-24 15:22:03']
['7', '22', '3', '2somename', '2008-10-24 15:45:51']

【讨论】:

排序的 'cmp' 参数在 2.6/3.0 中被删除,因此,最好使用提取排序键的 'key' 参数,否则,+1。 删除了'cmp=',无论如何都应该是第一个参数。顺便说一句,我使用的是 python 2.6.1,一切正常......【参考方案4】:

使用函数重新排序列表,以便我可以按列表中的每个项目进行分组。例如,我希望能够按第二列进行分组(以便所有 21 都在一起)

列表具有内置的排序方法,您可以提供提取排序键的函数。

>>> import pprint
>>> l.sort(key = lambda ll: ll[1])
>>> pprint.pprint(l)
[['4', '21', '1', '14', '2008-10-24 15:42:58'],
 ['5', '21', '3', '19', '2008-10-24 15:45:45'],
 ['6', '21', '1', '1somename', '2008-10-24 15:45:49'],
 ['3', '22', '4', '2somename', '2008-10-24 15:22:03'],
 ['7', '22', '3', '2somename', '2008-10-24 15:45:51']]

使用函数仅显示每个内部列表中的某些值。例如,我想将此列表缩减为仅包含“2somename”的第 4 个字段值

这看起来像是 list comprehensions 的工作

>>> [ll[3] for ll in l]
['14', '2somename', '19', '1somename', '2somename']

【讨论】:

[ll[3] for ll in l] 替换为[ll for ll in l if ll[3] == '2somename'] 并修复输出。【参考方案5】:

如果您要进行大量排序和过滤,您可能会喜欢一些辅助函数。

m = [
 ['4', '21', '1', '14', '2008-10-24 15:42:58'], 
 ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
 ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
 ['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
 ['7', '22', '3', '2somename', '2008-10-24 15:45:51']
]

# Sort and filter helpers.
sort_on   = lambda pos:     lambda x: x[pos]
filter_on = lambda pos,val: lambda l: l[pos] == val

# Sort by second column
m = sorted(m, key=sort_on(1))

# Filter on 4th column, where value = '2somename'
m = filter(filter_on(3,'2somename'),m)

【讨论】:

sort_on == operator.itemgetter 请使用 DEF 而不是 lambda。 @s.lott - 为什么在这里定义超过 lambdas? @Triptych:因为带有名称的 lambda 就像 def 一样,但更令人困惑,绝对没有任何好处。 嗯。在这种情况下,我认为 lambda 更具可读性。而“更令人困惑”当然是主观的!【参考方案6】:

对于第 (2) 部分,x 是你的数组,我想你想要,

[y for y in x if y[3] == '2somename']

这将返回一个仅包含第四个值为“2somename”的数据列表的列表...尽管 Kamil 似乎为您提供了使用 SQL 的最佳建议...

【讨论】:

【参考方案7】:

看起来很像您正在尝试将列表用作数据库。

如今,Python 在核心发行版中包含 sqlite 绑定。如果您不需要持久性,那么创建内存中的 sqlite 数据库非常容易(请参阅How do I create a sqllite3 in-memory database?)。

然后您可以使用 SQL 语句来完成所有这些排序和过滤,而无需重新发明***。

【讨论】:

卡米尔,你是对的。但是,我正在学习 Python,并希望使用列表来做一些事情,这样我就可以了解更多关于它们的信息。不过谢谢,我会检查一下【参考方案8】:

您只是在结构上创建索引,对吗?

>>> from collections import defaultdict
>>> def indexOn( things, pos ):
...     inx= defaultdict(list)
...     for t in things:
...             inx[t[pos]].append(t)
...     return inx
... 
>>> a=[
...  ['4', '21', '1', '14', '2008-10-24 15:42:58'], 
...  ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
...  ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
...  ['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
...  ['7', '22', '3', '2somename', '2008-10-24 15:45:51']
... ]

这是您的第一个请求,按位置 1 分组。

>>> import pprint
>>> pprint.pprint( dict(indexOn(a,1)) )
'21': [['4', '21', '1', '14', '2008-10-24 15:42:58'],
        ['5', '21', '3', '19', '2008-10-24 15:45:45'],
        ['6', '21', '1', '1somename', '2008-10-24 15:45:49']],
 '22': [['3', '22', '4', '2somename', '2008-10-24 15:22:03'],
        ['7', '22', '3', '2somename', '2008-10-24 15:45:51']]

这是您的第二个请求,按位置 3 分组。

>>> dict(indexOn(a,3))
'19': [['5', '21', '3', '19', '2008-10-24 15:45:45']], '14': [['4', '21', '1', '14', '2008-10-24 15:42:58']], '2somename': [['3', '22', '4', '2somename', '2008-10-24 15:22:03'], ['7', '22', '3', '2somename', '2008-10-24 15:45:51']], '1somename': [['6', '21', '1', '1somename', '2008-10-24 15:45:49']]
>>> pprint.pprint(_)
'14': [['4', '21', '1', '14', '2008-10-24 15:42:58']],
 '19': [['5', '21', '3', '19', '2008-10-24 15:45:45']],
 '1somename': [['6', '21', '1', '1somename', '2008-10-24 15:45:49']],
 '2somename': [['3', '22', '4', '2somename', '2008-10-24 15:22:03'],
               ['7', '22', '3', '2somename', '2008-10-24 15:45:51']] 

【讨论】:

【参考方案9】:

您可以使用 for 循环对嵌套列表中的元素进行排序和分组。代码将是:

l = [['3', '21', '1', '14', '2008-10-24 15:42:58'], 
['4', '22', '4','2somename','2008-10-24 15:22:03'], 
['5', '21', '3', '19', '2008-10-24 15:45:45'], 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
['7', '35', '3','2somename', '2008-10-24 15:45:51']]
col = int(input("Enter the column to search(1-5):"))
val = str(input("Enter the element to group by:"))
val1=[]
print('Searching...')
for x in l:
    cmp=x[col-1]
    if cmp==val:
        val1=x
        print(val1)
emp=[]
if val1 == emp:
    print('No search result. Please Try Again!!')

输出如下所示:

Enter the column to search(1-5):4
Enter the element to group by:2somename
Searching...
['4', '22', '4', '2somename', '2008-10-24 15:22:03']
['7', '35', '3', '2somename', '2008-10-24 15:45:51']

【讨论】:

以上是关于在 Python 中对嵌套列表进行排序和分组的主要内容,如果未能解决你的问题,请参考以下文章

学不会的python之通过某几个关键字排序分组一个字典列表(列表中嵌套字典)

学不会的python之通过某几个关键字排序分组一个字典列表(列表中嵌套字典)

Python - 在熊猫数据框中对列表中的行进行分组

有没有办法根据小数位在python中对数组/列表的值进行动态分组?

在Python列表中对嵌套字典进行排序? [复制]

在 C++ 中对向量进行分组排序