web-dev-qa-db-ja.com

Pythonで同様のインデックス/属性によってタプル/オブジェクトのリストをグループ化する方法は?

リストが与えられた

old_list = [obj_1, obj_2, obj_3, ...]

リストを作成したい:

new_list = [[obj_1, obj_2], [obj_3], ...]

どこ obj_1.some_attr == obj_2.some_attr

forループとifチェックを一緒にスローすることもできますが、これは醜いです。これにはPythonicな方法がありますか?ちなみに、オブジェクトの属性はすべて文字列です。

または、オブジェクトの代わりに(同じ長さの)タプルを含むリストのソリューションも高く評価されます。

33
Aufwind

defaultdict は、これを行う方法です。

forループはほとんど必須ですが、ifステートメントは必須ではありません。

from collections import defaultdict


groups = defaultdict(list)

for obj in old_list:
    groups[obj.some_attr].append(obj)

new_list = groups.values()
43
S.Lott

2つのケースがあります。どちらも次のインポートが必要です。

import itertools
import operator

itertools.groupbyoperator.attrgetter または operator.itemgetter のいずれかを使用します。

obj_1.some_attr == obj_2.some_attrでグループ化している場合:

get_attr = operator.attrgetter('some_attr')
new_list = [list(g) for k, g in itertools.groupby(sorted(old_list, key=get_attr), get_attr)]

a[some_index] == b[some_index]の場合:

get_item = operator.itemgetter(some_index)
new_list = [list(g) for k, g in itertools.groupby(sorted(old_list, key=get_item), get_item)]

キーの値が変更されるとitertools.groupbyが新しいグループを作成するため、並べ替えが必要なことに注意してください。


これを使用してS.Lottの回答のようなdictを作成できますが、collections.defaultdictを使用する必要はありません。

辞書内包表記を使用する(Python 3+、およびPython 2.7でのみ機能しますが、わかりません):

groupdict = {k: g for k, g in itertools.groupby(sorted_list, keyfunction)}

以前のバージョンのPythonの場合、またはより簡潔な代替手段として:

groupdict = dict(itertools.groupby(sorted_list, keyfunction))
24
JAB

itertools.groupby を使用することもできると思います。以下のコードは単なるサンプルであり、必要に応じて変更する必要があることに注意してください。

data = [[1,2,3],[3,2,3],[1,1,1],[7,8,9],[7,7,9]]

from itertools import groupby

# for example if you need to get data grouped by each third element you can use the following code
res = [list(v) for l,v in groupby(sorted(data, key=lambda x:x[2]), lambda x: x[2])]# use third element for grouping
13