[Machine Learning] 02. 판다스(2)

Nina·2021년 1월 30일
0
post-thumbnail

「권철민(2020).파이썬 머신러닝 완벽가이드(개정판).위키북스」 책으로 공부한 뒤 정리한 내용.

5. 데이터 셀렉션/필터링

DataFrame은 ix[ ], iloc[ ], loc[ ]을 사용해 셀렉션을 할 수 있다. ix[]의 경우 컬럼 명칭 기반 인덱싱, 컬럼 위치 기반 인덱싱 모두 가능하지만, 이는 혼란을 초래할 수 있기 때문에 향후 사라질 예정이기 때문에 iloc[ ]와 loc[ ]에 대해서만 정리하겠다.
먼저 .read_csv()로 DataFrame을 생성한다.

>>> import pandas as pd
>>> countries_df = pd.read_csv(r'~/Downloads/countries.csv', encoding="ISO-8859-1")
>>> countries_df.head(3)
        Country                               Region  Population  Area (sq. mi.) Pop. Density (per sq. mi.)  ... Birthrate Deathrate Agriculture  Industry Service
0  Afghanistan         ASIA (EX. NEAR EAST)             31056997          647500                       48,0  ...      46,6     20,34        0,38      0,24    0,38
1      Albania   EASTERN EUROPE                          3581655           28748                      124,6  ...     15,11      5,22       0,232     0,188   0,579
2      Algeria   NORTHERN AFRICA                        32930091         2381740                       13,8  ...     17,14      4,61       0,101       0,6   0,298

[3 rows x 20 columns]

(1) iloc[ ]

iloc[ ] 는 컬럼 위치 기반 인덱싱을 허용한다.

>>> countries_df.iloc[0,0]
'Afghanistan '
>>> countries_df.iloc[1,2]
3581655
>>> countries_df.iloc[0, 'Country']
Traceback (most recent call last):
  File "/Users/nina/miniconda3/envs/machine-learning/lib/python3.8/site-packages/pandas/core/indexing.py", line 722, in _has_valid_tuple
    self._validate_key(k, i)
  File "/Users/nina/miniconda3/envs/machine-learning/lib/python3.8/site-packages/pandas/core/indexing.py", line 1374, in _validate_key
    raise ValueError(f"Can only index by location with a [{self._valid_types}]")
ValueError: Can only index by location with a [integer, integer slice (START point is INCLUDED, END point is EXCLUDED), listlike of integers, boolean array]

iloc[ ]에 위치 인덱싱이 아닌 명칭을 입력하는 경우 오류가 발생한다. 또한 iloc[ ] 는 명확한 위치 기반 인덱싱이 사용되어야 하기 때문에 불린 인덱싱은 제공하지 않는다.

(2) loc[ ]

loc[ ] 은 명칭 기반으로 데이터를 추출한다.

>>> countries_df.loc[0, 'Country']
'Afghanistan '

이 경우 첫번째 인자인 0은 위치 인덱싱이 아닌 인덱스명을 의미한다.

>>> countries_deleted_df = countries_df.drop(labels=0, axis=0, inplace=False)
>>> countries_deleted_df.head(3)
           Country                               Region  Population  Area (sq. mi.) Pop. Density (per sq. mi.)  ... Birthrate Deathrate Agriculture  Industry Service
1         Albania   EASTERN EUROPE                          3581655           28748                      124,6  ...     15,11      5,22       0,232     0,188   0,579
2         Algeria   NORTHERN AFRICA                        32930091         2381740                       13,8  ...     17,14      4,61       0,101       0,6   0,298
3  American Samoa   OCEANIA                                   57794             199                      290,4  ...     22,46      3,27         NaN       NaN     NaN

[3 rows x 20 columns]
>>> countries_deleted_df.loc[0,'Country']
Traceback (most recent call last):
  File "/Users/nina/miniconda3/envs/machine-learning/lib/python3.8/site-packages/pandas/core/indexes/base.py", line 3080, in get_loc
    return self._engine.get_loc(casted_key)
  File "pandas/_libs/index.pyx", line 70, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/index.pyx", line 101, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 1625, in pandas._libs.hashtable.Int64HashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 1632, in pandas._libs.hashtable.Int64HashTable.get_item
KeyError: 0

따라서 인덱스명이 0인 로우를 삭제한 뒤 .loc[0, 'Country']를 사용해 값을 셀렉트하려고 하면 에러가 발생한다.

(3) 불린 인덱싱

불린 인덱싱은 [ ], ix[ ], loc[ ]에서 공통으로 지원한다.

>>> countries_df[countries_df['Population'] > 100000000][['Country','Population']]
            Country  Population
16      Bangladesh    147365352
27          Brazil    188078227
42           China   1313973713
94           India   1095351995
95       Indonesia    245452739
103          Japan    127463611
135         Mexico    107449525
152        Nigeria    131859731
156       Pakistan    165803560
169         Russia    142893540
214  United States    298444215

불린 인덱싱을 사용하여 인구가 1억이 넘는 국가들을 필터링하였다. 조건의 부정인 경우는 '~'을 사용하고, '&'와 '|'를 사용해 조건들을 결합할 수도 있다.

6. 정렬, Aggregation, GroupBy

(1) sort_values()

DataFrame과 Series의 정렬을 위해서는 sort_values() 메소드를 이용한다. sort_values()는 데이터 삭제와 마찬가지로 inplace 조건을 통해 원본 DataFrame을 유지할지, 변경할지를 결정할 수 있다.

>>> countries_sorted = countries_df.sort_values(by=['Population','Area (sq. mi.)'], ascending=False)
>>> countries_sorted.head(5)
            Country                               Region  Population  Area (sq. mi.) Pop. Density (per sq. mi.)  ... Birthrate Deathrate Agriculture  Industry Service
42           China         ASIA (EX. NEAR EAST)           1313973713         9596960                      136,9  ...     13,25      6,97       0,125     0,473   0,403
94           India         ASIA (EX. NEAR EAST)           1095351995         3287590                      333,2  ...     22,01      8,18       0,186     0,276   0,538
214  United States   NORTHERN AMERICA                      298444215         9631420                       31,0  ...     14,14      8,26        0,01     0,204   0,787
95       Indonesia         ASIA (EX. NEAR EAST)            245452739         1919440                      127,9  ...     20,34      6,25       0,134     0,458   0,408
27          Brazil               LATIN AMER. & CARIB       188078227         8511965                       22,1  ...     16,56      6,17       0,084       0,4   0,516

[5 rows x 20 columns]

(2) Aggregation 함수

DataFrame에서 바로 aggregation을 호출하면 모든 컬럼에 해당 aggregation을 적용한다.

>>> countries_df.count()
Country                               227
Region                                227
Population                            227
Area (sq. mi.)                        227
Pop. Density (per sq. mi.)            227
Coastline (coast/area ratio)          227
Net migration                         224
Infant mortality (per 1000 births)    224
GDP ($ per capita)                    226
Literacy (%)                          209
Phones (per 1000)                     223
Arable (%)                            225
Crops (%)                             225
Other (%)                             225
Climate                               205
Birthrate                             224
Deathrate                             223
Agriculture                           212
Industry                              211
Service                               212
dtype: int64
>>> countries_df[['Population','Area (sq. mi.)']].mean()
Population        2.874028e+07
Area (sq. mi.)    5.982270e+05
dtype: float64
>>> countries_df[['Population','Area (sq. mi.)']].sum()
Population        6524044551
Area (sq. mi.)     135797519
dtype: int64

(3) groupby()

>>> countries_groupby_region = countries_df.groupby(by='Region')
>>> countries_groupby_region
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fce2e2f9fd0>
>>> countries_groupby_region.sum()
                                     Population  Area (sq. mi.)  GDP ($ per capita)
Region
ASIA (EX. NEAR EAST)                 3687982236        23096712            225500.0
BALTICS                                 7184974          175015             33900.0
C.W. OF IND. STATES                   280081548        22100843             48000.0
EASTERN EUROPE                        119914717         1152222            117700.0
LATIN AMER. & CARIB                   561824599        20544084            390700.0
NEAR EAST                             195068377         4355586            167300.0
NORTHERN AFRICA                       161407133         6018890             27300.0
NORTHERN AMERICA                      331672307        21782471            130500.0
OCEANIA                                33131662         8519812            173200.0
SUB-SAHARAN AFRICA                    749437000        24341406            118500.0
WESTERN EUROPE                        396339998         3710478            757300.0

다음과 같은 방법으로 여러 aggregation 함수를 동시에 적용할 수 있다.

>>> countries_groupby_region['Population'].agg([sum, max, min])
                                            sum         max      min
Region
ASIA (EX. NEAR EAST)                 3687982236  1313973713   359008
BALTICS                                 7184974     3585906  1324333
C.W. OF IND. STATES                   280081548   142893540  2976372
EASTERN EUROPE                        119914717    38536869  2010347
LATIN AMER. & CARIB                   561824599   188078227     9439
NEAR EAST                             195068377    70413958   698585
NORTHERN AFRICA                       161407133    78887007   273008
NORTHERN AMERICA                      331672307   298444215     7026
OCEANIA                                33131662    20264082    11810
SUB-SAHARAN AFRICA                    749437000   131859731     7502
WESTERN EUROPE                        396339998    82422299    27928

>>> agg_format = {'Population':'sum', 'GDP ($ per capita)':'mean'}
>>> countries_groupby_region.agg(agg_format)
                                     Population  GDP ($ per capita)
Region
ASIA (EX. NEAR EAST)                 3687982236         8053.571429
BALTICS                                 7184974        11300.000000
C.W. OF IND. STATES                   280081548         4000.000000
EASTERN EUROPE                        119914717         9808.333333
LATIN AMER. & CARIB                   561824599         8682.222222
NEAR EAST                             195068377        10456.250000
NORTHERN AFRICA                       161407133         5460.000000
NORTHERN AMERICA                      331672307        26100.000000
OCEANIA                                33131662         8247.619048
SUB-SAHARAN AFRICA                    749437000         2323.529412
WESTERN EUROPE                        396339998        27046.428571

7. 결손 데이터 처리

결손 데이터는 컬럼의 값이 NULL인 경우를 의미하며, 이를 넘파이의 NaN으로 표시한다. 머신러닝 알고리즘은 기본적으로 이 NaN 값을 처리하지 않기 때문에 다른 값으로 대체해야 한다.

(1) isna()

isna()는 데이터가 NaN인지 아닌지를 알려준다.

>>> countries_df.isna().head(5)
   Country  Region  Population  Area (sq. mi.)  Pop. Density (per sq. mi.)  Coastline (coast/area ratio)  ...  Climate  Birthrate  Deathrate  Agriculture  Industry  Service
0    False   False       False           False                       False                         False  ...    False      False      False        False     False    False
1    False   False       False           False                       False                         False  ...    False      False      False        False     False    False
2    False   False       False           False                       False                         False  ...    False      False      False        False     False    False
3    False   False       False           False                       False                         False  ...    False      False      False         True      True     True
4    False   False       False           False                       False                         False  ...    False      False      False         True      True     True

[5 rows x 20 columns]
>>> countries_df.isna().sum()
Country                                0
Region                                 0
Population                             0
Area (sq. mi.)                         0
Pop. Density (per sq. mi.)             0
Coastline (coast/area ratio)           0
Net migration                          3
Infant mortality (per 1000 births)     3
GDP ($ per capita)                     1
Literacy (%)                          18
Phones (per 1000)                      4
Arable (%)                             2
Crops (%)                              2
Other (%)                              2
Climate                               22
Birthrate                              3
Deathrate                              4
Agriculture                           15
Industry                              16
Service                               15
dtype: int64

(2) fillna()

fillna()는 결손 데이터를 다른 값으로 대체하는 것을 가능하게 한다.

>>> countries_df['Climate'] = countries_df['Climate'].fillna('Unknown')
>>> countries_df['Climate'].isna().sum()
0

Climate 컬럼이 NULL인 경우 모두 'Unknown'이라는 값으로 대체하였고, 그 결과 22개이던 결손 데이터의 개수가 0으로 변하였다.

8. 데이터 가공

apply() 함수에 lambda 식을 결합하여 레코드별로 데이터를 가공할 수 있다.

>>> countries_df['new_population'] = countries_df['Population'].apply(lambda x: x//100)
>>> countries_df.head(5)
           Country                               Region  Population  Area (sq. mi.) Pop. Density (per sq. mi.)  ... Deathrate Agriculture Industry  Service new_population
0     Afghanistan         ASIA (EX. NEAR EAST)             31056997          647500                       48,0  ...     20,34        0,38     0,24     0,38         310569
1         Albania   EASTERN EUROPE                          3581655           28748                      124,6  ...      5,22       0,232    0,188    0,579          35816
2         Algeria   NORTHERN AFRICA                        32930091         2381740                       13,8  ...      4,61       0,101      0,6    0,298         329300
3  American Samoa   OCEANIA                                   57794             199                      290,4  ...      3,27         NaN      NaN      NaN            577
4         Andorra   WESTERN EUROPE                            71201             468                      152,1  ...      6,25         NaN      NaN      NaN            712

[5 rows x 21 columns]

if문을 사용해 조건에 따른 값을 반환할 수도 있다. 단, 파이썬의 elif는 지원하지 않는다. 따라서 엑셀의 if문을 사용하듯이 else에 조건을 계속해서 추가하여야한다. 다른 방법으로는 세분화된 분류를 수행하는 함수를 작성하는 것도 있다.

>>> countries_df['is_developed'] = countries_df['GDP ($ per capita)'].apply(lambda x: 'Developed' if x > 12000 else 'Developing')
>>> countries_df.head(10)
              Country                               Region  Population  Area (sq. mi.) Pop. Density (per sq. mi.)  ... Agriculture Industry Service  new_population is_developed
0        Afghanistan         ASIA (EX. NEAR EAST)             31056997          647500                       48,0  ...        0,38     0,24    0,38          310569   Developing
1            Albania   EASTERN EUROPE                          3581655           28748                      124,6  ...       0,232    0,188   0,579           35816   Developing
2            Algeria   NORTHERN AFRICA                        32930091         2381740                       13,8  ...       0,101      0,6   0,298          329300   Developing
3     American Samoa   OCEANIA                                   57794             199                      290,4  ...         NaN      NaN     NaN             577   Developing
4            Andorra   WESTERN EUROPE                            71201             468                      152,1  ...         NaN      NaN     NaN             712    Developed
5             Angola   SUB-SAHARAN AFRICA                     12127071         1246700                        9,7  ...       0,096    0,658   0,246          121270   Developing
6           Anguilla               LATIN AMER. & CARIB           13477             102                      132,1  ...        0,04     0,18    0,78             134   Developing
7  Antigua & Barbuda               LATIN AMER. & CARIB           69108             443                      156,0  ...       0,038     0,22   0,743             691   Developing
8          Argentina               LATIN AMER. & CARIB        39921833         2766890                       14,4  ...       0,095    0,358   0,547          399218   Developing
9            Armenia                  C.W. OF IND. STATES      2976372           29800                       99,9  ...       0,239    0,343   0,418           29763   Developing

[10 rows x 22 columns]
>>> def population_category(population):
...     cat = ''
...     if population <= 10000000: cat = 'small'
...     elif population <= 100000000: cat = 'medium'
...     else: cat = 'large'
...     return cat
...
>>> countries_df['population_category'] = countries_df['Population'].apply(lambda x: population_category(x))
>>> countries_df[['Population','population_category']].head(10)
   Population population_category
0    31056997              medium
1     3581655               small
2    32930091              medium
3       57794               small
4       71201               small
5    12127071              medium
6       13477               small
7       69108               small
8    39921833              medium
9     2976372               small
profile
https://dev.to/ninahwang

0개의 댓글