Python: 如何根据对象的特征或属性对列表进行分组?

11

我想把一个对象列表分成子列表,其中具有相同属性/特征的对象留在同一个子列表中。

假设我们有一个字符串列表:

["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()

我想知道是否有更好的方法来实现这个?


以什么方面更好?就我个人而言,我认为那个实现方式没有问题(假设它能正常工作,我没有检查过)。 - FamousJameous
2
由于您的数据未排序,因此您已找到了规范方法。您可以使用word_len_dict = defaultdict(list)来代替,这样您就不必一直测试键是否已存在。如果您的数据已经排序,请使用itertools.groupby() - Martijn Pieters
或者使用dict.setdefault(),这通常使得使用defaultdict变得不必要。 - Sven Marnach
7个回答

13

请查看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已经设计的方法,因为它会更快(特别是当要分组的项目数量增加时)。 - Martijn Pieters
完全同意。这就是为什么我做了一个注释的原因。并没有什么问题,只是想提供一种替代方案,如果速度不是问题/数据已经排序过的话。 - ospahiu

7

使用defaultdict(list),您可以省略键存在性检查:

from collections import defaultdict

word_len_dict = defaultdict(list)

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

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

1
您可以仅使用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

现在我不是说这种方法更好,除非你认为简洁的代码更好。你的版本(在我看来已经很棒了)更易读,更易于维护。

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)]))

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
如果你的目标是用更少的行数来实现,那么可以考虑使用推导式:
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]

如果你只是想快速编写代码,这可能不太清晰,但很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接