Сортировка и группировка вложенных списков в Python - PullRequest
39 голосов
/ 03 января 2009

У меня следующая структура данных (список списков)

[
 ['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. Используйте функцию, чтобы изменить порядок списка, чтобы я мог группировать по каждому элементу в списке. Например, я хотел бы иметь возможность группировать по второму столбцу (чтобы все 21 были вместе)

  2. Используйте функцию для отображения только определенных значений из каждого внутреннего списка. Например, я хотел бы сократить этот список, чтобы он содержал только 4-е значение поля «2somename»

чтобы список выглядел так

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

Ответы [ 8 ]

58 голосов
/ 03 января 2009

Для первого вопроса, первое, что вы должны сделать, это отсортировать список по второму полю, используя 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))

Теперь у итератор содержит кортежи (элемент, элемент итератор). Объяснять эти кортежи сложнее, чем показывать код:

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']]
11 голосов
/ 03 января 2009

Если вы присвоили его переменной "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])
3 голосов
/ 03 января 2009

Если я правильно понимаю ваш вопрос, следующий код должен выполнить работу:

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']
2 голосов
/ 03 января 2009

Для части (2), где x - это ваш массив, я думаю, вы хотите,

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

Который вернет список только ваших списков данных, чье четвертое значение равно 2somename ... Хотя кажется, что Камил дает вам лучший совет по переходу на SQL ...

2 голосов
/ 03 января 2009

Если вы будете много заниматься сортировкой и фильтрацией, вам могут понравиться некоторые вспомогательные функции.

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)
2 голосов
/ 03 января 2009

Используйте функцию, чтобы изменить порядок списка, чтобы я мог группировать по каждому элементу в списке. Например, я хотел бы иметь возможность группировать по второму столбцу (чтобы все 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']]

Используйте функцию для отображения только определенных значений из каждого внутреннего списка. Например, я хотел бы сократить этот список, чтобы он содержал только 4-е значение поля «2somename»

Это похоже на работу для списка пониманий

>>> [ll[3] for ll in l]
['14', '2somename', '19', '1somename', '2somename']
1 голос
/ 03 января 2009

Вы просто создаете индексы в своей структуре, верно?

>>> 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']]} 
1 голос
/ 03 января 2009

Похоже, вы пытаетесь использовать список в качестве базы данных.

В настоящее время Python включает привязки sqlite в основной дистрибутив. Если вам не нужно постоянство, очень легко создать базу данных sqlite в памяти (см. Как мне создать базу данных sqllite3 в памяти? ).

Затем вы можете использовать операторы SQL, чтобы выполнять всю эту сортировку и фильтрацию без необходимости заново изобретать колесо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...