4

次のようなラベルが付いたアイテムのグループがありますitem_labels = [('a', 3), ('b', 2), ('c', 1), ('d', 3), ('e', 2), ('f', 3)]

グループのサイズで並べ替えたい。たとえば、上記の例では、ラベル 3 のサイズは 3 で、ラベル 2 のサイズは 2 です。

と の組み合わせを使用してみましたが、うまくgroupbyいきsortedませんでした。

In [162]: sil = sorted(item_labels, key=op.itemgetter(1))

In [163]: sil
Out[163]: [('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]

In [164]: g = itt.groupby(sil,)
Display all 465 possibilities? (y or n)

In [164]: g = itt.groupby(sil, key=op.itemgetter(1))

In [165]: for k, v in g:
   .....:     print k, list(v)
   .....:
   .....:
1 [('c', 1)]
2 [('b', 2), ('e', 2)]
3 [('a', 3), ('d', 3), ('f', 3)]

In [166]: sg = sorted(g, key=lambda x: len(list(x[1])))

In [167]: sg
Out[167]: [] # not exactly know why I got an empty list here

これを行うために退屈な for ループをいつでも書くことができますが、もっとエレガントなものを見つけたいと思います。なにか提案を?便利なライブラリがあれば、喜んで使用します。例pandas:scipy

4

5 に答える 5

3
from collections import defaultdict
import operator
l=[('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
d=defaultdict(int)
for p in l: d[p[1]] += 1
print [ p for i in sorted(d.iteritems(), key=operator.itemgetter(1))
        for p in l if p[1] == i[1] ]
于 2013-06-24T21:51:33.103 に答える
3

Python2.7 以降では、Counter を使用します。

from collections import Counter
c = Counter(y for _, y in item_labels)
item_labels.sort(key=lambda t : c[t[1]])

python2.6 では、この目的のために、このコンストラクターは(@perreal によって提案されているように) この方法Counterを使用して実装できます。defaultdict

from collections import defaultdict
def Counter(x):
    d = defaultdict(int)
    for v in x: d[v]+=1
    return d

数字のみを扱っているため、数字があなたの例と同じくらい低いと仮定すると、実際にはリストを使用できます(これはPythonの古いバージョンと互換性があります):

def Counter(x):
    lst = list(x)
    d = [0] * (max(lst)+1)
    for v in lst: d[v]+=1
    return d

カウンターがなければ、これを簡単に行うことができます:

item_labels.sort(key=lambda t : len([x[1] for x in item_labels if x[1]==t[1] ]))

遅いですが、短いリストでは妥当です。


空のリストを取得した理由gは、ジェネレーターです。反復できるのは 1 回だけです。

于 2013-06-24T21:40:08.267 に答える
2

itertools.groupbyイテレータを返すため、この for ループ:for k, v in g:実際にそのイテレータを消費しました。

>>> it = iter([1,2,3])
>>> for x in it:pass
>>> list(it)          #iterator already consumed by the for-loop
[]

コード:

>>> lis = [('a', 3), ('b', 2), ('c', 1), ('d', 3), ('e', 2), ('f', 3)]
>>> from operator import itemgetter
>>> from itertools import groupby
>>> lis.sort(key = itemgetter(1) )
>>> new_lis = [list(v) for k,v in groupby(lis, key = itemgetter(1) )]
>>> new_lis.sort(key = len)
>>> new_lis
[[('c', 1)], [('b', 2), ('e', 2)], [('a', 3), ('d', 3), ('f', 3)]]

フラット化されたリストを取得するには、次を使用しますitertools.chain

>>> from itertools import chain
>>> list( chain.from_iterable(new_lis))
[('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
于 2013-06-24T21:38:59.300 に答える
2

@perrealおよび@Elazar の回答と同じですが、より適切な名前が付けられています。

from collections import defaultdict

size = defaultdict(int)
for _, group_id in item_labels:
   size[group_id] += 1

item_labels.sort(key=lambda (_, group_id): size[group_id])
print item_labels
# -> [('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
于 2013-06-24T22:16:48.347 に答える
1

別の方法を次に示します。

example=[('a', 3), ('b', 2), ('c', 1), ('d', 3), ('e', 2), ('f', 3)]

out={}
for t in example:
    out.setdefault(t[1],[]).append(t)

print sorted(out.values(),key=len)

版画:

[[('c', 1)], [('b', 2), ('e', 2)], [('a', 3), ('d', 3), ('f', 3)]]

フラットリストが必要な場合:

print [l for s in sorted(out.values(),key=len) for l in s]
[('c', 1), ('b', 2), ('e', 2), ('a', 3), ('d', 3), ('f', 3)]
于 2013-06-24T22:25:56.193 に答える