0
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Python 入れ子構造の辞書を展開してPandasのMultiIndexのようなことをする

Last updated at Posted at 2020-10-07

やりたいこと

  • 入れ子構造の辞書をアンダーバーで繋いで展開したい。
    • Input: d1 = {'A':{'i':0,'j':1},'B':{'i':2,'j':3}} のような辞書の要素に辞書が入るようなデータ
    • Output: d2 = {'A_i':0, 'A_j':1, 'B_i':2, 'B_j':3}のように一次元に開かれたデータ

関数(Not Smart)

  • 入れ子構造の辞書はつまるところ,木構造になる
  • イメージとしては木構造を割いて根と末端を直接つなぐ感じの処理になる
  • 深さ優先探索っぽく実装するとなると再帰関数が良さそう

ということで書いたのが以下のようなコードになります。


# key 変更
def changekey(dict,k1,k2):
    dict[k2] = dict[k1]
    del dict[k1]

# すべてのKeyにアンダーバー付きで修飾語をつける
def addkeyheader(dict,key):
    ks = list(dict.keys())
    for k in ks:
        changekey(dict,k,key+'_'+k)

# 本命の関数 深さ優先探索をした後に要素名を結合して返す
def dict_flatten(dict_,depth=2):
    newdict = {}
    for key in list(dict_.keys()):
        # 要素もdictなら再帰的に呼び出す 
        if isinstance(dict_[key], dict):
            if depth > 0:
                dic = dict_flatten(dict_[key],depth-1)
                addkeyheader(dic,key)
                newdict.update(dic)
            else:
                newdict[key] = dict_[key]
        # 要素がdictじゃないならそのまま
        else:
            newdict[key] = dict_[key]
    return newdict

よりスマートな実装

コメントにてよりスマートな実装を教えていただきました。

def _unwrap(dct, prefix):
    for key, value in dct.items():
        new_key = f'{prefix}_{key}'
        if isinstance(value, dict):
            yield from _unwrap(value, new_key)
        else:
            yield new_key[1:], value       

def unwrap(dct):
    return dict(_unwrap(dct, ''))

d2 = unwrap(d1)

想定していた使い道

  • 入れ子構造のdictは以下のように2次元状に配置される(参考)。
  • これとは異なり単にデータを単に並べたい場合がある。

Inputを変換すると以下のようになり,

>> pd.DataFrame.from_dict(d1)
   A  B
i  0  2
j  1  3

Outputを変換すると以下の様になります。

>> pd.DataFrame(d2,index=[""])
  A_i  A_j  B_i  B_j
    0    1    2    3

これでめでたしと思いきや,pandasの理解が浅かったです。

PandasでMultiIndexを用いてナチュラルに分解が可能である

わざわざ分解するまでもなく実用上はMultiIndexの機能で編集可能です。
https://qiita.com/Morinikiz/items/40faa91e7a83807c0552

  • dfをstack()またはunstack()することでまとめられる。
    • stackで列側を高レベルに,unstackで行側を高レベルに置ける
    • ただ,こちらの解説と挙動が逆なのはなぜ?
>>> df = pd.DataFrame(d1)
>>> df
   A  B
i  0  2
j  1  3
>>> df.unstack()
A  i    0
   j    1
B  i    2
   j    3
dtype: int64
>>> df.stack()
i  A    0
   B    2
j  A    1
   B    3
dtype: int64

Data Frameに戻すには最後にto_frame()を使えばいいです。

>>> df.unstack().to_frame("name")
     name
A i     0
  j     1
B i     2
  j     3
0
4
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?