데이터 분석 초보자를 위한 concat & merge

Gayeon Kim·2020년 9월 11일
2

데이터 분석

목록 보기
2/9

1. concat & merge, 그게 뭔데?


seaborn, scikit-learn 등 파이썬의 패키지가 자체적으로 제공하는 데이터 파일이 아닌 이상 데이터 분석을 하기 위해 받은 파일을 열어보면 파일이 한 개가 아닌 경우가 많다. 예를 들어, 여러분이 어떤 도서관으로부터 "Clara가 6월에 어떤 책을 빌렸는지 제목 목록을 출력해주세요"라는 요청과 함께 회원정보.csv, 보유도서목록.csv, 도서대출기록.csv라는 파일을 받았다고 가정하자. 어떤 책을 빌렸는지만 알면 되니까 도서대출기록.csv 파일 하나만 쓰면 되는 거 아니냐고? 그렇지 않다. 도서관에서 받은 파일을 열어보면 회원 이름은 회원정보.csv에, 책 제목은 보유도서목록.csv에, 그리고 대출 기록은 도서대출기록.csv에 있을 것이다. 따라서 Clara의 도서 대출 기록을 조회하려면 회원정보.csv에서 Clara의 회원번호를 확인해서 도서대출기록.csv을 확인한 후, 빌린 책의 제목들을 보유도서목록.csv 에서 확인해야 한다. 즉, 파일 여러 개를 합쳐야만 내가 원하는 정보를 얻을 수 있다. 이렇게 여러 데이터 프레임을 합쳐서 사용해야 할 때 쓰는 것이 바로 concat과 merge이다.


2. "왜 데이터를 나눠서 저장하는 건가요?"


위의 내용을 읽고 누군가는 "그럼 처음부터 한 파일에 정보를 다 담으면 되지 왜 귀찮게 나눠서 저장하는거야!"라고 생각했을 수 있다. 데이터를 나눠서 저장하는 가장 큰 이유는 보안과 효율성 때문이다.

NamePhone_NumberDateTitleAuthor
0Andrew59242020-05-02A Christmas CarolCharles Dickens
3Andrew59242020-05-03The Princess and the GoblinGeorge MacDonald
4Henry98732020-05-23Alice's Adventures in WonderlandLewis Carroll
5Peter83482020-05-03Peter PanJ. M. Barrie
6Peter83482020-05-28HeidiJohanna Spyri

도서관에서 위와 같이 파일 하나에 회원 정보와 보유 도서 목록, 그리고 도서 대출 기록을 다 저장한다고 가정하자. 표를 보면 Henry는 책을 딱 한 번 빌렸다. 그런데 만약 실수로 Henry의 도서 대출 기록이 삭제되면 어떻게 될까? Henry는 책을 한 번 밖에 빌리지 않았기 때문에 Henry의 기록이 있는 다른 row는 없다. 따라서 Henry의 도서 대출 기록이 실수로 삭제된다면 Henry의 회원 정보까지 모두 없어지게 되는 셈이다. 이처럼 데이터를 한 파일에 합쳐서 저장하면 데이터가 손실될 가능성이 높아진다. 하지만 회원 정보와 대출 기록을 분리해서 저장한다면 실수로 Henry의 대출 기록이 삭제된다 해도 Henry의 회원 정보는 삭제되지 않는다.

또 다른 예를 생각해보자. 위 표에는 책이 5권 밖에 없지만 보통의 도서관이라면 책이 그보다 훨씬 많을 것이다. 그럼 도서관에서 새로운 책을 구입해서 아직 한 번도 대출되지 않은 책은 위 파일에 어떻게 저장해야 할까? 지금 상황에서는 Name, Phone_Number, Date에 모두 결측치를 넣어서 저장하는 수밖에 없다. 반대로 만약 도서관에 있는 'A Christmas Carol' 책이 너무 낡아 대출이 불가능해져서 정리하려고 한다면? 그럼 이제 'A Christmas Carol'은 보유 도서가 아니니 파일에서 지워야 하므로 관련된 대출 기록도 모두 지워야 할까? 생각만 해도 복잡하다. 그러나 만약 대출 가능한 보유 도서 목록과 도서 대출 기록을 분리해서 저장한다면 이런 문제는 간단하게 해결된다.

이렇게 데이터를 분리해서 저장하면 데이터의 손실을 최소화 할 수 있고 데이터의 관리 또한 용이해진다. 이뿐만 아니라 도서관의 사이트에 접속했을 때 보유 도서 목록은 누구나 조회할 수 있지만 회원 정보는 관리자 외에는 조회할 수 없듯이, 중요한 정보는 별도의 데이터 파일로 관리하여 보안을 강화할 수 있다.



3. concat & merge 들여다보기


데이터 준비하기

  • 필요한 패키지 import
import pandas as pd
  • 데이터 만들기

지금부터 당신은 참새 초등학교의 도서관 사서라고 상상해보자. 참새 초등학교에는 11명의 학생이 있다. 그리고 참새 초등학교 도서관에는 총 15권의 책이 있다. 당신은 학생 각각에 대해서 학번, 이름, 성별, 그리고 전화번호에 대한 정보를 가지고 있고, 보유한 도서에 대해서는 도서 구분을 위한 ID, 제목, 저자에 대한 정보를 가지고 있다.

# 학생 데이터 생성
student_data = {
    "Code": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111],
    "Name": ["Andrew", "Clara", "Daniel", "Emma", "Henry", "Oliver", "Paul", "Peter", "Sarah", "Tiffany", "William"],
    "Sex": ["male", "female", "male", "female", "male", "male", "male", "male", "female", "female", "male"],
    "Phone_Number": [5924, 1933, 3481, 2752, 9873, 6277, 3519, 8348, 2525, 5749, 6684]
}

# 보유 도서 데이터 생성
book_data = {
    "ID":[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
    "Title":["Alice's Adventures in Wonderland", "A Little Princess", "Peter Pan", "The Jungle Book", "Little Women", "A Christmas Carol", "The Princess and the Goblin", "Ozma of Oz",  "Heidi", "The Adventures of Jimmy Skunk", "Jack and Jill", "The Secret Garden", "Sky Island", "Erick and Sally" , "A Little Union Scout"],
    "Author":["Lewis Carroll", "Frances Hodgson Burnett", "J. M. Barrie", "Rudyard Kipling", "Louisa May Alcott", "Charles Dickens", "George MacDonald", "L. Frank Baum", "Johanna Spyri", "Thornton W. Burgess", "Louisa May Alcott", "Frances Hodgson Burnett", "L. Frank Baum", "Johanna Spyri", "Joel Chandler Harris"],
}

# 각각의 데이터 프레임 생성
student_df = pd.DataFrame(student_data)
book_df = pd.DataFrame(book_data)

그리고 당신에게는 2020년 5월과 6월의 도서 대출 기록이 있다. (우선 반납은 고려하지 말도록 하자.)

# 5월 대출 기록 데이터 생성
ckeck_out_data_may = {
    "Code":[101, 108, 101, 102, 110, 105, 110, 110, 108, 109],
    "ID":[6, 3, 7, 4, 2, 1, 6, 8, 9, 6],
    "Date":["2020-05-02", "2020-05-03", "2020-05-03", "2020-05-11", "2020-05-12", "2020-05-23", "2020-05-26", "2020-05-27", "2020-05-28", "2020-05-29"]
}

# 6월 대출 기록 데이터 생성
ckeck_out_data_june = {
    "Code":[105, 102, 102, 106, 108, 106, 102, 106, 107],
    "ID":[7, 9, 2, 4, 6, 8, 3, 2, 3],
    "Date":["2020-06-08", "2020-06-08", "2020-06-08", "2020-06-11", "2020-06-17", "2020-06-18", "2020-06-19", "2020-06-19", "2020-06-23"]
}

# 각각의 데이터 프레임 생성
check_out_df_may = pd.DataFrame(ckeck_out_data_may)
ckeck_out_df_june = pd.DataFrame(ckeck_out_data_june)

그런데 어제 Peter가 다른 학교로 전학을 갔다. 그러므로 Peter의 정보는 학생 목록에서 지우도록 하자.

student_df = student_df.drop(7)

이제 참새 초등학교에는 총 10명의 학생이 있다.



[잠깐] 데이터 프레임 살펴보기

[student_df]

CodeNameSexPhone_Number
0101Andrew male 5924
1102Clarafemale1933

[book_df]

IDTitleAuthor
01Alice's Adventures in WonderlandLewis Carroll
12A Little PrincessFrances Hodgson Burnett
2 3Peter Pan J. M. Barrie

[check_out_df_may]

CodeIDDate
010162020-05-02
110832020-05-03

위의 그림들은 각 데이터 프레임의 일부이다. 그림을 보면 알 수 있듯이, student_df는 학생 정보만을, book_df는 책의 정보만 담고 있다. 그리고 check_out_df_may를 보면 책을 대출한 학생과 대출된 책에 대한 정보를 모두 가지고 있는 게 아니라, 대출한 학생의 학번과 대출된 책의 ID만을 가지고 있다.

check_out_df_may의 첫번째 row를 살펴보면, Code에 101이라고 되어있다. student_df를 보면 Code가 101인 학생은 Andrew이다. 이를 통해 check_out_df_may의 첫번째 row는 Andrew의 대출 기록이라는 것을 알 수 있다. 이번에는 check_out_df_may의 두번째 row를 보자. ID가 3이다. book_df를 보면 ID가 3인 책은 Peter Pan이다. 즉, check_out_df_may의 두번째 row는 누군가가 Peter Pan을 빌려간 기록이다

이렇게 데이터 프레임 A가 데이터 프레임 B를 참조하고 싶을 때, 그 열쇠가 되어주는 데이터 프레임 A의 항목을 외래키(Foreign Key, FK)라고 한다. 외래키는 데이터 프레임 B의 모든 항목에 대해서 가능한 건 아니다. 만약 학생의 이름을 외래키로 잡는다면, 동명이인이 있을 경우에 어떤 학생을 말하는 것인지 파악이 안되기 때문이다. 따라서 외래키는 학번, 도서 구분 ID처럼 참조하고자 하는 데이터 프레임에서 각 row를 구분할 수 있는 항목이어야 한다.

자, 그럼 본격적으로 concat과 merge에 대해서 알아보자.



1) concat


concat은 데이터 프레임 두 개를 단순히 합칠 때 사용한다. 예를 들면, 당신은 현재 아래와 같이 5월의 도서 대출 기록과 6월의 도서 대출 기록을 별도의 데이터 프레임으로 가지고 있다.

[check_out_df_may]

CodeIDDate
010162020-05-02
110832020-05-03
210172020-05-03
310242020-05-11
411022020-05-12
510512020-05-23
611062020-05-26
711082020-05-27
810892020-05-28
910962020-05-29

[check_out_df_june]

CodeIDDate
010572020-06-08
110292020-06-08
210222020-06-08
310642020-06-11
410862020-06-17
510682020-06-18
610232020-06-19
710622020-06-19
810732020-06-23

그런데 지금처럼 5월 도서 대출 기록과 6월 도서 대출 기록을 분리해 놓는다면, Andrew가 5~6월에 빌린 책을 알고 싶을 때에는 5월 기록에서 Andrew를 조회하고 또 6월 기록에서 Andrew의 기록을 조회해서 둘을 더해야 한다. 즉, 똑같은 작업을 두 번 반복해야 하는 것이다. 따라서 이런 경우에는 두 데이터 프레임을 합쳐서 하나의 데이터 프레임으로 만드는 게 활용하기 더 좋다.

check_out_df = pd.concat([check_out_df_may, ckeck_out_df_june])

[check_out_df]

CodeIDDate
010162020-05-02
110832020-05-03
210172020-05-03
310242020-05-11
411022020-05-12
510512020-05-23
611062020-05-26
711082020-05-27
810892020-05-28
910962020-05-29
010572020-06-08
110292020-06-08
210222020-06-08
310642020-06-11
410862020-06-17
510682020-06-18
610232020-06-19
710622020-06-19
810732020-06-23

이때 index와 column을 보면 알 수 있듯이, concat은 단순히 두 개의 데이터 프레임을 세로로(혹은 가로로) 합치는 것이다. 따라서 concat은 주로 5월 도서 대출 기록, 6월 도서 대출 기록처럼 공통된 내용에 대한 데이터 프레임을 하나로 합칠 때 사용한다.


[잠깐] 나중에 쓸 때를 대비해서 index를 다시 설정해두자.

check_out_df = check_out_df.reset_index(drop=True)



2) merge


concat이 단순히 두 데이터 프레임을 합치는 거라면, merge는 두 데이터 프레임을 공통된 항목을 기준으로 합치는 것이다.

[student_df]

CodeNameSexPhone_Number
0101Andrew male 5924
1102Clarafemale1933

[check_out_df]

CodeIDDate
010162020-05-02
110832020-05-03

위에서 우리가 check_out_df의 첫번째 row의 Code가 101인 것을 보고 student_df에서 Code가 101인 것을 찾아 해당 row가 Andrew의 도서 대출 기록이라는 것을 알아냈던 걸 기억하는가? 이 작업을 모든 행에 대해서 수행하는 게 바로 merge이다. student_dfcheck_out_df는 Code라는 공통된 항목이 있다. 따라서 두 데이터 프레임은 Code를 기준으로 아래처럼 합칠 수 있다.

CodeNameSexPhone_NumberIDDate
0101Andrewmale592462020-05-02
1101Andrewmale592472020-05-03
2102Clarafemale193392020-06-08

이제 한 데이터 프레임만 봐도 Andrew가 언제 책을 빌렸는지 알 수 있게 되었다.

merge의 방법에는 inner, left, right, outer가 있다. 지금부터 tudnet_dfcheck_out_df를 사용해서 4가지 방법의 결과를 비교해보자.

방법1) inner


inner는 두 데이터 프레임의 교집합을 출력한다. student_dfcheck_out_df를 inner로 merge한다면, 학생 목록에도 있고 도서 대출 목록에도 있는 경우, 즉 도서 대출 기록이 있는 학생만 출력이 된다.

merge_inner = pd.merge(student_df, check_out_df, on="Code", how="inner")
# on="기준이 될 컬럼" / how="방법"
CodeNameSexPhone_NumberIDDate
0101Andrewmale592462020-05-02
1101Andrewmale592472020-05-03
2102Clarafemale193342020-05-11
3102Clarafemale193392020-06-08
4102Clarafemale193322020-06-08
5102Clarafemale193332020-06-19
6105Henrymale987312020-05-23
7105Henrymale987372020-06-08
8106Olivermale627742020-06-11
9106Olivermale627782020-06-18
10106Olivermale627722020-06-19
11107Paulmale351932020-06-23
12109Sarahfemale252562020-05-29
13110Tiffanyfemale574922020-05-12
14110Tiffanyfemale574962020-05-26
15110Tiffanyfemale574982020-05-27

참새 초등학교의 현재 재학생은 총 10명이다. 그런데 위 데이터 프레임에는 학생이 총 7명 밖에 등장하지 않는다. 즉, 책을 빌리지 않은 학생의 정보는 출력되지 않았다. 또한 전학 간 Peter의 도서 대출 기록 역시 데이터 프레임에 나와있지 않다. 즉, 학생 목록에 없는 사람의 도서 대출 기록 역시 출력되지 않았다.

[잠깐] 데이터 프레임 속 학생 수는 어떻게 구하나요? - value_counts()

len(merge_inner["Code"].value_counts())

[잠깐] Peter가 애초에 책을 한 번도 안 빌렸을 수도 있잖아요!

check_out_df[check_out_df["Code"] == 108]
CodeIDDate
110832020-05-03
810892020-05-28
1410862020-06-17

Peter는 책을 3번이나 빌렸답니다.


방법2) left


left는 두 데이터 프레임의 교집합뿐만 아니라, 왼쪽 데이터 프레임에는 있고 오른쪽 데이터 프레임에는 없는 정보도 모두 출력한다. 다시 말해서, student_dfcheck_out_df를 left로 merge한다면 학생 목록에도 있고 도서 대출 기록에도 있는 경우 뿐만 아니라 학생 목록에는 있지만 도서 대출 기록에는 없는 경우(책을 한 번도 빌리지 않은 학생들)도 출력된다.

# 먼저 나온 데이터 프레임이 왼쪽, 뒤에 나온 데이터 프레임이 오른쪽이다.
merge_left = pd.merge(student_df, check_out_df, on="Code", how="left")
CodeNameSexPhone_NumberIDDate
0101Andrewmale592462020-05-02
1101Andrewmale592472020-05-03
2102Clarafemale193342020-05-11
3102Clarafemale193392020-06-08
4102Clarafemale193322020-06-08
5102Clarafemale193332020-06-19
6103Danielmale3481nannan
7104Emmafemale2752nannan
8105Henrymale987312020-05-23
9105Henrymale987372020-06-08
10106Olivermale627742020-06-11
11106Olivermale627782020-06-18
12106Olivermale627722020-06-19
13107Paulmale351932020-06-23
14109Sarahfemale252562020-05-29
15110Tiffanyfemale574922020-05-12
16110Tiffanyfemale574962020-05-26
17110Tiffanyfemale574982020-05-27
18111Williammale6684nannan

위 데이터 프레임에서 index가 6, 7, 18인 row를 보자. 도서 대출 기록에 해당하는 column의 값이 nan이다. Daniel, Emma, William은 도서 대출 기록이 없기 때문에 해당 항목을 채울 수 없으므로 nan이 입력된 것이다. 위 데이터 프레임에는 총 10명의 학생이 등장한다. 왼쪽 데이터 프레임에 해당하는 student_df에 있는 데이터는 모두 출력되었음을 알 수 있다. 그러나 오른쪽 데이터 프레임인 check_out_df에만 있는 Peter의 도서 대출 기록은 출력되지 않았다.


방법3) right


right는 이름에서 예상되듯이 left와 결과가 반대로 나온다. 왼쪽 데이터 프레임과 오른쪽 데이터 프레임의 교집합뿐만 아니라, 오른쪽 데이터 프레임에는 있지만 왼쪽 데이터 프레임에는 없는 row도 출력된다. 즉, student_dfcheck_out_df를 right로 merge한다면 학생 목록에도 있고 도서 기록에도 있는 경우 뿐만 아니라 도서 대출 기록에는 있지만 학생 목록에는 없는 경우도 출력된다. 따라서 참새 초등학교의 재학생이지만 도서 대출 기록은 없는 Daniel, Emma, William은 데이터 프레임에 등장하지 않을 것이며, 참새 초등학교 재학생 목록에는 없지만 도서 대출 기록은 있는 Peter의 도서 대출 기록은 데이터 프레임에 나올 것이다.

merge_right = pd.merge(student_df, check_out_df, on="Code", how="right")
CodeNameSexPhone_NumberIDDate
0101Andrewmale592462020-05-02
1101Andrewmale592472020-05-03
2102Clarafemale193342020-05-11
3102Clarafemale193392020-06-08
4102Clarafemale193322020-06-08
5102Clarafemale193332020-06-19
6105Henrymale987312020-05-23
7105Henrymale987372020-06-08
8106Olivermale627742020-06-11
9106Olivermale627782020-06-18
10106Olivermale627722020-06-19
11107Paulmale351932020-06-23
12109Sarahfemale252562020-05-29
13110Tiffanyfemale574922020-05-12
14110Tiffanyfemale574962020-05-26
15110Tiffanyfemale574982020-05-27
16108nannannan32020-05-03
17108nannannan92020-05-28
18108nannannan62020-06-17

예상했던 대로 Daniel, Emma, William은 데이터 프레임에 보이지 않는다. 반면 Code가 108번이었던 Peter의 도서 대출 기록은 학생 정보에 nan이 입력되어 index가 16, 17, 18인 row에 출력되었다.


방법4) outer


마지막으로 outer는 두 데이터 프레임의 합집합을 출력한다. 즉, 왼쪽 데이터 프레임과 오른쪽 데이터 프레임 중 한 군데라도 있다면 출력된다. 따라서 student_dfcheck_out_df를 right로 merge한다면 학생 목록에 있고 도서 대출 기록도 있는 경우, Daniel, Emma, William처럼 학생 기록에는 있지만 도서 대출 기록은 없는 경우, 그리고 Peter처럼 학생 기록에는 없지만 도서 대출 기록은 있는 경우 모두 출력된다.

merge_outer = pd.merge(student_df, check_out_df, on="Code", how="outer")
CodeNameSexPhone_NumberIDDate
0101Andrewmale592462020-05-02
1101Andrewmale592472020-05-03
2102Clarafemale193342020-05-11
3102Clarafemale193392020-06-08
4102Clarafemale193322020-06-08
5102Clarafemale193332020-06-19
6103Danielmale3481nannan
7104Emmafemale2752nannan
8105Henrymale987312020-05-23
9105Henrymale987372020-06-08
10106Olivermale627742020-06-11
11106Olivermale627782020-06-18
12106Olivermale627722020-06-19
13107Paulmale351932020-06-23
14109Sarahfemale252562020-05-29
15110Tiffanyfemale574922020-05-12
16110Tiffanyfemale574962020-05-26
17110Tiffanyfemale574982020-05-27
18111Williammale6684nannan
19108nannannan32020-05-03
20108nannannan92020-05-28
21108nannannan62020-06-17




지금까지 참새 초등학교의 학생 정보와 도서 대출 기록을 사용하여 merge의 방법 별 결과를 비교해보았다. 위 4가지 방법은 원하는 데이터 프레임의 모양에 맞게 선택해서 사용하면 된다.

그렇다면 merge는 두 개의 데이터 프레임에 대해서만 가능한 것일까? 맞기도 하고, 틀리기도 하다. merge는 한 번에 두 개의 데이터 프레임에 대해서만 가능하지만, 그 결과물을 또 다른 데이터 프레임과 merge 할 수 있다. 예를 들면 student_dfcheck_out_df를 merge한 후에 이를 다시 book_df와 merge하는 것이다.

merged = pd.merge(student_df, check_out_df, on="Code", how="inner")
merged = pd.merge(merged, book_df, on="ID", how="inner" )
CodeNameSexPhone_NumberIDDateTitleAuthor
0101Andrewmale592462020-05-02A Christmas CarolCharles Dickens
1109Sarahfemale252562020-05-29A Christmas CarolCharles Dickens
2110Tiffanyfemale574962020-05-26A Christmas CarolCharles Dickens

이제 누가 언제 무슨 제목의 책을 빌려갔는지, 5~6월에 가장 많이 대출된 책의 제목은 무엇인지 등을 쉽게 알 수 있다.


데이터 분석을 잘 하기 위해서는 데이터 프레임을 자유자재로 다뤄야 하며, concat과 merge는 데이터 프레임을 다룰 때 사용하는 기본적인 함수이다. 그렇기 때문에 개념을 잘 이해하여 효과적인 데이터 분석을 할 수 있기를 바란다.

0개의 댓글