22. 다중 인덱스#
import numpy as np
import pandas as pd
pd.options.display.max_rows = 20
pd.options.display.max_colwidth = 80
pd.options.display.max_columns = 20
np.random.seed(12345)
import matplotlib.pyplot as plt
plt.rc("figure", figsize=(10, 6))
np.set_printoptions(precision=4, suppress=True)
22.1. 시리즈 계층 인덱싱#
다중 인덱스 사용 시리즈
data = pd.Series(np.random.uniform(size=9),
                 index=[["a", "a", "a", "b", "b", "c", "c", "d", "d"],
                        [1, 2, 3, 1, 3, 1, 2, 2, 3]])
data
a  1    0.929616
   2    0.316376
   3    0.183919
b  1    0.204560
   3    0.567725
c  1    0.595545
   2    0.964515
d  2    0.653177
   3    0.748907
dtype: float64
data.index
MultiIndex([('a', 1),
            ('a', 2),
            ('a', 3),
            ('b', 1),
            ('b', 3),
            ('c', 1),
            ('c', 2),
            ('d', 2),
            ('d', 3)],
           )
시리즈 인덱싱/슬라이싱
0-레벨 인덱싱
data["b"]
1    0.204560
3    0.567725
dtype: float64
0-레벨 슬라싱
data["b":"c"]
b  1    0.204560
   3    0.567725
c  1    0.595545
   2    0.964515
dtype: float64
0-레벨 팬시 인덱싱
data.loc[["b", "d"]]
b  1    0.204560
   3    0.567725
d  2    0.653177
   3    0.748907
dtype: float64
0-레벨과 1-레벨 동시 인덱싱/슬라이싱:
loc속성과 쉼표로 구분되는 레벨 별 인덱싱/슬라이싱 적용
data.loc[:, 2]
a    0.316376
c    0.964515
d    0.653177
dtype: float64
스택과 언스택
언스택은 행 인덱스의 가장 깊은 라벨을 열 인덱스의 가장 깊은 라벨로 변환한다.
data.unstack()
| 1 | 2 | 3 | |
|---|---|---|---|
| a | 0.929616 | 0.316376 | 0.183919 | 
| b | 0.204560 | NaN | 0.567725 | 
| c | 0.595545 | 0.964515 | NaN | 
| d | NaN | 0.653177 | 0.748907 | 
스택은 열 인덱스의 가장 깊은 라벨을 행 인덱스의 가장 깊은 라벨로 변환한다.
data.unstack().stack()
a  1    0.929616
   2    0.316376
   3    0.183919
b  1    0.204560
   3    0.567725
c  1    0.595545
   2    0.964515
d  2    0.653177
   3    0.748907
dtype: float64
22.2. 데이터프레임 계층 인덱싱#
다중 행/열 인덱스 사용 데이터프레임
frame = pd.DataFrame(np.arange(12).reshape((4, 3)),
                     index=[["a", "a", "b", "b"], [1, 2, 1, 2]],
                     columns=[["Ohio", "Ohio", "Colorado"],
                              ["Green", "Red", "Green"]])
frame
| Ohio | Colorado | |||
|---|---|---|---|---|
| Green | Red | Green | ||
| a | 1 | 0 | 1 | 2 | 
| 2 | 3 | 4 | 5 | |
| b | 1 | 6 | 7 | 8 | 
| 2 | 9 | 10 | 11 | |
행과 열 인덱스의 각 레벨에 이름 지정
frame.index.names = ["key1", "key2"]
frame.columns.names = ["state", "color"]
frame
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key1 | key2 | |||
| a | 1 | 0 | 1 | 2 | 
| 2 | 3 | 4 | 5 | |
| b | 1 | 6 | 7 | 8 | 
| 2 | 9 | 10 | 11 | |
행 인덱스는 2개의 레벨로 구성
frame.index.nlevels
2
기본 인덱싱은 열 인덱스의 0-레벨에 적용
frame["Ohio"]
| color | Green | Red | |
|---|---|---|---|
| key1 | key2 | ||
| a | 1 | 0 | 1 | 
| 2 | 3 | 4 | |
| b | 1 | 6 | 7 | 
| 2 | 9 | 10 | 
22.2.1. 다중 인덱스 레벨 교환과 인덱스 라벨 정렬#
레벨 교환
레벨 이름 활용
frame.swaplevel("key1", "key2")
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key2 | key1 | |||
| 1 | a | 0 | 1 | 2 | 
| 2 | a | 3 | 4 | 5 | 
| 1 | b | 6 | 7 | 8 | 
| 2 | b | 9 | 10 | 11 | 
정수 레벨 활용
frame
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key1 | key2 | |||
| a | 1 | 0 | 1 | 2 | 
| 2 | 3 | 4 | 5 | |
| b | 1 | 6 | 7 | 8 | 
| 2 | 9 | 10 | 11 | |
frame.swaplevel(0, 1)
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key2 | key1 | |||
| 1 | a | 0 | 1 | 2 | 
| 2 | a | 3 | 4 | 5 | 
| 1 | b | 6 | 7 | 8 | 
| 2 | b | 9 | 10 | 11 | 
인덱스 라벨 정렬
먼저 1-레벨의 라벨을 정렬한다.
frame.sort_index(level=1)
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key1 | key2 | |||
| a | 1 | 0 | 1 | 2 | 
| b | 1 | 6 | 7 | 8 | 
| a | 2 | 3 | 4 | 5 | 
| b | 2 | 9 | 10 | 11 | 
레벨을 교환한다.
frame.swaplevel(0, 1).sort_index(level=0)
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key2 | key1 | |||
| 1 | a | 0 | 1 | 2 | 
| b | 6 | 7 | 8 | |
| 2 | a | 3 | 4 | 5 | 
| b | 9 | 10 | 11 | |
22.2.2. 레벨 단위 그룹화#
frame.groupby(level="key2").sum()
| state | Ohio | Colorado | |
|---|---|---|---|
| color | Green | Red | Green | 
| key2 | |||
| 1 | 6 | 8 | 10 | 
| 2 | 12 | 14 | 16 | 
레벨과 축을 활용한 그룹화
level="color": 열 인덱스의 1-레벨 기준axis="columns": 열에 사용된 라벨 단위로 그룹화
frame
| state | Ohio | Colorado | ||
|---|---|---|---|---|
| color | Green | Red | Green | |
| key1 | key2 | |||
| a | 1 | 0 | 1 | 2 | 
| 2 | 3 | 4 | 5 | |
| b | 1 | 6 | 7 | 8 | 
| 2 | 9 | 10 | 11 | |
frame.groupby(level="color", axis="columns").sum()
| color | Green | Red | |
|---|---|---|---|
| key1 | key2 | ||
| a | 1 | 2 | 1 | 
| 2 | 8 | 4 | |
| b | 1 | 14 | 7 | 
| 2 | 20 | 10 | 
22.2.3. 인덱스 지정과 초기화#
frame = pd.DataFrame({"a": range(7), "b": range(7, 0, -1),
                      "c": ["one", "one", "one", "two", "two",
                            "two", "two"],
                      "d": [0, 1, 2, 0, 1, 2, 3]})
frame
| a | b | c | d | |
|---|---|---|---|---|
| 0 | 0 | 7 | one | 0 | 
| 1 | 1 | 6 | one | 1 | 
| 2 | 2 | 5 | one | 2 | 
| 3 | 3 | 4 | two | 0 | 
| 4 | 4 | 3 | two | 1 | 
| 5 | 5 | 2 | two | 2 | 
| 6 | 6 | 1 | two | 3 | 
set_index()메서드: 열의 특정 라벨을 이용하여 (멀티)인덱스를 지정한다.
frame2 = frame.set_index(["c", "d"])
frame2
| a | b | ||
|---|---|---|---|
| c | d | ||
| one | 0 | 0 | 7 | 
| 1 | 1 | 6 | |
| 2 | 2 | 5 | |
| two | 0 | 3 | 4 | 
| 1 | 4 | 3 | |
| 2 | 5 | 2 | |
| 3 | 6 | 1 | 
인덱스에 사용된 열을 그대로 둘 수도 있다.
frame.set_index(["c", "d"], drop=False)
| a | b | c | d | ||
|---|---|---|---|---|---|
| c | d | ||||
| one | 0 | 0 | 7 | one | 0 | 
| 1 | 1 | 6 | one | 1 | |
| 2 | 2 | 5 | one | 2 | |
| two | 0 | 3 | 4 | two | 0 | 
| 1 | 4 | 3 | two | 1 | |
| 2 | 5 | 2 | two | 2 | |
| 3 | 6 | 1 | two | 3 | 
reset_index()메서드: (멀티)인덱스를 열로 변환시키고 정수 인덱스를 지정
frame2.reset_index()
| c | d | a | b | |
|---|---|---|---|---|
| 0 | one | 0 | 0 | 7 | 
| 1 | one | 1 | 1 | 6 | 
| 2 | one | 2 | 2 | 5 | 
| 3 | two | 0 | 3 | 4 | 
| 4 | two | 1 | 4 | 3 | 
| 5 | two | 2 | 5 | 2 | 
| 6 | two | 3 | 6 | 1 | 
drop=True옵션: 인덱스로 사용된 라벨을 모두 삭제
frame2.reset_index(drop=True)
| a | b | |
|---|---|---|
| 0 | 0 | 7 | 
| 1 | 1 | 6 | 
| 2 | 2 | 5 | 
| 3 | 3 | 4 | 
| 4 | 4 | 3 | 
| 5 | 5 | 2 | 
| 6 | 6 | 1 | 
22.3. 모양 변환#
22.3.1. 항목 재배열#
스택
언스택
data = pd.DataFrame(np.arange(6).reshape((2, 3)),
                    index=pd.Index(["Ohio", "Colorado"], name="state"),
                    columns=pd.Index(["one", "two", "three"],
                    name="number"))
data
| number | one | two | three | 
|---|---|---|---|
| state | |||
| Ohio | 0 | 1 | 2 | 
| Colorado | 3 | 4 | 5 | 
result = data.stack()
result
state     number
Ohio      one       0
          two       1
          three     2
Colorado  one       3
          two       4
          three     5
dtype: int64
result.unstack()
| number | one | two | three | 
|---|---|---|---|
| state | |||
| Ohio | 0 | 1 | 2 | 
| Colorado | 3 | 4 | 5 | 
result.unstack(level=0)
| state | Ohio | Colorado | 
|---|---|---|
| number | ||
| one | 0 | 3 | 
| two | 1 | 4 | 
| three | 2 | 5 | 
result.unstack(level="state")
| state | Ohio | Colorado | 
|---|---|---|
| number | ||
| one | 0 | 3 | 
| two | 1 | 4 | 
| three | 2 | 5 | 
s1 = pd.Series([0, 1, 2, 3], index=["a", "b", "c", "d"], dtype="Int64")
s2 = pd.Series([4, 5, 6], index=["c", "d", "e"], dtype="Int64")
data2 = pd.concat([s1, s2], keys=["one", "two"])
data2
one  a    0
     b    1
     c    2
     d    3
two  c    4
     d    5
     e    6
dtype: Int64
data2.unstack()
| a | b | c | d | e | |
|---|---|---|---|---|---|
| one | 0 | 1 | 2 | 3 | <NA> | 
| two | <NA> | <NA> | 4 | 5 | 6 | 
data2.unstack().stack()
one  a    0
     b    1
     c    2
     d    3
two  c    4
     d    5
     e    6
dtype: Int64
dropna=False키워드 인자: 항목 재배치 과정에서 발생하는 결측치를 그대로 둚
data2.unstack().stack(dropna=False)
one  a       0
     b       1
     c       2
     d       3
     e    <NA>
two  a    <NA>
     b    <NA>
     c       4
     d       5
     e       6
dtype: Int64
레벨 활용 스택/언스택
df = pd.DataFrame({"left": result, "right": result + 5},
                  columns=pd.Index(["left", "right"], name="side"))
df
| side | left | right | |
|---|---|---|---|
| state | number | ||
| Ohio | one | 0 | 5 | 
| two | 1 | 6 | |
| three | 2 | 7 | |
| Colorado | one | 3 | 8 | 
| two | 4 | 9 | |
| three | 5 | 10 | 
df.unstack(level="state")
| side | left | right | ||
|---|---|---|---|---|
| state | Ohio | Colorado | Ohio | Colorado | 
| number | ||||
| one | 0 | 3 | 5 | 8 | 
| two | 1 | 4 | 6 | 9 | 
| three | 2 | 5 | 7 | 10 | 
df.unstack(level="state").stack(level="side")
| state | Colorado | Ohio | |
|---|---|---|---|
| number | side | ||
| one | left | 3 | 0 | 
| right | 8 | 5 | |
| two | left | 4 | 1 | 
| right | 9 | 6 | |
| three | left | 5 | 2 | 
| right | 10 | 7 | 
테이블 데이텃세에서 긴 모양 데이터프레임 생성
예제: 거시경제 데이터셋 활용
데이터셋 다운로드
base_url = "https://raw.githubusercontent.com/codingalzi/datapy/master/jupyter-book/examples/"
file = "macrodata.csv"
data = pd.read_csv(base_url + file)
1959년부터 2009년까지 총 203개의 분기quarter별 데이터 샘플 포함
data.shape
(203, 14)
head()메서드: 처음 5행 확인. 열 라벨도 확인 가능
data.head()
| year | quarter | realgdp | realcons | realinv | realgovt | realdpi | cpi | m1 | tbilrate | unemp | pop | infl | realint | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1959.0 | 1.0 | 2710.349 | 1707.4 | 286.898 | 470.045 | 1886.9 | 28.98 | 139.7 | 2.82 | 5.8 | 177.146 | 0.00 | 0.00 | 
| 1 | 1959.0 | 2.0 | 2778.801 | 1733.7 | 310.859 | 481.301 | 1919.7 | 29.15 | 141.7 | 3.08 | 5.1 | 177.830 | 2.34 | 0.74 | 
| 2 | 1959.0 | 3.0 | 2775.488 | 1751.8 | 289.226 | 491.260 | 1916.4 | 29.35 | 140.5 | 3.82 | 5.3 | 178.657 | 2.74 | 1.09 | 
| 3 | 1959.0 | 4.0 | 2785.204 | 1753.7 | 299.356 | 484.052 | 1931.3 | 29.37 | 140.0 | 4.33 | 5.6 | 179.386 | 0.27 | 4.06 | 
| 4 | 1960.0 | 1.0 | 2847.699 | 1770.5 | 331.722 | 462.199 | 1955.5 | 29.54 | 139.6 | 3.50 | 5.2 | 180.007 | 2.31 | 1.19 | 
열 라벨은 거시경제 지표 14개
data.columns
Index(['year', 'quarter', 'realgdp', 'realcons', 'realinv', 'realgovt',
       'realdpi', 'cpi', 'm1', 'tbilrate', 'unemp', 'pop', 'infl', 'realint'],
      dtype='object')
열 일부 데이터만 사용
data = data.loc[:, ["year", "quarter", "realgdp", "infl", "unemp"]]
data.head()
| year | quarter | realgdp | infl | unemp | |
|---|---|---|---|---|---|
| 0 | 1959.0 | 1.0 | 2710.349 | 0.00 | 5.8 | 
| 1 | 1959.0 | 2.0 | 2778.801 | 2.34 | 5.1 | 
| 2 | 1959.0 | 3.0 | 2775.488 | 2.74 | 5.3 | 
| 3 | 1959.0 | 4.0 | 2785.204 | 0.27 | 5.6 | 
| 4 | 1960.0 | 1.0 | 2847.699 | 2.31 | 5.2 | 
기간 인덱스:
pd.PeriodIndex객체 활용. 년도(year)와 분기(quarter) 결합
주의사항: pop() 메서드를 사용하기에 data 데이터프레임에서 해당 열 삭제됨
periods = pd.PeriodIndex(year=data.pop("year"),       # pop()
                         quarter=data.pop("quarter"), # pop()
                         name="date")                 # 인덱스 이름
periods
PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
             '1960Q3', '1960Q4', '1961Q1', '1961Q2',
             ...
             '2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
             '2008Q4', '2009Q1', '2009Q2', '2009Q3'],
            dtype='period[Q-DEC]', name='date', length=203)
to_timestamp("D"): 일 단위의DatetimeIndex로 변환
data.index = periods.to_timestamp("D")
data.index
DatetimeIndex(['1959-01-01', '1959-04-01', '1959-07-01', '1959-10-01',
               '1960-01-01', '1960-04-01', '1960-07-01', '1960-10-01',
               '1961-01-01', '1961-04-01',
               ...
               '2007-04-01', '2007-07-01', '2007-10-01', '2008-01-01',
               '2008-04-01', '2008-07-01', '2008-10-01', '2009-01-01',
               '2009-04-01', '2009-07-01'],
              dtype='datetime64[ns]', name='date', length=203, freq='QS-OCT')
data.head()
| realgdp | infl | unemp | |
|---|---|---|---|
| date | |||
| 1959-01-01 | 2710.349 | 0.00 | 5.8 | 
| 1959-04-01 | 2778.801 | 2.34 | 5.1 | 
| 1959-07-01 | 2775.488 | 2.74 | 5.3 | 
| 1959-10-01 | 2785.204 | 0.27 | 5.6 | 
| 1960-01-01 | 2847.699 | 2.31 | 5.2 | 
리인덱싱
data = data.reindex(columns=["realgdp", "infl", "unemp"])
열 인덱스에 이름 지정
data.columns.name = "item"
data.head()
| item | realgdp | infl | unemp | 
|---|---|---|---|
| date | |||
| 1959-01-01 | 2710.349 | 0.00 | 5.8 | 
| 1959-04-01 | 2778.801 | 2.34 | 5.1 | 
| 1959-07-01 | 2775.488 | 2.74 | 5.3 | 
| 1959-10-01 | 2785.204 | 0.27 | 5.6 | 
| 1960-01-01 | 2847.699 | 2.31 | 5.2 | 
항목 재배열: 스택/언스택 활용
data.stack()
date        item   
1959-01-01  realgdp     2710.349
            infl           0.000
            unemp          5.800
1959-04-01  realgdp     2778.801
            infl           2.340
                         ...    
2009-04-01  infl           3.370
            unemp          9.200
2009-07-01  realgdp    12990.341
            infl           3.560
            unemp          9.600
Length: 609, dtype: float64
data.stack().reset_index()
| date | item | 0 | |
|---|---|---|---|
| 0 | 1959-01-01 | realgdp | 2710.349 | 
| 1 | 1959-01-01 | infl | 0.000 | 
| 2 | 1959-01-01 | unemp | 5.800 | 
| 3 | 1959-04-01 | realgdp | 2778.801 | 
| 4 | 1959-04-01 | infl | 2.340 | 
| ... | ... | ... | ... | 
| 604 | 2009-04-01 | infl | 3.370 | 
| 605 | 2009-04-01 | unemp | 9.200 | 
| 606 | 2009-07-01 | realgdp | 12990.341 | 
| 607 | 2009-07-01 | infl | 3.560 | 
| 608 | 2009-07-01 | unemp | 9.600 | 
609 rows × 3 columns
long_data = (data.stack()
             .reset_index()
             .rename(columns={0: "value"}))
long_data[:10]
| date | item | value | |
|---|---|---|---|
| 0 | 1959-01-01 | realgdp | 2710.349 | 
| 1 | 1959-01-01 | infl | 0.000 | 
| 2 | 1959-01-01 | unemp | 5.800 | 
| 3 | 1959-04-01 | realgdp | 2778.801 | 
| 4 | 1959-04-01 | infl | 2.340 | 
| 5 | 1959-04-01 | unemp | 5.100 | 
| 6 | 1959-07-01 | realgdp | 2775.488 | 
| 7 | 1959-07-01 | infl | 2.740 | 
| 8 | 1959-07-01 | unemp | 5.300 | 
| 9 | 1959-10-01 | realgdp | 2785.204 | 
22.3.2. 피버팅#
긴 모양 데이터프레임을 넓은 모양 데이터프레임으로 변환하는 과정을 살펴 본다.
한 개의 열로 구성된 갑을 사용하는 피버팅
pivoted = long_data.pivot(index="date", columns="item", values="value")
pivoted.head()
| item | infl | realgdp | unemp | 
|---|---|---|---|
| date | |||
| 1959-01-01 | 0.00 | 2710.349 | 5.8 | 
| 1959-04-01 | 2.34 | 2778.801 | 5.1 | 
| 1959-07-01 | 2.74 | 2775.488 | 5.3 | 
| 1959-10-01 | 0.27 | 2785.204 | 5.6 | 
| 1960-01-01 | 2.31 | 2847.699 | 5.2 | 
여러 개의 열로 구성된 갑을 사용하는 피버팅
long_data["value2"] = np.random.standard_normal(len(long_data))
long_data[:10]
| date | item | value | value2 | |
|---|---|---|---|---|
| 0 | 1959-01-01 | realgdp | 2710.349 | 1.248804 | 
| 1 | 1959-01-01 | infl | 0.000 | 0.774191 | 
| 2 | 1959-01-01 | unemp | 5.800 | -0.319657 | 
| 3 | 1959-04-01 | realgdp | 2778.801 | -0.624964 | 
| 4 | 1959-04-01 | infl | 2.340 | 1.078814 | 
| 5 | 1959-04-01 | unemp | 5.100 | 0.544647 | 
| 6 | 1959-07-01 | realgdp | 2775.488 | 0.855588 | 
| 7 | 1959-07-01 | infl | 2.740 | 1.343268 | 
| 8 | 1959-07-01 | unemp | 5.300 | -0.267175 | 
| 9 | 1959-10-01 | realgdp | 2785.204 | 1.793095 | 
값으로 사용되는 열 별로 열 인덱스 지정. 따라서 다중 인덱스가 열 인덱스로 사용됨.
pivoted = long_data.pivot(index="date", columns="item")
pivoted.head()
| value | value2 | |||||
|---|---|---|---|---|---|---|
| item | infl | realgdp | unemp | infl | realgdp | unemp | 
| date | ||||||
| 1959-01-01 | 0.00 | 2710.349 | 5.8 | 0.774191 | 1.248804 | -0.319657 | 
| 1959-04-01 | 2.34 | 2778.801 | 5.1 | 1.078814 | -0.624964 | 0.544647 | 
| 1959-07-01 | 2.74 | 2775.488 | 5.3 | 1.343268 | 0.855588 | -0.267175 | 
| 1959-10-01 | 0.27 | 2785.204 | 5.6 | -0.652929 | 1.793095 | -1.886837 | 
| 1960-01-01 | 2.31 | 2847.699 | 5.2 | 0.644448 | 1.059626 | -0.007799 | 
pivoted["value"].head()
| item | infl | realgdp | unemp | 
|---|---|---|---|
| date | |||
| 1959-01-01 | 0.00 | 2710.349 | 5.8 | 
| 1959-04-01 | 2.34 | 2778.801 | 5.1 | 
| 1959-07-01 | 2.74 | 2775.488 | 5.3 | 
| 1959-10-01 | 0.27 | 2785.204 | 5.6 | 
| 1960-01-01 | 2.31 | 2847.699 | 5.2 | 
피버팅은 set_index() 메서드와 unstack() 메서드를 연속적으로 적용하는 것과 동일한 결과를 낸다.
long_data.head(10)
| date | item | value | value2 | |
|---|---|---|---|---|
| 0 | 1959-01-01 | realgdp | 2710.349 | 1.248804 | 
| 1 | 1959-01-01 | infl | 0.000 | 0.774191 | 
| 2 | 1959-01-01 | unemp | 5.800 | -0.319657 | 
| 3 | 1959-04-01 | realgdp | 2778.801 | -0.624964 | 
| 4 | 1959-04-01 | infl | 2.340 | 1.078814 | 
| 5 | 1959-04-01 | unemp | 5.100 | 0.544647 | 
| 6 | 1959-07-01 | realgdp | 2775.488 | 0.855588 | 
| 7 | 1959-07-01 | infl | 2.740 | 1.343268 | 
| 8 | 1959-07-01 | unemp | 5.300 | -0.267175 | 
| 9 | 1959-10-01 | realgdp | 2785.204 | 1.793095 | 
long_data.set_index(["date", "item"])
| value | value2 | ||
|---|---|---|---|
| date | item | ||
| 1959-01-01 | realgdp | 2710.349 | 1.248804 | 
| infl | 0.000 | 0.774191 | |
| unemp | 5.800 | -0.319657 | |
| 1959-04-01 | realgdp | 2778.801 | -0.624964 | 
| infl | 2.340 | 1.078814 | |
| ... | ... | ... | ... | 
| 2009-04-01 | infl | 3.370 | 0.904582 | 
| unemp | 9.200 | 1.374036 | |
| 2009-07-01 | realgdp | 12990.341 | 1.132698 | 
| infl | 3.560 | 0.314682 | |
| unemp | 9.600 | 1.515960 | 
609 rows × 2 columns
unstacked = long_data.set_index(["date", "item"]).unstack(level="item")
unstacked.head()
| value | value2 | |||||
|---|---|---|---|---|---|---|
| item | infl | realgdp | unemp | infl | realgdp | unemp | 
| date | ||||||
| 1959-01-01 | 0.00 | 2710.349 | 5.8 | 0.774191 | 1.248804 | -0.319657 | 
| 1959-04-01 | 2.34 | 2778.801 | 5.1 | 1.078814 | -0.624964 | 0.544647 | 
| 1959-07-01 | 2.74 | 2775.488 | 5.3 | 1.343268 | 0.855588 | -0.267175 | 
| 1959-10-01 | 0.27 | 2785.204 | 5.6 | -0.652929 | 1.793095 | -1.886837 | 
| 1960-01-01 | 2.31 | 2847.699 | 5.2 | 0.644448 | 1.059626 | -0.007799 | 
22.3.3. 언피버팅#
넓은 모양 데이터프레임을 긴 모양 데이터프레임으로 변환하는 과정을 살펴 본다.
df = pd.DataFrame({"key": ["foo", "bar", "baz"],
                   "A": [1, 2, 3],
                   "B": [4, 5, 6],
                   "C": [7, 8, 9]})
df
| key | A | B | C | |
|---|---|---|---|---|
| 0 | foo | 1 | 4 | 7 | 
| 1 | bar | 2 | 5 | 8 | 
| 2 | baz | 3 | 6 | 9 | 
set_index()메서드와stack()메서드를 연속적용하면 여러 개의 열이 하나의 열로 구성된 긴 모양 데이터프레임 생성 가능
df.set_index(["key"])
| A | B | C | |
|---|---|---|---|
| key | |||
| foo | 1 | 4 | 7 | 
| bar | 2 | 5 | 8 | 
| baz | 3 | 6 | 9 | 
df.set_index(["key"]).stack()
key   
foo  A    1
     B    4
     C    7
bar  A    2
     B    5
     C    8
baz  A    3
     B    6
     C    9
dtype: int64
reshaped = df.set_index(["key"]).stack().reset_index()
reshaped
| key | level_1 | 0 | |
|---|---|---|---|
| 0 | foo | A | 1 | 
| 1 | foo | B | 4 | 
| 2 | foo | C | 7 | 
| 3 | bar | A | 2 | 
| 4 | bar | B | 5 | 
| 5 | bar | C | 8 | 
| 6 | baz | A | 3 | 
| 7 | baz | B | 6 | 
| 8 | baz | C | 9 | 
reshaped.columns = ["key", "variable", "value"]
reshaped.sort_values("key")
| key | variable | value | |
|---|---|---|---|
| 3 | bar | A | 2 | 
| 4 | bar | B | 5 | 
| 5 | bar | C | 8 | 
| 6 | baz | A | 3 | 
| 7 | baz | B | 6 | 
| 8 | baz | C | 9 | 
| 0 | foo | A | 1 | 
| 1 | foo | B | 4 | 
| 2 | foo | C | 7 | 
pd.melt()함수: 위 과정을 한 번에 처리한다.id_vars키워드 인자: 그룹 식별자 사용될 값들의 열 지정.
melted = pd.melt(df, id_vars="key")
melted
| key | variable | value | |
|---|---|---|---|
| 0 | foo | A | 1 | 
| 1 | bar | A | 2 | 
| 2 | baz | A | 3 | 
| 3 | foo | B | 4 | 
| 4 | bar | B | 5 | 
| 5 | baz | B | 6 | 
| 6 | foo | C | 7 | 
| 7 | bar | C | 8 | 
| 8 | baz | C | 9 | 
melted = melted.sort_values("key")
melted
| key | variable | value | |
|---|---|---|---|
| 1 | bar | A | 2 | 
| 4 | bar | B | 5 | 
| 7 | bar | C | 8 | 
| 2 | baz | A | 3 | 
| 5 | baz | B | 6 | 
| 8 | baz | C | 9 | 
| 0 | foo | A | 1 | 
| 3 | foo | B | 4 | 
| 6 | foo | C | 7 | 
pivot()메서드를 적용하면 다시 넓은 모양 데이터프레임으로 변환
reshaped = melted.pivot(index="key", columns="variable",
                        values="value")
reshaped
| variable | A | B | C | 
|---|---|---|---|
| key | |||
| bar | 2 | 5 | 8 | 
| baz | 3 | 6 | 9 | 
| foo | 1 | 4 | 7 | 
reshaped.reset_index()
| variable | key | A | B | C | 
|---|---|---|---|---|
| 0 | bar | 2 | 5 | 8 | 
| 1 | baz | 3 | 6 | 9 | 
| 2 | foo | 1 | 4 | 7 | 
value_vars=["A", "B"]키워드 인자:value열에 사용되는 값과 연관된 열 지정. 여기서는A,B열의 값만 사용.
pd.melt(df, id_vars="key", value_vars=["A", "B"])
| key | variable | value | |
|---|---|---|---|
| 0 | foo | A | 1 | 
| 1 | bar | A | 2 | 
| 2 | baz | A | 3 | 
| 3 | foo | B | 4 | 
| 4 | bar | B | 5 | 
| 5 | baz | B | 6 | 
id_vars키워드 인자 생략: 그룹 식별자 사용하지 않음
pd.melt(df, value_vars=["A", "B", "C"])
| variable | value | |
|---|---|---|
| 0 | A | 1 | 
| 1 | A | 2 | 
| 2 | A | 3 | 
| 3 | B | 4 | 
| 4 | B | 5 | 
| 5 | B | 6 | 
| 6 | C | 7 | 
| 7 | C | 8 | 
| 8 | C | 9 | 
pd.melt(df, value_vars=["key", "A", "B"])
| variable | value | |
|---|---|---|
| 0 | key | foo | 
| 1 | key | bar | 
| 2 | key | baz | 
| 3 | A | 1 | 
| 4 | A | 2 | 
| 5 | A | 3 | 
| 6 | B | 4 | 
| 7 | B | 5 | 
| 8 | B | 6 |