계층적 색인: 축에 대해 다중 색인 단계를 지정할 수 있도록 함.
높은 차원의 데이터를 낮은 차원의 형식으로 다룰 수 있게 해주는 기능
import pandas as pd
import numpy as np
계층적 색인
data = pd.Series(np.random.randn(9),
index = [['a','a','a','b','b','c','c','d','d'],[1,2,3,1,3,1,2,2,3]])
data
a 1 -0.718279
2 -1.293206
3 0.838317
b 1 0.587797
3 0.416080
c 1 2.074077
2 -1.228737
d 2 -0.503776
3 1.030454
dtype: float64
MultiIndex를 색인으로 하는 Series. 색인의 계층을 보여주고 있음
data.index
MultiIndex([('a', 1),
('a', 2),
('a', 3),
('b', 1),
('b', 3),
('c', 1),
('c', 2),
('d', 2),
('d', 3)],
)
계층적으로 색인된 객체는 데이터의 부분집합을 부분적 색인으로 접근 가능
data['b']
1 0.587797
3 0.416080
dtype: float64
data['b':'c']
b 1 0.587797
3 0.416080
c 1 2.074077
2 -1.228737
dtype: float64
data.loc[['b','d']]
b 1 0.587797
3 0.416080
d 2 -0.503776
3 1.030454
dtype: float64
data.loc[:,2]
a -1.293206
c -1.228737
d -0.503776
dtype: float64
계층적 색인은 데이터를 재형성하고 피벗테이블 생성 같은 그룹 기반의 작업할 때 중요하게 사용
data.unstack()
|
1 |
2 |
3 |
a |
-0.718279 |
-1.293206 |
0.838317 |
b |
0.587797 |
NaN |
0.416080 |
c |
2.074077 |
-1.228737 |
NaN |
d |
NaN |
-0.503776 |
1.030454 |
data.unstack().stack()
a 1 -0.718279
2 -1.293206
3 0.838317
b 1 0.587797
3 0.416080
c 1 2.074077
2 -1.228737
d 2 -0.503776
3 1.030454
dtype: float64
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 |
frame['Ohio']
|
color |
Green |
Red |
key1 |
key2 |
|
|
a |
1 |
0 |
1 |
2 |
3 |
4 |
b |
1 |
6 |
7 |
2 |
9 |
10 |
MultiIndex는 따로 생성한 다음에 재사용 가능
계층의 순서를 바꾸고 정렬
swaplevel: 넘겨받은 두 개의 계층 번호나 이름이 뒤바뀐 새로운 객체 반환 (but 데이터는 변경되지 x)
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 |
sort_index: 단일 계층에 속한 데이터 정렬
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 |
계층별 요약 통계
level 옵션: 어떤 한 축에 대해 합을 구하고 싶은 단계 지정
frame.sum(level='key2')
/var/folders/1b/6fw3j9nd4_qgymn82k3hygww0000gn/T/ipykernel_4080/2004046222.py:1: FutureWarning: Using the level keyword in DataFrame and Series aggregations is deprecated and will be removed in a future version. Use groupby instead. df.sum(level=1) should use df.groupby(level=1).sum().
frame.sum(level='key2')
state |
Ohio |
Colorado |
color |
Green |
Red |
Green |
key2 |
|
|
|
1 |
6 |
8 |
10 |
2 |
12 |
14 |
16 |
원하는 출력은 나오지만 warning이 있다..
frame.sum(level='color', axis=1)
/var/folders/1b/6fw3j9nd4_qgymn82k3hygww0000gn/T/ipykernel_4080/4133796543.py:1: FutureWarning: Using the level keyword in DataFrame and Series aggregations is deprecated and will be removed in a future version. Use groupby instead. df.sum(level=1) should use df.groupby(level=1).sum().
frame.sum(level='color', axis=1)
|
color |
Green |
Red |
key1 |
key2 |
|
|
a |
1 |
2 |
1 |
2 |
8 |
4 |
b |
1 |
14 |
7 |
2 |
20 |
10 |
DataFrame에서 로우나 컬럼을 계층별로 합할 수 있음
DataFrame의 컬럼 사용하기
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: 하나 이상의 컬럼을 색인으로 하는 새로운 DataFrame 생성
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 |
컬럼을 명시적으로 남겨두지 않으면 DataFrame에서 삭제됨
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 |
데이터 합치기
- pandas.merge: 하나 이상의 키를 기준으로 DataFrame의 로우를 합침. SQL 또는 다른 관계형 데이터베이스의 join 연산과 유사
- pandas.concat: 하나의 축을 따라 객체를 이어 붙임
- combine_first: 두 객체를 포개서 한 객체에서 누락된 데이터를 다른 객체에 있는 값으로 채울 수 있도록 함
데이터베이스 스타일로 DataFrame 합치기
병합, 조인 연산: 하나 이상의 키를 사용하여 데이터 집합의 로우를 합칩
df1 = pd.DataFrame({'key':['b','b','a','c','a','a','b'],'data1':range(7)})
df2 = pd.DataFrame({'key':['a','b','d'], 'data2':range(3)})
df1
|
key |
data1 |
0 |
b |
0 |
1 |
b |
1 |
2 |
a |
2 |
3 |
c |
3 |
4 |
a |
4 |
5 |
a |
5 |
6 |
b |
6 |
df2
|
key |
data2 |
0 |
a |
0 |
1 |
b |
1 |
2 |
d |
2 |
pd.merge(df1,df2)
|
key |
data1 |
data2 |
0 |
b |
0 |
1 |
1 |
b |
1 |
1 |
2 |
b |
6 |
1 |
3 |
a |
2 |
0 |
4 |
a |
4 |
0 |
5 |
a |
5 |
0 |
만약, 두 객체에 중복된 컬럼 이름이 없다면 따로 지정.
df3 = pd.DataFrame({'1key':['b','b','a','c','a','a','b'], 'data1':range(7)})
df4 = pd.DataFrame({'rkey':['a','b','d'],'data2':range(3)})
pd.merge(df3, df4, left_on = '1key', right_on = 'rkey')
|
1key |
data1 |
rkey |
data2 |
0 |
b |
0 |
b |
1 |
1 |
b |
1 |
b |
1 |
2 |
b |
6 |
b |
1 |
3 |
a |
2 |
a |
0 |
4 |
a |
4 |
a |
0 |
5 |
a |
5 |
a |
0 |
merge 함수는 기본적으로 내부 조인을 수행하여 교집합인 결과를 반환
외부 조인은 합집합인 결과 반환(how 인자 사용)
pd.merge(df1, df2, how = 'outer')
|
key |
data1 |
data2 |
0 |
b |
0.0 |
1.0 |
1 |
b |
1.0 |
1.0 |
2 |
b |
6.0 |
1.0 |
3 |
a |
2.0 |
0.0 |
4 |
a |
4.0 |
0.0 |
5 |
a |
5.0 |
0.0 |
6 |
c |
3.0 |
NaN |
7 |
d |
NaN |
2.0 |
다대다 병합은 직관적이지 않음.
df1 = pd.DataFrame({'key':['b','b','a','c','a','b'],'data1':range(6)})
df2 = pd.DataFrame({'key':['a','b','a','b','d'],'data2':range(5)})
df1
|
key |
data1 |
0 |
b |
0 |
1 |
b |
1 |
2 |
a |
2 |
3 |
c |
3 |
4 |
a |
4 |
5 |
b |
5 |
df2
|
key |
data2 |
0 |
a |
0 |
1 |
b |
1 |
2 |
a |
2 |
3 |
b |
3 |
4 |
d |
4 |
pd.merge(df1, df2, on = 'key', how = 'left')
|
key |
data1 |
data2 |
0 |
b |
0 |
1.0 |
1 |
b |
0 |
3.0 |
2 |
b |
1 |
1.0 |
3 |
b |
1 |
3.0 |
4 |
a |
2 |
0.0 |
5 |
a |
2 |
2.0 |
6 |
c |
3 |
NaN |
7 |
a |
4 |
0.0 |
8 |
a |
4 |
2.0 |
9 |
b |
5 |
1.0 |
10 |
b |
5 |
3.0 |
다대다 조인은 두 로우의 데카르트 곱 반환 => 조인 메서드는 결과에 나타나는 구별되는 키에 대해서만 적용
pd.merge(df1, df2, how = 'inner')
|
key |
data1 |
data2 |
0 |
b |
0 |
1 |
1 |
b |
0 |
3 |
2 |
b |
1 |
1 |
3 |
b |
1 |
3 |
4 |
b |
5 |
1 |
5 |
b |
5 |
3 |
6 |
a |
2 |
0 |
7 |
a |
2 |
2 |
8 |
a |
4 |
0 |
9 |
a |
4 |
2 |
겹치는 컬럼 이름에 대한 처리
- 축 이름 변경하여 수동으로 컬럼 이름 겹치게 하기
- suffixes 인자로 DataFrame 객체에서 겹치는 컬럼 이름 뒤에 붙일 문자열 지정
left = pd.DataFrame({'key1':['foo','foo','bar'],
'key2':['one','two','one'],
'lval':[1,2,3]})
right = pd.DataFrame({'key1':['foo','foo','bar','bar'],
'key2':['one','one','one','two'],
'rval':[4,5,6,7]})
pd.merge(left, right, on = 'key1')
|
key1 |
key2_x |
lval |
key2_y |
rval |
0 |
foo |
one |
1 |
one |
4 |
1 |
foo |
one |
1 |
one |
5 |
2 |
foo |
two |
2 |
one |
4 |
3 |
foo |
two |
2 |
one |
5 |
4 |
bar |
one |
3 |
one |
6 |
5 |
bar |
one |
3 |
two |
7 |
pd.merge(left, right, on = 'key1', suffixes=('_left','_right'))
|
key1 |
key2_left |
lval |
key2_right |
rval |
0 |
foo |
one |
1 |
one |
4 |
1 |
foo |
one |
1 |
one |
5 |
2 |
foo |
two |
2 |
one |
4 |
3 |
foo |
two |
2 |
one |
5 |
4 |
bar |
one |
3 |
one |
6 |
5 |
bar |
one |
3 |
two |
7 |
색인 병합하기
병합하는 키가 DataFrame의 색인일 경우
left_index = True 혹은 right_index = True 옵션 사용하여 해당 색인을 병합 키로 사용
left1 = pd.DataFrame({'key':['a','b','a','a','b','c'], 'value':range(6)})
right1 = pd.DataFrame({'group_val':[3.5, 7]}, index = ['a','b'])
left1
|
key |
value |
0 |
a |
0 |
1 |
b |
1 |
2 |
a |
2 |
3 |
a |
3 |
4 |
b |
4 |
5 |
c |
5 |
right1
pd.merge(left1, right1, left_on = 'key', right_index = True)
|
key |
value |
group_val |
0 |
a |
0 |
3.5 |
2 |
a |
2 |
3.5 |
3 |
a |
3 |
3.5 |
1 |
b |
1 |
7.0 |
4 |
b |
4 |
7.0 |
pd.merge(left1, right1, left_on = 'key', right_index = True, how = 'outer')
|
key |
value |
group_val |
0 |
a |
0 |
3.5 |
2 |
a |
2 |
3.5 |
3 |
a |
3 |
3.5 |
1 |
b |
1 |
7.0 |
4 |
b |
4 |
7.0 |
5 |
c |
5 |
NaN |
계층 색인된 데이터는 암묵적으로 여러 키 병합
lefth = pd.DataFrame({'key1': ['Ohio','Ohio','Ohio','Nevada','Nevada'],
'key2': [2000, 2001, 2002, 2001, 2002],
'data': np.arange(5.)})
righth = pd.DataFrame(np.arange(12).reshape((6,2)),
index = [['Nevada','Nevada','Ohio','Ohio','Ohio','Ohio'],
[2001, 2000, 2000, 2000, 2001, 2002]], columns = ['event1', 'event2'])
lefth
|
key1 |
key2 |
data |
0 |
Ohio |
2000 |
0.0 |
1 |
Ohio |
2001 |
1.0 |
2 |
Ohio |
2002 |
2.0 |
3 |
Nevada |
2001 |
3.0 |
4 |
Nevada |
2002 |
4.0 |
righth
|
|
event1 |
event2 |
Nevada |
2001 |
0 |
1 |
2000 |
2 |
3 |
Ohio |
2000 |
4 |
5 |
2000 |
6 |
7 |
2001 |
8 |
9 |
2002 |
10 |
11 |
=> 리스트로 여러 개의 컬럼을 지정해서 병합해야함. (how = 'outer' 사용)
pd.merge(lefth, righth, left_on = ['key1','key2'], right_index = True)
|
key1 |
key2 |
data |
event1 |
event2 |
0 |
Ohio |
2000 |
0.0 |
4 |
5 |
0 |
Ohio |
2000 |
0.0 |
6 |
7 |
1 |
Ohio |
2001 |
1.0 |
8 |
9 |
2 |
Ohio |
2002 |
2.0 |
10 |
11 |
3 |
Nevada |
2001 |
3.0 |
0 |
1 |
pd.merge(lefth, righth, left_on = ['key1','key2'], right_index = True, how = 'outer')
|
key1 |
key2 |
data |
event1 |
event2 |
0 |
Ohio |
2000 |
0.0 |
4.0 |
5.0 |
0 |
Ohio |
2000 |
0.0 |
6.0 |
7.0 |
1 |
Ohio |
2001 |
1.0 |
8.0 |
9.0 |
2 |
Ohio |
2002 |
2.0 |
10.0 |
11.0 |
3 |
Nevada |
2001 |
3.0 |
0.0 |
1.0 |
4 |
Nevada |
2002 |
4.0 |
NaN |
NaN |
4 |
Nevada |
2000 |
NaN |
2.0 |
3.0 |
left2 = pd.DataFrame([[1.,2.], [3.,4.], [5.,6.]], index = ['a','c','e'], columns = ['Ohio', 'Nevada'])
right2 = pd.DataFrame([[7.,8.], [9.,10.], [11.,12.], [13,14]], index = ['b','c','d','e'],
columns = ['Missouri', 'Alabama'])
left2
|
Ohio |
Nevada |
a |
1.0 |
2.0 |
c |
3.0 |
4.0 |
e |
5.0 |
6.0 |
right2
|
Missouri |
Alabama |
b |
7.0 |
8.0 |
c |
9.0 |
10.0 |
d |
11.0 |
12.0 |
e |
13.0 |
14.0 |
pd.merge(left2, right2, how = 'outer', left_index = True, right_index = True)
|
Ohio |
Nevada |
Missouri |
Alabama |
a |
1.0 |
2.0 |
NaN |
NaN |
b |
NaN |
NaN |
7.0 |
8.0 |
c |
3.0 |
4.0 |
9.0 |
10.0 |
d |
NaN |
NaN |
11.0 |
12.0 |
e |
5.0 |
6.0 |
13.0 |
14.0 |
색인으로 병합할 때 join 메서드 이용하면 편리.
join 메서드는 컬럼이 겹치지 않으며 완전히 같거나 유사한 색인 구조를 가진 여러 개의 DataFrame 객체를 병합할 때 사용 가능
left2.join(right2, how='outer')
|
Ohio |
Nevada |
Missouri |
Alabama |
a |
1.0 |
2.0 |
NaN |
NaN |
b |
NaN |
NaN |
7.0 |
8.0 |
c |
3.0 |
4.0 |
9.0 |
10.0 |
d |
NaN |
NaN |
11.0 |
12.0 |
e |
5.0 |
6.0 |
13.0 |
14.0 |
left1.join(right1, on='key')
|
key |
value |
group_val |
0 |
a |
0 |
3.5 |
1 |
b |
1 |
7.0 |
2 |
a |
2 |
3.5 |
3 |
a |
3 |
3.5 |
4 |
b |
4 |
7.0 |
5 |
c |
5 |
NaN |
색인 대 색인으로 두 DataFrame 병합
another = pd.DataFrame([[7.,8.], [9.,10.], [11.,12.], [16.,17.]], index = ['a','c','e','f'],
columns = ['New York', 'Oregon'])
another
|
New York |
Oregon |
a |
7.0 |
8.0 |
c |
9.0 |
10.0 |
e |
11.0 |
12.0 |
f |
16.0 |
17.0 |
left2.join([right2, another])
|
Ohio |
Nevada |
Missouri |
Alabama |
New York |
Oregon |
a |
1.0 |
2.0 |
NaN |
NaN |
7.0 |
8.0 |
c |
3.0 |
4.0 |
9.0 |
10.0 |
9.0 |
10.0 |
e |
5.0 |
6.0 |
13.0 |
14.0 |
11.0 |
12.0 |
left2.join([right2, another], how='outer')
|
Ohio |
Nevada |
Missouri |
Alabama |
New York |
Oregon |
a |
1.0 |
2.0 |
NaN |
NaN |
7.0 |
8.0 |
c |
3.0 |
4.0 |
9.0 |
10.0 |
9.0 |
10.0 |
e |
5.0 |
6.0 |
13.0 |
14.0 |
11.0 |
12.0 |
b |
NaN |
NaN |
7.0 |
8.0 |
NaN |
NaN |
d |
NaN |
NaN |
11.0 |
12.0 |
NaN |
NaN |
f |
NaN |
NaN |
NaN |
NaN |
16.0 |
17.0 |
축 따라 이어 붙이기
numpy에서는 ndarray를 이어붙이는 concatenate 함수 이용
arr = np.arange(12).reshape((3,4))
arr
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
np.concatenate([arr,arr], axis = 1)
array([[ 0, 1, 2, 3, 0, 1, 2, 3],
[ 4, 5, 6, 7, 4, 5, 6, 7],
[ 8, 9, 10, 11, 8, 9, 10, 11]])
at pandas.. => 축마다 이름이 있어서 배열을 쉽게 이어붙일 수 있도록 구성
고려사항
- 만약 연결하려는 두 객체의 색인이 서로 다르면 결과는 그 색인의 교집합? 합집합?
- 합쳐진 결과에서 합쳐지기 전 객체의 데이터 구분할 수 있어야할까?
- 어떤 축으로 연결할 것인지 고려해야하나? (많을 경우 DataFrame의 기본 정수 라벨이 가장 먼저 무시)
pandas에서는 concat 함수 이용
s1 = pd.Series([0,1], index=['a','b'])
s2 = pd.Series([2,3,4], index=['c','d','e'])
s3 = pd.Series([5,6], index=['f','g'])
pd.concat([s1,s2,s3])
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
concat 함수는 axis=0을 기본값으로 하며 새로운 Series 객체 생성
만약 axis=1 이상이라면 결과는 Series가 아닌 DataFrame (axis=1은 칼럼 의미)
pd.concat([s1,s2,s3], axis=1)
|
0 |
1 |
2 |
a |
0.0 |
NaN |
NaN |
b |
1.0 |
NaN |
NaN |
c |
NaN |
2.0 |
NaN |
d |
NaN |
3.0 |
NaN |
e |
NaN |
4.0 |
NaN |
f |
NaN |
NaN |
5.0 |
g |
NaN |
NaN |
6.0 |
s4 = pd.concat([s1,s3])
s4
a 0
b 1
f 5
g 6
dtype: int64
pd.concat([s1,s4], axis=1)
|
0 |
1 |
a |
0.0 |
0 |
b |
1.0 |
1 |
f |
NaN |
5 |
g |
NaN |
6 |
pd.concat([s1,s4], axis=1, join='inner')
join_axes: 병합하려는 축을 직접 지정 가능
pd.concat([s1,s4], axis=1, join_axes = [['a','c','b','e']])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/var/folders/1b/6fw3j9nd4_qgymn82k3hygww0000gn/T/ipykernel_4080/1941623965.py in <module>
----> 1 pd.concat([s1,s4], axis=1, join_axes = [['a','c','b','e']])
~/opt/anaconda3/lib/python3.9/site-packages/pandas/util/_decorators.py in wrapper(*args, **kwargs)
309 stacklevel=stacklevel,
310 )
--> 311 return func(*args, **kwargs)
312
313 return wrapper
TypeError: concat() got an unexpected keyword argument 'join_axes'
join_axes를 지원하지 않는다는 것 같다... => reindex 사용!
pd.concat([s1,s4], axis=1).reindex(['a','c','b','e'])
|
0 |
1 |
a |
0.0 |
0.0 |
c |
NaN |
NaN |
b |
1.0 |
1.0 |
e |
NaN |
NaN |
계층적 색인을 생성하려면 keys 인자 사용
Series를 이어붙이기 전의 개별 Series를 구분할 수 없으므로 이어 붙인 축에 대해 계층적 색인 생성하여 식별 가능하도록 함!)
result = pd.concat([s1,s1,s3], keys=['one','two','three'])
result
one a 0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64
result.unstack()
|
a |
b |
f |
g |
one |
0.0 |
1.0 |
NaN |
NaN |
two |
0.0 |
1.0 |
NaN |
NaN |
three |
NaN |
NaN |
5.0 |
6.0 |
Series를 axis=1로 병합하게 되면 keys는 DataFrame의 컬럼 제목이 됨
pd.concat([s1,s2,s3], axis=1, keys=['one','two','three'])
|
one |
two |
three |
a |
0.0 |
NaN |
NaN |
b |
1.0 |
NaN |
NaN |
c |
NaN |
2.0 |
NaN |
d |
NaN |
3.0 |
NaN |
e |
NaN |
4.0 |
NaN |
f |
NaN |
NaN |
5.0 |
g |
NaN |
NaN |
6.0 |
DataFrame 객체에 대해서도 이와 같음
df1 = pd.DataFrame(np.arange(6).reshape(3,2), index=['a','b','c'], columns=['one','two'])
df2 = pd.DataFrame(5+np.arange(4).reshape(2,2), index=['a','c'], columns=['three','four'])
df1
|
one |
two |
a |
0 |
1 |
b |
2 |
3 |
c |
4 |
5 |
df2
pd.concat([df1,df2], axis=1, keys=['level1','level2'])
|
level1 |
level2 |
|
one |
two |
three |
four |
a |
0 |
1 |
5.0 |
6.0 |
b |
2 |
3 |
NaN |
NaN |
c |
4 |
5 |
7.0 |
8.0 |
리스트 대신 객체의 사전을 넘기면 사전의 키가 keys 옵션으로 사용
pd.concat({'level1':df1, 'level2':df2}, axis=1)
|
level1 |
level2 |
|
one |
two |
three |
four |
a |
0 |
1 |
5.0 |
6.0 |
b |
2 |
3 |
NaN |
NaN |
c |
4 |
5 |
7.0 |
8.0 |
pd.concat([df1,df2], axis=1, keys=['level1','level2'], names=['upper','lower'])
upper |
level1 |
level2 |
lower |
one |
two |
three |
four |
a |
0 |
1 |
5.0 |
6.0 |
b |
2 |
3 |
NaN |
NaN |
c |
4 |
5 |
7.0 |
8.0 |
df1 = pd.DataFrame(np.random.randn(3,4), columns=['a','b','c','d'])
df2 = pd.DataFrame(np.random.randn(2,3), columns=['b','d','a'])
df1
|
a |
b |
c |
d |
0 |
-0.031695 |
-0.074865 |
-0.567876 |
-0.106580 |
1 |
0.175408 |
-0.300737 |
1.752298 |
0.311653 |
2 |
0.383386 |
0.625466 |
-0.717174 |
0.603836 |
df2
|
b |
d |
a |
0 |
-0.865482 |
-0.811584 |
0.205644 |
1 |
0.506437 |
0.279058 |
-1.258077 |
=>DataFrame의 로우 색인이 분석에 필요한 데이터를 포함하고 있지 않을 때
ignore_index=True 옵션 사용
pd.concat([df1,df2], ignore_index=True)
|
a |
b |
c |
d |
0 |
-0.031695 |
-0.074865 |
-0.567876 |
-0.106580 |
1 |
0.175408 |
-0.300737 |
1.752298 |
0.311653 |
2 |
0.383386 |
0.625466 |
-0.717174 |
0.603836 |
3 |
0.205644 |
-0.865482 |
NaN |
-0.811584 |
4 |
-1.258077 |
0.506437 |
NaN |
0.279058 |
겹치는 데이터 합치기
a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan], index=['f','e','d','c','b','a'])
b = pd.Series(np.arange(len(a), dtype=np.float64), index=['f','e','d','c','b','a'])
b[-1] = np.nan
a
f NaN
e 2.5
d NaN
c 3.5
b 4.5
a NaN
dtype: float64
b
f 0.0
e 1.0
d 2.0
c 3.0
b 4.0
a NaN
dtype: float64
np.where(pd.isnull(a), b, a)
array([0. , 2.5, 2. , 3.5, 4.5, nan])
b[:-2].combine_first(a[2:])
a NaN
b 4.5
c 3.0
d 2.0
e 1.0
f 0.0
dtype: float64
df1 = pd.DataFrame({'a':[1., np.nan, 5., np.nan], 'b':[np.nan, 2., np.nan, 6.], 'c': range(2,18,4)})
df2 = pd.DataFrame({'a':[5., 4., np.nan, 3., 7.], 'b':[np.nan, 3., 4., 6., 8.]})
df1
|
a |
b |
c |
0 |
1.0 |
NaN |
2 |
1 |
NaN |
2.0 |
6 |
2 |
5.0 |
NaN |
10 |
3 |
NaN |
6.0 |
14 |
df2
|
a |
b |
0 |
5.0 |
NaN |
1 |
4.0 |
3.0 |
2 |
NaN |
4.0 |
3 |
3.0 |
6.0 |
4 |
7.0 |
8.0 |
df1.combine_first(df2)
|
a |
b |
c |
0 |
1.0 |
NaN |
2.0 |
1 |
4.0 |
2.0 |
6.0 |
2 |
5.0 |
4.0 |
10.0 |
3 |
3.0 |
6.0 |
14.0 |
4 |
7.0 |
8.0 |
NaN |
재형성과 피벗
: 표 형식의 데이터를 재배치하는 기본 연산
계층적 색인으로 재형성하기
- stack: 데이터의 컬럼을 로우로 피벗(또는 회전) 시킨다.
- unstack: 로우를 컬럼으로 피벗시킨다.
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(0)
state |
Ohio |
Colorado |
number |
|
|
one |
0 |
3 |
two |
1 |
4 |
three |
2 |
5 |
result.unstack('state')
state |
Ohio |
Colorado |
number |
|
|
one |
0 |
3 |
two |
1 |
4 |
three |
2 |
5 |
해당 레벨에 있는 모든 값이 하위 그룹에 속하지 않을 경우 => unstack을 하게 되면 누락된 데이터 생길 수 있음
s1 = pd.Series([0,1,2,3], index=['a','b','c','d'])
s2 = pd.Series([4,5,6], index=['c','d','e'])
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.0 |
1.0 |
2.0 |
3.0 |
NaN |
two |
NaN |
NaN |
4.0 |
5.0 |
6.0 |
stack 메서드는 누락된 데이터를 자동으로 걸러냄 => 연산을 쉽게 원상 복구 가능
data2.unstack().stack()
one a 0.0
b 1.0
c 2.0
d 3.0
two c 4.0
d 5.0
e 6.0
dtype: float64
data2.unstack().stack(dropna=False)
one a 0.0
b 1.0
c 2.0
d 3.0
e NaN
two a NaN
b NaN
c 4.0
d 5.0
e 6.0
dtype: float64
DataFrame을 unstack()할 때 unstack 레벨은 결과에서 가장 낮은 단계가 됨
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('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 |
stack 호출할 때 쌀을 축의 이름 지정 가능
df.unstack('state').stack('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 |
긴 형식에서 넓은 형식으로 피벗하기
db 또는 csv 파일에 여러 개의 시게열 데이터를 저장하는 일반적인 방법: 시간 순서대로 나열
data = pd.read_csv('/Users/cha/Desktop/숙명 데분 스터디/data/macrodata.csv')
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 |
periods = pd.PeriodIndex(year = data.year, quarter = data.quarter, name = 'date')
columns = pd.Index(['realgdp', 'infl','unemp'], name='item')
data = data.reindex(columns = columns)
data.index = periods.to_timestamp('D','end')
ldata = data.stack().reset_index().rename(columns={0: 'value'})
ldata[:10]
|
date |
item |
value |
0 |
1959-03-31 23:59:59.999999999 |
realgdp |
2710.349 |
1 |
1959-03-31 23:59:59.999999999 |
infl |
0.000 |
2 |
1959-03-31 23:59:59.999999999 |
unemp |
5.800 |
3 |
1959-06-30 23:59:59.999999999 |
realgdp |
2778.801 |
4 |
1959-06-30 23:59:59.999999999 |
infl |
2.340 |
5 |
1959-06-30 23:59:59.999999999 |
unemp |
5.100 |
6 |
1959-09-30 23:59:59.999999999 |
realgdp |
2775.488 |
7 |
1959-09-30 23:59:59.999999999 |
infl |
2.740 |
8 |
1959-09-30 23:59:59.999999999 |
unemp |
5.300 |
9 |
1959-12-31 23:59:59.999999999 |
realgdp |
2785.204 |
pivot 메서드
pivoted = ldata.pivot('date','item','value')
pivoted
item |
infl |
realgdp |
unemp |
date |
|
|
|
1959-03-31 23:59:59.999999999 |
0.00 |
2710.349 |
5.8 |
1959-06-30 23:59:59.999999999 |
2.34 |
2778.801 |
5.1 |
1959-09-30 23:59:59.999999999 |
2.74 |
2775.488 |
5.3 |
1959-12-31 23:59:59.999999999 |
0.27 |
2785.204 |
5.6 |
1960-03-31 23:59:59.999999999 |
2.31 |
2847.699 |
5.2 |
... |
... |
... |
... |
2008-09-30 23:59:59.999999999 |
-3.16 |
13324.600 |
6.0 |
2008-12-31 23:59:59.999999999 |
-8.79 |
13141.920 |
6.9 |
2009-03-31 23:59:59.999999999 |
0.94 |
12925.410 |
8.1 |
2009-06-30 23:59:59.999999999 |
3.37 |
12901.504 |
9.2 |
2009-09-30 23:59:59.999999999 |
3.56 |
12990.341 |
9.6 |
203 rows × 3 columns
ldata['value2'] = np.random.randn(len(ldata))
ldata[:10]
|
date |
item |
value |
value2 |
0 |
1959-03-31 23:59:59.999999999 |
realgdp |
2710.349 |
0.244226 |
1 |
1959-03-31 23:59:59.999999999 |
infl |
0.000 |
0.962365 |
2 |
1959-03-31 23:59:59.999999999 |
unemp |
5.800 |
0.404761 |
3 |
1959-06-30 23:59:59.999999999 |
realgdp |
2778.801 |
0.471211 |
4 |
1959-06-30 23:59:59.999999999 |
infl |
2.340 |
-0.817099 |
5 |
1959-06-30 23:59:59.999999999 |
unemp |
5.100 |
-0.114613 |
6 |
1959-09-30 23:59:59.999999999 |
realgdp |
2775.488 |
-0.432555 |
7 |
1959-09-30 23:59:59.999999999 |
infl |
2.740 |
-0.918431 |
8 |
1959-09-30 23:59:59.999999999 |
unemp |
5.300 |
0.523383 |
9 |
1959-12-31 23:59:59.999999999 |
realgdp |
2785.204 |
1.649171 |
pivoted = ldata.pivot('date','item')
pivoted[:5]
|
value |
value2 |
item |
infl |
realgdp |
unemp |
infl |
realgdp |
unemp |
date |
|
|
|
|
|
|
1959-03-31 23:59:59.999999999 |
0.00 |
2710.349 |
5.8 |
0.962365 |
0.244226 |
0.404761 |
1959-06-30 23:59:59.999999999 |
2.34 |
2778.801 |
5.1 |
-0.817099 |
0.471211 |
-0.114613 |
1959-09-30 23:59:59.999999999 |
2.74 |
2775.488 |
5.3 |
-0.918431 |
-0.432555 |
0.523383 |
1959-12-31 23:59:59.999999999 |
0.27 |
2785.204 |
5.6 |
0.672839 |
1.649171 |
1.146470 |
1960-03-31 23:59:59.999999999 |
2.31 |
2847.699 |
5.2 |
-1.015282 |
1.491567 |
-0.210033 |
pivoted['value'][:5]
item |
infl |
realgdp |
unemp |
date |
|
|
|
1959-03-31 23:59:59.999999999 |
0.00 |
2710.349 |
5.8 |
1959-06-30 23:59:59.999999999 |
2.34 |
2778.801 |
5.1 |
1959-09-30 23:59:59.999999999 |
2.74 |
2775.488 |
5.3 |
1959-12-31 23:59:59.999999999 |
0.27 |
2785.204 |
5.6 |
1960-03-31 23:59:59.999999999 |
2.31 |
2847.699 |
5.2 |
unstacked = ldata.set_index(['date','item']).unstack('item')
unstacked[:7]
|
value |
value2 |
item |
infl |
realgdp |
unemp |
infl |
realgdp |
unemp |
date |
|
|
|
|
|
|
1959-03-31 23:59:59.999999999 |
0.00 |
2710.349 |
5.8 |
0.962365 |
0.244226 |
0.404761 |
1959-06-30 23:59:59.999999999 |
2.34 |
2778.801 |
5.1 |
-0.817099 |
0.471211 |
-0.114613 |
1959-09-30 23:59:59.999999999 |
2.74 |
2775.488 |
5.3 |
-0.918431 |
-0.432555 |
0.523383 |
1959-12-31 23:59:59.999999999 |
0.27 |
2785.204 |
5.6 |
0.672839 |
1.649171 |
1.146470 |
1960-03-31 23:59:59.999999999 |
2.31 |
2847.699 |
5.2 |
-1.015282 |
1.491567 |
-0.210033 |
1960-06-30 23:59:59.999999999 |
0.14 |
2834.390 |
5.2 |
1.492080 |
0.398274 |
-1.866305 |
1960-09-30 23:59:59.999999999 |
2.70 |
2839.022 |
5.6 |
2.580628 |
0.629838 |
-0.291634 |
긴 형식에서 긴 형식으로 피벗하기
pandas.melt: pivot과 반대되는 연산, 반드시 어떤 컬럼을 그룹 구분자로 사용할 것인지 지정.
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 |
melted = pd.melt(df,['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 |
reshaped = melted.pivot('key', 'variable', '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 |
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 |
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 |