데이터 사이언스 스쿨 자료를 토대로 공부한 내용입니다.
실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.
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 |
# 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
# on을 이용해 직접 기준이 되는 키를 지정
pd.merge(df1,df2, on = "고객명")
|
고객명 |
날짜 |
데이터_x |
데이터_y |
0 |
춘향 |
2018-01-01 |
20000 |
여자 |
1 |
춘향 |
2018-01-02 |
30000 |
여자 |
2 |
몽룡 |
2018-01-01 |
100000 |
남자 |
키 이름이 다른 경우
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 명령으로 합친다.
단 데이터프레임은 다음 조건을 만족해야 한다.
-
각각 5 x 5 이상의 크기를 가진다.
-
공통 열을 하나 이상 가진다. 다만 공통 열의 이름은 서로 다르다.
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'])
# 위 아래로 연결
pd.concat([s1,s2])
A 0
B 1
A 2
B 3
C 4
dtype: int64
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
# 옆으로 연결
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