Разбить список кортежей в под-списки одного и того же кортежа - программирование
Подтвердить что ты не робот

Разбить список кортежей в под-списки одного и того же кортежа

У меня есть огромный список кортежей в этом формате. Второе поле каждого кортежа - поле категории.

    [(1, 'A', 'foo'),
    (2, 'A', 'bar'),
    (100, 'A', 'foo-bar'),

    ('xx', 'B', 'foobar'),
    ('yy', 'B', 'foo'),

    (1000, 'C', 'py'),
    (200, 'C', 'foo'),
    ..]

Каков наиболее эффективный способ разбить его на подлисты той же категории (A, B, C. и т.д.)?

4b9b3361

Ответ 1

Используйте itertools.groupby:

import itertools
import operator

data=[(1, 'A', 'foo'),
    (2, 'A', 'bar'),
    (100, 'A', 'foo-bar'),

    ('xx', 'B', 'foobar'),
    ('yy', 'B', 'foo'),

    (1000, 'C', 'py'),
    (200, 'C', 'foo'),
    ]

for key,group in itertools.groupby(data,operator.itemgetter(1)):
    print(list(group))

дает

[(1, 'A', 'foo'), (2, 'A', 'bar'), (100, 'A', 'foo-bar')]
[('xx', 'B', 'foobar'), ('yy', 'B', 'foo')]
[(1000, 'C', 'py'), (200, 'C', 'foo')]

Или, чтобы создать один список с каждой группой в качестве подписок, вы можете использовать понимание списка:

[list(group) for key,group in itertools.groupby(data,operator.itemgetter(1))]

Второй аргумент itertools.groupby - это функция, которая itertools.groupby применяется к каждому элементу в data (первый аргумент). Ожидается, что он вернет key. itertools.groupby затем группирует все смежные элементы с тем же key.

operator.itemgetter(1) выбирает второй элемент в последовательности.

Например, если

row=(1, 'A', 'foo')

затем

operator.itemgetter(1)(row)

равно 'A'.


Как отмечает @eryksun в комментариях, если категории кортежей появляются в некотором случайном порядке, вы должны сначала отсортировать data перед тем, как применить itertools.groupby. Это связано с тем, что itertools.groupy собирает смежные элементы с тем же ключом в группы.

Чтобы отсортировать кортежи по категориям, используйте:

data2=sorted(data,key=operator.itemgetter(1))

Ответ 2

collections.defaultdict

itertools.groupby требует, чтобы входные данные были отсортированы по ключевому полю, в противном случае вам придется сначала выполнить сортировку, что повлечет за собой затраты O (n log n). Для гарантированной сложности времени O (n) вы можете использовать defaultdict списков:

from collections import defaultdict

dd = defaultdict(list)
for item in data:
    dd[item[1]].append(item)

res = list(dd.values())

print(res)

[[(1, 'A', 'foo'), (2, 'A', 'bar'), (100, 'A', 'foo-bar')],
 [('xx', 'B', 'foobar'), ('yy', 'B', 'foo')],
 [(1000, 'C', 'py'), (200, 'C', 'foo')]]

Ответ 3

Чтобы получить несколько списков синглетонов из списка кортежей:

foo = ((1,2), (3, 4), (5, 6), (7,8) , (9, 10))
[[z[i] for z in foo] for i in (0,1)]

Если вы предпочитаете получать несколько кортежей одиночных чисел:

zip(*[(1,4),(2,5),(3,6)])