좋은 데이터사이언티스트들은 상당한 시간을 data를 cleaning 하거나 formatting 하는데 쓴다고 할 정도로 Data wrangling은 중요하다.
Data wrangling 이란?
Data wrangling is the process of transforming and mapping data from one "raw" data form into another format with the intent of making it more appropriate for further analysis.
즉, 가공되지 않은 날 것의 data를 분석에 용이하게 가공하고 다듬는 작업을 뜻한다.
Data wrangling 에는 여러 기법들이 있는데, 이번 포스트에서는 data cleaning 에 대해 알아보자.
Data Cleaning
cleaning 말 그대로 청소하는 것이라고 생각하자.
적절하지 않은 data(예를 들어 키와 관련된 data에서 음수일 경우)이거나 missing value인 경우에 이를 처리해주는 작업이다.
누락된 missing value의 경우 NaN(Not a number) 또는 N/A(Not applicable) 등으로 표현된다.
이런 data들을 발견했을 때 할 수 있는 조치로는,
- discarding the sample(row) : 해당 sample을 그냥 버리는 것
- discarding the variable(column) : 해당 variable를 그냥 버리는 것
- replacing missing or incorrect values (data imputation) : mean, median, mode, 또는 knn을 이용하여 적절한 value를 넣어주는 것
// 다만 mean, median 등의 일정한 값으로 치환하는 것은 그 특정값의 빈도가 높아지는 문제가 생길 수 있으므로, knn을 통해 sample의 값을 취하는 것도 하나의 방법이 된다.
- leaving them as they are : 그대로 두는 것
등의 방법이 있다.
Example
import numpy as np
import pandas as pd
# sample data
np.random.seed(48)
df = pd.DataFrame(10*np.random.rand(4,2).round(2))
df.columns = ["A", "B"]
print(df)
'''
A B
0 0.2 8.9
1 2.8 3.0
2 7.9 3.2
3 8.6 4.5
'''
먼저 정상적인 data를 만들어준 후,
# adding nan
df.A[1] = np.nan
print(df)
'''
A B
0 0.2 8.9
1 NaN 3.0
2 7.9 3.2
3 8.6 4.5
'''
A[1]에 nan를 넣어준다.
Pandas의 DataFrame에는 missing value를 찾아주는 기능이 있다.
# checking missing values
df.isnull()
예상했던대로 A[1]에 missing value가 있음을 확인할 수 있다.
df.isnull().sum()
'''
A 1
B 0
dtype: int64
'''
위 코드는 해당 열에 몇 개의 missing value가 있는지를 알려준다.
위에서 봤던 여러 data cleaning 의 방법들 중에 missing value가 있는 sample 자체를 버리는 경우를 살펴보자.
df2 = df.dropna()
print(df2)
'''
A B
0 0.2 8.9
2 7.9 3.2
3 8.6 4.5
'''
A[1]이 nan 이었으므로 1번째 행이 삭제된 것을 확인할 수 있다.
위에서 df 변수에 저장되어있는 데이터프레임에는 원래의 data값이 저장되어 있고, dropna() 메소드는 df에서 missing value를 갖는 행들을 drop한 새로운 dataFrame을 리턴해준다.
그렇다면 해당 열을 삭제하는 경우를 살펴보자.
print(df.dropna(axis=1))
'''
B
0 8.9
1 3.0
2 3.2
3 4.5
'''
axis를 1로 주었으므로 열이 지워졌다. dropna의 axis 인자의 default값은 0이므로 행을 삭제하는 경우는 인자를 주지 않아도 됐었다.
print(df.dropna(how="all"))
print(df.dropna(how="any"))
'''
A B
0 0.2 8.9
1 NaN 3.0
2 7.9 3.2
3 8.6 4.5
A B
0 0.2 8.9
2 7.9 3.2
3 8.6 4.5
'''
위의 경우는 how 옵션을 통해 어떻게 데이터가 처리되는지 보여준다.
all의 경우 해당 축으로(default는 가로) 모든 value가 missing value이면 해당 축(행이면 sample 열이면 variable)을 삭제하고, any의 경우는 하나라도 NaN이 존재하면 삭제한다는 의미이다.
df.B[1] = np.nan
print(df)
print(df.dropna(how="all"))
'''
A B
0 0.2 8.9
1 NaN NaN
2 7.9 3.2
3 8.6 4.5
A B
0 0.2 8.9
2 7.9 3.2
3 8.6 4.5
'''
위의 예를 통해 how 옵션에 all 을 줬을 경우를 확실하게 이해할 수 있다.
다음은 thresh(threshold) 옵션에 대해 알아보자.
df = pd.DataFrame([[np.nan, np.nan, np.nan],
[1,np.nan,np.nan],
[2,3,np.nan],
[4,5,6],
[np.nan,7,8]])
print(df)
'''
0 1 2
0 NaN NaN NaN
1 1.0 NaN NaN
2 2.0 3.0 NaN
3 4.0 5.0 6.0
4 NaN 7.0 8.0
'''
위의 데이터를 이용해서 한번 thresh 옵션을 통해 변화를 살펴보자.
print(df.dropna(thresh=3, axis=1))
'''
0 1
0 NaN NaN
1 1.0 NaN
2 2.0 3.0
3 4.0 5.0
4 NaN 7.0
'''
thresh옵션의 인자로 주는 값(여기서는 3)은 NaN이 아닌 최소한의 value의 개수이다. 위의 경우엔 axis=1이므로 열이 되겠고, 2열을 보면 NaN이 아닌 valid 한 value가 2개(6.0과 8.0)이므로 최소 3개가 되지 못하므로 삭제된다. 나머지 열은 정상적인 value가 3개 이상이므로 삭제되지 않는다. 이 threshold 옵션이 NaN의 개수로 혼동할 수 있으므로 잘 기억해두자.
다음은 missing value에 해당하는 열 혹은 행을 삭제하는 것이 아니라 적절한 값으로 replace해주는 방법을 알아보자.
대체될 수 있는 값으로는
- Mean, Median, Mode
- Last Observation Carried Forward (LOCF)
- Next Observation Carried Backward (NOCB)
- Linear Interpolation
- KNN
- Arbitrary Value Imputation
등이 있다.
코드를 통해 익혀보자.
import numpy as np
import pandas as pd
# sample data
np.random.seed(11)
df = pd.DataFrame(10*np.random.rand(10,3).round(2))
df.columns = ["A", "B", "C"]
print(df)
'''
A B C
0 1.8 0.2 4.6
1 7.2 4.2 4.9
2 0.1 4.9 9.4
3 8.5 7.3 1.1
4 8.9 8.6 1.7
5 6.3 0.2 1.2
6 3.2 1.6 7.6
7 8.2 3.4 3.2
8 1.1 0.8 7.1
9 6.0 0.6 4.8
'''
데이터프레임을 하나 만들어주고,
# insert nan
idx = np.random.rand(10,3) < 0.1
df[idx] = np.nan
print(df)
'''
A B C
0 1.8 0.2 4.6
1 7.2 4.2 4.9
2 0.1 4.9 9.4
3 8.5 NaN 1.1
4 NaN 8.6 NaN
5 6.3 0.2 NaN
6 NaN 1.6 7.6
7 8.2 3.4 3.2
8 1.1 NaN NaN
9 6.0 0.6 4.8
'''
value 값이 0.1 보다 작으면 모두 nan으로 대체한다.
fillna 메소드를 이용하면 nan 을 0으로 채워준다.
print(df.fillna(0))
'''
A B C
0 1.8 0.2 4.6
1 7.2 4.2 4.9
2 0.1 4.9 9.4
3 8.5 0.0 1.1
4 0.0 8.6 0.0
5 0.0 0.2 0.0
6 0.0 1.6 7.6
7 8.2 3.4 3.2
8 1.1 0.0 0.0
9 6.0 0.6 4.8
'''
LOCF와 NOCB의 경우는 다음과 같다.
LOCF는 가장 이전의 유효한 value를 가져오는 것, NOCB는 그 반대이다.
method옵션의 인자값으로 ffill을 주면 LOCF, bfill을 주면 NOCB이다.
다만, 제일 마지막 경우를 보면 axis=1, method="bfill"이므로 열을 기준으로 NOCB를 적용하는 것인데, 여전히 NaN으로 남아있는 이유는 C 열을 기준으로 오른쪽에 열이 없으므로 가져올 수 있는 값이 없기 때문이다.
그래서 보통 NOCB를 적용하고 LOCF를 한번 더 적용해주면 모든 NaN 값이 replace된다.
마찬가지로, df.mean 메소드를 이용해서 구한 mean(median 등도 같은 방법)를 fillna메소드를 통해 replace 할 수도 있다.
//문제제기 및 피드백 언제든지 감사히 받겠습니다.
'Computer Science > DL || ML' 카테고리의 다른 글
Data wrangling #3 - Outlier, Data Encoding (0) | 2022.06.06 |
---|---|
Data wrangling #2 - Data Scaling (0) | 2022.06.06 |
[ML] KNN(k-Nearest Neighbor) (0) | 2022.05.07 |
[ML] 선형회귀(Linear Regression) (0) | 2022.04.29 |