ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 데이터 준비: 조인, 병합, 변형
    Python 2023. 11. 27. 17:12
    계층적 색인

     

    계층적인 색인(hierarchical indexing)은 축에 대해 둘 이상의 인덱스 단계를 지정할 수 있도록 함

      > 고차원 데이터를 낮은 차원의 형식으로 다룰 수 있게 해주는 기능

    In [43]: data = pd.Series(np.random.uniform(size=9),
        ...:                  index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
        ...:                         [1, 2, 3, 1, 3, 1, 2, 2, 3]])
    
    In [44]: data
    Out[44]:
    a  1    0.317961
       2    0.034492
       3    0.213850
    b  1    0.604714
       3    0.786766
    c  1    0.564637
       2    0.672718
    d  2    0.159659
       3    0.301101
    dtype: float64
    
    # MultiIndex를 인덱스로 하는 Series이며 인덱스의 계층 확인 가능
    # 바로 위 단계 인덱스를 이용해 하위 계층 직접 접근 가능
    In [45]: data.index
    Out[45]:
    MultiIndex([('a', 1),
                ('a', 2),
                ('a', 3),
                ('b', 1),
                ('b', 3),
                ('c', 1),
                ('c', 2),
                ('d', 2),
                ('d', 3)],
               )
    
    # 계층적으로 인덱싱된 객체는 데이터의 부분집합을 부분적 색인(partical indexing)으로 접근 가능
    In [46]: data['b']
    Out[46]:
    1    0.604714
    3    0.786766
    dtype: float64
    
    In [47]: data['b':'c']
    Out[47]:
    b  1    0.604714
       3    0.786766
    c  1    0.564637
       2    0.672718
    dtype: float64
    
    In [48]: data[['b','d']]
    Out[48]:
    b  1    0.604714
       3    0.786766
    d  2    0.159659
       3    0.301101
    dtype: float64
    
    # 하위 계층 객체 선택 가능
    In [49]: data
    Out[49]:
    a  1    0.317961
       2    0.034492
       3    0.213850
    b  1    0.604714
       3    0.786766
    c  1    0.564637
       2    0.672718
    d  2    0.159659
       3    0.301101
    dtype: float64
    
    In [50]: data.loc[:, 2]
    Out[50]:
    a    0.034492
    c    0.672718
    d    0.159659
    dtype: float64

     

    계층적 색인은 데이터를 재구성하고 피벗 테이블 생성 같은 그룹 기반으로 작업할 때 중요하게 사용됨

    # unstack 메서드로 새롭게 배열
    In [51]: data.unstack()
    Out[51]:
              1         2         3
    a  0.317961  0.034492  0.213850
    b  0.604714       NaN  0.786766
    c  0.564637  0.672718       NaN
    d       NaN  0.159659  0.301101
    
    # unstack <-> stack
    In [52]: data.unstack().stack()
    Out[52]:
    a  1    0.317961
       2    0.034492
       3    0.213850
    b  1    0.604714
       3    0.786766
    c  1    0.564637
       2    0.672718
    d  2    0.159659
       3    0.301101
    dtype: float64

     

    DataFrame에서는 두 축 모두 계층적 색인을 가질 수 있음

    In [53]: frame = pd.DataFrame(np.arange(12).reshape((4, 3)),
        ...:                      index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
        ...:                      columns=[['Ohio', 'Ohio', 'Colorado'],
        ...:                               ['Green', 'Red', 'Green']])
    
    In [54]: frame
    Out[54]:
         Ohio     Colorado
        Green Red    Green
    a 1     0   1        2
      2     3   4        5
    b 1     6   7        8
      2     9  10       11
    
    In [55]: frame.index.names = ['key1', 'key2']
    
    In [56]: frame.columns.names = ['state', 'color']
    
    In [57]: frame
    Out[57]:
    state      Ohio     Colorado
    color     Green Red    Green
    key1 key2
    a    1        0   1        2
         2        3   4        5
    b    1        6   7        8
         2        9  10       11
    
    # nlevels 속성으로 색인이 몇 개의 계층을 갖는지 확인
    In [58]: frame.index.nlevels
    Out[58]: 2
    
    In [59]: frame.columns.nlevels
    Out[59]: 2
    
    # 열의 부분집합을 부분적 색인으로 접근
    In [60]: frame['Ohio']
    Out[60]:
    color      Green  Red
    key1 key2
    a    1         0    1
         2         3    4
    b    1         6    7
         2         9   10

     

    계층의 순서를 바꾸고 정렬

    # swaplevel은 넘겨받은 두 개의 계층 번호나 이름이 뒤바뀐 새로운 객체 반환
    # (데이터는 변경되지 않음)
    In [61]: frame.swaplevel('key1', 'key2')
    Out[61]:
    state      Ohio     Colorado
    color     Green Red    Green
    key2 key1
    1    a        0   1        2
    2    a        3   4        5
    1    b        6   7        8
    2    b        9  10       11
    
    # 기본적으로 sort_index 메서드는 모든 색인 계층을 사용해 사전 순으로 데이터 정렬
    # level 인수를 사용해 단일 계층만 사용하거나 일부 계층만 선택해 정렬할 수 있음
    In [62]: frame.sort_index(level=1)
    Out[62]:
    state      Ohio     Colorado
    color     Green Red    Green
    key1 key2
    a    1        0   1        2
    b    1        6   7        8
    a    2        3   4        5
    b    2        9  10       11
    
    In [63]: frame
    Out[63]:
    state      Ohio     Colorado
    color     Green Red    Green
    key1 key2
    a    1        0   1        2
         2        3   4        5
    b    1        6   7        8
         2        9  10       11
         
    In [64]: frame.swaplevel(0, 1).sort_index(level=0)
    Out[64]:
    state      Ohio     Colorado
    color     Green Red    Green
    key2 key1
    1    a        0   1        2
         b        6   7        8
    2    a        3   4        5
         b        9  10       11

    객체가 계층적 색인으로 상위 계층부터 사전 순으로 정렬되어 있다면 (sort_index(level=0)이나 sort_index()의 결과처럼) 데이터를 선택하는 성능이 훨씬 좋아짐

     

    계층별 요약 통계

    행이나 열의 합을 계층별로 구할 수 있음

    In [65]: frame.groupby(level='key2').sum()
    Out[65]:
    state  Ohio     Colorado
    color Green Red    Green
    key2
    1         6   8       10
    2        12  14       16
    
    In [66]: frame.groupby(level='color', axis='columns').sum()
    Out[66]:
    color      Green  Red
    key1 key2
    a    1         2    1
         2         8    4
    b    1        14    7
         2        20   10

     

    DataFrame의 열 사용

    In [67]: frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1),
        ...:                       'c': ['one', 'one', 'one', 'two', 'two',
        ...:                             'two', 'two'],
        ...:                       'd': [0, 1, 2, 0, 1, 2, 3]})
    
    In [68]: frame
    Out[68]:
       a  b    c  d
    0  0  7  one  0
    1  1  6  one  1
    2  2  5  one  2
    3  3  4  two  0
    4  4  3  two  1
    5  5  2  two  2
    6  6  1  two  3
    
    # DataFrame의 set_index 함수는 하나 이상의 열을 인덱스로 하는 새로운 DataFrame 생성
    In [69]: frame2 = frame.set_index(['c', 'd'])
    
    In [70]: frame2
    Out[70]:
           a  b
    c   d
    one 0  0  7
        1  1  6
        2  2  5
    two 0  3  4
        1  4  3
        2  5  2
        3  6  1
    
    # 아래와 같이 set_index 메서드에 drop=False를 명시적으로 지정하지 않으면
    # 인덱스로 지정한 열은 DataFrame에서 삭제됨
    In [71]: frame.set_index(['c', 'd'], drop=False)
    Out[71]:
           a  b    c  d
    c   d
    one 0  0  7  one  0
        1  1  6  one  1
        2  2  5  one  2
    two 0  3  4  two  0
        1  4  3  two  1
        2  5  2  two  2
        3  6  1  two  3
    
    # reset_index <-> set_index
    # 계층적 색인 단계가 열로 이동
    In [78]: frame2.reset_index()
    Out[78]:
         c  d  a  b
    0  one  0  0  7
    1  one  1  1  6
    2  one  2  2  5
    3  two  0  3  4
    4  two  1  4  3
    5  two  2  5  2
    6  two  3  6  1

     

    데이터 합치기

     

    판다스 객체에 저장된 데이터 합치는 방법

      · pandas.merge: 하나 이상의 키를 기준으로 DataFrame의 행을 연결, SQL이나 다른 관계형 데이터베이스의 조인 연산과 유사

      · pandas.concat: 하나의 축을 따라 객체를 이어 붙임

      · combine_first: 두 객체를 겹쳐서 한 객체에서 누락된 데이터를 다른 객체에 있는 값으로 채움

     

    데이터베이스 스타일로 DataFrame 합치기

    병합(머지 merge)나 조인 연산은 하나 이상의 키를 사용해 데이터 집합의 행을 합침

    In [79]: df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
        ...:                     'data1': pd.Series(range(7), dtype='Int64')})
    
    In [80]: df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
        ...:                     'data2': pd.Series(range(3), dtype='Int64')})
    
    In [81]: df1
    Out[81]:
      key  data1
    0   b      0
    1   b      1
    2   a      2
    3   c      3
    4   a      4
    5   a      5
    6   b      6
    
    In [82]: df2
    Out[82]:
      key  data2
    0   a      0
    1   b      1
    2   d      2
    
    In [83]: pd.merge(df1, df2)
    Out[83]:
      key  data1  data2
    0   b      0      1
    1   b      1      1
    2   b      6      1
    3   a      2      0
    4   a      4      0
    5   a      5      0
    
    # 어떤 열을 병합할 것인지 명시하지 않았으나 pandas.merge 함수는 겹치는 열의 이름을 키로 사용
    # 그러나 명시적으로 지정하는 습관 들일 것
    In [84]: pd.merge(df1, df2, on='key')
    Out[84]:
      key  data1  data2
    0   b      0      1
    1   b      1      1
    2   b      6      1
    3   a      2      0
    4   a      4      0
    5   a      5      0
    
    # 두 객체에 공통되는 열 이름이 하나도 없는 경우 별도로 지정
    In [85]: df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
        ...:                     'data1': pd.Series(range(7), dtype='Int64')})
    
    In [86]: df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'],
        ...:                     'data2': pd.Series(range(3), dtype='Int64')})
    
    In [87]: pd.merge(df3, df4, left_on='lkey', right_on='rkey')
    Out[87]:
      lkey  data1 rkey  data2
    0    b      0    b      1
    1    b      1    b      1
    2    b      6    b      1
    3    a      2    a      0
    4    a      4    a      0
    5    a      5    a      0

    결과에서 c와 d에 해당하는 값이 빠진 것 확인 > merge 함수는 기본적으로 내부 조인(inner join)을 수행해 교집합 결과를 반환함

    merge 함수의 how에 left, right, outer를 넘기면 조인 방식 설정 가능

    In [88]: pd.merge(df1, df2, how='outer')
    Out[88]:
      key  data1  data2
    0   b      0      1
    1   b      1      1
    2   b      6      1
    3   a      2      0
    4   a      4      0
    5   a      5      0
    6   c      3   <NA>
    7   d   <NA>      2
    
    In [89]: pd.merge(df3, df4, left_on='lkey', right_on='rkey', how='outer')
    Out[89]:
      lkey  data1 rkey  data2
    0    b      0    b      1
    1    b      1    b      1
    2    b      6    b      1
    3    a      2    a      0
    4    a      4    a      0
    5    a      5    a      0
    6    c      3  NaN   <NA>
    7  NaN   <NA>    d      2
    
    In [90]: pd.merge(df3, df4, left_on='lkey', right_on='rkey', how='left')
    Out[90]:
      lkey  data1 rkey  data2
    0    b      0    b      1
    1    b      1    b      1
    2    a      2    a      0
    3    c      3  NaN   <NA>
    4    a      4    a      0
    5    a      5    a      0
    6    b      6    b      1
    
    In [91]: pd.merge(df3, df4, left_on='lkey', right_on='rkey', how='right')
    Out[91]:
      lkey  data1 rkey  data2
    0    a      2    a      0
    1    a      4    a      0
    2    a      5    a      0
    3    b      0    b      1
    4    b      1    b      1
    5    b      6    b      1
    6  NaN   <NA>    d      2
    
    In [92]: pd.merge(df1, df2, how='left')
    Out[92]:
      key  data1  data2
    0   b      0      1
    1   b      1      1
    2   a      2      0
    3   c      3   <NA>
    4   a      4      0
    5   a      5      0
    6   b      6      1
    
    In [93]: pd.merge(df1, df2, on='key', how='left')
    Out[93]:
      key  data1  data2
    0   b      0      1
    1   b      1      1
    2   a      2      0
    3   c      3   <NA>
    4   a      4      0
    5   a      5      0
    6   b      6      1

     

    외부 조인에서는 다른 DataFrame의 인덱스와 일치하지 않는 왼쪽 또는 오른쪽 DataFrame 객체의 행을 NA 값으로 표시

    옵션 동작
    how='inner' / 아무것도 지정하지 않았을 경우 inner join 양쪽 테이블 모두에 존재하는 키 조합 사용(교집합)
    how='left' 왼쪽 테이블에 존재하는 모든 키 조합 사용
    how='right' 오른쪽 테이블에 존재하는 모든 키 조합 사용
    how='outer' 양쪽 테이블에 존재하는 모든 키 조합 사용(합집합)

     

    다대다 병합은 일치하는 키에 대한 데카르트 곱(곱집합) Cartesian product을 생성

    In [94]: df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
        ...:                     'data1': pd.Series(range(6), dtype='Int64')})
    
    In [95]: df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'],
        ...:                     'data2': pd.Series(range(5), dtype='Int64')})
    
    In [96]: pd.merge(df1, df2, how='left')
    Out[96]:
       key  data1  data2
    0    b      0      1
    1    b      0      3
    2    b      1      1
    3    b      1      3
    4    a      2      0
    5    a      2      2
    6    c      3   <NA>
    7    a      4      0
    8    a      4      2
    9    b      5      1
    10   b      5      3
    
    In [97]: pd.merge(df1, df2, on='key', how='left')
    Out[97]:
       key  data1  data2
    0    b      0      1
    1    b      0      3
    2    b      1      1
    3    b      1      3
    4    a      2      0
    5    a      2      2
    6    c      3   <NA>
    7    a      4      0
    8    a      4      2
    9    b      5      1
    10   b      5      3

     

    여러 개의 키를 병합하려면 열 이름이 담긴 리스트를 넘김

    In [98]: left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'],
        ...:                      'key2': ['one', 'two', 'one'],
        ...:                      'lval': pd.Series([1, 2, 3], dtype='Int64')})
    
    In [99]: right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'],
        ...:                       'key2': ['one', 'one', 'one', 'two'],
        ...:                       'rval': pd.Series([4, 5, 6, 7], dtype='Int64')})
    
    In [100]: pd.merge(left, right, on=['key1', 'key2'], how='outer')
    Out[100]:
      key1 key2  lval  rval
    0  foo  one     1     4
    1  foo  one     1     5
    2  foo  two     2  <NA>
    3  bar  one     3     6
    4  bar  two  <NA>     7

     

    병합 연산에서 고려해야 할 마지막 사항은 겹치는 열 이름 처리하는 방식

    In [101]: pd.merge(left, right, on='key1')
    Out[101]:
      key1 key2_x  lval key2_y  rval
    0  foo    one     1    one     4
    1  foo    one     1    one     5
    2  foo    two     2    one     4
    3  foo    two     2    one     5
    4  bar    one     3    one     6
    5  bar    one     3    two     7
    
    # suffixes 옵션으로 두 DataFrame 객체에서 겹치는 열 이름 뒤에 붙일 문자열 지정 가능
    In [102]: pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
    Out[102]:
      key1 key2_left  lval key2_right  rval
    0  foo       one     1        one     4
    1  foo       one     1        one     5
    2  foo       two     2        one     4
    3  foo       two     2        one     5
    4  bar       one     3        one     6
    5  bar       one     3        two     7

     

    인덱스 병합

    병합하려는 키가 DataFrame의 색인행 레이블일 경우 left_index=True 또는 right_index=True 옵션으로 해당 인덱스를 병합 키로 사용 가능

    In [103]: left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
         ...:                       'value': pd.Series(range(6), dtype='Int64')})
    
    In [104]: right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])
    
    In [105]: left1
    Out[105]:
      key  value
    0   a      0
    1   b      1
    2   a      2
    3   a      3
    4   b      4
    5   c      5
    
    In [106]: right1
    Out[106]:
       group_val
    a        3.5
    b        7.0
    
    In [107]: pd.merge(left1, right1, left_on='key', right_index=True)
    Out[107]:
      key  value  group_val
    0   a      0        3.5
    2   a      2        3.5
    3   a      3        3.5
    1   b      1        7.0
    4   b      4        7.0
    
    In [108]: pd.merge(left1, right1, left_on='key', right_index=True, how='outer')
    Out[108]:
      key  value  group_val
    0   a      0        3.5
    2   a      2        3.5
    3   a      3        3.5
    1   b      1        7.0
    4   b      4        7.0
    5   c      5        NaN
    In [109]: lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio',
         ...:                                'Nevada', 'Nevada'],
         ...:                       'key2': [2000, 2001, 2002, 2001, 2002],
         ...:                       'data': pd.Series(range(5), dtype='Int64')})
    
    In [110]: righth_index = pd.MultiIndex.from_arrays(
         ...:     [
         ...:         ['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'],
         ...:         [2001, 2000, 2000, 2000, 2001, 2002]
         ...:     ]
         ...: )
    
    In [111]: righth = pd.DataFrame({'event1': pd.Series([0, 2, 4, 6, 8, 10], dtype='Int64',
         ...:                                  index=righth_index),
         ...:                        'event2': pd.Series([1, 3, 5, 7, 9, 11], dtype='Int64',
         ...:                                  index=righth_index)})
    
    In [112]: lefth
    Out[112]:
         key1  key2  data
    0    Ohio  2000     0
    1    Ohio  2001     1
    2    Ohio  2002     2
    3  Nevada  2001     3
    4  Nevada  2002     4
    
    In [113]: righth
    Out[113]:
                 event1  event2
    Nevada 2001       0       1
           2000       2       3
    Ohio   2000       4       5
           2000       6       7
           2001       8       9
           2002      10      11
           
    # 리스트로 여러 개의 열을 지정해 병합(중복되는 인덱스 값을 다룰 때는 how='outer' 옵션 사용)
    In [115]: pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)
    Out[115]:
         key1  key2  data  event1  event2
    0    Ohio  2000     0       4       5
    0    Ohio  2000     0       6       7
    1    Ohio  2001     1       8       9
    2    Ohio  2002     2      10      11
    3  Nevada  2001     3       0       1
    
    In [116]: pd.merge(lefth, righth, left_on=['key1', 'key2'],
         ...:          right_index=True, how='outer')
    Out[116]:
         key1  key2  data  event1  event2
    0    Ohio  2000     0       4       5
    0    Ohio  2000     0       6       7
    1    Ohio  2001     1       8       9
    2    Ohio  2002     2      10      11
    3  Nevada  2001     3       0       1
    4  Nevada  2002     4    <NA>    <NA>
    4  Nevada  2000  <NA>       2       3
    
    # 양쪽에 공통으로 존재하는 여러 개의 인덱스를 병합할 수도 있음
    In [117]: left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]],
         ...:                      index=['a', 'c', 'e'],
         ...:                      columns=['Ohio', 'Nevada']).astype('Int64')
    
    In [118]: right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]],
         ...:                       index=['b', 'c', 'd', 'e'],
         ...:                       columns=['Missouri', 'Alabama']).astype('Int64')
    
    In [119]: left2
    Out[119]:
       Ohio  Nevada
    a     1       2
    c     3       4
    e     5       6
    
    In [120]: right2
    Out[120]:
       Missouri  Alabama
    b         7        8
    c         9       10
    d        11       12
    e        13       14
    
    In [121]: pd.merge(left2, right2, how='outer', left_index=True, right_index=True)
    Out[121]:
       Ohio  Nevada  Missouri  Alabama
    a     1       2      <NA>     <NA>
    b  <NA>    <NA>         7        8
    c     3       4         9       10
    d  <NA>    <NA>        11       12
    e     5       6        13       14

     

    DataFrame의 join 메서드는 열이 겹치지 않으며 완전히 같거나 유사한 인덱스 구조를 가진 여러 개의 DataFrame 객체를 병합할 때 사용

    In [122]: left2.join(right2, how='outer')
    Out[122]:
       Ohio  Nevada  Missouri  Alabama
    a     1       2      <NA>     <NA>
    b  <NA>    <NA>         7        8
    c     3       4         9       10
    d  <NA>    <NA>        11       12
    e     5       6        13       14
    
    # pandas.merge와 비교하면 DataFrame의 join 메서드는 기본적으로 왼쪽 조인 수행
    # join 메서드를 호출한 DataFrame의 열 중 하나에 대해 조인을 수행할 수도 있음
    In [123]: left1.join(right1, on='key')
    Out[123]:
      key  value  group_val
    0   a      0        3.5
    1   b      1        7.0
    2   a      2        3.5
    3   a      3        3.5
    4   b      4        7.0
    5   c      5        NaN

     

    마지막으로 인덱스 대 인덱스로 두 DataFrame을 합치려면 간단히 병합하려는 DataFrame의 리스트를 join 메서드에 넘기면 됨

    > 보통 이런 병합은 pandas.concat 사용

    In [124]: another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
         ...:                        index=['a', 'c', 'e', 'f'],
         ...:                        columns=['New York', 'Oregon'])
    
    In [125]: another
    Out[125]:
       New York  Oregon
    a       7.0     8.0
    c       9.0    10.0
    e      11.0    12.0
    f      16.0    17.0
    
    In [126]: left2.join([right2, another])
    Out[126]:
       Ohio  Nevada  Missouri  Alabama  New York  Oregon
    a     1       2      <NA>     <NA>       7.0     8.0
    c     3       4         9       10       9.0    10.0
    e     5       6        13       14      11.0    12.0
    
    In [127]: left2.join([right2, another], how='outer')
    Out[127]:
       Ohio  Nevada  Missouri  Alabama  New York  Oregon
    a     1       2      <NA>     <NA>       7.0     8.0
    c     3       4         9       10       9.0    10.0
    e     5       6        13       14      11.0    12.0
    b  <NA>    <NA>         7        8       NaN     NaN
    d  <NA>    <NA>        11       12       NaN     NaN
    f  <NA>    <NA>      <NA>     <NA>      16.0    17.0

     

    축 따라 이어 붙이기

    데이터를 합치는 다른 방법에는 이어 붙이기(결합) concatenation (적층 stacking으로도 부름) 방법이 있음

    # 넘파이의 ndarray 연결하는 concatenate 함수
    In [3]: arr = np.arange(12).reshape((3, 4))
    
    In [4]: arr
    Out[4]:
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])
    
    In [5]: np.concatenate([arr, arr])
    Out[5]:
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11],
           [ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11]])
    
    In [6]: np.concatenate([arr, arr], axis=1)
    Out[6]:
    array([[ 0,  1,  2,  3,  0,  1,  2,  3],
           [ 4,  5,  6,  7,  4,  5,  6,  7],
           [ 8,  9, 10, 11,  8,  9, 10, 11]])

     

    판다스의 concat 함수

    In [7]: s1 = pd.Series([0, 1], index=['a', 'b'], dtype='Int64')
    
    In [8]: s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'], dtype='Int64')
    
    In [9]: s3 = pd.Series([5, 6], index=['f', 'g'], dtype='Int64')
    
    In [10]: s1
    Out[10]:
    a    0
    b    1
    dtype: Int64
    
    In [11]: s2
    Out[11]:
    c    2
    d    3
    e    4
    dtype: Int64
    
    In [12]: s3
    Out[12]:
    f    5
    g    6
    dtype: Int64
    
    In [13]: pd.concat([s1, s2, s3])
    Out[13]:
    a    0
    b    1
    c    2
    d    3
    e    4
    f    5
    g    6
    dtype: Int64
    
    # pandas.concat 함수는 axis='index'를 기본값으로 함
    # axis='columns'를 넘기면 결과는 Series가 아닌 DataFrame이 됨
    In [14]: pd.concat([s1, s2, s3], axis='columns')
    Out[14]:
          0     1     2
    a     0  <NA>  <NA>
    b     1  <NA>  <NA>
    c  <NA>     2  <NA>
    d  <NA>     3  <NA>
    e  <NA>     4  <NA>
    f  <NA>  <NA>     5
    g  <NA>  <NA>     6

    겹치는 축이 없어 외부 조인(outer 메서드)으로 합집합을 얻었지만 join='inner'를 넘겨 교집합을 구할 수도 있음

    In [15]: s4 = pd.concat([s1, s3])
    
    In [16]: pd.concat([s1, s4], axis='columns')
    Out[16]:
          0  1
    a     0  0
    b     1  1
    f  <NA>  5
    g  <NA>  6
    
    In [17]: pd.concat([s1, s4], axis='columns', join='inner')
    Out[17]:
       0  1
    a  0  0
    b  1  1

     

    Series를 이어 붙이기 전의 개별 Series를 결과에서 구분할 수 없는 잠재적 문제가 생길 수도 있음

    이어 붙인 축에 대해서 계층적 인덱스를 생성해 식별 가능하도록 만들 수 있음

      > 계층적 인덱스를 생성하려면 keys 인수 사용

    In [18]: result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three'])
    
    In [19]: result
    Out[19]:
    one    a    0
           b    1
    two    a    0
           b    1
    three  f    5
           g    6
    dtype: Int64
    
    In [20]: result.unstack()
    Out[20]:
              a     b     f     g
    one       0     1  <NA>  <NA>
    two       0     1  <NA>  <NA>
    three  <NA>  <NA>     5     6
    
    In [21]: result = pd.concat([s1, s2, s3], keys=['one', 'two', 'three'])
    
    In [22]: result
    Out[22]:
    one    a    0
           b    1
    two    c    2
           d    3
           e    4
    three  f    5
           g    6
    dtype: Int64
    
    In [23]: result.unstack()
    Out[23]:
              a     b     c     d     e     f     g
    one       0     1  <NA>  <NA>  <NA>  <NA>  <NA>
    two    <NA>  <NA>     2     3     4  <NA>  <NA>
    three  <NA>  <NA>  <NA>  <NA>  <NA>     5     6
    
    In [24]: result.unstack().stack()
    Out[24]:
    one    a    0
           b    1
    two    c    2
           d    3
           e    4
    three  f    5
           g    6
    dtype: Int64
    
    # Series를 axis='columns'로 병합할 경우 keys는 DataFrame의 열 제목이 됨
    In [25]: result = pd.concat([s1, s2, s3], axis='columns', keys=['one', 'two', 'three'])
    
    In [26]: result
    Out[26]:
        one   two  three
    a     0  <NA>   <NA>
    b     1  <NA>   <NA>
    c  <NA>     2   <NA>
    d  <NA>     3   <NA>
    e  <NA>     4   <NA>
    f  <NA>  <NA>      5
    g  <NA>  <NA>      6

     

    동일한 방식 DataFrame 객체에도 적용

     

    In [27]: df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'], columns=['one', 'two'])
    
    In [28]: df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'], columns=['three', 'four'])
    
    In [29]: df1
    Out[29]:
       one  two
    a    0    1
    b    2    3
    c    4    5
    
    In [30]: df2
    Out[30]:
       three  four
    a      5     6
    c      7     8
    
    In [31]: pd.concat([df1, df2], axis='columns', keys=['level1', 'level2'])
    Out[31]:
      level1     level2
         one two  three four
    a      0   1    5.0  6.0
    b      2   3    NaN  NaN
    c      4   5    7.0  8.0

    여기서 keys 인수는 계층적 인덱스를 생성하기 위해 사용

    첫 번째 계층은 이어 붙인 각 DataFrame 객체를 구분하는 용도로 사용

    # 리스트 대신 객체의 딕셔너리를 넘기면 keys 옵션으로 딕셔너리 키를 사용
    In [32]: pd.concat({'level1': df1, 'level2': df2}, axis='columns')
    Out[32]:
      level1     level2
         one two  three four
    a      0   1    5.0  6.0
    b      2   3    NaN  NaN
    c      4   5    7.0  8.0
    
    # 새로 생성된 계층의 이름은 names 인수를 통해 지정
    In [33]: pd.concat([df1, df2], axis='columns', keys=['level1', 'level2'], names=['upper', 'lower'])
    Out[33]:
    upper level1     level2
    lower    one two  three four
    a          0   1    5.0  6.0
    b          2   3    NaN  NaN
    c          4   5    7.0  8.0
    
    # DataFrame의 행 인덱스가 분석에 필요한 데이터를 포함하고 있지 않은 경우
    In [34]: df1 = pd.DataFrame(np.random.standard_normal((3, 4)), columns=['a', 'b', 'c', 'd'])
    
    In [35]: df2 = pd.DataFrame(np.random.standard_normal((2, 3)), columns=['b', 'd', 'a'])
    
    In [36]: df1
    Out[36]:
              a         b         c         d
    0  0.593673 -0.487405  1.239291 -0.576333
    1  1.037841  1.336549 -1.352392  0.123572
    2  0.215907  1.324048  0.383819 -1.311291
    
    In [37]: df2
    Out[37]:
              b         d         a
    0 -1.037233 -0.900059 -0.363276
    1  0.395054  0.827801  0.252252
    
    # ignore_index=True 옵션으로 각 DataFrame의 인덱스 무시,
    # 열에 있는 데이터만 이어 붙인 후 새로운 기본 인덱스 할당
    In [38]: pd.concat([df1, df2], ignore_index=True)
    Out[38]:
              a         b         c         d
    0  0.593673 -0.487405  1.239291 -0.576333
    1  1.037841  1.336549 -1.352392  0.123572
    2  0.215907  1.324048  0.383819 -1.311291
    3 -0.363276 -1.037233       NaN -0.900059
    4  0.252252  0.395054       NaN  0.827801

     

     

    겹치는 데이터 합치기

    두 데이터셋의 색인 중 일부거나 겹치거나 전체가 겹치는 경우 병합이나 이어 붙이기로 데이터를 합칠 수 없음

    # 배열 기반 if-else 구문과 동일한 기능을 하는 넘파이의 where 함수 사용
    In [39]: a = pd.Series([np.nan, 2.5, 0, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a'])
    
    In [40]: b = pd.Series([0., np.nan, 2., np.nan, np.nan, 5.], index=['a', 'b', 'c', 'd', 'e', 'f'])
    
    In [41]: a
    Out[41]:
    f    NaN
    e    2.5
    d    0.0
    c    3.5
    b    4.5
    a    NaN
    dtype: float64
    
    In [42]: b
    Out[42]:
    a    0.0
    b    NaN
    c    2.0
    d    NaN
    e    NaN
    f    5.0
    dtype: float64
    
    In [43]: np.where(pd.isna(a), b, a)
    Out[43]: array([0. , 2.5, 0. , 3.5, 4.5, 5. ])

    여기서 a 값이 null일 때 b가 선택되고 그렇지 않으면 null이 아닌 a 값 선택, numpy.where를 사용하면 인덱스 레이블의 정렬 여부 확인 x

    인덱스에 따라 값을 정렬하고 싶다면 Series의 combine_first 메서드 사용

    In [44]: pd.concat([a, b], axis='columns')
    Out[44]:
         0    1
    f  NaN  5.0
    e  2.5  NaN
    d  0.0  NaN
    c  3.5  2.0
    b  4.5  NaN
    a  NaN  0.0
    
    In [45]: a.combine_first(b)
    Out[45]:
    a    0.0
    b    4.5
    c    3.5
    d    0.0
    e    2.5
    f    5.0
    dtype: float64

     

    DataFrame 객체의 combine_first 결괏값은 모든 열 이름의 합집합

    In [46]: df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan],
        ...:                     'b': [np.nan, 2., np.nan, 6],
        ...:                     'c': range(2, 18, 4)})
    
    In [47]: df2 = pd.DataFrame({'a': [5., 4, np.nan, 3, 7],
        ...:                     'b': [np.nan, 3., 4, 6, 8]})
    
    In [48]: df1
    Out[48]:
         a    b   c
    0  1.0  NaN   2
    1  NaN  2.0   6
    2  5.0  NaN  10
    3  NaN  6.0  14
    
    In [49]: df2
    Out[49]:
         a    b
    0  5.0  NaN
    1  4.0  3.0
    2  NaN  4.0
    3  3.0  6.0
    4  7.0  8.0
    
    In [50]: df1.combine_first(df2)
    Out[50]:
         a    b     c
    0  1.0  NaN   2.0
    1  4.0  2.0   6.0
    2  5.0  4.0  10.0
    3  3.0  6.0  14.0
    4  7.0  8.0   NaN

     

    재구성과 피벗

     

    표 형식의 데이터를 재배치하는 다양한 기본 연산을 재구성(reshape) 또는 피벗(pivot) 연산이라 부름

     

    계층적 인덱스로 재구성

    · stack: 데이터의 열을 행으로 피벗(또는 회전)

    · unstack: 행을 열로 피벗

    In [51]: data = pd.DataFrame(np.arange(6).reshape((2, 3)),
        ...:                     index=pd.Index(['Ohio', 'Colorado'], name='state'),
        ...:                     columns=pd.Index(['one', 'two', 'three'], name='number'))
    
    In [52]: data
    Out[52]:
    number    one  two  three
    state
    Ohio        0    1      2
    Colorado    3    4      5
    
    # stack 메서드를 사용해 열이 행으로 피벗
    In [53]: result = data.stack()
    
    In [54]: result
    Out[54]:
    state     number
    Ohio      one       0
              two       1
              three     2
    Colorado  one       3
              two       4
              three     5
    dtype: int32
    
    # unstack 메서드를 사용하면 계층적 인덱스를 가진 Series로부터 다시 DataFrame 얻을 수 있음
    In [55]: result.unstack()
    Out[55]:
    number    one  two  three
    state
    Ohio        0    1      2
    Colorado    3    4      5
    
    # 기본적으로 가장 안쪽에 있는 것부터 끄집어냄(stack도 동일)
    # level 이름이나 숫자를 전달해 끄집어낼 단계 지정 가능
    In [57]: result.unstack(level=0)
    Out[57]:
    state   Ohio  Colorado
    number
    one        0         3
    two        1         4
    three      2         5
    
    In [58]: result.unstack(level='state')
    Out[58]:
    state   Ohio  Colorado
    number
    one        0         3
    two        1         4
    three      2         5
    
    In [59]: result.unstack(level=1)
    Out[59]:
    number    one  two  three
    state
    Ohio        0    1      2
    Colorado    3    4      5
    
    In [60]: result.unstack(level='number')
    Out[60]:
    number    one  two  three
    state
    Ohio        0    1      2
    Colorado    3    4      5

     

    해당 단계에 있는 모든 값이 하위 그룹에 속하지 않을 경우 unstack을 수행했을 때 누락된 데이터가 생김

    In [62]: s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'], dtype='Int64')
    
    In [63]: s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'], dtype='Int64')
    
    In [64]: data2 = pd.concat([s1, s2], keys=['one', 'two'])
    
    In [65]: data2
    Out[65]:
    one  a    0
         b    1
         c    2
         d    3
    two  c    4
         d    5
         e    6
    dtype: Int64
    
    In [66]: data2.unstack()
    Out[66]:
            a     b  c  d     e
    one     0     1  2  3  <NA>
    two  <NA>  <NA>  4  5     6
    
    In [67]: data2.unstack().stack()
    Out[67]:
    one  a    0
         b    1
         c    2
         d    3
    two  c    4
         d    5
         e    6
    dtype: Int64
    
    In [68]: data2.unstack().stack(dropna=False)
    Out[68]:
    one  a       0
         b       1
         c       2
         d       3
         e    <NA>
    two  a    <NA>
         b    <NA>
         c       4
         d       5
         e       6
    dtype: Int64

     

    DataFrame을 unstack하면 unstack은 결과에서 가장 낮은 단계가 됨

    In [69]: df = pd.DataFrame({'left': result, 'right': result + 5},
        ...:                   columns=pd.Index(['left', 'right'], name='side'))
    
    In [70]: df
    Out[70]:
    side             left  right
    state    number
    Ohio     one        0      5
             two        1      6
             three      2      7
    Colorado one        3      8
             two        4      9
             three      5     10
    
    In [71]: df.unstack(level='state')
    Out[71]:
    side   left          right
    state  Ohio Colorado  Ohio Colorado
    number
    one       0        3     5        8
    two       1        4     6        9
    three     2        5     7       10
    
    # stack도 쌓을 축의 이름 지정 가능
    In [72]: df.unstack(level='state').stack(level='side')
    Out[72]:
    state         Colorado  Ohio
    number side
    one    left          3     0
           right         8     5
    two    left          4     1
           right         9     6
    three  left          5     2
           right        10     7
           
           In [73]: df
    Out[73]:
    side             left  right
    state    number
    Ohio     one        0      5
             two        1      6
             three      2      7
    Colorado one        3      8
             two        4      9
             three      5     10
    
    In [74]: df.unstack()
    Out[74]:
    side     left           right
    number    one two three   one two three
    state
    Ohio        0   1     2     5   6     7
    Colorado    3   4     5     8   9    10
    
    In [75]: df.unstack(level='state')
    Out[75]:
    side   left          right
    state  Ohio Colorado  Ohio Colorado
    number
    one       0        3     5        8
    two       1        4     6        9
    three     2        5     7       10

     

    긴 형식에서 넓은 형식으로 피벗 (가로 세로 바꾸기)

    데이터베이스나 csv 파일에 여러 개의 시계열 데이터를 저장하는 일반적인 방법은 시간 순서대로 나열하는 방법(긴 형식 또는 적층 형식이라고도 부름), 이 형식에서 개별 값은 표에서 단일 행으로 표현됨

    In [77]: # ipython 환경에서 현재 경로 보기
    
    In [78]: %pwd
    Out[78]: 'C:\\Users\\Desktop\\project\\basic'
    
    In [79]: data = pd.read_csv('macrodata.csv')
    
    In [80]: data = data.loc[:, ['year', 'quarter', 'realgdp', 'infl', 'unemp']]
    
    In [81]: data.head()    # 앞에서 5줄만 보기
    Out[81]:
       year  quarter   realgdp  infl  unemp
    0  1959        1  2710.349  0.00    5.8
    1  1959        2  2778.801  2.34    5.1
    2  1959        3  2775.488  2.74    5.3
    3  1959        4  2785.204  0.27    5.6
    4  1960        1  2847.699  2.31    5.2

     

    특정한 시간이 아닌 시간 간격을 나타내는 pandas.PeriodIndex를 이용해 연도(year)와 분기(quarter)열을 합친 후 각 분기 말에 날짜/시간 값(datetime)을 포함하도록 인덱스로 설정

    In [83]: periods = pd.PeriodIndex(year=data.pop('year'),
        ...:                          quarter=data.pop('quarter'),
        ...:                          name='date')
    
    In [84]: periods
    Out[84]:
    PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
                 '1960Q3', '1960Q4', '1961Q1', '1961Q2',
                 ...
                 '2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
                 '2008Q4', '2009Q1', '2009Q2', '2009Q3'],
                dtype='period[Q-DEC]', name='date', length=203)
                
    In [85]: data.index = periods.to_timestamp('D')
    
    In [86]: data.head()
    Out[86]:
                 realgdp  infl  unemp
    date
    1959-01-01  2710.349  0.00    5.8
    1959-04-01  2778.801  2.34    5.1
    1959-07-01  2775.488  2.74    5.3
    1959-10-01  2785.204  0.27    5.6
    1960-01-01  2847.699  2.31    5.2

     

    행의 일부분 선택하고 열 인덱스에 'item' 이름 지정

    In [87]: data = data.reindex(columns=['realqdp', 'infl', 'unemp'])
    
    In [88]: data.columns.name = 'item'
    
    In [89]: data.head()
    Out[89]:
    item        realqdp  infl  unemp
    date
    1959-01-01      NaN  0.00    5.8
    1959-04-01      NaN  2.34    5.1
    1959-07-01      NaN  2.74    5.3
    1959-10-01      NaN  0.27    5.6
    1960-01-01      NaN  2.31    5.2
    
    # stack으로 재구성, reset_index를 사용해 새로운 인덱스 계층을 열로 바꿈
    # 최종적으로 데이터 값을 담고 있는 열의 이름을 'value'로 지정
    In [90]: long_data = (data.stack().reset_index().rename(columns={0: 'value'}))
    
    In [91]: long_data[:10]
    Out[91]:
            date   item  value
    0 1959-01-01   infl   0.00
    1 1959-01-01  unemp   5.80
    2 1959-04-01   infl   2.34
    3 1959-04-01  unemp   5.10
    4 1959-07-01   infl   2.74
    5 1959-07-01  unemp   5.30
    6 1959-10-01   infl   0.27
    7 1959-10-01  unemp   5.60
    8 1960-01-01   infl   2.31
    9 1960-01-01  unemp   5.20

     

    DataFrame의 pivot 메서드 사용

    In [92]: pivoted = long_data.pivot(index='date', columns='item', values='value')
    
    In [93]: pivoted.head()
    Out[93]:
    item        infl  unemp
    date
    1959-01-01  0.00    5.8
    1959-04-01  2.34    5.1
    1959-07-01  2.74    5.3
    1959-10-01  0.27    5.6
    1960-01-01  2.31    5.2

     

    pivot 메서드의 처음 두 값은 각각 행과 열 인덱스로 사용할 열 이름, 마지막 value는 DataFrame에 채워 넣을 값을 담은 열

    한 번에 두 개의 열을 변형하는 데 사용하는 경우

    In [94]: long_data.index.name = None
    
    In [95]: long_data['value2'] = np.random.standard_normal(len(long_data))
    
    In [96]: long_data[:10]
    Out[96]:
            date   item  value    value2
    0 1959-01-01   infl   0.00 -0.493828
    1 1959-01-01  unemp   5.80 -0.447998
    2 1959-04-01   infl   2.34 -0.178906
    3 1959-04-01  unemp   5.10  2.024150
    4 1959-07-01   infl   2.74  0.818945
    5 1959-07-01  unemp   5.30 -0.854059
    6 1959-10-01   infl   0.27 -0.333144
    7 1959-10-01  unemp   5.60  0.863888
    8 1960-01-01   infl   2.31 -0.386003
    9 1960-01-01  unemp   5.20 -0.410906
    
    # 마지막 인수를 생략해 계층적 열을 갖는 DataFrame을 얻을 수 있음
    In [97]: pivoted = long_data.pivot(index='date', columns='item')
    
    In [98]: pivoted.head()
    Out[98]:
               value          value2
    item        infl unemp      infl     unemp
    date
    1959-01-01  0.00   5.8 -0.493828 -0.447998
    1959-04-01  2.34   5.1 -0.178906  2.024150
    1959-07-01  2.74   5.3  0.818945 -0.854059
    1959-10-01  0.27   5.6 -0.333144  0.863888
    1960-01-01  2.31   5.2 -0.386003 -0.410906
    
    In [99]: pivoted['value'].head()
    Out[99]:
    item        infl  unemp
    date
    1959-01-01  0.00    5.8
    1959-04-01  2.34    5.1
    1959-07-01  2.74    5.3
    1959-10-01  0.27    5.6
    1960-01-01  2.31    5.2
    
    # pivot 메서드는 set_index를 사용해 계층적 인덱스를 만들고 unstack 메서드로 형태를 변경하는 것과 동일
    In [100]: unstacked = long_data.set_index(['date', 'item']).unstack(level='item')
    
    In [101]: unstacked.head()
    Out[101]:
               value          value2
    item        infl unemp      infl     unemp
    date
    1959-01-01  0.00   5.8 -0.493828 -0.447998
    1959-04-01  2.34   5.1 -0.178906  2.024150
    1959-07-01  2.74   5.3  0.818945 -0.854059
    1959-10-01  0.27   5.6 -0.333144  0.863888
    1960-01-01  2.31   5.2 -0.386003 -0.410906

    'Python' 카테고리의 다른 글

    주피터 노트북 설정  (0) 2023.12.07
    그래프와 시각화  (1) 2023.11.28
    데이터 정제 및 준비  (0) 2023.11.21
    데이터 로딩과 저장, 파일 형식  (0) 2023.11.20
    판다스  (0) 2023.11.14
Designed by Tistory.