중첩 된 사전의 항목에서 Pandas DataFrame 생성


89

구조가있는 중첩 사전 'user_dict'가 있다고 가정합니다.

  • 수준 1 : UserId (긴 정수)
  • 수준 2 : 범주 (문자열)
  • 레벨 3 : 여러 속성 (floats, int 등)

예를 들어,이 사전의 항목은 다음과 같습니다.

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

의 각 항목 user_dict은 동일한 구조 user_dict를 가지며 pandas DataFrame에 공급하려는 많은 항목을 포함하여 속성에서 시리즈를 구성합니다. 이 경우 계층 적 인덱스가 목적에 유용합니다.

특히, 내 질문은 DataFrame 생성자가 사전의 "수준 3"값에서 시리즈를 작성해야한다는 것을 이해하는 데 도움이되는 방법이 있는지 여부입니다.

다음과 같이 시도하면 :

df = pandas.DataFrame(users_summary)

"수준 1"(사용자 ID)의 항목은 열로 간주되며, 이는 내가 달성하려는 것과 반대입니다 (사용자 ID를 인덱스로 사용).

사전 항목을 반복 한 후 시리즈를 구성 할 수 있다는 것을 알고 있지만보다 직접적인 방법이 있다면 이것은 매우 유용 할 것입니다. 비슷한 질문은 파일에 나열된 json 객체에서 pandas DataFrame을 구성 할 수 있는지 여부를 묻는 것입니다.


더 간단한 대안 은 이 답변 을 참조하십시오 .
cs95

답변:


138

Pandas MultiIndex는 튜플 목록으로 구성됩니다. 따라서 가장 자연스러운 접근 방식은 키가 필요한 다중 인덱스 값에 해당하는 튜플이되도록 입력 사전의 모양을 변경하는 것입니다. 그런 다음 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

5
Pandas json 지원 (io 도구) 및 정규화를 살펴 보셨습니까? pandas.pydata.org/pandas-docs/dev/io.html#normalization
Wouter Overmeire

1
나를 위해 첫 번째 방법은 튜플이있는 단일 인덱스로 데이터 프레임을 만들었습니다. 두 번째 방법은 원하는 / 예상대로 작동했습니다!
arturomp

이 새 열의 이름을 지정하는 방법에 대한 팁이 있습니까? 예를 들어,이 숫자 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/… ) 모두 실제로 필요한 것에 따라 다릅니다.
Wouter Overmeire 2019 년

30

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
훌륭한! 훨씬 더 :)
pg2455

3
여전히 더 많은 내부 범주가 있다면 어떻게할까요? 등 12:{cat1:{cat11:{att1:val1,att2:val2}}}. 다시 말해, 어떤 사람은 관련없는 범주의 수에 대한 솔루션을 어떻게 일반화할까요?
Lucas Aimaretto

1
@LucasAimaretto 일반적으로 임의로 중첩 된 구조는 json_normalize. 내가 가진 다른 답변 그것이 어떻게 작동하는지 보여줍니다.
cs95

1
예를 들어가 v단일 정수 이면 작동하지 않습니다 . 그런 경우에 대안을 알고 있습니까?
sk

11

그래서 저는 딕셔너리를 반복하기 위해 for 루프를 사용했지만 훨씬 더 빠르게 작동하는 한 가지는 패널로 변환 한 다음 데이터 프레임으로 변환하는 것입니다. 사전이 있다고 가정 해보자.

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
패널은 최신 버전의 pandas (작성 당시 v0.23)에서 더 이상 사용되지 않습니다.
cs95

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.