티스토리 뷰
#10 판다스 chapter 10. 데이터 연결하기
데이터 연결하기
-분석하기 좋은 데이터
- 분석하기 좋은 데이터란??
분석하기 좋은 데이터란 데이터 집합을 분석하기 아주 좋은 상태로 만들어 놓은 것을 말한다. 데이터 분석 단계에서 데이터 정리는 아주 중요하다.
- 깔끔한 데이터의 조건
- 1) 데이터 분석 목적에 맞는 데이터를 모아 새로운 표를 만들어야 한다.
- 2) 측정한 값은 행(row)을 구성해야 한다.
- 3) 변수는 열(column)로 구성해야 한다.
- 깔끔한 데이터는 데이터 연결부터!!
예를 들어 주식 데이터를 분석하는 과정에서 '기업 정보' 가 있는 데이터 집합과 '주식 가격'이 있는 데이터 집합이 있을 때 '첨단 산업 기업의 주식 가격에 대한 데이터'를 보려면 어떻게 해야 할까? 일단 '기업 정보' 에서 첨단 기술을 가진 기업을 찾고, 이 기업들의 '주식 가격'을 찾아야 한다. 그럼 다음 찾아낸 2개의 데이터를 연결하면 된다. 이렇게 데이터 집합은 연관성이 깊은 값끼리 모여 있기 때문에 데이터 연결을 통해 필요한 데이터를 만드는 과정이 반드시 필요하다.
-데이터 연결 기초
1) concat 메서드로 데이터 연결하기
데이터를 연결하려면 concat 메서드를 사용하면 된다. 다음 예제를 통해 concat 메서드의 사용법을 익혀보자.
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
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) 연결한 데이터 프레임에서 행 데이터를 추출해 보자. concat 메서드는 전달받은 리스트의 요소 순서대로 데이터를 연결합니다. 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)
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
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
2) 열 방향으로 데이터 연결하기
만약 행 방향이 아니라 열 방향으로 데이터를 연결하려면 concat 메서드의 axis 인자를 1로 지정하면 된다.
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']
df1.columns = ['A', 'C', 'F', 'H']
print(df1)
print(type(df1))
A C F H
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 B C D
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 C F H E G B D
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 b4 d4 a4 c4 NaN NaN
1 NaN NaN b5 d5 a5 c5 NaN NaN
2 NaN NaN b6 d6 a6 c6 NaN NaN
3 NaN NaN b7 d7 a7 c7 NaN NaN
0 a8 c8 NaN NaN NaN NaN b8 d8
1 a9 c9 NaN NaN NaN NaN b9 d9
2 a10 c10 NaN NaN NaN NaN b10 d10
3 a11 c11 NaN NaN NaN NaN b11 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 b0
1 a1 b1
2 a2 b2
3 a3 b3
0 a8 c8
1 a9 c9
2 a10 c10
3 a11 c11
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 C F H
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 B C D
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 C F H E F G H A B C D
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
4 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 C F H A B C D
0 a0 b0 c0 d0 a8 b8 c8 d8
2 a2 b2 c2 d2 a9 b9 c9 d9
- 알아두면 좋은 지식
여기까지 배운것이 데이터베이스의 주요 개념 중 하나인 내부조인과 외부조인이다. 간략히 개념을 설명하겠다.
-내부조인
둘 이상의 데이터프레임에서 조건에 맞는 행을 연결하는 것이다.
-외부조인
두 데이터프레임 중 어떤 데이터프레임을 기준으로 할 것인지에 따라 왼쪽 외부 조인고 오른쪽 외부 조인, 완전 외부 조인으로 나뉜다. 왼쪽 외부 조인은 데이터프레임을 연결할 때 왼쪽 데이터프레임을 모두 포함하여 연결하는 것이고 오른쪽 외부 조인은 데이터프레임을 연결할 때 오른쪽 데이터프레임을 모두 포함하여 연결하는 것이다. 완전 외부 조인은 왼쪽과 오른쪽 데이터프레임을 모두 포함하여 연결한다.
데이터 연결 마무리
판다스는 데이터 연결 전용 메서드인 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
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
2) visited 데이터프레임의 일부 데이터만 떼어 따라해보자.
visited_subset = visited.loc[[0,2,6], ]
3) merge 메서드는 기본적으로 내주 조인을 실행하며 메서드를 사용한 데이터프레임을 왼쪽으로 지정하고 첫 번째 인잣값으로 지정한 데이터프레임을 오른쪽으로 지정한다. left_on, right_on 인자는 값이 일치해야 할 왼쪽과 오른쪽 데이터프레임의 열을 지정한다. 즉, 왼쪽 데이터프레임의 열과 오른쪽 데이터프레임의 열의 값이 일치하면 왼쪽 데이터프레임을 기준으로 연결한다.
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,vistied 데이터 프레임을 이용하여 데이터를 연결한 것이다.
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
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
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
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 데이터프레임의 첫 번째 행을 살펴보면 양쪽 데이터프레임에 있었던 중복된 열 이름에 접미사 _x, _y 가 추가 되었다. _x는 왼쪽 데이터프레임의 열을 의미하고 _y 는 오른쪽 데이터프레임의 열을 의미한다.
print(ps_vs.loc[0, ])
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
이번에는 데이터를 연결하는 다양한 방법을 알아보았다. 특히 누락값과 중복값을 해결하기 위한 여러가지 방법에 대해 알아보았다. 시작부에 얘기했던 깔끔한 데이터의 조건 중 하나인 '데이터 분석 목적에 맞는 데이터를 모아 새로운 표를 만들어야 합니다.' 는 바로 누락값이나 중복값이 없는 상태로 데이터가 잘 연결되어 있어야 한다는 것이다.
출처 : 데이터 분석을 위한 판다스 입문
'판다스 입문' 카테고리의 다른 글
판다스 입문(깔끔한 데이터 만들기) (0) | 2021.03.21 |
---|---|
판다스 입문(누락값 처리하기) (0) | 2021.03.21 |
판다스 입문(데이터 프레임,시리즈로 그래프 그리기/seaborn 라이브러리로 그래프 스타일 설정하기) (0) | 2021.03.20 |
판다스 입문(그래프 그리기 - matplotlib, seaborn 라이브러리 사용하기) (0) | 2021.03.20 |
판다스 입문 (그래프 그리기-앤스콤 4분할 그래프) (0) | 2021.03.20 |