2012-02-07 14 views
4

私はPythonには初めてで、正しいやり方をする方法を学んでいます。python:辞書からヒストグラムを作成する

私は辞書のリストを持っていますd。各辞書はユーザーを表し、user_id、年齢などの情報を含んでいます。このリストdには、同じユーザーを表す複数の辞書が含まれていてもかまいません。私は、指定された年齢でdにいるユーザの数を示すヒストグラムを作成したいと思います。どのように効率的な方法でそれを行うには?

編集: 重複しているリストを削除する必要があることを強調したいと思います。

答えて

3

さて、この問題への古典的なアプローチは、defaultdictを作成するには、次のようになります。

import collections 
histogram = collections.defaultdict(int) 

そして、辞書のリストの名前としてd_list代わりのdを使用してリストに辞書を反復し、( )、

for d in d_list: 
    histogram[d['age']] += 1 

しかし、私に混乱する追加情報が記載されています。あなたは、複数のdictが同じユーザーを表すことができると言っています。これらの重複をヒストグラムから削除したいですか?それがあなたの疑問である場合、1つの方法はの辞書にユーザーを格納することです。(firstname, lastname)タプルをキーとして使用します。その後、同じユーザーを表す連続した辞書が互いに打ち砕かれ、ユーザーごとに1つのレコードしか保存されませんでした。その後、の値を辞書(おそらくuser_records.itervalues()を使用)に繰り返します。

この一般的なアプローチは、一意のユーザーを識別するのに最適な各レコードの値を使用するように変更できます。 user_idの値がユーザーごとに一意の場合は、(firstname, lastname)の代わりにその値をキーとして使用します。しかし、あなたの質問は、user_idが同じ2人のユーザーにとって必ずしも同じではないと(私に)示唆しました。

あなたが排除重​​複を持っていたら、あなたは、Python> = 2.7を使用している場合しかし、ショートカットもあります:

histogram = collections.Counter(d['age'] for d in user_records.itervalues()) 

いくつかのサンプルコードを...我々はrecord_listを持っていると言う:

>>> record_list 
[{'lastname': 'Mann', 'age': 23, 'firstname': 'Joe'}, 
{'lastname': 'Moore', 'age': 23, 'firstname': 'Alex'}, 
{'lastname': 'Sault', 'age': 33, 'firstname': 'Marie'}, 
{'lastname': 'Mann', 'age': 23, 'firstname': 'Joe'}] 
>>> user_ages = dict(((d['firstname'], d['lastname']), d['age']) for d in record_list) 
>>> user_ages 
{('Joe', 'Mann'): 23, ('Alex', 'Moore'): 23, ('Marie', 'Sault'): 33} 

ご覧のとおり、record_listには重複がありますが、user_ages dictはありません。現在、年齢のカウントを取得するのは、値をCounterで実行するのと同じくらい簡単です。

>>> collections.Counter(user_ages.itervalues()) 
Counter({23: 2, 33: 1}) 

同じことは、特定のユーザーの一意の識別子として機能する任意の文字列または不変オブジェクトで行うことができます。

+0

これを拡張できますか:「(firstname、lastname)タプルをキーとしてuser_recordsの辞書にユーザーを格納する方法があります。その後、同じユーザーを表す連続した辞書が互いに衝突し、保存される。 – ashim

+0

実際には "Python> = 2.7"を意味することに注意してください。 :) – Dougal

+0

文字列のタプルを辞書のキーにすることができ、指定されたキーは辞書に一度しか表示できないため、重複は自動的に削除されます。いくつかのサンプルコードを投稿します。 – senderle

-2

@ senderleの回答を改善しようとしたら、うまくいけば問題はよく分かりました。

私はリストのキーはユーザーIDで、データがageプロパティを持つオブジェクトである辞書を、含まれているとします。

import collections 
# Merge all dictionaries to one uid->age mapping (I'm sure there's a shorter way) 
all_ages={} 
for d1 in d: 
    for uid,data in d1.iteritems(): 
     all_ages[uid]=data.age 
# Count distinct users per age 
histogram = collections.defaultdict(int) 
for uid,age in all_ages.iteritems(): 
    histogram[age]+=1 
+0

辞書に多くのマッピングがある場合、この行 'for uid、data in d1'は動作します – ashim

+0

@capoluca ugorenの答えは本当に意味をなさない。 'for uid、data in d1'行は' d1'の* keys *を実際にループし、それらが2タプルであることを期待しています。これはほとんどあなたがしたいことではありません。彼は 'dItiteritems()'をループすることを意味するかもしれませんが、そのデータフォーマットは(all_ages'ループには意味がありますが)まだまだ勝ちです。また、Pythonには '++'はありません(代わりに '+ = 1'を使います)。 Senderleの答えは同じ基本的なアプローチですが、実際には意味があります。 – Dougal

+0

@Dougal、私は文法が間違っていることを認めます(それをテストしませんでした)。両方の繰り返しに 'iteritems'を使用し、' + = 1'はそれを修正します。 senderleの答えとの違いは、各辞書に複数のユーザーがいると仮定しているのに対し、各辞書はユーザーであると仮定しています。 – ugoren

2

あなたがリストにグループに持っているすべての辞書をitertools.groupbyを使用することができます同じ年齢で、その後、それらのリストの長さを計算するだけです。例えば

import itertools 

l = [{'user_id': 1, 'age': 20}, 
    {'user_id': 2, 'age': 21}, 
    {'user_id': 3, 'age': 21}, 
    {'user_id': 4, 'age': 20}, 
    {'user_id': 5, 'age': 21}, 
    {'user_id': 6, 'age': 21}, 
    ] 

def get_age(d): 
    return d.get('age') 

print [(age, len(list(group))) 
     for age, group in itertools.groupby(sorted(l, key=get_age), 
              key=get_age)] 

出力例:

[(20, 2), (21, 5)] 

注@Dougalにより指摘したように、リストはsortedでなければなりません。そうでなければitertools.groupbyは期待どおりに動作しません。

+2

'groupby'は、リストが関連する属性によってソートされていると仮定しているので、' itertools.groupby(sorted(l、key = key_func)、key = key_func) '' key_func = lambda d:d [ 'age'] '(または' operator.itemgetter( 'age') ')。 – Dougal

+1

@Dougalあなたは完全に正しいです、私はそれを明確にするために私の答えを更新しました。また 'KeyError'例外を避けるために' operator.itemgetter'の代わりに関数を使用しました。ご意見ありがとうございます。 – jcollado

+0

groupbyとの最大の不満は、それが "グローバル"グループの場合、まずそれをソートする必要があるということです。あなたの例では、user_id:4が20歳に変更された場合、結果は "[((20,1)、(21,2)、(20,1)、(21,2)]"となります。キーでグループ化し、グループ化するとエラーが発生しやすくなります。 –

関連する問題