[Python] 데이터 사이언스 스쿨 - 4.6 데이터프레임 합성

Updated:

데이터 사이언스 스쿨 자료를 토대로 공부한 내용입니다.

실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.


4.6 데이터프레임 합성

merge 함수를 사용한 데이터프레임 병합

import numpy as np
import pandas as pd

INNER, FULL, LEFT/RIGHT JOIN

df1 = pd.DataFrame({
    '고객번호': [1001, 1002, 1003, 1004, 1005, 1006, 1007],
    '이름': ['둘리', '도우너', '또치', '길동', '희동', '마이콜', '영희']
}, columns=['고객번호', '이름'])
df1
고객번호 이름
0 1001 둘리
1 1002 도우너
2 1003 또치
3 1004 길동
4 1005 희동
5 1006 마이콜
6 1007 영희
df2 = pd.DataFrame({
    '고객번호': [1001, 1001, 1005, 1006, 1008, 1001],
    '금액': [10000, 20000, 15000, 5000, 100000, 30000]
}, columns=['고객번호', '금액'])
df2
고객번호 금액
0 1001 10000
1 1001 20000
2 1005 15000
3 1006 5000
4 1008 100000
5 1001 30000
# 기본적으로는 inner join
pd.merge(df1, df2)
고객번호 이름 금액
0 1001 둘리 10000
1 1001 둘리 20000
2 1001 둘리 30000
3 1005 희동 15000
4 1006 마이콜 5000
  • pd.merge()를 사용하면 두 데이터 프레임을 결합할 수 있다.

  • 기본적으로 아무 옵션이 없다면 INNER JOIN으로 결합하며 결합 기준은 같은 열이름이다.

# full outer join
pd.merge(df1, df2, how='outer')
고객번호 이름 금액
0 1001 둘리 10000.0
1 1001 둘리 20000.0
2 1001 둘리 30000.0
3 1002 도우너 NaN
4 1003 또치 NaN
5 1004 길동 NaN
6 1005 희동 15000.0
7 1006 마이콜 5000.0
8 1007 영희 NaN
9 1008 NaN 100000.0
  • how 옵션에 outer를 설정하면 FULL OUTER JOIN으로 결합한다.
# left outer join
pd.merge(df1, df2, how='left')
고객번호 이름 금액
0 1001 둘리 10000.0
1 1001 둘리 20000.0
2 1001 둘리 30000.0
3 1002 도우너 NaN
4 1003 또치 NaN
5 1004 길동 NaN
6 1005 희동 15000.0
7 1006 마이콜 5000.0
8 1007 영희 NaN
# right outer join
pd.merge(df1, df2, how='right')
고객번호 이름 금액
0 1001 둘리 10000
1 1001 둘리 20000
2 1001 둘리 30000
3 1005 희동 15000
4 1006 마이콜 5000
5 1008 NaN 100000
  • how 옵션에 left/right를 설정하면 LEFT/RIGHT OUTER JOIN으로 결합한다.

키 값이 같은 데이터가 여러개 있는 경우

df1 = pd.DataFrame({
    '품종': ['setosa', 'setosa', 'virginica', 'virginica'],
    '꽃잎길이': [1.4, 1.3, 1.5, 1.3]},
    columns=['품종', '꽃잎길이'])
df1
품종 꽃잎길이
0 setosa 1.4
1 setosa 1.3
2 virginica 1.5
3 virginica 1.3
df2 = pd.DataFrame({
    '품종': ['setosa', 'virginica', 'virginica', 'versicolor'],
    '꽃잎너비': [0.4, 0.3, 0.5, 0.3]},
    columns=['품종', '꽃잎너비'])
df2
품종 꽃잎너비
0 setosa 0.4
1 virginica 0.3
2 virginica 0.5
3 versicolor 0.3
# 같은 키 값에 대해 모든 경우의 수가 나옴
pd.merge(df1, df2)
품종 꽃잎길이 꽃잎너비
0 setosa 1.4 0.4
1 setosa 1.3 0.4
2 virginica 1.5 0.3
3 virginica 1.5 0.5
4 virginica 1.3 0.3
5 virginica 1.3 0.5
  • 결합 기준인 열에 같은 키 값이 여러개 있는 경우 모든 경우의 수가 출력된다.

이름이 같지만 키로 사용하지 않고 싶은 경우

df1 = pd.DataFrame({
    '고객명': ['춘향', '춘향', '몽룡'],
    '날짜': ['2018-01-01', '2018-01-02', '2018-01-01'],
    '데이터': ['20000', '30000', '100000']})
df1
고객명 날짜 데이터
0 춘향 2018-01-01 20000
1 춘향 2018-01-02 30000
2 몽룡 2018-01-01 100000
df2 = pd.DataFrame({
    '고객명': ['춘향', '몽룡'],
    '데이터': ['여자', '남자']})
df2
고객명 데이터
0 춘향 여자
1 몽룡 남자
# on을 이용해 직접 기준이 되는 키를 지정
pd.merge(df1,df2, on = "고객명")
고객명 날짜 데이터_x 데이터_y
0 춘향 2018-01-01 20000 여자
1 춘향 2018-01-02 30000 여자
2 몽룡 2018-01-01 100000 남자
  • 위 예시는 데이터라는 공통 열이 있지만 실제 두 데이터 프레임의 데이터 열은 서로 다른 성격이다.

  • 이런 경우 on 옵션으로 직접 결합 기준이 되는 열을 지정한다.

키 이름이 다른 경우

df1 = pd.DataFrame({
    '이름': ['영희', '철수', '철수'],
    '성적': [1, 2, 3]})
df1
이름 성적
0 영희 1
1 철수 2
2 철수 3
df2 = pd.DataFrame({
    '성명': ['영희', '영희', '철수'],
    '성적2': [4, 5, 6]})
df2
성명 성적2
0 영희 4
1 영희 5
2 철수 6
# 기준으로 삼을 두 키를 left_on, right_on에 직접 지정
pd.merge(df1,df2, left_on="이름", right_on="성명")
이름 성적 성명 성적2
0 영희 1 영희 4
1 영희 1 영희 5
2 철수 2 철수 6
3 철수 3 철수 6
  • 두 데이터 프레임의 열 이름은 다르지만 결합 기준으로 사용하고 싶다면 legt_on, right_on으로 직접 기준 열을 지정한다.
# 키 이름을 일치 시켜 병합
df2.rename(columns={'성명':'이름'}, inplace=True)
pd.merge(df1,df2)
이름 성적 성적2
0 영희 1 4
1 영희 1 5
2 철수 2 6
3 철수 3 6
  • 다른 방법으론 데이터 프레임의 열 이름을 똑같게 만들어준 후 결합한다.

  • 실제로 결합을 사용한다면 데이터를 변경하는 것보다는 on, how, left_on, right_on등을 하나씩 지정하여 결합하는 것이 좋아 보인다.

인덱스를 기준열로 사용

df1 = pd.DataFrame({
    '도시': ['서울', '서울', '서울', '부산', '부산'],
    '연도': [2000, 2005, 2010, 2000, 2005],
    '인구': [9853972, 9762546, 9631482, 3655437, 3512547]})
df1
도시 연도 인구
0 서울 2000 9853972
1 서울 2005 9762546
2 서울 2010 9631482
3 부산 2000 3655437
4 부산 2005 3512547
df2 = pd.DataFrame(
    np.arange(12).reshape((6, 2)),
    index=[['부산', '부산', '서울', '서울', '서울', '서울'],
           [2000, 2005, 2000, 2005, 2010, 2015]],
    columns=['데이터1', '데이터2'])
df2
데이터1 데이터2
부산 2000 0 1
2005 2 3
서울 2000 4 5
2005 6 7
2010 8 9
2015 10 11
# 기준이 되는 키, 기준이 되는 인덱스 지정
pd.merge(df1, df2, left_on=['도시', '연도'], right_index=True)
도시 연도 인구 데이터1 데이터2
0 서울 2000 9853972 4 5
1 서울 2005 9762546 6 7
2 서울 2010 9631482 8 9
3 부산 2000 3655437 0 1
4 부산 2005 3512547 2 3
  • 위 예시는 도시와 연도라는 같은 의미를 가지는 결합 기준이 한쪽은 열로서, 한쪽은 인덱스로 존재한다.

  • 이 경우 left_index, rigth_index 옵션을 True로 설정해서 한쪽이 인덱스를 기준으로 한다고 명시한다.

df1 = pd.DataFrame(
    [[1., 2.], [3., 4.], [5., 6.]],
    index=['a', 'c', 'e'],
    columns=['서울', '부산'])
df1
서울 부산
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
df2 = pd.DataFrame(
    [[7., 8.], [9., 10.], [11., 12.], [13, 14]],
    index=['b', 'c', 'd', 'e'],
    columns=['대구', '광주'])
df2
대구 광주
b 7.0 8.0
c 9.0 10.0
d 11.0 12.0
e 13.0 14.0
# 양쪽 모두 인덱스 지정 
pd.merge(df1, df2, how='outer', left_index=True, right_index=True)
서울 부산 대구 광주
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
  • 위 예시는 left_index, right_index를 모두 True로 설정하여 양쪽 모두 인덱스를 기준으로 결합한다.
# join 메서드 이용 결합
df1.join(df2, how='outer')
서울 부산 대구 광주
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() 함수로도 같은 기능으로 결합이 가능하다.

연습 문제 4.6.1

두 개의 데이터프레임을 만들고 merge 명령으로 합친다.

단 데이터프레임은 다음 조건을 만족해야 한다.

  1. 각각 5 x 5 이상의 크기를 가진다.

  2. 공통 열을 하나 이상 가진다. 다만 공통 열의 이름은 서로 다르다.

df1 = pd.DataFrame([
    ["서울","서초구",200,300,400],
    ["부산","남구",300,201,250],
    ["창원","성남구",342,221,255],
    ["성남","분당구",430,200,350],
    ["창원","의창구",320,200,240]],
    
    columns = ["지역","구","C1","C2","C3"]
)
df1
지역 C1 C2 C3
0 서울 서초구 200 300 400
1 부산 남구 300 201 250
2 창원 성남구 342 221 255
3 성남 분당구 430 200 350
4 창원 의창구 320 200 240
df2 = pd.DataFrame([
    ["서울","서초구",45,200,300,400],
    ["서울","강남구",25,300,201,250],
    ["대구","중구",30,342,221,255],
    ["성남","분당구",100,430,200,350],
    ["창원","의창구",200,320,200,240]],
    
    columns = ["region","gu","c1","c2","c3","c4"]
)
df2
region gu c1 c2 c3 c4
0 서울 서초구 45 200 300 400
1 서울 강남구 25 300 201 250
2 대구 중구 30 342 221 255
3 성남 분당구 100 430 200 350
4 창원 의창구 200 320 200 240
# inner join
pd.merge(df1,df2, left_on = ["지역", "구"], right_on = ["region", "gu"])
지역 C1 C2 C3 region gu c1 c2 c3 c4
0 서울 서초구 200 300 400 서울 서초구 45 200 300 400
1 성남 분당구 430 200 350 성남 분당구 100 430 200 350
2 창원 의창구 320 200 240 창원 의창구 200 320 200 240
# outer join
df1_2 = df1.set_index(["지역", "구"])    # 지역과 구를 행 인덱스로
df2_2 = df2.set_index(["region", "gu"])  # region과 gu를 행 인덱스로

df2_2.index.names = ["지역","구"]       # df2의 행 인덱스 이름을 변경

pd.merge(df1_2,df2_2, left_index =True, right_index = True, how="outer")
C1 C2 C3 c1 c2 c3 c4
지역
대구 중구 NaN NaN NaN 30.0 342.0 221.0 255.0
부산 남구 300.0 201.0 250.0 NaN NaN NaN NaN
서울 강남구 NaN NaN NaN 25.0 300.0 201.0 250.0
서초구 200.0 300.0 400.0 45.0 200.0 300.0 400.0
성남 분당구 430.0 200.0 350.0 100.0 430.0 200.0 350.0
창원 성남구 342.0 221.0 255.0 NaN NaN NaN NaN
의창구 320.0 200.0 240.0 200.0 320.0 200.0 240.0

concat 함수를 사용한 데이터 연결

키를 사용하지 않고 단순 연결

s1 = pd.Series([0, 1], index=['A', 'B'])
s2 = pd.Series([2, 3, 4], index=['A', 'B', 'C'])
s1
A    0
B    1
dtype: int64
s2
A    2
B    3
C    4
dtype: int64
# 위 아래로 연결
pd.concat([s1,s2])
A    0
B    1
A    2
B    3
C    4
dtype: int64
  • pd.concat()을 사용하면 결합 기준 없이 단순하게 데이터를 연결 가능하다.

  • axis는 0이 디폴트이며 세로로 결합한다.

df1 = pd.DataFrame(
    np.arange(6).reshape(3, 2),
    index=['a', 'b', 'c'],
    columns=['데이터1', '데이터2'])
df1
데이터1 데이터2
a 0 1
b 2 3
c 4 5
df2 = pd.DataFrame(
    5 + np.arange(4).reshape(2, 2),
    index=['a', 'c'],
    columns=['데이터3', '데이터4'])
df2
데이터3 데이터4
a 5 6
c 7 8
# 옆으로 연결
pd.concat([df1,df2], axis=1)
데이터1 데이터2 데이터3 데이터4
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0

연습 문제 2

어느 회사의 전반기(1월 ~ 6월) 실적을 나타내는 데이터프레임과 후반기(7월 ~ 12월) 실적을 나타내는 데이터프레임을 만든 뒤 합친다.

실적 정보는 “매출”, “비용”, “이익” 으로 이루어진다. (이익 = 매출 - 비용).

또한 1년간의 총 실적을 마지막 행으로 덧붙인다.

# 전반기 실적
np.random.seed(10)
df1 = pd.DataFrame(np.random.randint(100,500, size = (6,2)),
                   index = ["01월","02월","03월","04월","05월","06월"],
                   columns = ["매출", "비용"]
)
df1["이익"] = df1["매출"] - df1["비용"]
df1
매출 비용 이익
01월 365 225 140
02월 115 420 -305
03월 469 223 246
04월 256 321 -65
05월 469 108 361
06월 173 356 -183
# 하반기 실적
np.random.seed(17)
df2 = pd.DataFrame(np.random.randint(100,500, size = (6,2)),
                   index = ["07월","08월","09월","10월","11월","12월"],
                   columns = ["매출", "비용"]
)
df2["이익"] = df2["매출"] - df2["비용"]
df2
매출 비용 이익
07월 211 341 -130
08월 243 490 -247
09월 413 340 73
10월 378 131 247
11월 424 139 285
12월 107 485 -378
# 데이터 결합
df3 = pd.concat([df1,df2])
df3.loc["총 실적",:] = df3.sum()
df3
매출 비용 이익
01월 365.0 225.0 140.0
02월 115.0 420.0 -305.0
03월 469.0 223.0 246.0
04월 256.0 321.0 -65.0
05월 469.0 108.0 361.0
06월 173.0 356.0 -183.0
07월 211.0 341.0 -130.0
08월 243.0 490.0 -247.0
09월 413.0 340.0 73.0
10월 378.0 131.0 247.0
11월 424.0 139.0 285.0
12월 107.0 485.0 -378.0
총 실적 3623.0 3579.0 44.0

Leave a comment