- 결측값 혹은 원래의 값을 다른 값으로 교체(replacing generic values)
- List 를 다른 List로 교체
- mapping dict 로 교체
- DataFrame의 특정 칼럼 값 교체
하는 방법을 소개하겠습니다.
fillna() 를 사용한 결측값 대체와 replace()를 사용한 결측값 교체가 유사한면이 있구요, 다만 replace() 는 결측값 말고도 모든 값을 대상으로 할 수 있고, list, mapping dict 등으로 좀더 유연하고 포괄적으로 사용할 수 있는 장점이 있습니다.
replace() method를 가지고 결측값, 실측값 모두를 대상으로 교체(replacement)하는 예를 들어보겠습니다. 필요한 모듈을 importing하고, 결측값을 포함한 간단한 Series 먼저 시작하겠습니다.
In [1]:import pandas as pd
In [2]:import numpy as np
In [3]:from pandas import DataFrame, Series
In [4]: ser = Series([1, 2, 3, 4, np.nan])
In [5]: ser
Out[5]:
0 1.0 1 2.0 2 3.0 3 4.0 4 NaN dtype: float64
(1) 결측값, 실측값을 다른 값으로 교체하기 : replace(old_val, new_val)
# replacing a value with other value
In [6]: ser.replace(2, 20)
Out[6]:
0 1.0 1 20.0 2 3.0 3 4.0 4 NaN dtype: float64
# replacing missing value(NaN) with other value
In [7]: ser.replace(np.nan, 5)
Out[7]:
0 1.0 1 2.0 2 3.0 3 4.0 4 5.0 dtype: float64
(2) list 를 다른 list 값으로 교체하기 : replace([old1, old2, ...], [new1, new2, ...])
이번 포스팅에서는 Python pandas의 interpolate() method를 사용해서 결측값을 보간하는 방법(interpolation of missing values)에 대해서 소개하겠습니다.
- (1) 시계열데이터의 값에 선형으로 비례하는 방식으로 결측값 보간
(interpolate TimeSeries missing values linearly)
- (2) 시계열 날짜 index를 기준으로 결측값 보간
(interploate TimeSeries missing values along with Time-index)
- (3) DataFrame 값에 선형으로 비례하는 방식으로 결측값 보간
(interpolate DataFrame missing values linearly)
- (4) 결측값 보간 개수 제한하기
(limit the number of consecutive interpolation)
시계열 데이터 분석이라든지 (X, Y, Z) 좌표 공간 상에서 측정된 데이터(근접한 공간 상 데이터 간 상관성/연속성이 있는 경우)등에서 사용하면 매우 유용하고 편리한 method 입니다.
이전 포스팅의 결측값 대체는 '특정의 동일 값'으로 채우는 방식(filling, imputation)이었던 반면에, 이번 포스팅의 결측값 보간(interploation)은 실측값과 실측값 사이의 결측값을 마치 '그라데이션(gradation)' 기법으로 색깔을 조금씩 변화시켜가면서 부드럽게 채워나가는 방법이라고 이해하시면 되겠습니다.
자, 그럼 필요한 모듈을 import 하고, 결측값이 들어있는 TimeSeries 데이터를 만든 후에, 결측값을 보간해보겠습니다.
아래의 예시는 'C1'칼럼과 'C2' 칼럼에 대해서만 각 칼럼의 평균을 가지고 -> 각 칼럼에 있는 대체값을 대체하는 경우입니다. 좀 헷갈릴 수 있는데요, 위의 2개의 예시의 혼합 형태라고 보시면 되겠습니다.
('C3'의 NaN 값은 결측값 그대로 있습니다.)
## 'C1'칼럼과 'C2' 칼럼에 대해서만 각 칼럼의 평균을 가지고 -> 각 칼럼에 있는 결측값을 대체
## mean values of 'C1', 'C2'
df.mean()['C1':'C2']
# C1 0.470224
# C2 0.790456
# dtype: float64
## filling the missing value of 'C1', 'C2' with each mean values
df.fillna(df.mean()['C1':'C2'])
# C1 C2 C3
# 0 0.470224 0.893384 2.388055
# 1 0.470224 0.012155 NaN
# 2 0.099263 0.790456 -1.330312
# 3 2.468724 0.790456 1.046972
# 4 -1.157316 1.465829 NaN
(5)결측값을 다른 변수의 값으로 대체하기 (filling missing values with another columns' values)
두가지 방법이 있는데요, 먼저 np.where()와 pd.notnumm() 를 사용해서 np.where(pd.notnull(df['C2']) == True, df['C2'], df['C1']) 처럼 'C2' 칼럼에서 결측값이 없으면 'C2' 칼럼의 값을 그대로 사용하고, 'C2'칼럼에 결측값이 있으면 'C1' 칼럼의 값을 가져다가 결측값을 채워보겠습니다.
## (5) 결측값을 다른 변수의 값으로 대체하기
## (filling missing values with another columns' values)
df_2 = pd.DataFrame({
'C1': [1, 2, 3, 4, 5],
'C2': [6, 7, 8, 9, 10]})
## put NaNs as an example
df_2.loc[[1, 3], ['C2']] = np.nan
df_2
# C1 C2
# 0 1 6.0
# 1 2 NaN
# 2 3 8.0
# 3 4 NaN
# 4 5 10.0
## making new column by filling missing values with another column's value
# way 1 : by np.where => quick
df_2['C2_New'] = np.where(pd.notnull(df_2['C2']) == True,
df_2['C2'], df_2['C1'])
df_2
# C1 C2 C2_New
# 0 1 6.0 6.0
# 1 2 NaN 2.0
# 2 3 8.0 8.0
# 3 4 NaN 4.0
# 4 5 10.0 10.0
아래의 loop programming은 위와 동일한 결과를 반환하지만 시간은 훨~씬 오래 걸린다는 점 유의하시구요, 그냥 '아, 이렇게도 할 수 있구나...' 정도로만 참고하시기 바랍니다.
(당연히, 위의 np.where()와 pd.notnull() 을 사용하는 것이 속도도 빠르고 코드도 짧고 쉽기 때문에 추천)
## way 2 : by loop programming => takes a long time
for i in df_2.index:
if pd.notnull(df_2.loc[i, 'C2']) == True:
df_2.loc[i, 'C2_New_2'] = df_2.loc[i, 'C2']
else:
df_2.loc[i, 'C2_New_2'] = df_2.loc[i, 'C1']
df_2
# C1 C2 C2_New C2_New_2
# 0 1 6.0 6.0 6.0
# 1 2 NaN 2.0 2.0
# 2 3 8.0 8.0 8.0
# 3 4 NaN 4.0 4.0
# 4 5 10.0 10.0 10.0
In [5]: df Out[5]: C1 C2 a 0 1 b 2 3 c 4 5 d 6 7 e 8 9
In [6]: df.loc[['b', 'e'], ['C1']] = None
In [7]: df.loc[['b', 'c'], ['C2']] = None
In [8]: df Out[8]: C1 C2 a 0.0 1.0 b NaNNaN c 4.0 NaN d 6.0 7.0 e NaN 9.0
(1) sum(), cumsum() methods 계산 시 : NaN은 '0'으로 처리
# When summing data, NA (missing) values will be treated as zero In [9]: df.sum() # sum by columns Out[9]: C1 10.0 C2 17.0 dtype: float64
In [10]: df['C1'].sum() # sum of 'C1' column Out[10]: 10.0
In [11]: df['C1'].cumsum() # cumulative sum of 'C1' column Out[11]: a 0.0 b NaN c 4.0 d 10.0 e NaN Name: C1, dtype: float64
(2) mean(), std() 연산 시 : NaN은 분석 대상(population)에서 제외
In [8]: df Out[8]: C1 C2 a 0.0 1.0 b NaNNaN c 4.0 NaN d 6.0 7.0 e NaN 9.0
# As for mean(), std() methods, NA (missing) values will be ignored In [12]: df.mean() # mean by columns Out[12]: C1 3.333333 # (0+4+6)/3 = 3.333333 C2 5.666667 # (1+7+9)/3 = 5.66667 dtype: float64
In [13]: df.mean(1) # mean by row Out[13]: a 0.5 b NaN c 4.0 d 6.5 e 9.0 dtype: float64 In [14]: df.std() Out[14]: C1 3.055050 C2 4.163332 dtype: float64
(3) DataFrame 칼럼 간 연산 시 : NaN이 하나라도 있으면 NaN 반환
아래 예시의 'C3' 칼럼에서 'c', 'e' index 값을 유심히 보시기 바랍니다. '+'을 하는 'C1'과 'C2' 칼럼의 값 중에서 하나라도 NaN 이면 'C3' 값은 NaN 입니다. (물론 'C1'과 'C2' 모두 NaN이면 이들의 합인 'C3'는 NaN이구요.)
In [8]: df Out[8]: C1 C2 a 0.0 1.0 b NaNNaN c 4.0 NaN d 6.0 7.0 e NaN 9.0
In [15]: df['C3'] = df['C1'] + df['C2']
In [16]: df Out[16]: C1 C2 C3 a 0.0 1.0 1.0 b NaN NaN NaN c 4.0 NaN NaN d 6.0 7.0 13.0 e NaN 9.0 NaN
(4) DataFrame 간 연산 : 동일한 칼럼끼리는 NaN을 '0'으로 처리하여 연산, 동일한 칼럼이 없는 경우(한쪽에만 칼럼이 있는 경우)는 모든 값을 NaN으로 반환
아래 예제의 df, df_2 의 두 개의 DataFrame에서는 'C1' 만이 동일한 (공통의) 칼럼이며, 나머지는 한쪽 DataFrame에만 있는 칼럼들로 구성이 되어 있습니다.
이들 df, df_2 두 개의 DataFrame을 '+' 하면 'C1' 만 제대로 덧셈 연산이 되고 ( NaN 은 '0'으로 처리하여 연산 ), 나머지 칼럼은 모든 값이 NaN으로 변환되었음을 알 수 있습니다.
아래 예시의 'df_all' DataFrame 에서 ['A', 'B'] 칼럼의 ['0', '1'] index 위치에 있는 관측치에 'None'을 할당하여 결측치를 만들어보았습니다.
'A'칼럼의 경우 'string' 데이터 형식인데요, 'None'을 할당하니 'None'으로 입력되었습니다. 반면에, 'B' 칼럼의 경우 'float' 데이터 형식인데요, 'None'을 할당하니 'NaN'으로 자동으로 입력되었습니다.
DataFrame의 행, 열을 기준으로 indexing을 하고 싶을 때는 DataFrame.ix[[row1, row2], ['col1', 'col2']] 을 사용하면 됩니다. 아래 예시를 참고하세요.
In [11]: df_all Out[11]: A B KEY C D 0 A00.5 K0 NaN NaN 1 A12.2 K1 NaN NaN 2 A2 3.6 K2 C2 D2 3 A3 0.4 K3 C3 D3 4 NaN NaN K4 C4 D4 5 NaN NaN K5 C5 D5
In [12]: df_all.loc[[0, 1], ['A', 'B']] = None
In [13]: df_all Out[13]: A B KEY C D 0 NoneNaN K0 NaN NaN 1 NoneNaN K1 NaN NaN 2 A2 3.6 K2 C2 D2 3 A3 0.4 K3 C3 D3 4 NaN NaNK4 C4 D4 5 NaN NaNK5 C5 D5
In [14]: df_all[['A', 'B']].isnull() Out[14]: A B 0 TrueTrue 1 True True 2 False False 3 False False 4 True True 5 True True
(3) 칼럼별 결측값 개수 구하기 : df.isnull().sum()
# counting missing value numbers for all columns In [15]: df_all.isnull().sum() Out[15]: A 2 B 2 KEY 0 C 2 D 2 dtype: int64
# counting missing value numbers for 'A' column In [16]: df_all['A'].isnull().sum() Out[16]: 2
반대로, 칼럼별 결측값이 아닌 값의 개수를 구하려면 df.notnull().sum() 을 사용하면 됩니다.
# counting notnull value numbers for all columns In [17]: df_all.notnull().sum() Out[17]: A 4 B 4 KEY 6 C 4 D 4 dtype: int64
(4) 행(row) 단위로 결측값 개수 구하기 : df.isnull().sum(1) 행(row) 단위로 실측값 개수 구하기 : df.notnull().sum(1)
In [18]: df_all Out[18]: A B KEY C D 0 A0 0.5 K0 NaN NaN 1 A1 2.2 K1 NaN NaN 2 A2 3.6 K2 C2 D2 3 A3 0.4 K3 C3 D3 4 NaN NaN K4 C4 D4 5 NaN NaN K5 C5 D5
In [19]: df_all['NaN_cnt'] = df_all.isnull().sum(1)
In [20]: df_all['NotNull_cnt'] = df_all.notnull().sum(1)
In [21]: df_all Out[21]: A B KEY C D NaN_cntNotNull_cnt 0 A0 0.5 K0 NaN NaN 24 1 A1 2.2 K1 NaN NaN 24 2 A2 3.6 K2 C2 D2 06 3 A3 0.4 K3 C3 D3 06 4 NaN NaN K4 C4 D4 24 5 NaN NaN K5 C5 D5 24
이번 포스팅에서는 pandas의 merge(), join() 함수를 사용해서 index를 기준으로 DataFrame을 합치는 방법을 소개하도록 하겠습니다.
SQL이나 R 사용자라면 index 사용하는게 좀 낯설을 수도 있을 것 같습니다.
먼저 필요한 Library를 importing하고, 간단한 DataFrame 을 예로 만들어 보겠습니다.
In [1]:import pandas as pd
In [2]:from pandas import DataFrame
In [3]: df_left = DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
...: 'B': ['B0', 'B1', 'B2', 'B3']},
...:index=['K0', 'K1', 'K2', 'K3'])
In [4]: df_right = DataFrame({'C': ['C2', 'C3', 'C4', 'C5'],
...: 'D': ['D2', 'D3', 'D4', 'D5']},
...:index=['K2', 'K3', 'K4', 'K5'])
...:
In [5]: df_left
Out[5]:
A B K0 A0 B0 K1 A1 B1 K2 A2 B2 K3 A3 B3
In [6]: df_right
Out[6]:
C D K2 C2 D2 K3 C3 D3 K4 C4 D4 K5 C5 D5
index를 기준으로 DataFrame을 합치는 방법에는 pd.merge() 와 join() 두 가지 방법이 있는데요, join() 이 code가 간결한 편이며, code에 대한 가독성은 pd.merge()가 좀더 명확한 편입니다.
(1) index를 기준으로 Left Join 하기 (Left join on index)
# Left joining on index
# way 1 : by merge()
In [7]:pd.merge(df_left, df_right,
...:left_index=True, right_index=True,
...:how='left')
Out[7]:
A B C D K0 A0 B0 NaN NaN K1 A1 B1 NaN NaN K2 A2 B2 C2 D2 K3 A3 B3 C3 D3
# way 2 : by join
In [8]: df_left.join(df_right, how='left')
Out[8]:
A B C D K0 A0 B0 NaN NaN K1 A1 B1 NaN NaN K2 A2 B2 C2 D2 K3 A3 B3 C3 D3
(2) index를 기준으로 Right Join 하기 (Right join on index)
# Right join on index
# way 1 : merge()
In [9]:pd.merge(df_left, df_right,
...:left_index=True, right_index=True,
...:how='right')
Out[9]:
A B C D K2 A2 B2 C2 D2 K3 A3 B3 C3 D3 K4 NaN NaN C4 D4 K5 NaN NaN C5 D5
# way 2 : join()
In [10]: df_left.join(df_right, how='right')
Out[10]:
A B C D K2 A2 B2 C2 D2 K3 A3 B3 C3 D3 K4 NaN NaN C4 D4 K5 NaN NaN C5 D5
(3) index를 기준으로 inner join 하기 (inner join on index)
# inner join on index
# way 1 : by merge()
In [11]:pd.merge(df_left, df_right,
...:left_index=True, right_index=True,
...:how='inner')
Out[11]:
A B C D K2 A2 B2 C2 D2 K3 A3 B3 C3 D3
# way 2 : by join()
In [12]: df_left.join(df_right, how='inner')
Out[12]:
A B C D K2 A2 B2 C2 D2 K3 A3 B3 C3 D3
(4) index를 기준으로 outer join 하기 (outer join on index)
# outer join on index
# way 1 : by pd.merge()
In [13]:pd.merge(df_left, df_right,
...:left_index=True, right_index=True,
...:how='outer')
Out[13]:
A B C D K0 A0 B0 NaN NaN K1 A1 B1 NaN NaN K2 A2 B2 C2 D2 K3 A3 B3 C3 D3 K4 NaN NaN C4 D4 K5 NaN NaN C5 D5
# way 2 : by join()
In [14]: df_left.join(df_right, how='outer')
Out[14]:
A B C D K0 A0 B0 NaN NaN K1 A1 B1 NaN NaN K2 A2 B2 C2 D2 K3 A3 B3 C3 D3 K4 NaN NaN C4 D4 K5 NaN NaN C5 D5
위의 4개의 index 기준 DataFrame 병합 사례에서는 양쪽 DataFrame 모두 index를 사용했습니다.
그런데 만약 한쪽 DataFrame은 index를 기준으로 하고, 나머지 한쪽 DataFrame에서는 Key 변수를 기준으로 해서 두 DataFrame을 합쳐야 한다면 어떻게 해야 할까요?
pd.merge()와 join() 두 가지 방법을 how='left' 의 경우만 예를 들어서 설명하겠습니다. 역시 join() 이 script가 간결한 반면, pd.merge()가 병합의 기준을 명시해줌으로써 가독성은 더 좋습니다. 뭘 사용할지는 개인의 취향에 따라 선택하시면 됩니다.
(5) index와 Key를 혼합해서 DataFrame 합치기 (Joining key columns on an index)
먼저 df_left_2 는 'KEY' 를 가진 DataFrame으로 만들고, df_right_2는 index를 가진 DataFrame으로 만든 후에 이 둘을 'KEY'와 index를 혼합해서 사용해서 합쳐보겠습니다.
# making DataFrame
In [15]: df_left_2 = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],
...: 'A': ['A0', 'A1', 'A2', 'A3'],
...: 'B': ['B0', 'B1', 'B2', 'B3']})
...:
In [16]: df_right_2 = DataFrame({'C': ['C2', 'C3', 'C4', 'C5'],
...: 'D': ['D2', 'D3', 'D4', 'D5']},
...:index=['K2', 'K3', 'K4', 'K5'])
...:
In [17]: df_left_2 # with 'KEY'
Out[17]:
A B KEY 0 A0 B0 K0 1 A1 B1 K1 2 A2 B2 K2 3 A3 B3 K3
In [18]: df_right_2 # with 'index'
Out[18]:
C D K2 C2 D2 K3 C3 D3 K4 C4 D4 K5 C5 D5
이제 Key와 index를 혼합해서 두 DataFrame을 합쳐보겠습니다.
# joining key columns on an index
# way 1 : pd.merge()
In [19]:pd.merge(df_left_2, df_right_2,
...:left_on='KEY', right_index=True,
...:how='left')
...:
Out[19]:
A B KEY C D 0 A0 B0 K0 NaN NaN 1 A1 B1 K1 NaN NaN 2 A2 B2 K2 C2 D2 3 A3 B3 K3 C3 D3
# way 2 : join()
In [20]: df_left_2.join(df_right_2, on='KEY', how='left')
Out[20]:
A B KEY C D 0 A0 B0 K0 NaN NaN 1 A1 B1 K1 NaN NaN 2 A2 B2 K2 C2 D2 3 A3 B3 K3 C3 D3
이번 포스팅에서는 SQL을 사용해서 Database의 Table 들을 Join/Merge 하는 것과 유사하게 Python pandas의 pd.merge() 함수를 사용해서 DataFrame을 Key 기준으로 inner, outer, left, outer join 하여 합치는 방법을 소개하도록 하겠습니다.
SQL을 사용하는데 익숙한 분석가라면 매우 쉽고 빠르게 이해하실 수 있을 것입니다. 그리고 Python의 merge() 기능은 메모리 상에서 매우 빠르게 작동함으로 사용하는데 있어 불편함이 덜할 것 같습니다.
pandas merge 함수 설정값들은 아래와 같이 여러개가 있는데요, 이중에서 'how'와 'on'은 꼭 기억해두셔야 합니다.
pd.merge(left, right, # merge할 DataFrame 객체 이름 how='inner', # left, rigth, inner (default), outer on=None, # merge의 기준이 되는 Key 변수 left_on=None, # 왼쪽 DataFrame의 변수를 Key로 사용 right_on=None, # 오른쪽 DataFrame의 변수를 Key로 사용 left_index=False, # 만약 True 라면, 왼쪽 DataFrame의 index를 merge Key로 사용 right_index=False, # 만약 True 라면, 오른쪽 DataFrame의 index를 merge Key로 사용 sort=True, # merge 된 후의 DataFrame을 join Key 기준으로 정렬 suffixes=('_x', '_y'), # 중복되는 변수 이름에 대해 접두사 부여 (defaults to '_x', '_y' copy=True, # merge할 DataFrame을 복사 indicator=False) # 병합된 이후의 DataFrame에 left_only, right_only, both 등의 출처를 알 수 있는 부가 정보 변수 추가
먼저, pandas, DataFrame library를 importing 한 후에, 2개의 DataFrame을 만들어보겠습니다.
In [1]:import pandas as pd
In [2]:from pandas import DataFrame
In [3]: df_left = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],
...: 'A': ['A0', 'A1', 'A2', 'A3'],
...: 'B': ['B0', 'B1', 'B2', 'B3']})
...:
In [4]: df_right = DataFrame({'KEY': ['K2', 'K3', 'K4', 'K5'],
...: 'C': ['C2', 'C3', 'C4', 'C5'],
...: 'D': ['D2', 'D3', 'D4', 'D5']})
...:
In [5]: df_left
Out[5]:
A B KEY 0 A0 B0 K0 1 A1 B1 K1 2 A2 B2 K2 3 A3 B3 K3
In [6]: df_right
Out[6]:
C D KEY 0 C2 D2 K2 1 C3 D3 K3 2 C4 D4 K4 3 C5 D5 K5
'how' 의 left, right, inner, outer 별로 위에서 만든 'df_left'와 'df_right' 두 개의 DataFrame을 'KEY' 변수를 기준으로 merge 해보겠습니다. SQL join에 익숙하신 분이라면 쉽게 이해할 수 있을 것입니다.
(1) Merge method : left (SQL join name : LEFT OUTER JOIN)
In [7]: df_merge_how_left = pd.merge(df_left, df_right,
...:how='left',
...:on='KEY')
...:
In [8]: df_merge_how_left
Out[8]:
A B KEY C D 0 A0 B0 K0 NaN NaN 1 A1 B1 K1 NaN NaN 2 A2 B2 K2 C2 D2 3 A3 B3 K3 C3 D3
(2) Merge method : right (SQL join name : RIGHT OUTER JOIN)
In [9]: df_merge_how_right = pd.merge(df_left, df_right,
...:how='right',
...:on='KEY')
In [10]: df_merge_how_right
Out[10]:
A B KEY C D 0 A2 B2 K2 C2 D2 1 A3 B3 K3 C3 D3 2 NaN NaN K4 C4 D4 3 NaN NaN K5 C5 D5
In [11]: df_merge_how_inner = pd.merge(df_left, df_right,
...:how='inner', # default
...:on='KEY')
...:
In [12]: df_merge_how_inner
Out[12]:
A B KEY C D 0 A2 B2 K2 C2 D2 1 A3 B3 K3 C3 D3
(4) Merge method : outer (SQL join name : FULL OUTER JOIN)
In [13]: df_merge_how_outer = pd.merge(df_left, df_right,
...:how='outer',
...:on='KEY')
...:
In [14]: df_merge_how_outer
Out[14]:
A B KEY C D 0 A0 B0 K0 NaN NaN 1 A1 B1 K1 NaN NaN 2 A2 B2 K2 C2 D2 3 A3 B3 K3 C3 D3 4 NaN NaN K4 C4 D4 5 NaN NaN K5 C5 D5
[참고] Hive 조인 문 : INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL JOIN, CARTESIAN PRODUCT JOIN, MAP-SIDE JOIN, UNION ALL :http://rfriend.tistory.com/216
(5) indicator = True : 병합된 이후의 DataFrame에 left_only, right_only, both 등의
출처를 알 수 있는 부가정보 변수 추가
In [15]: pd.merge(df_left, df_right, how='outer', on='KEY',
...:indicator=True)
Out[15]:
A B KEY C D _merge 0 A0 B0 K0 NaN NaN left_only 1 A1 B1 K1 NaN NaN left_only 2 A2 B2 K2 C2 D2 both 3 A3 B3 K3 C3 D3 both 4 NaN NaN K4 C4 D4 right_only 5 NaN NaN K5 C5 D5 right_only
위에서는 indicator=True로 했더니 '_merge'라는 새로운 변수가 생겼습니다.
이 방법 외에도, 아래처럼 indicator='변수 이름(예: indicator_info)'을 설정해주면, 새로운 변수 이름에 indicator 정보가 반환됩니다.
In [16]: pd.merge(df_left, df_right, how='outer', on='KEY',
...:indicator='indicator_info')
Out[16]:
A B KEY C D indicator_info 0 A0 B0 K0 NaN NaN left_only 1 A1 B1 K1 NaN NaN left_only 2 A2 B2 K2 C2 D2 both 3 A3 B3 K3 C3 D3 both 4 NaN NaN K4 C4 D4 right_only 5 NaN NaN K5 C5 D5 right_only
(6) 변수 이름이 중복될 경우 접미사 붙이기 : suffixes = ('_x', '_y')
'B'와 'C' 의 변수 이름이 동일하게 있는 두 개의 DataFrame을 만든 후에, KEY를 기준으로 합치기(merge)를 해보겠습니다. 변수 이름이 중복되므로 Data Source를 구분할 수 있도록 suffixes = ('string', 'string') 을 사용해서 중복되는 변수의 뒷 부분에 접미사를 추가해보겠습니다. default는 suffixes = ('_x', '_y') 입니다.
# making DataFrames with overlapping columns
In [17]: df_left_2 = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],
...: 'A': ['A0', 'A1', 'A2', 'A3'],
...:'B': ['B0', 'B1', 'B2', 'B3'],
...:'C': ['C0', 'C1', 'C2', 'C3']})
In [18]: df_right_2 = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],
분석을 하다보면 여기저기 흩어져 있는 여러 개의 데이터 테이블을 모아서 합쳐야 하는 일이 생기곤 합니다. 나를 대신해서 누군가가 데이터 전처리를 해주지 않는다고 했을 때는 말이지요.
정규화해서 Database 관리를 하는 곳이라면 주제별로 Data Entity를 구분해서 여러 개의 Table들로 데이터가 나뉘어져 있을 것입니다.
특히, 데이터의 속성 형태가 동일한 데이터셋(homogeneously-typed objects)끼리 합칠 때사용할 수 있는 pandas의 DataFrame 합치는 방법(concatenating DataFrames)으로 이번 포스팅에서는 pd.concat() 함수를 소개하겠습니다.
(R의 rbind(), cbind() 와 유사함)
pd.concat() 의 parameter 값들의 default setting은 아래와 같습니다. 하나씩 예를 들어가면서 소개하겠습니다.
A B C D A B C E 0 A0 B0 C0 D0 A0 B0 C0 E0 1 A1 B1 C1 D1 A1 B1 C1 E1 2 A2 B2 C2 D2 NaN NaN NaN NaN 3 NaN NaN NaN NaN A2 B2 C2 E2
# comparison 2
In [29]: df_14_inner_axis1 = pd.concat([df_1, df_4], join='inner', axis=1)
In [30]: df_14_inner_axis1
Out[30]:
A B C D A B C E 0 A0 B0 C0 D0 A0 B0 C0 E0 1 A1 B1 C1 D1 A1 B1 C1 E1
# reuse the exact index from the original DataFrame : reindex()
In [31]: df_14_axis1_reindex = pd.concat([df_1, df_4], axis=1).reindex(df_1.index)
In [32]: df_14_axis1_reindex
Out[32]:
A B C D A B C E 0 A0 B0 C0 D0 A0 B0 C0 E0 1 A1 B1 C1 D1 A1 B1 C1 E1 2 A2 B2 C2 D2 NaN NaN NaN NaN
* (참고) 최신버전의 pandas를 사용하면서 join_axes 매개변수를 사용한다면 아래와 같은 TypeError: concat() got an unexpected keyword argument 'join_axes'에러 메시지가 뜰 것입니다. 본 블로그를 2016년도에 썼다보니 그동안 pandas 매개변수 업데이터된 내용을 블로그 포스팅에 미처 반영 못한 부분이 있었습니다. (본문 바로잡을 수 있도록 댓글 남겨주신 김명찬님 감사합니다.)
join_axes 매개변수는 사용이 중단되었네요.(join_axes is deprecated.) 대신에 위의 In [32]의 예에서처럼 reindex() 를 사용해서 기존의 index를 재사용할 수 있습니다.
TypeError: concat() got an unexpected keyword argument 'join_axes'
(4) 기존 index를 무시하고 싶을 때 : ignore_index
In [33]: df_5 = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
...: 'B': ['B0', 'B1', 'B2'],
...: 'C': ['C0', 'C1', 'C2'],
...: 'D': ['D0', 'D1', 'D2']},
...: index=['r0', 'r1', 'r2'])
In [34]: df_6 = pd.DataFrame({'A': ['A3', 'A4', 'A5'],
...: 'B': ['B3', 'B4', 'B5'],
...: 'C': ['C3', 'C4', 'C5'],
...: 'D': ['D3', 'D4', 'D5']},
...: index=['r3', 'r4', 'r5'])
In [35]: df_56_with_index = pd.concat([df_5, df_6], ignore_index=False) # default
In [36]: df_56_with_index
Out[36]:
A B C D r0 A0 B0 C0 D0 r1 A1 B1 C1 D1 r2 A2 B2 C2 D2 r3 A3 B3 C3 D3 r4 A4 B4 C4 D4 r5 A5 B5 C5 D5
# if you want ignore current index, use 'ignore_index=True'
In [37]: df_56_ignore_index = pd.concat([df_5, df_6], ignore_index=True)# index 0~(n-1)
In [38]: df_56_ignore_index
Out[38]:
A B C D 0 A0 B0 C0 D0 1 A1 B1 C1 D1 2 A2 B2 C2 D2 3 A3 B3 C3 D3 4 A4 B4 C4 D4 5 A5 B5 C5 D5
(5) 계층적 index (hierarchical index) 만들기 : keys
# concatenating DataFrames : Construct hierarchical index using 'keys'
In [40]: df_56_with_keys = pd.concat([df_5, df_6], keys=['df_5', 'df_6'])
In [41]: df_56_with_keys
Out[41]:
A B C D df_5r0 A0 B0 C0 D0 r1 A1 B1 C1 D1 r2 A2 B2 C2 D2 df_6r3 A3 B3 C3 D3 r4 A4 B4 C4 D4 r5 A5 B5 C5 D5
참고로, 계층적 index를 가지고 indexing 하는 방법을 아래에 예를 들어 소개하겠습니다. 'df_56_with_keys' DataFrame은 index가 1층, 2층으로 계층을 이루고 있으므로 indexing 할 때 1층용 index와 2층용 index를 따로 따로 사용하면 됩니다. 아래 예시를 참고하세요.
In [42]: df_56_with_keys.loc['df_5']
Out[42]:
A B C D r0 A0 B0 C0 D0 r1 A1 B1 C1 D1 r2 A2 B2 C2 D2
In [43]: df_56_with_keys.loc['df_5'][0:2]
Out[43]:
A B C D r0 A0 B0 C0 D0 r1 A1 B1 C1 D1
(6) index에 이름 부여하기 : names
In [44]: df_56_with_name = pd.concat([df_5, df_6],
...: keys=['df_5', 'df_6'],
...:names=['df_name', 'row_number'])
In [45]: df_56_with_name
Out[45]:
A B C D df_name row_number df_5 r0 A0 B0 C0 D0 r1 A1 B1 C1 D1 r2 A2 B2 C2 D2 df_6 r3 A3 B3 C3 D3 r4 A4 B4 C4 D4 r5 A5 B5 C5 D5
(7) index 중복 여부 점검 : verify_integrity
df_7, df_8 DataFrame에 'r2' index를 중복으로 포함시킨 후에 pd.concat() 을 적용해보겠습니다. verify_integrity=False (디폴트이므로 별도 입력 안해도 됨) 에서는 아무 에러 메시지 없이 위+아래로 잘 합쳐집니다 ('r2' index가 위+아래로 2번 중복해서 나타남). 반면에, verify_integrity=True 를 설정해주면 만약 index 중복이 있을 경우 'ValueError: Indexes have overlapping values: xxx' 에러 메시지가 뜨면서 합치기가 아예 안됩니다.
In [48]: df_7 = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
...: 'B': ['B0', 'B1', 'B2'],
...: 'C': ['C0', 'C1', 'C2'],
...: 'D': ['D0', 'D1', 'D2']},
...: index=['r0', 'r1', 'r2'])
...:
In [49]: df_8 = pd.DataFrame({'A': ['A2', 'A3', 'A4'],
...: 'B': ['B2', 'B3', 'B4'],
...: 'C': ['C2', 'C3', 'C4'],
...: 'D': ['D2', 'D3', 'D4']},
...: index=['r2', 'r3', 'r4'])
In [50]: df_7
Out[50]:
A B C D r0 A0 B0 C0 D0 r1 A1 B1 C1 D1 r2 A2 B2 C2 D2
In [51]: df_8
Out[51]:
A B C D r2 A2 B2 C2 D2 r3 A3 B3 C3 D3 r4 A4 B4 C4 D4
# concatenating DataFrames without overlap checking : verify_integrity=False
In [52]: df_78_F_verify_integrity = pd.concat([df_7, df_8],
...:verify_integrity=False) # default
In [53]: df_78_F_verify_integrity
Out[53]:
A B C D r0 A0 B0 C0 D0 r1 A1 B1 C1 D1 r2 A2 B2 C2 D2 r2 A2 B2 C2 D2 r3 A3 B3 C3 D3 r4 A4 B4 C4 D4
# index overlap checking, using verify_integrity=True
In [54]: df_78_T_verify_integrity = pd.concat([df_7, df_8],
...:verify_integrity=True)
Traceback (most recent call last):
File "<ipython-input-56-5512ad3b5016>", line 2, in <module> verify_integrity=True)
File "C:\Anaconda3\lib\site-packages\pandas\tools\merge.py", line 845, in concat copy=copy)
File "C:\Anaconda3\lib\site-packages\pandas\tools\merge.py", line 984, in __init__ self.new_axes = self._get_new_axes()
File "C:\Anaconda3\lib\site-packages\pandas\tools\merge.py", line 1073, in _get_new_axes new_axes[self.axis] = self._get_concat_axis()
File "C:\Anaconda3\lib\site-packages\pandas\tools\merge.py", line 1132, in _get_concat_axis self._maybe_check_integrity(concat_axis)
File "C:\Anaconda3\lib\site-packages\pandas\tools\merge.py", line 1141, in _maybe_check_integrity % str(overlap))
ValueError: Indexes have overlapping values: ['r2']