대부분의 경우 데이터는 다수의 소스나 파일로부터 나오므로, 데이터프레임을 합치는 작업은 아주 중요한 작업 중 하나로, 여기서는 Pandas 라이브러리를 사용하여 복수의 데이터프레임을 병합하는 방법에 대해 살펴보자.
Concatenate DataFrames
먼저 pandas 라이브러리를 임포트한다.
import pandas as pd
파이썬 딕셔너리로 샘플로 사용할 데이터프레임을 다음과 같이 만든다.
dummy_data1 = {
'id': ['1', '2', '3', '4', '5'],
'Feature1': ['A', 'C', 'E', 'G', 'I'],
'Feature2': ['B', 'D', 'F', 'H', 'J']}
딕셔너리 dummy_data1 의 키는 컬럼명이고 리스트의 값들은 각각의 행에 대응하는 데이터이다. 이를 pandas 데이터프레임으로 변환하기 위해 DataFrame() 함수를 사용한다. columns 인수를 사용하여 컬럼의 이름을 지정한다.
df1 = pd.DataFrame(dummy_data1, columns = ['id', 'Feature1', 'Feature2'])
df1
보시다시피 id, Feature1, 그리고 Feature2 의 3개의 컬럼을 가진 데이터프레임이 완성되었다. 기본적으로 생성되는 이름 없는 컬럼인 row 라벨도 있다. 위와 마찬가지로 데이터프레임 df2, df3 를 만들자.
dummy_data2 = {
'id': ['1', '2', '6', '7', '8'],
'Feature1': ['K', 'M', 'O', 'Q', 'S'],
'Feature2': ['L', 'N', 'P', 'R', 'T']}
df2 = pd.DataFrame(dummy_data2, columns = ['id', 'Feature1', 'Feature2'])
df2
dummy_data3 = {
'id': ['1', '2', '3', '4', '5', '7', '8', '9', '10', '11'],
'Feature3': [12, 13, 14, 15, 16, 17, 15, 12, 13, 23]}
df3 = pd.DataFrame(dummy_data3, columns = ['id', 'Feature3'])
df3
pandas의 concat() 함수를 이용하여 행을 따라 데이터프레임을 합칠 수 있다. concat() 함수의 인수로 데이터프레임의 이름을 넣어준다.
df_row = pd.concat([df1, df2])
df_row
이제 데이터프레임 df1, df2 가 df_row 라는 하나의 데이터프레임으로 합쳐진 것을 볼 수 있다. 하지만 row 라벨이 적절해보이지 않는다. ignore_index 인수를 True 로 설정하여 기존의 row 라벨을 무시하고 새롭게 번호 매김할 수 있도록 하자.
df_row_reindex = pd.concat([df1, df2], ignore_index=True)
df_row_reindex
pandas 는 데이터프레임을 합친 후 데이터프레임에 라벨링할 수 있는 옵션이 있는데 keys 인수를 이용하여 해당 데이터가 어떤 데이터프레임에서 왔는지 알아볼 수 있게 라벨링하는 옵션을 제공한다. 아래와 같이 라벨 이름을 리스트 형식으로 넣어 keys 인수를 이용하여 합치면 된다.
frames = [df1,df2]
df_keys = pd.concat(frames, keys=['x', 'y'])
df_keys
loc 메소드를 이용하여 라벨 y fmf rkwls 데이터프레임 df2 의 데이터만 따로 뽑아낼 수 있다.
df_keys.loc['y']
concat() 함수에 딕셔너리 형식으로 전달하면, 딕셔너리 키가 keys 인수처럼 작동한다.
pieces = {'x': df1, 'y': df2}
df_piece = pd.concat(pieces)
df_piece
concat() 는 데이터 전체 복사본도 만들 수 있다. list comprehension 을 이용하여 다수의 데이터셋에 일괄 작업도 가능하다.
frames = [ process_your_file(f) for f in files ]
result = pd.concat(frames)
axis 파라메터 값을 1로 지정하여 컬럼으 따라 데이터프레임을 합칠 수도 있다.
df_col = pd.concat([df1,df2], axis=1)
df_col
Merge DataFrames
2개의 데이터프레임이 동일한 개체에 대해서 서로 다른 종류의 정보를 가지고 있고, 특정 컬럼에 의해 링크되어 있는 경우를 생각해보자. 이러한 데이터프레임을 합치기 위해 concat(), merge() , join() 등의 함수를 사용할 수 있는데, 이번 섹션에서는 이 중 merge() 함수에 대해 살펴본다.
(df1, df2 를 합쳐서 생성된) 데이터프레임 df_row 와 df3 를 공통되는 컬럼 (또는 key)인 id 를 기반으로 합쳐보자. 이를 위해서 데이터프레임의 이름과 여기서는 id 인 공통되는 컬럼명을 추가적인 인수로 merge() 함수로 넘긴다.
df_merge_col = pd.merge(df_row, df3, on='id')
df_merge_col
2개의 데이터프레임에 공통되는 컬럼인 id 를 기반으로 합쳐져 단일 데이터프레임이 된 것을 볼 수 있다. 예를 들어 id 값 1 은 데이터프레임 df_row 의 A, B 그리고 K, L 에 존재하고 있었다. 따라서 id 1 은 최종 데이터프레임 df_merge_col 에 반복해서 등장하고 데이터프레임 df3 에서 온 Feature3 컬럼의 값인 12 역시 반복해서 나옴을 알 수 있다.
위의 예와는 달리 합칠 데이터프레임이 서로 다른 컬럼 이름을 가지고 있는 경우에는 좌측 데이터프레임 이름을 left_on 인수로, 우측 데이터프레임의 이름을 right_on 인수에 지정해서 다음과 같이 합칠 수 있다.
df_merge_difkey = pd.merge(df_row, df3, left_on='id', right_on='id')
df_merge_difkey
다음과 같이 append() 함수를 이용하여 행을 데이터프레임에 추가하여 새로운 데이터프레임을 만들 수 있다.
add_row = pd.Series(['10', 'X1', 'X2', 'X3'],
index=['id','Feature1', 'Feature2', 'Feature3'])
df_add_row = df_merge_col.append(add_row, ignore_index=True)
df_add_row
Join DataFrames
pandas 데이터프레임을 합칠 때 유용한 다양한 join 로직에 대해 살펴보자.
Full Outer Join
FULL OUTER JOIN 은 left outer join 과 right outer join 의 결과를 합친 것이다. 합쳐진 데이터프레임은 양쪽 데이터프레임의 모든 레코드를 가지게 된다. 그리고 없는 값에 대해서는 NaN 을 채워넣는다. how 인수에 outer 라고 명시하여 실행하면 된다.
df_outer = pd.merge(df1, df2, on='id', how='outer')
df_outer
해당 컬럼이 어떤 데이터프레임에서 왔는지 보여주기 위해 컬럼명에 기본 접미사 x, y 가 붙은 것을 볼 수 있다. merge() 함수의 suffixes 인수를 다음과 같이 지정할 수도 있다.
df_suffix = pd.merge(df1, df2, left_on='id',right_on='id',how='outer',suffixes=('_left','_right'))
df_suffix
Inner Join
INNER JOIN 는 양쪽 데이터프레임에 공통되는 레코드 셋트만 생성해낸다.
df_inner = pd.merge(df1, df2, on='id', how='inner')
df_inner
Right Join
RIGHT JOIN 는 데이터프레임 B (우측 데이터프레임) 의 전체 레코드 셋트와 이와 매칭되는 데이터프레임 A 를 보여준다. 만약 양쪽에 매칭되는 부분이 없다면 우측 사이드는 null 을 포함한다.
df_right = pd.merge(df1, df2, on='id', how='right')
df_right
Left Join
LEFT JOIN 은 마찬가지로 데이터프레임 DataFrame A (좌측 DataFrame) 의 모든 레코드 셋과 이와 매칭되는 데이터프레임 B 의 레코드를 가져온다. 매칭되는 부분이 없으며, 좌측 사이드는 null 을 포함하게 된다.
df_left = pd.merge(df1, df2, on='id', how='left')
df_left
Joining on index
인덱스 또는 row 라벨에 기반하여 조인하는 경우로, right_index 와 left_index 를 True 로 지정하면 된다.
df_index = pd.merge(df1, df2, right_index=True, left_index=True)
df_index
Time-series friendly merging
Pandas 는 시계열 데이터프레임을 합칠 수 있는 특별한 함수를 제공한다. 이 중 가장 많이 쓰는 함수로 merge_asof() 가 있다. merge_asof() 는 완전히 일치하는 키 대신에 가장 근접한 키를 기반으로 한다는 점을 제외하면 left-join 과 유사하다. 좌측 데이터프레임의 각각의 행에 대해 좌측프레임의 키보다 적은 우측 데이터프레임에서 최종 행을 선택한다. 양쪽 데이터프레임은 key 기반으로 정렬되어야 한다.
선택적으로 asof 병합은 그룹 기반 병합을 수행한다. 예를 들어, trades 및 quotes 데이터를 가지고 asof 병합을 하고 싶다고 하자. 여기서 좌측 데이터프레임은 trades 이고 우측 데이터프레임은 quotes 이다. 키 타임 기반으로 asof 병합되었고, 티커 심볼로 그룹 기반 병합되었다.
trades = pd.DataFrame({
'time': pd.to_datetime(['20160525 13:30:00.023',
'20160525 13:30:00.038',
'20160525 13:30:00.048',
'20160525 13:30:00.048',
'20160525 13:30:00.048']),
'ticker': ['MSFT', 'MSFT','GOOG', 'GOOG', 'AAPL'],
'price': [51.95, 51.95,720.77, 720.92, 98.00],
'quantity': [75, 155,100, 100, 100]},
columns=['time', 'ticker', 'price', 'quantity'])
quotes = pd.DataFrame({
'time': pd.to_datetime(['20160525 13:30:00.023',
'20160525 13:30:00.023',
'20160525 13:30:00.030',
'20160525 13:30:00.041',
'20160525 13:30:00.048',
'20160525 13:30:00.049',
'20160525 13:30:00.072',
'20160525 13:30:00.075']),
'ticker': ['GOOG', 'MSFT', 'MSFT','MSFT', 'GOOG', 'AAPL', 'GOOG','MSFT'],
'bid': [720.50, 51.95, 51.97, 51.99,720.50, 97.99, 720.50, 52.01],
'ask': [720.93, 51.96, 51.98, 52.00,720.93, 98.01, 720.88, 52.03]},
columns=['time', 'ticker', 'bid', 'ask'])
trades
quotes
df_merge_asof = pd.merge_asof(trades, quotes,
on='time',
by='ticker')
df_merge_asof
자세히 살펴보면 AAPL 티커 행에 NaN 이 등장했음을 알 수 있다. 이는 우측 데이터프레임 quotes 가 AAPL 티커에 대해서 (좌측 테이블의 시간인) 13:30:00.048 보다 적은 값을 가진 게 없기 때문에 bid 와 ask 컬럼에 등장하게 된 것이다.
만약 quote time 과 trade time 이 2ms 이내인 경우만 asof 병합을 하고자 한다면, tolerance 인수를 다음과 같이 지정해주면 된다.
df_merge_asof_tolerance = pd.merge_asof(trades, quotes,
on='time',
by='ticker',
tolerance=pd.Timedelta('2ms'))
df_merge_asof_tolerance
앞선 결과와 비교해보면 2ms 범위내에 있지 않은 레코드는 제외되었음을 알 수 있다.
더욱 자세한 내용은 아래 링크에서 살펴볼 수 있다.
https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html
'프로그래밍 Programming' 카테고리의 다른 글
VNC를 통해서 라즈베리 파이로 파일 전송하기 Transferring files to and from your Raspberry Pi (0) | 2020.08.26 |
---|---|
라즈베리파이 Cannot Currently Show the Desktop' 에러 How to Fix Raspberry Pi's 'Cannot Currently Show the Desktop' Error (1) | 2020.08.26 |
np.random.seed 란 무엇인가? (0) | 2020.08.08 |
Numpy linspace 함수 살펴보기 numpy.linspace() in Python (0) | 2020.08.08 |
아나콘다 환경 공유하기 Sharing an environment (0) | 2020.07.28 |