Pandas

[Do it! Pandas] 데이터프레임과 시리즈, 간단한 그래프 그리기

PKkomi 2023. 2. 8. 09:20

판다스에서 데이터프레임과 시리즈는 판다스를 효율적으로 사용하게 해주는 자료형이다.

데이터프레임(DataFrame)은 엑셀에서의 시트와 동일한 개념이며 시리즈(Series)는 시트의 열 1개를 의미한다.

 

데이터를 가져와 살펴보자.

 

import pandas as pd
df = pd.read_csv('../data/gapminder.tsv', sep='\t')

print(df.head(), '\n')
print(type(df), '\n')
print(df.shape, '\n')
print(df.columns, '\n') # 판다스에서는 object가 string!
print(df.info())

 

pandas의 read_csv메서드는 데이터 집합을 읽어들어와 데이터프레임으로 전환해준다.

gapminder라는 파일을 가져와보았다.

 

       country continent  year  lifeExp       pop   gdpPercap
0  Afghanistan      Asia  1952   28.801   8425333  779.445314
1  Afghanistan      Asia  1957   30.332   9240934  820.853030
2  Afghanistan      Asia  1962   31.997  10267083  853.100710
3  Afghanistan      Asia  1967   34.020  11537966  836.197138
4  Afghanistan      Asia  1972   36.088  13079460  739.981106 

<class 'pandas.core.frame.DataFrame'> 

(1704, 6) 

Index(['country', 'continent', 'year', 'lifeExp', 'pop', 'gdpPercap'], dtype='object') 

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1704 entries, 0 to 1703
Data columns (total 6 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   country    1704 non-null   object 
 1   continent  1704 non-null   object 
 2   year       1704 non-null   int64  
 3   lifeExp    1704 non-null   float64
 4   pop        1704 non-null   int64  
 5   gdpPercap  1704 non-null   float64
dtypes: float64(2), int64(2), object(2)
memory usage: 80.0+ KB
None

 

 

1704개의 행과 6개의 열로 이루어진 데이터임을 확인하였다. 

type메서드를 통해 읽어온 데이터가 데이터프레임이라는 것을 알 수 있다.

판다스에서는 string을 object로 표기한다는 특징이 있다.

head메서드는 실제 데이터프레임의 일부를 보여준다. n이라는 인자에 숫자를 넣어 보고 싶은 데이터 행의 개수를 정할 수 있다.

info는 각 열이 어떤 정보를 어떤 자료형으로 담고 있으며 그 수가 몇 개 인지에 대한 정보를 준다.

 

print(df.loc[0]) # loc는 행의 index number로 추출
country      Afghanistan
continent           Asia
year                1952
lifeExp           28.801
pop              8425333
gdpPercap     779.445314
Name: 0, dtype: object

 

loc라는 내부함수를 이용하여 각 행의 데이터에 접근할 수 있다. 

loc는 행의 index number를 인자로 넣어준다.

 

print(df.iloc[0]) # iloc는 행 number로 추출
print(df.iloc[-1]) # iloc는 loc와 달리 음수도 가능!
country      Afghanistan
continent           Asia
year                1952
lifeExp           28.801
pop              8425333
gdpPercap     779.445314
Name: 0, dtype: object

country        Zimbabwe
continent        Africa
year               2007
lifeExp          43.487
pop            12311143
gdpPercap    469.709298
Name: 1703, dtype: object

 

iloc는 행에 지정된 number로 각 행의 데이터에 접근한다.

가져온 데이터프레임은 index number와 마찬가지로 0부터 시작하기 때문에 여기서는 iloc와 loc의 결과가 같다.

iloc는 loc와는 다르게 음수를 사용하여 값을 출력할 수 있다.

 

print(df.tail(n=3))
print(type(df.loc[0]), type(df.tail(n=1))) # loc와 tail의 type이 다르다!
       country continent  year  lifeExp       pop   gdpPercap
1701  Zimbabwe    Africa  1997   46.809  11404948  792.449960
1702  Zimbabwe    Africa  2002   39.989  11926563  672.038623
1703  Zimbabwe    Africa  2007   43.487  12311143  469.709298

<class 'pandas.core.series.Series'> <class 'pandas.core.frame.DataFrame'>

 

tail은 마지막 행데이터부터 n인자에 지정한 수만큼의 데이터에 접근한다.

특이한 점은 tail메서드를 통해 접근한 데이터의 타입은 데이터프레임이지만 loc의 경우에는 시리즈이다. 

 

subset = df.loc[:, ['year', 'pop']] # loc는 열의 문자열 이름을 넣어서 추출
print(subset.head())

subset = df.iloc[:, [2, 4, -1]] # iloc는 열의 인덱스 번호를 넣어서 추출
print(subset.head())
   year       pop
0  1952   8425333
1  1957   9240934
2  1962  10267083
3  1967  11537966
4  1972  13079460

   year       pop   gdpPercap
0  1952   8425333  779.445314
1  1957   9240934  820.853030
2  1962  10267083  853.100710
3  1967  11537966  836.197138
4  1972  13079460  739.981106

 

loc와 iloc 메서드를 사용하여 열 데이터에도 접근이 가능하다. 

loc는 열의 이름을 통해 접근하고 iloc는 열의 인덱스를 통해 접근한다.

아무래도 iloc를 사용하면 따로 출력해보지 않는 이상 어떤 데이터인지 불분명하므로 loc를 통해 열 이름으로 접근하는 것이 더 좋다.

 

print(df.loc[10:13, ['country', 'lifeExp', 'gdpPercap']])
       country  year
0  Afghanistan  1952
1  Afghanistan  1957
2  Afghanistan  1962
3  Afghanistan  1967
4  Afghanistan  1972

 

위와 같이 슬라이싱을 하는 것도 가능하다.

 

 

groupby 메서드를 이용하여 연도별('year')로 정보에 접근이 가능하다.

groupby 메서드는 여러 개의 항목을 세분화해서 그룹화할 수 있다는 점에서 강점을 지닌다. 

print(df.groupby('year')['lifeExp'].mean()) 
# 연도별로(각 국가마다 같은 연도의 데이터가 존재하므로) 모든 국가의 lifeExp의 평균을 구한 값
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

 

내가 접근한 방식에 따라서 groupby만 적용한 데이터는 데이터프레임, 추가로 lifeExp 열에 접근한 데이터는 시리즈의 형태이며 서로 다른 메모리 주소에 저장되어 있는 것을 알 수 있다.

groupby를 통해 얻은 데이터는 아래와 같은 타입(SeriesGroupBy)을 가진다는 것을 알 수 있다.

 

print(df.groupby('year')) 
print(df.groupby('year')['lifeExp']) 
# groupby를 통해 나온 데이터는 데이터프레임의 형태로 저장되어 다음의(0x7fc57cded400) 메모리주소에 저장되었다는 것을 확인할 수 있음.
print(type((df.groupby('year')['lifeExp'])))
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fc57cded490>
<pandas.core.groupby.generic.SeriesGroupBy object at 0x7fc57cded130>
<class 'pandas.core.groupby.generic.SeriesGroupBy'>

 

데이터의 개수를 구하고 싶을 때는 nunique라는 메서드를 사용하면 된다. 매우 유용하다!

 

print(df.groupby('continent')['country'].nunique()) # 각 항목에 해당하는 데이터의 개수를 출력
# 여기서는 각 대륙별 몇 개의 나라가 포함되어 있는지 개수를 구하는 코드!
continent
Africa      52
Americas    25
Asia        33
Europe      30
Oceania      2
Name: country, dtype: int64

 

데이터의 추이를 보고 싶을 때 그래프만한 것이 없다.

matplotlib를 가져와 간단한 그래프를 그려보자.

 

import matplotlib.pyplot as plt

global_yearly_mean_life_expectancy = df.groupby('year')['lifeExp'].mean()
print(global_yearly_mean_life_expectancy)
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

 

임의로 연도별 국가들의 gdp의 평균 데이터를 가져왔다. 

이제 이 데이터로 쉽게 그래프를 그릴 수 있다.

 

global_yearly_mean_life_expectancy.plot()
<AxesSubplot:xlabel='year'>
연도별 국가들의 gdp 평균

그래프를 통해 국가들의 평균 gdp가 꾸준히 상승하는 것을 한 눈에 볼 수 있다.