Pandas

[Do it! Pandas] 5. 데이터 연결하기

PKkomi 2023. 2. 12. 13:36

먼저 concat 메서드에 대해 알아보자.

concat메서드는 여러 개의 데이터프레임을 연결하는 메서드이다. 두 개의 데이터프레임을 연결할 때는 append를 써도 된다.

 

실습에서 사용할 데이터를 가져와서 확인해준다.

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')

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

 

가져온 데이터프레임들을 concat 메서드로 연결시킬 수 있다.

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
3   a3   b3   c3   d3
0   a4   b4   c4   d4
1   a5   b5   c5   d5
2   a6   b6   c6   d6
3   a7   b7   c7   d7
0   a8   b8   c8   d8
1   a9   b9   c9   d9
2  a10  b10  c10  d10
3  a11  b11  c11  d11

3개의 데이터프레임이 모두 같은 열 이름을 가지고 있으므로 행방향으로 연결된다.

 

그렇다면 시리즈를 데이터프레임에 연결하면 어떻게 될까?

새로운 시리즈를 생성하여 합쳐보자.

new_row_series = pd.Series(['n1', 'n2', 'n3', 'n4']) # 리스트를 시리즈로 변경하여 새로운 시리즈를 생성.
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

concat 메소드로 데이터프레임과 시리즈를 연결하면 시리즈는 새로운 열로 추가가 된다.
시리즈가 열로 추가되는 이유는 시리즈는 열 이름이 없기 때문에 행으로 추가할 수가 없기 때문이다.
이때 NaN은 누락값이다.

 

 

데이터프레임끼리의 연결도 가능하다.

행 1개로 구성된 데이터프레임을 생성하여 연결해보자.

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

print(pd.concat([df1, new_row_df]))
    A   B   C   D
0  n1  n2  n3  n4 

    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

A, B, C, D라는 열 이름을 가지며 하나의 행으로 이루어진 데이터프레임을 생성하여 연결하였다.

같은 열이름을 가진 데이터프레임을 연결하면 데이터프레임이 행 방향으로 연결된다.  

 

이렇게 연결된 데이터프레임은 부자연스러운 인덱스를 가진다. 이때, ignore_index=True를 사용하면 처음부터 다시 인덱스를 설정힐 수 있다.

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

 

 

이제 앞선 df1, df2, df3를 행 방향으로 연결할 수 있다.

그리고 axis=1로 해주면 데이터가 열 방향으로 연결된다.

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

col_concat = pd.concat([df1, df2, df3], axis=1)
print(col_concat)
      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    a4   b4   c4   d4
5    a5   b5   c5   d5
6    a6   b6   c6   d6
7    a7   b7   c7   d7
8    a8   b8   c8   d8
9    a9   b9   c9   d9
10  a10  b10  c10  d10
11  a11  b11  c11  d11

    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

 

col_concat에 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

 

데이터프레임에 열을 추가하듯이 열 이름을 담은 리스트를 넣어주면 이름이 바뀐다.

df1.columns = ['A', 'B', 'C', 'D'] # 각 데이터의 열 이름을 바꿔줌.
df2.columns = ['E', "F", "G", "H"]
df3.columns = ['A', "C", "F", "H"] 
print(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

 

 

데이터프레임을 행 방향으로 합칠 때, 공통되지 않는 열 이름의 데이터는 누락값으로 처리된다.

row_concat_2 = pd.concat([df1, df2, df3])
print(row_concat_2)
     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
 
 

pd.concat으로 데이터프레임을 합칠 때 join='inner'로 설정해주면 연결한 데이터프레임간의 공통 열의 데이터들만 합쳐진다.

만약 공통 열이 존재하지 않으면 empty DataFrame이 출력된다.

print(pd.concat([df1, df3], ignore_index=True, join='inner'))
     A    C
0   a0   c0
1   a1   c1
2   a2   c2
3   a3   c3
4   a8   b8
5   a9   b9
6  a10  b10
7  a11  b11

 

열 방향으로도 같은 방법으로 데이터를 연결할 수 있다.

똑같은 방법이기 때문에 넘어가도록 하자.

 

 

다음으로 merge메서드에 대해 알아보자.

merge메서드는 concat과는 다르게 기본적으로 내부에서 join='inner'가 설정되어 있기 때문에 누락값이 존재하지 않는다.

또한 연결하려는 두 데이터프레임간의 같은 값을 갖는 열이 존재해야 한다는 특징이 있다.

 

4개의 데이터를 가져와서 데이터를 확인해보자.

     A    C
0   a0   c0
1   a1   c1
2   a2   c2
3   a3   c3
4   a8   b8
5   a9   b9
6  a10  b10
7  a11  b11
      ident   personal    family
0      dyer    William      Dyer
1        pb      Frank   Pabodie
2      lake   Anderson      Lake
3       roe  Valentina   Roerich
4  danforth      Frank  Danforth 

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

    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 

     taken person quant  reading
0     619   dyer   rad     9.82
1     619   dyer   sal     0.13
2     622   dyer   rad     7.80
3     622   dyer   sal     0.09
4     734     pb   rad     8.41
5     734   lake   sal     0.05
6     734     pb  temp   -21.50
7     735     pb   rad     7.22
8     735    NaN   sal     0.06
9     735    NaN  temp   -26.00
10    751     pb   rad     4.35
11    751     pb  temp   -18.50
12    751   lake   sal     0.10
13    752   lake   rad     2.19
14    752   lake   sal     0.09
15    752   lake  temp   -16.00
16    752    roe   sal    41.60
17    837   lake   rad     1.46
18    837   lake   sal     0.21
19    837    roe   sal    22.50
20    844    roe   rad    11.25

 

site가 3행으로 이루어져 있으므로 먼저 visited의 0, 2, 6행만 가져와서 merge 해보자.

merge 메서드를 사용한 데이터프레임(site)를 왼쪽, 첫번째 인잣값(visited_subset)이 오른쪽 데이터프레임으로 연결된다.

leff_on과 right_on인자는 왼쪽과 오른쪽 데이터프레임에서 같은 값을 갖는 열을 지정해줘야 한다. 지정하지 않으면 코드 실행되지 않는다.

이때 left_on은 왼쪽 데이터프레임(site)의 열(name), right_on은 오른쪽 데이터프레임(visited_subset)의 열(site)을 지정하고 이 두 열의 값이 같아야 한다. 다른 값을 가지는 열을 지정하면 에러가 발생하며 concat 메서드를 사용하라고 한다.

visited_subset = visited.loc[[0, 2, 6], ] # 데이터프레임 visited의 일부.
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

 

name열과 site열의 데이터가 같은 값을 가지는 것을 알 수 있다. 

merge는 concat과 마찬가지로 행의 수가 달라도 된다. 하지만 concat과는 다르게 merge를 사용하면 데이터를 합치면서 누락값까지 한 번에 제거해준다는 이점이 있다.

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

dated의 NaN은 원래 visited에 존재하는 누락값이다. merge 메서드가 합칠 때 생기는 누락값은 제거하지만 기존에 데이터가 가지고 있던 누락값은 제거하지 못한다.

 

 

person과 survey, visited와 survey도 merge해보자.

ps = person.merge(survey, left_on='ident', right_on='person')
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
2   dyer    William     Dyer    622   dyer   rad     7.80
3   dyer    William     Dyer    622   dyer   sal     0.09
4     pb      Frank  Pabodie    734     pb   rad     8.41
5     pb      Frank  Pabodie    734     pb  temp   -21.50
6     pb      Frank  Pabodie    735     pb   rad     7.22
7     pb      Frank  Pabodie    751     pb   rad     4.35
8     pb      Frank  Pabodie    751     pb  temp   -18.50
9   lake   Anderson     Lake    734   lake   sal     0.05
10  lake   Anderson     Lake    751   lake   sal     0.10
11  lake   Anderson     Lake    752   lake   rad     2.19
12  lake   Anderson     Lake    752   lake   sal     0.09
13  lake   Anderson     Lake    752   lake  temp   -16.00
14  lake   Anderson     Lake    837   lake   rad     1.46
15  lake   Anderson     Lake    837   lake   sal     0.21
16   roe  Valentina  Roerich    752    roe   sal    41.60
17   roe  Valentina  Roerich    837    roe   sal    22.50
18   roe  Valentina  Roerich    844    roe   rad    11.25

열 person의 dyer, pb, lake, roe를 기준으로 하여 순서대로 같은 값을 갖는 데이터를 정렬하며 연결한 것을 확인할 수 있다.
데이터프레임 person의 danforth는 survey에는 존재하지 않는 값이므로 join이 적용되어 결과에 포함되지 않는다.

 

vs = visited.merge(survey, left_on='ident', right_on='taken')
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
3     622   DR-1  1927-02-10    622   dyer   sal     0.09
4     734   DR-3  1939-01-07    734     pb   rad     8.41
5     734   DR-3  1939-01-07    734   lake   sal     0.05
6     734   DR-3  1939-01-07    734     pb  temp   -21.50
7     735   DR-3  1930-01-12    735     pb   rad     7.22
8     735   DR-3  1930-01-12    735    NaN   sal     0.06
9     735   DR-3  1930-01-12    735    NaN  temp   -26.00
10    751   DR-3  1930-02-26    751     pb   rad     4.35
11    751   DR-3  1930-02-26    751     pb  temp   -18.50
12    751   DR-3  1930-02-26    751   lake   sal     0.10
13    752   DR-3         NaN    752   lake   rad     2.19
14    752   DR-3         NaN    752   lake   sal     0.09
15    752   DR-3         NaN    752   lake  temp   -16.00
16    752   DR-3         NaN    752    roe   sal    41.60
17    837  MSK-4  1932-01-14    837   lake   rad     1.46
18    837  MSK-4  1932-01-14    837   lake   sal     0.21
19    837  MSK-4  1932-01-14    837    roe   sal    22.50
20    844   DR-1  1932-03-22    844    roe   rad    11.25

 

 

마지막으로 ps와 vs를 merge해보자.

두 데이터프레임은 서로 공통되는 열이 많아 리스트 형태로 모두 넘겨주어야 한다.

ps와 vs의 중복된 열 이름은 접미사 _x 또는 _y가 추가되는 것을 확인할 수 있다. _x는 왼쪽 데이터프레임, _y는 오른쪽 데이터프레임의 열 이름에 추가된다.

ps_vs = ps.merge(vs, left_on=['ident', 'taken', 'quant', 'reading'],
                right_on=['person', 'ident', 'quant', 'reading'])
print(ps_vs.head())
 ident_x personal   family  taken_x person_x quant  reading  ident_y  site  \
0    dyer  William     Dyer      619     dyer   rad     9.82      619  DR-1   
1    dyer  William     Dyer      619     dyer   sal     0.13      619  DR-1   
2    dyer  William     Dyer      622     dyer   rad     7.80      622  DR-1   
3    dyer  William     Dyer      622     dyer   sal     0.09      622  DR-1   
4      pb    Frank  Pabodie      734       pb   rad     8.41      734  DR-3   

        dated  taken_y person_y  
0  1927-02-08      619     dyer  
1  1927-02-08      619     dyer  
2  1927-02-10      622     dyer  
3  1927-02-10      622     dyer  
4  1939-01-07      734       pb