Pythonで統計処理した結果を表示する場合の便利関数を書いた
- 全てのキーの組み合わせが無い場合でも、defaultdictの初期値を用いて表を完成させるように修正。
- pandasのMultiIndexを使って表示する処理を追記。
統計処理ではネストされた辞書(dict)が多用することで、比較的簡単に所望の複合キーに対応する値を取得しておくことができる。例えば、population[prefecture][gender][age]
のようなものだ。これを例えば、横軸prefecture、縦軸gender x ageという具合に、三次元のグラフが出来る表として出力するとき、第一次辞書のキー(この場合はprefecture)を横軸に取れば良いのだが、横軸方向に値を表示していくことを考えると、少し面倒な処理が必要になる。このような表は良く使うので、そのための関数を持つライブラリを書いて使ってみた。もし、横軸にageを取りたければ、population[age][prefecture][gender]
というネストされた辞書を作って集計しておけば良い。
実質的にネストの次元はおそらく際限なく増やすことが可能だが、今回は5次元までとしている。もう少し機能を追加したら、PYPIしてみるのもいいかも。月や曜日でもうまくソートできるようにしたい。また、第一次辞書のキーだけではなく任意の辞書のキーを横軸に取れるようにする、など。表示形式としてはセパレータを指定できるので、TSVやCSVにも対応できる。
なお、defaultdictは、キーに対するデフォルトの値を指定することができる(実際には値ではなく、新しいオブジェクトを返すファクトリ関数を指定する)。キーの存在を確認する必要なく、そのキーに対するデフォルトの値を仮定して処理が書けるのでキーの存在確認を行う必要がなく、複雑な辞書を扱う際にシンプルに書ける。
$ cat a.py from collections import defaultdict from chart import chart adata = defaultdict(lambda:defaultdict(int)) n = 0 for a in ["1","2","3"]: for b in ["xxx","yyy","zzz"]: n += 1 adata[a][b] = n chart(adata) print() bdata = defaultdict(lambda:defaultdict(lambda:defaultdict(int))) n = 0 for a in ["A","B","C","D","E","F","G"]: for b in ["xxx","yyy","zzz"]: for c in ["hhh","iii","jjj"]: n += 1 bdata[a][b][c] = n chart(bdata,"\t") print() cdata = defaultdict(lambda:defaultdict(lambda:defaultdict(int))) cdata["A"]["xxx"]["hhh"] = 1 cdata["B"]["yyy"]["iii"] = 2 cdata["C"]["zzz"]["jjj"] = 3 cdata["D"]["zzz"]["iii"] = 4 cdata["E"]["yyy"]["hhh"] = 5 cdata["F"]["xxx"]["iii"] = 6 cdata["G"]["xxx"]["jjj"] = 7 chart(cdata,"\t") print() chart(cdata)
$ python a.py ,1,2,3 xxx,1,4,7, yyy,2,5,8, zzz,3,6,9, A B C D E F G xxx hhh 1 10 19 28 37 46 55 xxx iii 2 11 20 29 38 47 56 xxx jjj 3 12 21 30 39 48 57 yyy hhh 4 13 22 31 40 49 58 yyy iii 5 14 23 32 41 50 59 yyy jjj 6 15 24 33 42 51 60 zzz hhh 7 16 25 34 43 52 61 zzz iii 8 17 26 35 44 53 62 zzz jjj 9 18 27 36 45 54 63 A B C D E F G xxx hhh 1 0 0 0 0 0 0 xxx iii 0 0 0 0 0 6 0 xxx jjj 0 0 0 0 0 0 7 yyy hhh 0 0 0 0 5 0 0 yyy iii 0 2 0 0 0 0 0 yyy jjj 0 0 0 0 0 0 0 zzz hhh 0 0 0 0 0 0 0 zzz iii 0 0 0 4 0 0 0 zzz jjj 0 0 3 0 0 0 0 ,,A,B,C,D,E,F,G xxx,hhh,1,0,0,0,0,0,0, xxx,iii,0,0,0,0,0,6,0, xxx,jjj,0,0,0,0,0,0,7, yyy,hhh,0,0,0,0,5,0,0, yyy,iii,0,2,0,0,0,0,0, yyy,jjj,0,0,0,0,0,0,0, zzz,hhh,0,0,0,0,0,0,0, zzz,iii,0,0,0,4,0,0,0, zzz,jjj,0,0,3,0,0,0,0,
pandasのMultiIndexを利用した場合
データ分析で良く使うpandasのMultiIndexを用いた場合、stack()
unstack()
を使えば、同様の処理がより柔軟に行えるようだ。ただし、ネストされたdictionaryをそのままDataFrameに入れてMultiIndexとして使うことはできず、以下のようなリフォーム処理が必要になる。つまり、キーをタプルとしてdictionaryを作成する。
reformed_bdata = { (key1,key3,key2) : [val3] for key1, val1 in bdata.items() for key2, val2 in val1.items() for key3, val3 in val2.items() } bdf = pd.DataFrame(reformed_bdata) print(bdf.stack().stack()) reformed_cdata = { (key1,key3,key2) : [val3] for key1, val1 in cdata.items() for key2, val2 in val1.items() for key3, val3 in val2.items() } cdf = pd.DataFrame(reformed_cdata) print(cdf.stack().stack())
A B C D E F G 0 xxx hhh 1 10 19 28 37 46 55 iii 2 11 20 29 38 47 56 jjj 3 12 21 30 39 48 57 yyy hhh 4 13 22 31 40 49 58 iii 5 14 23 32 41 50 59 jjj 6 15 24 33 42 51 60 zzz hhh 7 16 25 34 43 52 61 iii 8 17 26 35 44 53 62 jjj 9 18 27 36 45 54 63 A B C D E F G 0 xxx hhh 1 0 0 0 0 0 0 iii 0 0 0 0 0 6 0 jjj 0 0 0 0 0 0 7 yyy hhh 0 0 0 0 5 0 0 iii 0 2 0 0 0 0 0 jjj 0 0 0 0 0 0 0 zzz hhh 0 0 0 0 0 0 0 iii 0 0 0 4 0 0 0 jjj 0 0 3 0 0 0 0
setdefault
collectionsモジュールをインポートしなくても、setdefaultを使えば、キーの確認をせずにキーが存在するものとして値を扱える。
$ cat setdefault_test.py a = {} a.setdefault("aaa",0) a["aaa"] += 1 print(a) b = {} b.setdefault("aaa",[]) b["aaa"].append(10) print(b) $ python setdefault_test.py {'aaa': 1} {'aaa': [10]}