ネストされた辞書のアイテムからパンダDataFrameを構築します


90

構造を持つネストされた辞書 'user_dict'があるとします。

  • レベル1: UserId(長整数)
  • レベル2:カテゴリ(文字列)
  • レベル3:さまざまな属性(float、intなど)

たとえば、この辞書のエントリは次のようになります。

user_dict[12] = {
    "Category 1": {"att_1": 1, 
                   "att_2": "whatever"},
    "Category 2": {"att_1": 23, 
                   "att_2": "another"}}

の各アイテムuser_dictは同じ構造でありuser_dict、パンダのDataFrameにフィードするアイテムが多数含まれており、属性からシリーズを構築します。この場合、階層インデックスがこの目的に役立ちます。

具体的には、私の質問は、シリーズが辞書の「レベル3」の値から構築されるべきであることをDataFrameコンストラクターが理解するのを助ける方法があるかどうかです。

私が次のようなことを試みた場合:

df = pandas.DataFrame(users_summary)

「レベル1」(ユーザーID)の項目は列として取得されます。これは、私が達成したいものとは逆です(ユーザーIDをインデックスとして使用します)。

辞書のエントリを繰り返し処理した後でシリーズを作成できることはわかっていますが、もっと直接的な方法があれば、これは非常に便利です。同様の質問は、ファイルにリストされているjsonオブジェクトからパンダDataFrameを構築できるかどうかを尋ねることです。


より簡単な代替案については、この回答を参照してください。
cs 9519年

回答:


141

pandas MultiIndexは、タプルのリストで構成されています。したがって、最も自然なアプローチは、入力dictの形状を変更して、そのキーが必要な多重指数値に対応するタプルになるようにすることです。次にpd.DataFrame.from_dict、オプションを使用して、を使用してデータフレームを構築できますorient='index'

user_dict = {12: {'Category 1': {'att_1': 1, 'att_2': 'whatever'},
                  'Category 2': {'att_1': 23, 'att_2': 'another'}},
             15: {'Category 1': {'att_1': 10, 'att_2': 'foo'},
                  'Category 2': {'att_1': 30, 'att_2': 'bar'}}}

pd.DataFrame.from_dict({(i,j): user_dict[i][j] 
                           for i in user_dict.keys() 
                           for j in user_dict[i].keys()},
                       orient='index')


               att_1     att_2
12 Category 1      1  whatever
   Category 2     23   another
15 Category 1     10       foo
   Category 2     30       bar

別のアプローチは、コンポーネントのデータフレームを連結してデータフレームを構築することです。

user_ids = []
frames = []

for user_id, d in user_dict.iteritems():
    user_ids.append(user_id)
    frames.append(pd.DataFrame.from_dict(d, orient='index'))

pd.concat(frames, keys=user_ids)

               att_1     att_2
12 Category 1      1  whatever
   Category 2     23   another
15 Category 1     10       foo
   Category 2     30       bar

11
これを一般化して、任意の深さの不規則なリストで機能する合理的な方法はありますか?たとえば、任意の深さのリスト。一部のブランチは他のブランチよりも短い場合があり、短いブランチが最後に到達しない場合はNoneまたはnanが使用されますか?
naught101 2013

5
pandas jsonサポート(ioツール)と正規化を見たことがありますか?pandas.pydata.org/pandas-docs/dev/io.html#normalization
Wouter Overmeire 2013年

1
私にとって、最初のメソッドは、タプルを持つ単一のインデックスを持つデータフレームを作成しました。2番目の方法は期待どおりに機能しました!
arturomp 2018

これらの新しい列に名前を付ける方法に関するヒントはありますか?たとえば、これらの番号12と15を列「id」に入れたい場合です。
cheremushkin

1
@cheremushkin 12と15は、行 'id'にあり、転置すると(pandas.pydata.org/pandas-docs/stable/reference/api/…)、列 'id'にあります。スタックを解除することもできます(pandas.pydata.org/pandas-docs/stable/reference/api/…)それはすべて、本当に必要なものによって異なります。
WouterOvermeire19年

33

pd.concat辞書を受け入れます。これを念頭に置いて、辞書の理解を使用してキーをサブフレームにマッピングする辞書を作成することにより、単純さとパフォーマンスの点で現在受け入れられている回答を改善することができます。

pd.concat({k: pd.DataFrame(v).T for k, v in user_dict.items()}, axis=0)

または、

pd.concat({
        k: pd.DataFrame.from_dict(v, 'index') for k, v in user_dict.items()
    }, 
    axis=0)

              att_1     att_2
12 Category 1     1  whatever
   Category 2    23   another
15 Category 1    10       foo
   Category 2    30       bar

4
鮮やかさ!はるかに良い:)
pg24 5519年

3
あなたがまださらに内側のカテゴリーを持っているなら、あなたはそれをどのように行いますか?など12:{cat1:{cat11:{att1:val1,att2:val2}}}。言い換えれば、関係のない数のカテゴリにソリューションを一般化する方法はありますか?
ルーカスアイマレット

1
@LucasAimaretto通常、任意にネストされた構造はjson_normalize。でフラット化できます。私が持っている別の答え、それがどのように動作するかを示しています。
cs 9519

1
vたとえば、が単一の整数の場合は機能しません。そのような場合の代替案を知っていますか?
SK

11

そのため、以前はforループを使用して辞書を反復処理していましたが、はるかに高速に機能することがわかったのは、パネルに変換してからデータフレームに変換することです。あなたが辞書を持っているとしましょうd

import pandas as pd
d
{'RAY Index': {datetime.date(2014, 11, 3): {'PX_LAST': 1199.46,
'PX_OPEN': 1200.14},
datetime.date(2014, 11, 4): {'PX_LAST': 1195.323, 'PX_OPEN': 1197.69},
datetime.date(2014, 11, 5): {'PX_LAST': 1200.936, 'PX_OPEN': 1195.32},
datetime.date(2014, 11, 6): {'PX_LAST': 1206.061, 'PX_OPEN': 1200.62}},
'SPX Index': {datetime.date(2014, 11, 3): {'PX_LAST': 2017.81,
'PX_OPEN': 2018.21},
datetime.date(2014, 11, 4): {'PX_LAST': 2012.1, 'PX_OPEN': 2015.81},
datetime.date(2014, 11, 5): {'PX_LAST': 2023.57, 'PX_OPEN': 2015.29},
datetime.date(2014, 11, 6): {'PX_LAST': 2031.21, 'PX_OPEN': 2023.33}}}

コマンド

pd.Panel(d)
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 2 (major_axis) x 4 (minor_axis)
Items axis: RAY Index to SPX Index
Major_axis axis: PX_LAST to PX_OPEN
Minor_axis axis: 2014-11-03 to 2014-11-06

ここで、pd.Panel(d)[item]はデータフレームを生成します

pd.Panel(d)['SPX Index']
2014-11-03  2014-11-04  2014-11-05 2014-11-06
PX_LAST 2017.81 2012.10 2023.57 2031.21
PX_OPEN 2018.21 2015.81 2015.29 2023.33

次に、コマンドto_frame()を押して、データフレームに変換できます。また、reset_indexを使用して、長軸と短軸をインデックスとして使用するのではなく、列に変換します。

pd.Panel(d).to_frame().reset_index()
major   minor      RAY Index    SPX Index
PX_LAST 2014-11-03  1199.460    2017.81
PX_LAST 2014-11-04  1195.323    2012.10
PX_LAST 2014-11-05  1200.936    2023.57
PX_LAST 2014-11-06  1206.061    2031.21
PX_OPEN 2014-11-03  1200.140    2018.21
PX_OPEN 2014-11-04  1197.690    2015.81
PX_OPEN 2014-11-05  1195.320    2015.29
PX_OPEN 2014-11-06  1200.620    2023.33

最後に、フレームの外観が気に入らない場合は、to_frame()を呼び出す前に、パネルの転置関数を使用して外観を変更できます。こちらのドキュメントを参照してください http://pandas.pydata.org/pandas-docs/dev/generated /pandas.Panel.transpose.html

例として

pd.Panel(d).transpose(2,0,1).to_frame().reset_index()
major        minor  2014-11-03  2014-11-04  2014-11-05  2014-11-06
RAY Index   PX_LAST 1199.46    1195.323     1200.936    1206.061
RAY Index   PX_OPEN 1200.14    1197.690     1195.320    1200.620
SPX Index   PX_LAST 2017.81    2012.100     2023.570    2031.210
SPX Index   PX_OPEN 2018.21    2015.810     2015.290    2023.330

お役に立てれば。


8
Panelは、パンダの最新バージョン(執筆時点ではv0.23)では非推奨です。
cs 9519年

6

誰かがマルチインデックスなしで「長い形式」(リーフ値は同じタイプ)でデータフレームを取得したい場合は、次のようにすることができます。

pd.DataFrame.from_records(
    [
        (level1, level2, level3, leaf)
        for level1, level2_dict in user_dict.items()
        for level2, level3_dict in level2_dict.items()
        for level3, leaf in level3_dict.items()
    ],
    columns=['UserId', 'Category', 'Attribute', 'value']
)

    UserId    Category Attribute     value
0       12  Category 1     att_1         1
1       12  Category 1     att_2  whatever
2       12  Category 2     att_1        23
3       12  Category 2     att_2   another
4       15  Category 1     att_1        10
5       15  Category 1     att_2       foo
6       15  Category 2     att_1        30
7       15  Category 2     att_2       bar

(元の質問では、おそらく(I.)レベル1と2を多重指数、レベル3を列として使用し、(II。)dictの値を反復する以外の方法について質問することを望んでいることはわかっていますが、この回答が引き続き適切であることを願っています。便利です(I.):ネストされたdictをこの形にする方法を見つけようとした私のような人々にとって、googleはこの質問のみを返します(II。):他の回答にもいくつかの反復が含まれているため、これを見つけました柔軟で読みやすいアプローチですが、パフォーマンスについてはよくわかりません。)


0

検証された答えに基づいて、私にとってこれは最もうまくいきました:

ab = pd.concat({k: pd.DataFrame(v).T for k, v in data.items()}, axis=0)
ab.T
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.