포스트

Hierarchical_Indexing

Hierarchical_Indexing

Hierarchical_Indexing

1
2
import pandas as pd
import numpy as np

A Multiply Indexed Series

The bad way

1
2
3
4
5
6
7
8
index = [('California', 2000), ('California', 2010),
         ('New York', 2000), ('New York', 2010),
         ('Texas', 2000), ('Texas', 2010)]
populations = [33871648, 37253956,
               18976457, 19378102,
               20851820, 25145561]
pop = pd.Series(populations, index=index)
pop
1
2
3
4
5
6
7
(California, 2000)    33871648
(California, 2010)    37253956
(New York, 2000)      18976457
(New York, 2010)      19378102
(Texas, 2000)         20851820
(Texas, 2010)         25145561
dtype: int64
1
pop[('California', 2010):('Texas', 2000)]
1
2
3
4
5
(California, 2010)    37253956
(New York, 2000)      18976457
(New York, 2010)      19378102
(Texas, 2000)         20851820
dtype: int64
1
2
3
# 2010년의 모든 값을 선택해야 한다면 데이터 먼징(munging)을 해야 한다
# 깔끔하지 못하고 대규모 데이터의 경우 비효율적
pop[[i for i in pop.index if i[1] == 2010]]
1
2
3
4
(California, 2010)    37253956
(New York, 2010)      19378102
(Texas, 2010)         25145561
dtype: int64

The Better Way: Pandas MultiIndex

1
2
index = pd.MultiIndex.from_tuples(index)
index
1
2
3
4
5
6
7
MultiIndex([('California', 2000),
            ('California', 2010),
            (  'New York', 2000),
            (  'New York', 2010),
            (     'Texas', 2000),
            (     'Texas', 2010)],
           )
1
2
pop = pop.reindex(index)
pop
1
2
3
4
5
6
7
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
1
pop[:, 2010]
1
2
3
4
California    37253956
New York      19378102
Texas         25145561
dtype: int64

MultiIndex as extra dimension

1
2
3
# 다중 인덱스를 가진 Series를 전형적인 인덱스를 가진 DataFrame으로 변환
pop_df = pop.unstack()
pop_df
20002010
California3387164837253956
New York1897645719378102
Texas2085182025145561
1
pop_df.stack()
1
2
3
4
5
6
7
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
1
2
3
4
5
6
# 계측적 인덱싱을 통해 데이터 타입에 대한 유연성 확보
pop_df = pd.DataFrame({'total': pop,
                       'under18': [9267089, 9284094,
                                   4687374, 4318033,
                                   5906301, 6879014]})
pop_df
totalunder18
California2000338716489267089
2010372539569284094
New York2000189764574687374
2010193781024318033
Texas2000208518205906301
2010251455616879014
1
2
3
# 고차원 데이터를 쉽고 빠르게 가공하고 탐색 가능
f_u18 = pop_df['under18'] / pop_df['total']
f_u18.unstack()
20002010
California0.2735940.249211
New York0.2470100.222831
Texas0.2832510.273568

Methods of MultiIndex Creation

1
2
3
4
5
6
# 다중 인덱스를 가진 Series나 DataFrame을 생성하는 가장 간단한 방식은
# 생성자에 2개 이상의 인덱스 배열 리스트를 전달하는 것
df = pd.DataFrame(np.random.rand(4, 2),
                  index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
                  columns=['data1', 'data2'])
df
data1data2
a10.4586820.273650
20.4462610.781636
b10.6155800.353759
20.3568260.624523
1
2
3
4
5
6
7
8
# 튜플을 키로 갖는 딕셔너리를 전달하면 Pandas는 자동으로 인식해 MultiIndex를 사용
data = {('California', 2000): 33871648,
        ('California', 2010): 37253956,
        ('Texas', 2000): 20851820,
        ('Texas', 2010): 25145561,
        ('New York', 2000): 18976457,
        ('New York', 2010): 19378102}
pd.Series(data)
1
2
3
4
5
6
7
California  2000    33871648
            2010    37253956
Texas       2000    20851820
            2010    25145561
New York    2000    18976457
            2010    19378102
dtype: int64

명시적 MultiIndex 생성자 Explicit MultiIndex constructors

1
2
# 각 레벨 내에 인덱스 값을 제공하는 간단한 배열 리스트로부터 MultiIndex 생성
pd.MultiIndex.from_arrays([['a', 'a', 'b', 'b'], [1, 2, 1, 2]])
1
2
3
4
5
MultiIndex([('a', 1),
            ('a', 2),
            ('b', 1),
            ('b', 2)],
           )
1
2
# 각 점의 여러 인덱스 값을 제공하는 튜플 리스트로부터 생성
pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1), ('b', 2)])
1
2
3
4
5
MultiIndex([('a', 1),
            ('a', 2),
            ('b', 1),
            ('b', 2)],
           )
1
2
# 단일 인덱스의 데카르트 곱(Cartesian product)으로부터 MultiIndex를 생성
pd.MultiIndex.from_product([['a', 'b'], [1, 2]])
1
2
3
4
5
MultiIndex([('a', 1),
            ('a', 2),
            ('b', 1),
            ('b', 2)],
           )
1
2
3
4
5
# levels(MultiIndex의 고유한 값들의 집합을 나타내는 리스트)
# codes(각 데이터 포인트가 levels에 어떻게 매핑되는지를 나타내는 정수 값들의 리스트)
# levels와 codes를 전달함으로써 그 내부 인코딩을 사용해 직접 MultiIndex를 생성
pd.MultiIndex(levels=[['a', 'b'], [1, 2]],
              codes=[[0, 0, 1, 1], [0, 1, 0, 1]])
1
2
3
4
5
MultiIndex([('a', 1),
            ('a', 2),
            ('b', 1),
            ('b', 2)],
           )

MultiIndex level names

1
2
3
# MultiIndex생성자에 names 인수를 전달하거나 생성 후에 인덱스의 name속성을 설정해 이름을 지정
pop.index.names = ['state', 'year']
pop
1
2
3
4
5
6
7
8
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64

MultiIndex for columns

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 계층적 인덱스와 열 hierarchical indices and columns
index = pd.MultiIndex.from_product([[2013, 2014], [1, 2]],
                                   names=['year', 'visit'])
columns = pd.MultiIndex.from_product([['Bob', 'Guido', 'Sue'], ['HR', 'Temp']],
                                     names=['subject', 'type'])

# 일부 데이터 모형 만들기 mock some data
data = np.round(np.random.randn(4, 6), 1)
data[:, ::2] *= 10
data += 37

# create the DataFrame
health_data = pd.DataFrame(data, index=index, columns=columns)
health_data
subjectBobGuidoSue
typeHRTempHRTempHRTemp
yearvisit
2013137.038.031.035.037.036.4
237.036.525.039.368.038.2
2014143.035.324.038.237.038.4
237.037.647.036.562.036.7
1
2
# 특정 subject로 최상위 열의 인덱스를 지정하고 그 subject의 정보를 포함하는 전체 DataFrame을 가져올 수 있다
health_data['Guido']
typeHRTemp
yearvisit
2013131.035.0
225.039.3
2014124.038.2
247.036.5

Indexing and Slicing a MultiIndex

Multiply indexed Series

1
pop
1
2
3
4
5
6
7
8
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64
1
2
# 여러 용어로 인덱싱해서 단일 요소에 접근 가능
pop['California', 2000]
1
33871648
1
2
# 부분 인덱싱(partial indexing)이나 인덱스 레벨 중 하나만 인덱싱하는 것도 지원
pop['California']
1
2
3
4
year
2000    33871648
2010    37253956
dtype: int64
1
2
# MultiIndex가 정렬되어 있다면 부분 슬라이싱 가능
pop.loc['California':'New York']
1
2
3
4
5
6
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
dtype: int64
1
2
# index가 정렬되어 있다면 첫 번째 인덱스에 빈 슬라이스를 전달함으로써 더 낮은 레벨에서 부분인덱싱 수행 가능
pop[:, 2000]
1
2
3
4
5
state
California    33871648
New York      18976457
Texas         20851820
dtype: int64
1
2
# 다른 유형의 데이터 인덱싱과 선택 방식 적용 가능
pop[pop > 22000000]
1
2
3
4
5
state       year
California  2000    33871648
            2010    37253956
Texas       2010    25145561
dtype: int64
1
2
# 팬시 인덱싱을 이용한 데이터 선택 가능
pop[['California', 'Texas']]
1
2
3
4
5
6
state       year
California  2000    33871648
            2010    37253956
Texas       2000    20851820
            2010    25145561
dtype: int64

Multiply indexed DataFrames

1
2
# 열은 DataFrame의 기본 요소
health_data
subjectBobGuidoSue
typeHRTempHRTempHRTemp
yearvisit
2013137.038.031.035.037.036.4
237.036.525.039.368.038.2
2014143.035.324.038.237.038.4
237.037.647.036.562.036.7
1
2
# 다중 인덱스를 가진 Series에서 사용된 구문이 열에 적용
health_data['Guido', 'HR']
1
2
3
4
5
6
year  visit
2013  1        31.0
      2        25.0
2014  1        24.0
      2        47.0
Name: (Guido, HR), dtype: float64
1
2
# 인덱서 사용
health_data.iloc[:2, :2]
subjectBob
typeHRTemp
yearvisit
2013137.038.0
237.036.5
1
2
# 인덱서에서 개별 인덱스는 다중 인덱스의 튜플로 전달
health_data.loc[:, ('Bob', 'HR')]
1
2
3
4
5
6
year  visit
2013  1        37.0
      2        37.0
2014  1        43.0
      2        37.0
Name: (Bob, HR), dtype: float64
1
2
# 튜플 내 슬라이스 생성 시 에러 발생
health_data.loc[(:, 1), (:, 'HR')]
1
2
3
4
  Cell In[32], line 2
    health_data.loc[(:, 1), (:, 'HR')]
                     ^
SyntaxError: invalid syntax
1
2
3
# slice()를 사용해 원하는 슬라이스를 명시적으로 만들면 에러를 피할 수 있지만 Pandas에선 IndexSlice 객체를 사용
idx = pd.IndexSlice
health_data.loc[idx[:, 1], idx[:, 'HR']]
subjectBobGuidoSue
typeHRHRHR
yearvisit
2013137.031.037.0
2014143.024.037.0

다중 인덱스 재정렬 Rearranging Multi-Indices

정렬된 인덱스와 정렬되지 않은 인덱스 Sorted and unsorted indices

1
2
3
4
5
# 대부분의 MultiIndex 슬라이싱 연산은 인덱스가 정렬되어 있지 않으면 실패
index = pd.MultiIndex.from_product([['a', 'c', 'b'], [1, 2]])
data = pd.Series(np.random.rand(6), index=index)
data.index.names = ['char', 'int']
data
1
2
3
4
5
6
7
8
char  int
a     1      0.284602
      2      0.411979
c     1      0.068748
      2      0.564982
b     1      0.045159
      2      0.356365
dtype: float64
1
2
3
4
5
6
# 미정렬 인덱스는 부분 슬라이싱 불가
try:
    data['a':'b']
except KeyError as e:
    print(type(e))
    print(e)
1
2
<class 'pandas.errors.UnsortedIndexError'>
'Key length (1) was greater than MultiIndex lexsort depth (0)'
1
2
3
# MultiIndex 정렬
data = data.sort_index()
data
1
2
3
4
5
6
7
8
char  int
a     1      0.284602
      2      0.411979
b     1      0.045159
      2      0.356365
c     1      0.068748
      2      0.564982
dtype: float64
1
2
# 정렬된 인덱스를 사용하면 부분 슬라이싱은 정상 동작
data['a':'b']
1
2
3
4
5
6
char  int
a     1      0.284602
      2      0.411979
b     1      0.045159
      2      0.356365
dtype: float64

Stacking and unstacking indices

1
2
# 정렬된 다중 인덱스에서 간단한 2차원 표현으로 변경 가능하며, 이 때 선택적으로 사용할 레벨 지정 가능
pop.unstack(level=0)
stateCaliforniaNew YorkTexas
year
2000338716481897645720851820
2010372539561937810225145561
1
pop.unstack(level=1)
year20002010
state
California3387164837253956
New York1897645719378102
Texas2085182025145561
1
2
# 원래 시리즈로 회복하는 데 사용 가능
pop.unstack().stack()
1
2
3
4
5
6
7
8
state       year
California  2000    33871648
            2010    37253956
New York    2000    18976457
            2010    19378102
Texas       2000    20851820
            2010    25145561
dtype: int64

Index setting and resetting

1
2
3
# 인덱스 레이블을 열로 바꿈
pop_flat = pop.reset_index(name='population')
pop_flat
stateyearpopulation
0California200033871648
1California201037253956
2New York200018976457
3New York201019378102
4Texas200020851820
5Texas201025145561
1
2
# 다중 인덱스를 갖는 DataFrame 반환
pop_flat.set_index(['state', 'year'])
population
stateyear
California200033871648
201037253956
New York200018976457
201019378102
Texas200020851820
201025145561
이 기사는 저작권자의 CC BY-NC 4.0 라이센스를 따릅니다.