「권철민(2020).파이썬 머신러닝 완벽가이드(개정판).위키북스」 책으로 공부한 뒤 정리한 내용.
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]
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[ ] 는 명확한 위치 기반 인덱싱이 사용되어야 하기 때문에 불린 인덱싱은 제공하지 않는다.
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']를 사용해 값을 셀렉트하려고 하면 에러가 발생한다.
불린 인덱싱은 [ ], 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억이 넘는 국가들을 필터링하였다. 조건의 부정인 경우는 '~'을 사용하고, '&'와 '|'를 사용해 조건들을 결합할 수도 있다.
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]
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
>>> 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
결손 데이터는 컬럼의 값이 NULL인 경우를 의미하며, 이를 넘파이의 NaN으로 표시한다. 머신러닝 알고리즘은 기본적으로 이 NaN 값을 처리하지 않기 때문에 다른 값으로 대체해야 한다.
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
fillna()는 결손 데이터를 다른 값으로 대체하는 것을 가능하게 한다.
>>> countries_df['Climate'] = countries_df['Climate'].fillna('Unknown')
>>> countries_df['Climate'].isna().sum()
0
Climate 컬럼이 NULL인 경우 모두 'Unknown'이라는 값으로 대체하였고, 그 결과 22개이던 결손 데이터의 개수가 0으로 변하였다.
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