-
chapter-6 누락값 처리하기판다스 2021. 9. 23. 00:33In [2]:
from numpy import NaN,NAN,nan
2.¶
누락값은 0,''와 같은 값과는 다른 개념이라는 것에 주의해야 합니다. 누락값은 말 그대로 데이터 자체가 없다는 것을 의미합니다. 그래서 '같다' 라는 개념도 없죠. 다음은 누락값과 True, False,0,''을 비교한 결과입니다.
In [3]:print(NaN == True)
False
In [4]:print(NaN==False)
False
In [5]:print(NaN==0)
False
In [6]:print(NaN=="")
False
3.¶
과정 2에서도 언급했듯이 누락값은 값 자체가 없기 때문에 자기 자신과 비교해도 True가 아닌 False가 출력됩니다.
In [11]:print(NaN==NaN)
False
In [12]:print(NaN==nan)
False
In [13]:print(NaN==NAN)
False
In [14]:print(nan==NAN)
False
4.¶
그러면 누락값은 어떻게 확인할 수 있을까요? 다행히 판다스에는 누락값을 확인하는 메서드인 isnull이 있습니다. 오른쪽은 isnull메서드로 누락값을 검사한 예입니다.
In [15]:import pandas as pd
In [16]:print(pd.isnull(NaN))
True
In [17]:print(pd.isnull(nan))
True
In [18]:print(pd.isnull(NAN))
True
5.¶
반대의 경우도 검사할 수 있습니다. 오른쪽은 notnull 메서드로 누락값이 아닌 경우를 검사한 예입니다.
In [20]:print(pd.notnull(NaN))
False
In [21]:print(pd.notnull(42))
True
In [22]:print(pd.notnull('missing'))
True
누락값이 생기는 이유¶
누락값은 왜 생길까요? 누락값은 처음부터 누락값이 있는 데이터를 불러오거나 데이터를 연결, 입력하는 등의 과정에서 생길 수 있습니다. 그러면 다음 예제를 통해 누락값이 생기는 이유에 대해 자세히 알아보겠습니다.
In [24]:visited=pd.read_csv('data/survey_visited.csv') survey=pd.read_csv('data/survey_survey.csv')
In [25]: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
In [26]: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.¶
과정 1에서 구한 데이터 집합을 연결해 볼까요? 그러면 누락값이 많이 생겨난 것을 볼 수 있습니다.
In [27]: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
3. 데이터를 입력할 때 누락값이 생기는 경우¶
누락값은 데이터를 잘못 입력하여 생길 수도 있습니다. 다음은 시리즈를 생성할 때 데이터프레임에 없는 열과 행 데이터를 입력하여 누락값이 생긴 것입니다. scientists 데이터프레임을 확인하면 missing이라는 열과 함께 행 데이터에 누락값이 추가된 것을 확인할 수 있습니다.
In [28]:num_legs=pd.Series({'goat':4, 'amoeda':nan}) print(num_legs) print(type(num_legs))
goat 4.0 amoeda NaN dtype: float64 <class 'pandas.core.series.Series'>
In [31]:scientists=pd.DataFrame({ 'Name':['Rosaline Franklin', 'William Gosset'], 'Occupation': ['Chemist', 'Statistician'], 'Born':['1920-07-25','1876-06-13'], 'Died':['1958-04-16','1937-10-16'], 'missing':[NaN,nan]})
In [32]:print(scientists) print(type(scientists))
Name Occupation Born Died missing 0 Rosaline Franklin Chemist 1920-07-25 1958-04-16 NaN 1 William Gosset Statistician 1876-06-13 1937-10-16 NaN <class 'pandas.core.frame.DataFrame'>
4. 범위를 지정하여 데이터를 추출할 때 누락값이 생기는 경우¶
데이터프레임에 존재하지 않는 데이터를 추출하면 누락값이 생깁니다. 이번에는 갭마인더 데이터 집합을 불러와 실습해 보겠습니다.
In [34]:gapminder=pd.read_csv('data/gapminder.tsv', sep='\t')
5.¶
다음은 gapminder 데이터프레임을 연도별로 그룹화한 다음 ilfeExp 열의 평군을 구한것입니다.
In [40]:life_exp=gapminder.groupby(['year'])['lifeExp'].mean() print(life_exp)
year 1952 49.057620 1957 51.507401 1962 53.609249 1967 55.678290 1972 57.647386 1977 59.570157 1982 61.533197 1987 63.212613 1992 64.160338 1997 65.014676 2002 65.694923 2007 67.007423 Name: lifeExp, dtype: float64
6.¶
다음은 range 메서드를 이용하여 life_Exp열에서 2000~2009년의 데이터를 추출한 것입니다. 그런데 이렇게 데이터를 추출하면 처음부터 life_Exp 열에 없었던 연도가 포함되기 때문에 누락값이 많이 발생합니다. 이를 해결하기 위해서는 불린 추출을 이용하여 데이터를 추출하면 됩니다.
In [45]:y2000=life_exp[life_exp.index > 2000] print(y2000)
year 2002 65.694923 2007 67.007423 Name: lifeExp, dtype: float64
In [46]:ebola=pd.read_csv('data/country_timeseries.csv')
2.¶
먼저 count 메서드로 누락값이 아닌 값의 개수를 구해 보겠습니다.
In [48]:print(ebola.count())
Date 122 Day 122 Cases_Guinea 93 Cases_Liberia 83 Cases_SierraLeone 87 Cases_Nigeria 38 Cases_Senegal 25 Cases_UnitedStates 18 Cases_Spain 16 Cases_Mali 12 Deaths_Guinea 92 Deaths_Liberia 81 Deaths_SierraLeone 87 Deaths_Nigeria 38 Deaths_Senegal 22 Deaths_UnitedStates 18 Deaths_Spain 16 Deaths_Mali 12 dtype: int64
3.¶
과정 2의 결과만 잘 활용해도 누락값의 개수를 쉽게 구할 수 있습니다. shape[0]에 전체 행의 데이터 개수가 저장되어 있다는 점을 이용하여 shape[0]에서 누락값이 아닌 값의 개수를 빼면 누락값의 개수를 구할 수 있습니다.
In [49]:num_rows=ebola.shape[0] num_missing=num_rows-ebola.count() print(num_missing)
Date 0 Day 0 Cases_Guinea 29 Cases_Liberia 39 Cases_SierraLeone 35 Cases_Nigeria 84 Cases_Senegal 97 Cases_UnitedStates 104 Cases_Spain 106 Cases_Mali 110 Deaths_Guinea 30 Deaths_Liberia 41 Deaths_SierraLeone 35 Deaths_Nigeria 84 Deaths_Senegal 100 Deaths_UnitedStates 104 Deaths_Spain 106 Deaths_Mali 110 dtype: int64
4.¶
count 메서드를 사용해도 되지만 count_nonzero,isnull 메서드를 조합해도 누락값의 개수를 구할 수 있습니다.
In [51]:import numpy as np
In [52]:print(np.count_nonzero(ebola.isnull()))
1214
In [53]:print(np.count_nonzero(ebola['Cases_Guinea'].isnull()))
29
5.¶
시리즈에 포함된 value_counts 메서드는 지정한 열의 빈도를 구하는 메서드입니다. value_counts 메서드를 사용해 Cases_Guinea 열의 누락값 개수를 구하려면 다음과 같이 입력합니다.
In [55]:print(ebola.Cases_Guinea.value_counts(dropna=False).head())
NaN 29 86.0 3 112.0 2 390.0 2 495.0 2 Name: Cases_Guinea, dtype: int64
누락값 처리하기¶
누락값은 누락값을 임의의 값으로 변경하거나 데이터프레임에 이미 있는 값으로 대신 채우는 방법 등으로 처리할 수 있습니다. 그러면 누락값을 처리하는 방법을 하나씩 알아보겠습니다.
1. 누락값 변경하기¶
데이터프레임에 포함된 fillna 메서드에 0을 대입하면 누락값을 0으로 변경합니다 fillna메서드는 처리해야 하는 데이터프레임의 크기가 매우 크고 메모리를 효율적으로 사용해야 하는 경우에 자주 사용하는 메서드입니다.
In [56]:print(ebola.fillna(0).iloc[0:10,0:5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 0 1/5/2015 289 2776.0 0.0 10030.0 1 1/4/2015 288 2775.0 0.0 9780.0 2 1/3/2015 287 2769.0 8166.0 9722.0 3 1/2/2015 286 0.0 8157.0 0.0 4 12/31/2014 284 2730.0 8115.0 9633.0 5 12/28/2014 281 2706.0 8018.0 9446.0 6 12/27/2014 280 2695.0 0.0 9409.0 7 12/24/2014 277 2630.0 7977.0 9203.0 8 12/21/2014 273 2597.0 0.0 9004.0 9 12/20/2014 272 2571.0 7862.0 8939.0
2.¶
fillna 메서드의 method 인잣값을 ffill로 지정하면 누락값이 나타나기 전의 값으로 누락값이 변경됩니다. 예를 들어 6행의 누락값은 누라값이 나타나기 전의 값인 5행의 값을 사용하여 누락값을 처리합니다. 하지만 0,1행은 처음부터 누락값이기 때문에 누락값이 그래도 남아 있습니다.
In [57]:print(ebola.fillna(method='ffill').iloc[0:10,0:5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 0 1/5/2015 289 2776.0 NaN 10030.0 1 1/4/2015 288 2775.0 NaN 9780.0 2 1/3/2015 287 2769.0 8166.0 9722.0 3 1/2/2015 286 2769.0 8157.0 9722.0 4 12/31/2014 284 2730.0 8115.0 9633.0 5 12/28/2014 281 2706.0 8018.0 9446.0 6 12/27/2014 280 2695.0 8018.0 9409.0 7 12/24/2014 277 2630.0 7977.0 9203.0 8 12/21/2014 273 2597.0 7977.0 9004.0 9 12/20/2014 272 2571.0 7862.0 8939.0
3.¶
method 인잣값을 dfill로 지정하면 누락값이 나타난 이후의 첫 번째 값으로 앞쪽의 누락값이 모두 변경됩니다. 즉 , 과정2의 반대 방향으로 누락값을 처리한다고 생각하면 됩니다. 하지만 이 방법도 마지막 값이 누락값인 경우에는 처리하지 못한다는 단점이 있습니다.
In [59]:print(ebola.fillna(method='bfill').iloc[0:10,0:5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 0 1/5/2015 289 2776.0 8166.0 10030.0 1 1/4/2015 288 2775.0 8166.0 9780.0 2 1/3/2015 287 2769.0 8166.0 9722.0 3 1/2/2015 286 2730.0 8157.0 9633.0 4 12/31/2014 284 2730.0 8115.0 9633.0 5 12/28/2014 281 2706.0 8018.0 9446.0 6 12/27/2014 280 2695.0 7977.0 9409.0 7 12/24/2014 277 2630.0 7977.0 9203.0 8 12/21/2014 273 2597.0 7862.0 9004.0 9 12/20/2014 272 2571.0 7862.0 8939.0
4.¶
과정 2,3에서는 데이터프레임의 값을 그대로 사용하여 누락값을 처리했습니다. interpolate 메서드는 누락값 양쪽에 있는 값을 이용하여 중간값을 구한 다음 누락값을 처리합니다. 이렇게 하면 데이터프레임이 일정한 간격을 유지하고 있는 것처럼 수정할 수 있습니다.
In [61]:print(ebola.interpolate().iloc[0:10,0:5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 0 1/5/2015 289 2776.0 NaN 10030.0 1 1/4/2015 288 2775.0 NaN 9780.0 2 1/3/2015 287 2769.0 8166.0 9722.0 3 1/2/2015 286 2749.5 8157.0 9677.5 4 12/31/2014 284 2730.0 8115.0 9633.0 5 12/28/2014 281 2706.0 8018.0 9446.0 6 12/27/2014 280 2695.0 7997.5 9409.0 7 12/24/2014 277 2630.0 7977.0 9203.0 8 12/21/2014 273 2597.0 7919.5 9004.0 9 12/20/2014 272 2571.0 7862.0 8939.0
5. 누락값 삭제하기¶
누락값이 필요 없을 경우에는 누락값을 삭제해도 됩니다. 하지만 누락값을 무작정 삭제하면 데이터가 너무 편향되거나 데이터의 개수가 너무 적어질 수도 있습니다. 그래서 누락값을 삭제할 때는 분석하는 사람이 잘 판단해야 합니다. 먼저 ebola의 데이터 구조를 확인해 보겠습니다.
In [62]:print(ebola.shape)
(122, 18)
6.¶
누락값을 삭제하기 위해 dropna 메서드를 사용하겠습니다 누락값이 포함된 행들이 모두 삭제되기 때문에 많은 데이터가 삭제되었습니다.
In [63]:ebola_dropna=ebola.dropna() print(ebola_dropna.shape)
(1, 18)
In [64]:print(ebola_dropna)
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone \ 19 11/18/2014 241 2047.0 7082.0 6190.0 Cases_Nigeria Cases_Senegal Cases_UnitedStates Cases_Spain Cases_Mali \ 19 20.0 1.0 4.0 1.0 6.0 Deaths_Guinea Deaths_Liberia Deaths_SierraLeone Deaths_Nigeria \ 19 1214.0 2963.0 1267.0 8.0 Deaths_Senegal Deaths_UnitedStates Deaths_Spain Deaths_Mali 19 0.0 1.0 0.0 6.0
누락값이 포함된 데이터 계산하기¶
만약 누락값이 포함된 데이터를 계산하려면 어떻게 해야 할까요? 다음은 여러 지역에서 발생한 ebola발병 수를 구하는 실습입니다. ebola 데이터프레임에는 누락값이 많습니다. 이점을 염두에 두고 다음 실습을 진행해 보세요.
In [66]:ebola['Cases_multiple']=ebola['Cases_Guinea']+ebola['Cases_Liberia']+ebola['Cases_SierraLeone']
2.¶
과정 1에서 계산한 Cases_multiple 열을 포함하여 ebola_subset이라는 데이터프레임을 새로 만들어서 어떤 값이 존재하는지 확인해 보겠습니다. Cases_Guinea, Cases_Liberia, Cases_SierraLeone에서 누락값이 하나라도 있는 행은 계산 결과가 NaN이 되었음을 알 수 있습니다. 즉,계산 결과 누락값이 더 많이 생겼습니다.
In [69]:ebola_subset=ebola.loc[:,['Cases_Guinea', 'Cases_Liberia', 'Cases_SierraLeone', 'Cases_multiple']] print(ebola_subset.head(n=10))
Cases_Guinea Cases_Liberia Cases_SierraLeone Cases_multiple 0 2776.0 NaN 10030.0 NaN 1 2775.0 NaN 9780.0 NaN 2 2769.0 8166.0 9722.0 20657.0 3 NaN 8157.0 NaN NaN 4 2730.0 8115.0 9633.0 20478.0 5 2706.0 8018.0 9446.0 20170.0 6 2695.0 NaN 9409.0 NaN 7 2630.0 7977.0 9203.0 19810.0 8 2597.0 NaN 9004.0 NaN 9 2571.0 7862.0 8939.0 19372.0
3.¶
Cases_multiple 열을 sum 메서드를 사용해 더하면 세 지역의 ebola 발병 수의 합을 구할 수 있습니다. 이때 sum 메서드를 그냥 사용하면 누락값을 포함해 계산합니다. 따라서 결괏값도 누락값이 됩니다. 누락값을 무시한 채 계산하려면 skipna 인작값을 True로 설정하면 됩니다.
In [71]:print(ebola.Cases_Guinea.sum(skipna=True))
84729.0
In [72]:print(ebola.Cases_Guinea.sum(skipna=False))
nan
마무리하며¶
데이터 분석 분야에서는 누락값을 처리하는 능력이 매우 중요합니다. 데이터를 통해 의사결정이나 추록을 하려면 반드시 필요한 능력이죠. 이장에서는 누락값을 처리하는 여러 가지방법을 알아보았습니다. 누락값은 언제 어디서든 생길 수 있기 때문에 누락값을 처리하는 방법은 반드시 알아두어야 합니다.
출처 : "do it 데이터 분석을 위한 판다스 입문"
'판다스' 카테고리의 다른 글
chapter-8 판다스 자료형 (0) 2021.09.23 chapter-7 깔끔한 데이터 (0) 2021.09.23 chapter-03 판다스 데이터프레임과 시리즈 (0) 2021.09.17 chapter-5 데이터 연결하기 (0) 2021.09.17 chapter-4 그래프 그리기 (0) 2021.09.17