ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Chapter 05 데이터 연결하기
    Do it! 판다스 입문 2022. 6. 21. 12:09

    분석하기 좋은 데이터란?

    분석하기 좋은 데이터란 데이터 집합을 분석하기 좋은 상태로 만들어 놓은 것을 말합니다. 데이터 분석 단계에서 데이터 정리는 아주 중요합니다. 실제로 데이터 분석 작업의 70% 이상을 차지하고 있는 작업이 데이터 정리 작업이죠. 분석하기 좋은 데이터는 다음 조건을 만족해야 하며 이 조건을 만족하는 데이터를 특별히 깔끔한 데이터(Tidy Data)라고 부릅니다.

     

    깔끔한 데이터의 조건

    •  데이터 분석 목적에 맞는 데이터를 모아 새로운 표(Table)를 만들어야 합니다.
    • 측정한 값은 행(row)을 구성해야 합니다.
    • 변수는 열(column)로 구성해야 합니다.

     

    아직은 깔끔한 데이터가 왜 중요한지 알 수 없겠지만 실습을 하나씩 진행하다 보면 깔끔한 데이터의 중요성에 대해 자연스럽게 이해할 수 있을 것입니다.

     

     

    깔끔한 데이터는 데이터 연결부터

    예를 들어 주식 데이터를 분석하는 과정에서 '기업 정보'가 있는 데이터 집합과 '주식 가격'이 있는 데이터 집합이 있을 때 '첨단 산업 기업의 주식 가격에 대한 데이터'를 보려면 어떻게 해야 할까요? 일단 '기업 정보'에서 첨단 기술을 가진 기업을 찾아야 합니다. 그리고 이 기업들의 '주식 가격'을 찾아야겠죠. 그런 다음 찾아낸 2개의 데이터를 연결하면 됩니다. 이렇게 데이터 집합은 연관성이 깊은 값끼리 모여 있기 때문에 데이터 연결을 통해 필요한 데이터를 만드는 과정이 반드시 필요합니다. 그러면 다음 실습을 통해 데이터 연결을 어떻게 하는지 알아보겠습니다.

     

     

    데이터 연결하기

    1. concat 메서드로 데이터 연결하기

    데이터를 연결하려면 concat 메서드를 사용하면 됩니다. 다음 예제를 통해 concat 메서드의 사용법을 익혀보겠습니다. 준비된 CSV 파일을 읽어 들여 변수 df1, 2, 3에 저장합니다.

    import pandas as pd

    df1 = pd.read_csv('../data/concat_1.csv')
    df2 = pd.read_csv('../data/concat_2.csv')
    df3 = pd.read_csv('../data/concat_3.csv')

     

    2. concat 메서드에 연결하려는 데이터프레임을 리스트에 담아 전달하면 연결한 데이터프레임을 반환합니다. concat 메서드는 데이터프레임을 연결할 때 위에서 아래 방향으로 연결합니다. 그리고 df1, 2, 3은 열의 이름이 모두 A, B, C, D로 같습니다. 그래서 데이터프레임을 연결한 다음에도 열이 그대로 유지됩니다.

    row_concat = pd.concat([df1, df2, df3])
    print(row_concat)

           A      B     C     D
    0   a0    b0    c0    d0
    1   a1    b1    c1    d1
    2   a2    b2    c2    d2
    ...
    2   a10  b10  c10  d10
    3   a11  b11  c11  d11

     

    3. 연결한 데이터프레임에서 행 데이터를 추출해 볼까요? concat 메서드는 전달받은 리스트의 요소 순서대로 데이터를 연결합니다. 그래서 기존 데이터프레임에 있던 인덱스도 그대로 유지됩니다. 다음은 데이터프레임에서 네 번째 행을 추출한 것입니다.

    print(row_concat.iloc[3, ])

    A      a3
    B      b3
    C      c3
    D      d3
    Name: 3, dtype: object

     

    4. 데이터프레임에 시리즈 연결하기

    이번에는 데이터프레임에 시리즈를 추가해 보겠습니다. 먼저 리스트를 시리즈로 변환합니다.

    new_row_series = pd.Series(['n1', 'n2', 'n3', 'n4'])

     

    5. concat 메서드로 데이터프레임과 시리즈를 연결해 볼까요? 시리즈가 새로운 행으로 추가될 것 같죠? 하지만 행이 아니라 새로운 열로 추가됩니다. 그래서 NaN이라는 값도 많이 생겼습니다. 앞으로 NaN을 누락값이라고 부르겠습니다.

    print(pd.concat([df1, new_row_series]))

           A       B      C      D     0
    0    a0     b0     c0     d0    NaN
    1    a1     b1     c1     d1    NaN
    2    a2     b2     c2     d2    NaN
    3    a3     b3     c3     d3    NaN
    0  NaN  NaN  NaN  NaN     n1
    1  NaN  NaN  NaN  NaN     n2
    2  NaN  NaN  NaN  NaN     n3
    3  NaN  NaN  NaN  NaN     n4

     

     

     

     

     

    행이 1개라도 반드시 데이터프레임에 담아 연결해야 합니다

    시리즈를 데이터프레임의 새로운 행으로 연결하려고 하면 제대로 되지 않습니다. 왜 그럴까요? 시리즈에는 열 이름이 없기 때문입니다. 그래서 시리즈를 새로운 열로 간주하여 0이라는 이름의 열로 추가한 것이죠.

     

     

    행 1개로 구성된 데이터프레임 생성하여 연결하기

    1. 시리즈는 행이 1개인 데이터프레임이라고 생각해도 됩니다. 다음은 1개의 행을 가지는 데이터프레임을 생성하여 df1에 연결한 것입니다.

    new_row_df = pd.DataFrame([['n1', 'n2', 'n3', 'n4']], columns=['A', 'B', 'C', 'D'])
    print(new_row_df)

           A       B      C      D  
    0    n1     n2     n3     n4

    print(pd.concat([df1, new_row_df]))

           A       B      C      D    
    0    a0     b0     c0     d0
    1    a1     b1     c1     d1
    2    a2     b2     c2     d2  
    3    a3     b3     c3     d3  
    0    n1     n2     n3     n4

     

    2. concat 메서드는 한 번에 2개 이상의 데이터프레임을 연결할 수 있는 메서드입니다. 만약 연결할 데이터프레임이 1개라면 append 메서드를 사용해도 됩니다.

    print(df1.append(new_row_df))

           A       B      C      D    
    0    a0     b0     c0     d0
    1    a1     b1     c1     d1
    2    a2     b2     c2     d2  
    3    a3     b3     c3     d3  
    0    n1     n2     n3     n4

     

    3. append 메서드와 딕셔너리를 사용하면 더욱 간편하게 행을 연결할 수 있습니다. 이때 ignore_index를 True로 설정하면 데이터를 연결한 다음 데이터프레임의 인덱스를 0부터 다시 지정합니다.

    data_dict = {'A': 'n1', 'B': 'n2', 'C': 'n3', 'D': 'n4'}
    print(df1.append(data_dict, ignore_index=True))

           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    n1     n2     n3     n4

     

     

     

     

    다양한 방법으로 데이터 연결하기

    판다스는 데이터를 연결하는 다양한 방법을 제공합니다. 다음 예제를 통해 데이터를 연결하는 다양한 방법에 대해 알아보겠습니다.

     

     

    다양한 방법으로 데이터 연결하기

    1. ignore_index 인자 사용하기

    바로 앞에서 실습했던 내용이죠? ignore_index를 True로 지정하면 데이터를 연결한 다음 데이터프레임의 인덱스를 0부터 다시 지정합니다.

    row_concat_i = pd.concat([df1, df2, df3], ignore_index=True)
    print(row_concat_i)

           A       B      C      D    
    0    a0     b0     c0     d0
    1    a1     b1     c1     d1
    2    a2     b2     c2     d2
    ...
    10  a10  b10  c10  d10
    11  a11   b11  c11  d11

     

    2. 열 방향으로 데이터 연결하기

    만약 행 방향이 아니라 열 방향으로 데이터를 연결하려면 어떻게 해야 할까요? concat 메서드의 axis 인자를 1로 지정하면 됩니다. 다음은 df1, df2, df3을 열 방향으로 연결한 것입니다.

    col_concat = pd.concat([df1, df2, df3], axis=1)
    print(col_concat)

           A      B      C     D     A      B      C     D      A      B     C     D
    0    a0    b0    c0    d0    a4    b4    c4    d4    a8    b8    c8    d8
    1    a1    b1    c1    d1    a5    b5    c5    d5    a9    b9    c9    d9
    2    a2    b2    c2    d2    a6    b6    c6    d6   a10  b10  c10  d10
    3    a3    b3    c3    d3    a7    b7    c7    d7   a11  b11  c11  d11

     

    3. 만약 같은 열 이름이 있는 데이터프레임에서 열 이름으로 데이터를 추출하면 해당 열 이름의 데이터를 모두 추출합니다.

    print(col_concat['A'])

             A       A      A
    0     a0     a4     a8
    1     a1     a5     a9
    2     a2     a6   a10
    3     a3     a7   a11

     

    4. 다음과 같이 입력하면 간편하게 새로운 열을 추가할 수도 있습니다.

    col_concat['new_col_list'] = ['n1', 'n2', 'n3', 'n4']
    print(col_concat)

           A      B      C     D     A      B      C     D      A      B     C     D  new_col_list
    0    a0    b0    c0    d0    a4    b4    c4    d4    a8    b8    c8    d8                 n1
    1    a1    b1    c1    d1    a5    b5    c5    d5    a9    b9    c9    d9                 n2
    2    a2    b2    c2    d2    a6    b6    c6    d6   a10  b10  c10  d10                n3
    3    a3    b3    c3    d3    a7    b7    c7    d7   a11  b11  c11  d11                 n4

     

    5. 과정 2에서는 데이터프레임의 열 이름을 유지한 채 연결했기 때문에 열 이름이 중복되었습니다. 다음은 ignore_index를 True로 지정하여 열 이름을 다시 지정한 것입니다.

    print(pd.concat([df1, df2, df3], axis=1, ignore_index=True))

            0      1      2      3      4      5     6      7      8      9     10    11
    0    a0    b0    c0    d0    a4    b4    c4    d4    a8    b8    c8    d8
    1    a1    b1    c1    d1    a5    b5    c5    d5    a9    b9    c9    d9
    2    a2    b2    c2    d2    a6    b6    c6    d6   a10  b10  c10  d10
    3    a3    b3    c3    d3    a7    b7    c7    d7   a11  b11  c11  d11

     

    6. 공통 열과 공통 인덱스만 연결하기

    만약 열 이름의 일부가 서로 다른 데이터프레임을 연결하면 어떻게 될까요? 앞에서 사용한 df1, df2, df3의 열 이름을 다시 지정하겠습니다.

    df1.columns = ['A', 'B', 'C', 'D']
    df2.columns = ['E', 'F', 'G', 'H']
    df3.columns = ['A', 'C', 'F', 'H']
    print(df1)
    print(type(df1))

           A       B      C      D    
    0    a0     b0     c0     d0
    1    a1     b1     c1     d1
    2    a2     b2     c2     d2
    3    a3     b3     c3     d3
    <class 'pandas.core.frame.DataFrame'>

    print(df2)
    print(type(df2))

           E       F      G      H    
    0    a4     b4     c4     d4
    1    a5     b5     c5     d5
    2    a6     b6     c6     d6
    3    a7     b7     c7     d7
    <class 'pandas.core.frame.DataFrame'>

    print(df3)
    print(type(df3))

           A       C       F      H 
    0    a8     b8     c8     d8
    1    a9     b9     c9     d9
    2    a10   b10   c10   d10
    3    a11   b11    c11   d11
    <class 'pandas.core.frame.DataFrame'>

     

    7. 새롭게 열 이름을 부여한 데이터프레임 3개를 concat 메서드로 연결해 보겠습니다. 어떻게 되었나요? 열 이름이 정렬되며 연결되었습니다. 그리고 데이터프레임에 없는 열 이름의 데이터는 누락값으로 처리되었습니다. 누락값 없이 데이터를 연결하는 방법은 없을까요?

    row_concat = pd.concat([df1, df2, df3])
    print(row_concat)

           A       B      C      D        E       F       G      H
    0    a0     b0     c0     d0   NaN  NaN  NaN  NaN
    1    a1     b1     c1     d1   NaN  NaN  NaN  NaN
    2    a2     b2     c2     d2   NaN  NaN  NaN  NaN
    3    a3     b3     c3     d3   NaN  NaN  NaN  NaN
    0  NaN  NaN  NaN  NaN    a4     b4      c4     d4
    1  NaN  NaN  NaN  NaN    a5     b5      c5     d5
    2  NaN  NaN  NaN  NaN    a6     b6      c6     d6
    3  NaN  NaN  NaN  NaN    a7     b7      c7     d7
    0     a8  NaN     b8  NaN  NaN    c8   NaN     d8
    1     a9  NaN     b9  NaN  NaN    c9   NaN     d9
    2   a10  NaN   b10  NaN  NaN   c10  NaN   d10
    3   a11  NaN    b11  NaN  NaN   c11  NaN   d11

     

    8. 데이터프레임의 공통 열만 골라 연결하면 누락값이 생기지 않을 것입니다. 공통 열만 골라서 연결하려면 join 인자를 inner로 지정해야 합니다. 아쉽게도 df1, df2, df3은 공통 열이 없습니다. 따라서 새 데이터프레임의 공통 열을 연결한 결괏값으로 Empty DataFrame이 출력됩니다.

    print(pd.concat([df1, df2, df3], join='inner'))

    Empty DataFrame
    Columns: [ ]
    Index: [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]

     

    9. df1, df3의 공통 열만 골라 연결해 볼까요? 그러면 공통 열인 A와 C만 연결됩니다.

    print(pd.concat([df1, df3], ignore_index=False, join='inner'))

           A      C
    0    a0    c0
    1    a1    c1
    2    a2    c2
    3    a3    c3
    0    a8    b8
    1    a9    b9
    2  a10  b10
    3  a11  b11

     

    10. 이번에는 데이터프레임을 행 방향으로 연결해 볼까요? df1, df2, df3의 인덱스를 다시 지정해 보겠습니다.

    df1.index = [0, 1, 2, 3]
    df2.index = [4, 5, 6, 7]
    df3.index = [0, 2, 5, 7]

    print(df1)

           A       B      C      D    
    0    a0     b0     c0     d0
    1    a1     b1     c1     d1
    2    a2     b2     c2     d2
    3    a3     b3     c3     d3

    print(df2)

           E       F      G      H    
    4    a4     b4     c4     d4
    5    a5     b5     c5     d5
    6    a6     b6     c6     d6
    7    a7     b7     c7     d7

    print(df3)

           A       C       F      H 
    0    a8     b8     c8     d8
    2    a9     b9     c9     d9
    5    a10   b10   c10   d10
    7    a11   b11    c11   d11

     

    11. concat 메서드로 df1, df2, df3을 행 방향으로 연결하면 과정 2와 비슷한 결과가 출력됩니다.

    col_concat = pd.concat([df1, df2, df3], axis=1)
    print(col_concat)

           A       B      C      D        E       F       G      H       A       C       F       H
    0    a0     b0     c0     d0   NaN  NaN  NaN  NaN     a8     b8      c8     d8
    1    a1     b1     c1     d1   NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
    2    a2     b2     c2     d2   NaN  NaN  NaN  NaN     a9     b9      c9     d9
    3    a3     b3     c3     d3   NaN  NaN  NaN  NaN  NaN  NaN  NaN  NaN
    NaN  NaN  NaN  NaN    a4     b4      c4     d4  NaN  NaN  NaN  NaN
    5  NaN  NaN  NaN  NaN    a5     b5      c5     d5   a10   b10    c10   d10
    6  NaN  NaN  NaN  NaN    a6     b6      c6     d6  NaN  NaN  NaN  NaN
    7  NaN  NaN  NaN  NaN    a7     b7      c7     d7   a11   b11     c11   d11

     

    12. 과정 9와 비슷한 방법으로 df1, df3의 공통 행만 골라 연결해 볼까요? 그러면 공통 행인 0과 2만 출력됩니다.

    print(pd.concat([df1, df3], axis=1, join='inner'))

           A       B      C      D       A      C       F      H
    0    a0     b0     c0     d0    a8     b8     c8     d8
    2    a2     b2     c2     d2    a9     b9     c9     d9

     

    * 외부 조인과 내부 조인

    앞의 실습 예제는 데이터베이스의 주요 개념 중 하나인 내부 조인(Inner Join)과 외부 조인(Outer Join)을 실습한 것입니다. 만약 두 개념이 잘 이해되지 않는다면 아래에 정리한 내용을 읽어 보세요.

     

    내부 조인

    둘 이상의 데이터프레임에서 조건에 맞는 행을 연결하는 것입니다.

     

    외부 조인

    외부 조인은 두 데이터프레임 중 어떤 데이터프레임을 기준으로 할 것인지에 따라 왼쪽 외부 조인(Left Outer Join)과 오른쪽 오른쪽 외부 조인(Right Outer Join), 완전 외부 조인(Full Outer Join)으로 나눌 수 있습니다. 왼쪽 외부 조인은 데이터프레임을 연결할 때 왼쪽 데이터프레임을 모두 포함하여 연결하는 것이고 오른쪽 외부 조인은 데이터프레임을 연결할 때 오른쪽 데이터프레임을 모두 포함하여 연결하는 것입니다. 완전 외부 조인은 왼쪽과 오른쪽 데이터프레임을 모두 포함하여 연결합니다.

     

     

     

     

    데이터 연결 마무리

    판다스는 데이터 연결 전용 메서드인 merge를 제공합니다. merge 메서드의 사용 방법을 실습하면서 데이터 연결을 마무리하겠습니다.

     

     

    merge 메서드 사용하기

    1. 다음은 특정 위치의 날씨 정보에 필요한 데이터 집합을 모두 불러온 것입니다. person은 관측한 사람의 이름, site는 관측 위치, visited는 관측 날짜, survey는 날씨 정보입니다.

    person = pd.read_csv('../data/survey_person.csv')
    site = pd.read_csv('../data/survey_site.csv')
    survey = pd.read_csv('../data/survey_survey.csv')
    visited = pd.read_csv('../data/survey_visited.csv')

    print(person)

               ident    personal       family
    0          dyer      William         Dyer
    1             pb        Frank    Pabodie
    2          lake   Anderson         Lake
    3           roe    Valentina    Roerich
    4    danforth        Frank    Danforth

    print(site)

         name         lat         long
    0    DR-1   -49.85   -128.57
    1    DR-3   -47.15   -126.72
    2  MSK-4   -48.87   -123.40

    print(visited)

         ident          site            dated
    0     619       DR-1   1927-02-08
    1     622       DR-1   1927-02-10
    2     734       DR-3   1939-01-07
    3     735       DR-3   1930-01-12
    4     751       DR-3   1930-02-26
    5     752       DR-3              NaN
    6     837    MSK-4   1932-01-14
    7     844       DR-1   1932-03-22

    print(survey)

        taken  person  quant  reading
    0     619      dyer     rad       9.82
    1     619      dyer     sal        0.13
    2     622      dyer     rad       7.80
    ...
    19   837       roe     sal       22.50
    20   844       roe     rad       11.25

     

    2. visited 데이터프레임의 일부 데이터만 떼어 실습에 사용하겠습니다.

    visited_subset = visited.loc[[0, 2, 6],]

     

    3. merge 메서드는 기본적으로 내부 조인을 실행하며 메서드를 사용한 데이터프레임(site)을 왼쪽으로 지정하고 첫 번째 인잣값으로 지정한 데이터프레임(visited_subset)을 오른쪽으로 지정합니다. left_on, right_on 인자는 값이 일치해야 할 왼쪽과 오른쪽 데이터프레임의 열을 지정합니다. 즉, 왼쪽 데이터프레임(site)의 열(name)과 오른쪽 데이터프레임(visited)의 열(site)의 값이 일치하면 왼쪽 데이터프레임을 기준으로 연결합니다.

    o2o_merge = site.merge(visited_subset, left_on='name', right_on='site')
    print(o2o_merge)

         name         lat         long    ident         site            dated
    0    DR-1   -49.85   -128.57     619       DR-1   1927-02-08
    1    DR-3   -47.15   -126.72     734       DR-3   1939-01-07
    2  MSK-4   -48.87   -123.40    837     MSK-4   1932-01-14

     

    4. 다음은 site, visited 데이터프레임을 이용하여 데이터를 연결한 것입니다.

    m2o_merge = site.merge(visited, left_on='name', right_on='site')
    print(m2o_merge)

         name         lat         long    ident         site            dated
    0    DR-1   -49.85   -128.57     619       DR-1   1927-02-08
    1    DR-1   -49.85   -128.57     622       DR-1   1927-02-10
    2    DR-1   -49.85   -128.57     844       DR-1   1932-03-22
    3    DR-3   -47.15   -126.72     734       DR-3   1939-01-07
    4    DR-3   -47.15   -126.72     735       DR-3   1930-01-12
    5    DR-3   -47.15   -126.72     751       DR-3   1930-02-26
    6    DR-3   -47.15   -126.72     752       DR-3              NaN
    7  MSK-4  -48.87   -123.40     837     MSK-4   1932-01-14

     

    5. 다른 데이터프레임도 연결해 볼까요? 다음은 person, survey 데이터프레임과 visited, survey 데이터프레임을 merge 메서드로 연결한 것입니다.

    ps = person.merge(survey, left_on='ident', right_on='person')
    vs = visited.merge(survey, left_on='ident', right_on='taken')

    print(ps)

               ident    personal       family    taken  person  quant  reading
    0          dyer      William         Dyer      619      dyer      rad       9.82
    1          dyer      William         Dyer      619      dyer      sal        0.13
    ...
    17         roe    Valentina    Roerich      837       roe      sal       22.50
    18         roe    Valentina    Roerich      844       roe      rad       11.25

    print(vs)

         ident          site            dated    taken  person  quant  reading
    0     619       DR-1   1927-02-08      619      dyer     rad       9.82
    1     619       DR-1   1927-02-08      619      dyer     sal        0.13
    2     622       DR-1   1927-02-10      622      dyer     rad       7.80
    ...
    19   837    MSK-4   1932-01-14      837       roe      sal       22.50
    20   844       DR-1   1932-03-22      844       roe     rad       11.25

     

    6. left_on, right_on에 전달하는 값은 여러 개라도 상관이 없습니다. 다음과 같이 여러 개의 열 이름을 리스트에 담아 전달해도 됩니다. 다음은 ps 데이터프레임의 ident, taken, quant, reading 열의 값과 vs 데이터프레임의 person, ident, quant, reading 열의 값을 이용하여 ps와 vs 데이터프레임을 서로 연결한 것입니다.

    ps_vs = ps.merge(vs, left_on=['ident', 'taken', 'quant', 'reading'], right_on=['person', 'ident', 'quant', 'reading'])

     

    7. 과정 6에서 연결한 ps_vs 데이터프레임의 첫 번째 행을 살펴보면 양쪽 데이터프레임에 있었던 중복된 열 이름(ident, taken, person)에 접미사 _x, _y가 추가되어 있는 것을 알 수 있습니다. _x는 왼쪽 데이터프레임의 열을 의미하고 _y는 오른쪽 데이터프레임의 열을 의미합니다.

    print(ps_vs.loc[0, 1])
     
    ident_x               dyer
    personal        William
    family                 Dyer
    taken_x               619
    person_x            dyer
    quant                    rad
    reading                9.82
    ident_y                 619
    site                     DR-1
    dated        1927-02-08
    taken_y                 619
    person_y              dyer
    Name: 0, dtype: object

     

     

     

     

    마무리하며

    이 장에서는 데이터를 연결하는 다양한 방법을 알아보았습니다. 특히 누락값과 중복값을 해결하기 위한 여러 가지 방법에 대해 알아보았습니다. 누락값과 중복값은 데이터 분석을 방해하는 요소가 될 수 있기 때문이죠. 05장을 시작하며 소개한 깔끔한 데이터의 조건 중 하나인 '데이터 분석 목적에 맞는 데이터를 모아 새로운 표(Table)를 만들어야 합니다'는 바로 누락값이나 중복값이 없는 상태로 데이터가 잘 연결되어 있어야 한다는 말입니다.

     

     

     

     

     

    출처 : "판다스 입문"

Designed by Tistory.