2016-08-31 5 views
3

Я хочу выделить список объектов в подсписках, где объекты с одинаковыми атрибутами/характеристиками остаются в одном подсписке.Python: как сгруппировать список объектов по их характеристикам или атрибутам?

Предположим, у нас есть список строк:

["This", "is", "a", "sentence", "of", "seven", "words"] 

Мы хотим, чтобы отделить строки в зависимости от их длины следующим образом:

[['sentence'], ['a'], ['is', 'of'], ['This'], ['seven', 'words']] 

Программа я в настоящее время придумывают находится этот

sentence = ["This", "is", "a", "sentence", "of", "seven", "words"] 
word_len_dict = {} 
for word in sentence: 
    if len(word) not in word_len_dict.keys(): 
     word_len_dict[len(word)] = [word] 
    else: 
     word_len_dict[len(word)].append(word) 


print word_len_dict.values() 

Я хочу знать, есть ли лучший способ достичь этого?

+0

Лучше, каким образом? Я лично не вижу проблем с этой реализацией (при условии, что это работает, я не проверял это) – FamousJameous

+2

Поскольку ваши данные не отсортированы, вы нашли канонический метод.Вместо этого вы можете использовать 'word_len_dict = defaultdict (list)', поэтому вам не нужно продолжать тестирование, если ключ уже присутствует. Если ваши данные должны быть отсортированы уже, используйте 'itertools.groupby()'. –

+0

Или используйте 'dict.setdefault()', что часто делает использование 'defaultdict' ненужным. –

ответ

5

Посмотрите на itertools.groupby(). Обратите внимание, что ваш список должен быть отсортирован первым (дороже, чем ваш метод OP).

>>> from itertools import groupby 
>>> l = ["This", "is", "a", "sentence", "of", "seven", "words"] 
>>> print [list(g[1]) for g in groupby(sorted(l, key=len), len)] 
[['a'], ['is', 'of'], ['This'], ['seven', 'words'], ['sentence']] 

или если вы хотите словарь ->

>>> {k:list(g) for k, g in groupby(sorted(l, key=len), len)} 
{8: ['sentence'], 1: ['a'], 2: ['is', 'of'], 4: ['This'], 5: ['seven', 'words']} 
+1

Сортировка - операция O (NlogN). Группировка с использованием словаря - O (N). Используйте только 'groupby', если данные уже отсортированы *. Если нет, то придерживайтесь метода, который OP уже разработал, поскольку он будет быстрее (особенно когда число элементов в группе растет). –

+0

О, я полностью согласен. Вот почему я это сделал. Нет ничего плохого в методе OP, просто подумал, что я предлагаю альтернативу, если скорость не является проблемой/его данные уже отсортированы. – ospahiu

2

С defaultdict(list), вы можете пропустить проверку ключа существования:

from collections import defaultdict 

word_len_dict = defaultdict(list) 

for word in sentence: 
    word_len_dict[len(word)].append(word) 
0

Сейчас я не говорю, что это лучше в любом случае, если вы считаете, компактный код лучше. Ваша версия (которая очень хорошо imo) намного читабельна и удобна в обслуживании.

list_ = ["This", "is", "a", "sentence", "of", "seven", "words"] 

# for python 2 filter returns() a list 
result = filter(None,[[x for x in list_ if len(x) == i] for i in range(len(max(list_, key=lambda y: len(y)))+1)]) 

# for python 3 filter() returns an iterator 
result = list(filter(None,[[x for x in list_ if len(x) == i] for i in range(len(max(list_, key=lambda y: len(y)))+1)])) 
1

Док из itertools.groupby имеет пример, который соответствует именно то, что вы хотите.

keyfunc = lambda x: len(x) 
data = ["This", "is", "a", "sentence", "of", "seven", "words"] 
data = sorted(data, key=keyfunc) 
groups = [] 
for k, g in groupby(data, keyfunc): 
    groups.append(list(g)) 
print groups 
0
sentence = ["This", "is", "a", "sentence", "of", "seven", "words"] 
getLength = sorted(list(set([len(data) for data in sentence]))) 

result = [] 

for length in getLength: 
    result.append([data for data in sentence if length == len(data)]) 

print(result) 
0

Вы можете сделать это с Dict только с помощью setdefault функции:

sentence = ["This", "is", "a", "sentence", "of", "seven", "words"] 
word_len_dict = {} 
for word in sentence: 
    word_len_dict.setdefault(len(word), []).append(word) 

Что setdefault делает устанавливается ключ len(word) в словаре, если он не существует, и просто получить значение в случае, если это произойдет. Второй аргумент в setdefault - это значение по умолчанию, которое вы хотите сохранить вместе с этим ключом.

Важно отметить, что в случае, если ключ уже существует, значение по умолчанию, принятое в setdefault, не заменит старое значение. Это гарантирует, что каждый список будет создан только один раз, и после этого тот же список будет просто восстановлен setdefault.

0

Если ваша цель состоит в том, чтобы сделать это в меньшем количестве строк, всегда постижения:

data = ["This", "is", "a", "sentence", "of", "seven", "words"] 
# Get all unique length values 
unique_length_vals = set([len(word) for word in data]) 
# Get lists of same-length words 
res = [filter(lambda x: len(x) == lval, data) for lval in unique_length_vals] 

Это может быть менее ясно, но полезно, если вы просто хотите, чтобы закодировать что-то быстро.

Смежные вопросы