일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 나는리뷰어다2021
- jupytertheme
- deep learning
- MySQL
- 주피터노트북 커널 제거
- 회귀분석
- GitHub
- Udacity
- 딥러닝
- 주피터노트북
- ubuntu
- overfitting
- 모두를 위한 딥러닝
- feature scaling
- Python
- random forest
- pandas
- 주피터노트북 커널 추가
- Linear Regression
- 경사하강법
- 주피터테마
- deeplearning
- Machine Learning
- 데이터분석
- regression
- Git
- 주피터 노트북 테마
- 한빛미디어
- lol api
- 주피터노트북 커널 목록
- Today
- Total
유승훈
Python(2) - Tidy data 본문
1. Tidy Data
데이터는 엄청나게 다양한 형태와 모양으로 옵니다. 세상에 다루기 편한 데이터만 있는 것은 아니기 때문에 원하는대로 데이터의 모양을 바꾸는 방법도 알 필요가 있습니다. Hardley Wickham의 "Tidy Data"라는 페이퍼에서는 데이터분석에서 사용되는 여러가지 요소들에 맞게 데이터의 형태를 바꾸는 방법을 소개하고 있습니다.
사진의 두 테이블은 정확히 같은 데이터지만, 전혀 다른 형태를 가지고 있습니다. 깔끔한 데이터에 대한 조건을 살펴보자면
-
각 열은 다른 변수를 가지고 있다.
-
각 행은 개별적인 관측치를 담고 있다.
-
observational units form tables
이 조건을 만족하는 테이블은 둘 중 왼쪽 테이블입니다. 각 열이 이름, 치료법A, 치료법B를 담고있고, 각 행은 한명 한명의 사람에 대한 데이터를 담고 있습니다. 왼쪽같은 형태의 테이블을 각 행이 이름, 치료법, 결과를 담도록 바꿀수있습니다. 이 데이터는 한눈에 보기 좋은 형태는 아닙니다. 오히려 왼쪽의 데이터가 한눈에 들어오죠.
2. Melt
다른사람들과 공유하거나 보고하는데는 왼쪽의 데이터 형태가 좋지만, 데이터의 문제를 해결하거나 분석하기에는 오른쪽의 형태가 더 좋습니다. 그렇다면 어떻게 데이터의 형태를 바꿀수있을까요? 이때 Pandas의 melt 함수를 사용합니다.
왼쪽은 wide format, 오른쪽은 long format이라고 합니다. wide format은 한번의 관측이 하나의 row가 됩니다. 그리고 각각의 응답이 분리된 컬럼에 저장됩니다. 여기서는 사람 한명에게 처방된 처치들이 하나의 row에 들어가고, 각각의 처치에 대한 결과가 하나의 컬럼으로 분리되어있는것을 볼수있습니다. long format은 항목에 따른 관측이 각각의 row로 입력되어 있습니다. 때문에 사람 한명이 여러개의 row에 나뉘어 입력된것을 볼수있습니다. 위의 데이터에는 없지만 사람들의 특성(나이나 성별 등)이 있었다면 이름과 함께 같은 데이터가 여러번 등장했을것입니다.
pd.melt(df,id_vars='name')
name variable value
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
pd.melt(df,id_vars='name') # treat컬럼 뿐 아니라 다른 컬럼이 있는 경우
name variable value
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
6 Daniel sex m
7 John sex f
8 Jane sex m
왼쪽의 데이터를 melt함수로 분석하기 좋은 오른쪽 형태의 데이터로 변형시킬수있습니다. 가장 간단하게는 id_vars에 식별자인 컬럼의 이름을 넣어주면 됩니다. value_vars에는 형태를 변형(열 이름과 값을 각각 하나의 컬럼으로)할 컬럼의 이름을 넣습니다. 옵션상 넣지 않으면 id_vars외의 모든 컬럼을 변형합니다. 정확하게 구분하지 않으면 원하지 않는 결과를 얻을수도있습니다.
pd.melt(df,id_vars=['name','sex']) # 이렇게 id_vars로 식별자로 처리하는방법
name sex variable value
0 Daniel m treat_a 0
1 John f treat_a 12
2 Jane m treat_a 24
3 Daniel m treat_b 42
4 John f treat_b 31
5 Jane m treat_b 27
pd.melt(df,id_vars='name',value_vars=['treat_a','treat_b']) # 필요하지 않은 경우 변형할 컬럼만을 지정하는 방법
name variable value
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
이렇게 두가지 방식으로 처리할수있습니다. 지금은 각 사람의 정보이기 때문에 이름과 함께 식별자로 처리하는게 일반적인 방법이겠지만, 필요없는 경우에는 변형할 컬럼만 지정하는 방식도 선택할수있습니다.
pd.melt(df,id_vars='name',var_name='treatement',value_name='result')
name treatement result
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
마지막 인수는 변수명입니다. var_name은 변수명으로 만들어지는 컬럼의 이름을, value_name은 값으로 만들어지는 컬럼의 이름을 설정할수있습니다. 위에서 볼수있듯이 기본적으로 var_name='variable', value_name='value'로 설정되어 있습니다.
3. Pivot
Pandas의 Melt함수의 정반대는 Pivot함수입니다. 각각의 값을 분리된 열로 만듭니다. 이는 분석하기 좋은 형태에서 보고나 공유하기 좋은 형태의 데이터로 만들기 위함입니다. 여러 변수가 같은 컬럼에 들어있을 경우 Pivot을 사용하기도 합니다.
df
name variable value
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
pd.pivot(df,index='name',columns='variable')
value
variable treat_a treat_b
name
Daniel 0 42
Jane 24 27
John 12 31
가장 기본적으로는 각 행을 구분하는 열을 index에, 변수를 만들 범주로 활용될 열을 columns에, 범주로 만들어진 열에 값으로 입력될 데이터가 담긴 열을 values에 인수로 입력합니다. 데이터가 index, columns, values에 들어갈 딱 3열로 이루어져 있다면, index와 columns를 입력하는것만으로도 충분합니다.
사실 여기서 필수적으로 입력해야 하는것은 columns뿐입니다. index나 values의 설명을 살펴보면, 입력하지 않은 경우 어떻게 처리되는지(If None, If not specified)가 나와있습니다. 하지만 index와 columns 두개를 입력하라고 한 이유는 입력하지 않으면 가져다 쓰는 기존 인덱스(existing index)가 따로 손을 대지 않으면 row number가 되기 때문입니다. 결국 모든 row가 각각의 인덱스로 취급되는거죠.
pd.pivot(df,columns='variable') # columns만 입력한 경우 : 다른 열은 전부 value로 처리
name value
variable treat_a treat_b treat_a treat_b
0 Daniel NaN 0 NaN
1 John NaN 12 NaN
2 Jane NaN 24 NaN
3 NaN Daniel NaN 42
4 NaN John NaN 31
5 NaN Jane NaN 27
pd.pivot(df,columns='variable',values='value') # index를 입력하지 않은 경우 : 모든 행이 분리
variable treat_a treat_b
0 0 NaN
1 12 NaN
2 24 NaN
3 NaN 42
4 NaN 31
5 NaN 27
columns만 입력한 경우와, index를 입력하지 않은 경우를 볼수있습니다. columns만 입력했을때는 남은 name, value 열 둘 다 value로 처리됩니다. name열이 1번 value, value열이 두번째 value로 처리되는거죠. index는 각 row number가 되기 때문에 모든 행이 구분됩니다. Pivot해서 얻고자 하는 데이터와는 거리가 멀죠. index를 입력하지 않은 경우를 보면 모든 행이 분리되어서 같은 사람의 데이터임에도 행이 구분되어 처리됩니다. 이 또한 원하는 형태의 데이터와는 거리가 멉니다. 우리는 Python이라는 언어로 컴퓨터에게 일을 시키는것이기 때문에 가능한 자세하게 일을 할당해야 우리가 원하는 결과를 얻을수있다고 생각하면 될것같습니다.
df
name variable value type
0 Daniel treat_a 0 3
1 John treat_a 12 3
2 Jane treat_a 24 3
3 Daniel treat_b 42 3
4 John treat_b 31 3
5 Jane treat_b 27 3
pd.pivot(df,index='name',columns='variable')
value type
variable treat_a treat_b treat_a treat_b
name
Daniel 0 42 3 3
Jane 24 27 3 3
John 12 31 3 3
pd.pivot(df,index='name',columns='variable',values='value')
variable treat_a treat_b
name
Daniel 0 42
Jane 24 27
John 12 31
가능한 자세하게 입력해야하는 이유는 항상 데이터가 원하는 형태로 오지 않는다는데에도 있습니다. 필요하지 않은 컬럼(여기서는 type열이 되겠죠.)이 있는 경우, value를 정확하게 지정해주지 않으면 쓸데없는 열까지 value로 처리되죠. 이 또한 한눈에 보기 깔끔한 형태의 데이터가 아닙니다. 하지만 이렇게 제대로 잘 입력해줘도 Error가 나는 경우가 있습니다.
4. Pivot_table
df2
name variable value
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
pd.pivot(df2,index='name',columns='variable',values='value')
....
raise ValueError("Index contains duplicate entries, " "cannot reshape")
ValueError: Index contains duplicate entries, cannot reshape
위의 df 데이터를 붙여서 df2를 만들었습니다. 같은 데이터를 붙였기에 당연히 완벽하게 일치하는 데이터가 존재합니다. 이 데이터를 Pivot함수에 넣으면 중복된 데이터가 있기 때문에 재조합할수없다는 ValueError를 반환합니다. 이를 보완하는 것이 pivot_table입니다.
pd.pivot_table(df2,index='name',columns='variable',values='value',aggfunc=np.mean)
variable treat_a treat_b
name
Daniel 0 42
Jane 24 27
John 12 31
위에서 pivot을 했을때 오류가 났던 df2를 pivot_table에 넣었습니다. index, columns, values는 전부 똑같고, 바뀐것은 함수와 aggfunc라는 파라미터가 추가된것뿐입니다. aggfunc에는 index와 columns가 겹치는 데이터를 어떻게 처리할지 지정합니다. 겹치는 데이터를 합칠수도 있고(aggfunc=np.sum), 평균(aggfunc=np.mean)을 구할수도 있습니다.
df2
name variable value
0 Daniel treat_a 0
1 John treat_a 12
2 Jane treat_a 24
3 Daniel treat_b 42
4 John treat_b 31
5 Jane treat_b 27
6 Daniel treat_a 0
7 John treat_a 12
8 Jane treat_a 24
9 Daniel treat_b 42
10 John treat_b 31
11 Jane treat_b 27
12 Jane treat_c 0
pd.pivot_table(df2,index='name',columns='variable',values='value',aggfunc=np.sum)
variable treat_a treat_b treat_c
name
Daniel 0 84 NaN
Jane 48 54 0
John 24 62 NaN
pd.pivot_table(df2,index='name',columns='variable',values='value',aggfunc=np.sum,fill_value=999)
variable treat_a treat_b treat_c
name
Daniel 0 84 999
Jane 48 54 0
John 24 62 999
원래 treat_a와 treat_b만 있던 데이터에 treat_c를 추가했습니다. 세명의 사람중에 ,Jane만 treat_c를 가지고 있습니다. 이 데이터를 pivot하면 Daniel과 John은 treat_c가 없기 때문에 NaN이 됩니다. fill_value에 넣는 데이터로 NaN을 대체할수있습니다. 다만 생각해야할것은 가장 첫번째에 있는 variable은 열이 아니라 인덱스라는 것입니다.
abc = pd.pivot_table(df2,index='name',columns='variable',values='value',aggfunc=np.sum)
variable treat_a treat_b treat_c
name
Daniel 0.0 84.0 NaN
Jane 48.0 54.0 0.0
John 24.0 62.0 NaN
abc.index
Index(['Daniel', 'Jane', 'John'], dtype='object', name='name')
abc.loc[1]
# TypeError: cannot do label indexing on <class 'pandas.core.indexes.base.Index'>
# with these indexers [1] of <class 'int'>
abc.loc['John']
variable
treat_a 24.0
treat_b 62.0
treat_c NaN
Name: John, dtype: float64
abc의 index입니다. 기본값인 row number로 되어있지 않고, pivot_table을 해줄때 index 인수로 넣어준 name이 인덱스로 되어있는것을 볼수있습니다. 이렇게하면 row number로 인덱싱 되어있는것보다는 데이터를 처리하는데에 불편이 있습니다. 원하는대로 자르고 분리하기가 쉽지 않은것이죠. 이는 간편하게 reset_index를 통해 row number index로 바꿀수있습니다.
titan_pivot = df_titanic.pivot_table(index=['Sex','Survived'],values=['Age','Fare','Pclass','SibSp'])
Age Fare Pclass SibSp
Sex Survived
female 0 25.046875 23.024385 2.851852 1.209877
1 28.847716 51.938573 1.918455 0.515021
male 0 31.618056 21.960993 2.476496 0.440171
1 27.276022 40.821484 2.018349 0.385321
titan_pivot.index
MultiIndex([('female', 0),
('female', 1),
( 'male', 0),
( 'male', 1)],
names=['Sex', 'Survived'])
여기서부터는 인덱스가 여러개 필요해서 titanic 데이터로 예를 들겠습니다. 분석을 배우면서 많이 만나볼 데이터입니다. 위에서 볼수있듯이 Sex와 Survived를 index로 하고, 4개의 데이터를 Pivot했습니다. index에 두개를 입력했기때문에 pivot한 데이터도 당연히 두개의 인덱스를 갖습니다.
titan_pivot.reset_index()
Sex Survived Age Fare Pclass SibSp
0 female 0 25.046875 23.024385 2.851852 1.209877
1 female 1 28.847716 51.938573 1.918455 0.515021
2 male 0 31.618056 21.960993 2.476496 0.440171
3 male 1 27.276022 40.821484 2.018349 0.385321
titan_pivot.reset_index(level=0)
Sex Age Fare Pclass SibSp
Survived
0 female 25.046875 23.024385 2.851852 1.209877
1 female 28.847716 51.938573 1.918455 0.515021
0 male 31.618056 21.960993 2.476496 0.440171
1 male 27.276022 40.821484 2.018349 0.385321
titan_pivot.reset_index(level=1)
Survived Age Fare Pclass SibSp
Sex
female 0 25.046875 23.024385 2.851852 1.209877
female 1 28.847716 51.938573 1.918455 0.515021
male 0 31.618056 21.960993 2.476496 0.440171
male 1 27.276022 40.821484 2.018349 0.385321
titan_pivot.reset_index(level=1,drop=True)
Age Fare Pclass SibSp
Sex
female 25.046875 23.024385 2.851852 1.209877
female 28.847716 51.938573 1.918455 0.515021
male 31.618056 21.960993 2.476496 0.440171
male 27.276022 40.821484 2.018349 0.385321
아무것도 넣지 않으면 두 인덱스를 모두 컬럼으로 만들어줍니다. 하지만 항상 모든 인덱스가 필요없는것은 아니기 때문에, level에 원하는 인덱스의 위치값을 넣어 필요한 인덱스만 컬럼으로 넣어줄수있습니다. 해당하지 않는 인덱스는 당연히 인덱스로 남습니다. 때로는 인덱스가 당장 분석하는데에는 필요없는 데이터일수도 있습니다. 기본으로는 drop=False로 리셋된 인덱스를 열로 넣어주지만, 쓸데없는 경우에는 drop=True로 해주어 날리는것도 좋습니다.
titan_pivot
Age Fare Pclass SibSp
Sex Survived
female 0 25.046875 23.024385 2.851852 1.209877
1 28.847716 51.938573 1.918455 0.515021
male 0 31.618056 21.960993 2.476496 0.440171
1 27.276022 40.821484 2.018349 0.385321
titan_pivot.reset_index(inplace=True)
titan_pivot
Sex Survived Age Fare Pclass SibSp
0 female 0 25.046875 23.024385 2.851852 1.209877
1 female 1 28.847716 51.938573 1.918455 0.515021
2 male 0 31.618056 21.960993 2.476496 0.440171
3 male 1 27.276022 40.821484 2.018349 0.385321
마지막으로 inplace는 index를 리셋한 데이터를 그 객체 그대로 저장하는것입니다. 물론 객체의 이름을 그대로 다시 정의해줄수도 있지만, 옵션으로 하는 방법도 있다는 것을 알려드립니다. 익숙해지면 편리한 옵션일것이라고 생각됩니다.
이렇게 데이터를 long, wide format 바꾸는 pivot, melt, pivot_table 함수를 살펴봤습니다. 'Garbage in garbage out' 이라는 말이 있습니다. 아무리 성능이 좋은 모델이더라도 제대로 처리되지 않은 데이터를 넣으면 원하는 결과를 얻을수없다는 뜻입니다. 때문에 데이터를 모델이 원하는대로 잘 전처리하는것은 필수적인 일입니다.
'languages > Python' 카테고리의 다른 글
TypeError: 'float' object cannot be interpreted as an integer (2) | 2021.01.23 |
---|---|
카카오 API를 활용한 좌표->주소 변환하기(Python) (0) | 2020.12.21 |
Python(4) - glob, listdir (0) | 2020.03.17 |
Python(3) - Concat (0) | 2020.03.11 |
Python(1) - Basic Data attribute (0) | 2020.03.05 |