04. 판다스 데이터프레임 기초 이해
04. 판다스 데이터프레임 기초 이해 관련
01. 데이터프레임의 출력
판다스는 데이터가 위치한 URL을 입력하는 것만으로도 데이터를 바로 읽어오는 기능을 가지고 있습니다. csv 파일 형태의 데이터를 로드하여 데이터프레임에 저장해봅시다. csv 파일을 데이터프레임으로 읽을 때 pd.read_csv()
를 사용합니다. 데이터가 위치한 url을 기재하여 파일을 읽어옵시다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
의 타입을 확인해봅시다.
# 타입 확인
type(drink_df)
데이터프레임이라고 출력됩니다. drink_df
를 출력해봅시다.
drink_df
데이터프레임의 출력
head()
는 상위 5개의 행을 출력합니다.
# 상위 5개의 행을 출력
drink_df.head()
head(숫자)
는 상위 입력한 숫자만큼의 행을 출력합니다.
# 상위 10개의 행을 출력
drink_df.head(10)
tail()
은 하위 5개의 행을 출력합니다.
# 하위 5개의 행을 출력
drink_df.tail()
sample(숫자)
는 랜덤으로 선택된 10개의 행을 출력합니다. 데이터를 눈으로 파악할 때 연속적으로 실행하며 데이터를 파악하기에 좋습니다.
# 랜덤으로 선택된 10개의 행을 출력
drink_df.sample(10)
데이터프레임의 인덱스
index
는 데이터프레임의 인덱스를 확인합니다.
# 인덱스의 범위 확인
drink_df.index
#
# RangeIndex(start=0, stop=193, step=1)
현재 인덱스는 0부터 시작해서 +1씩(step
) 증가하여 192까지 있습니다. 참고로 193에서 stop한다는 것은 193은 포함하지 않는다는 의미입니다. 즉, 0번부터 192번까지의 샘플이 있는 셈이므로 총 샘플의 수는 193개입니다.
데이터프레임의 데이터타입
dtypes
는 각 데이터프레임의 열의 타입을 확인할 수 있습니다.
# 각 컬럼의 타입 출력
drink_df.dtypes
#
# country object
# beer_servings int64
# spirit_servings int64
# wine_servings int64
# total_litres_of_pure_alcohol float64
# continent object
# dtype: object
참고로 데이터프레임의 타입에서 object
라고 표현되는 부분은 해당 타입이 문자열이라는 의미입니다. int64
는 정수형 데이터, float64
는 실수형 데이터를 의미합니다.
다시 말해 위의 출력 결과를 해석해보면, country, continent 열은 문자열 데이터로 구성되어져 있고, beer_servings
, spirit_servings
, wine_servings
열은 정수형 데이터, total_litres_of_prue_alcohol
열은 실수형 데이터로 구성되어져 있다는 의미입니다. 이를 통해서 각 열의 데이터 자료형이 무엇인지를 파악할 수 있습니다. shape
는 데이터프레임의 행과 열의 수를 확인할 수 있습니다.
데이터프레임의 크기
# 데이터프레임의 행과 열의 개수 출력
drink_df.shape
위 출력 결과는 해당 데이터프레임이 193개의 행, 6개의 열을 가지고 있음을 알려줍니다.
데이터프레임의 행렬 변환
values
는 데이터프레임 형태를 Numpy 행렬 형태로 변환하여 출력합니다.
# Numpy 타입으로 출력
drink_df.values
#
# array([['Afghanistan', 0, 0, 0, 0.0, 'AS'],
# ['Albania', 89, 132, 54, 4.9, 'EU'],
# ['Algeria', 25, 0, 14, 0.7, 'AF'],
# ...,
# ['Yemen', 6, 0, 0, 0.1, 'AS'],
# ['Zambia', 32, 19, 4, 2.5, 'AF'],
# ['Zimbabwe', 64, 18, 4, 4.7, 'AF']], dtype=object)
type(drink_df.values)
#
# numpy.ndarray
Numpy에 대해서는 이 수업에서 다루지는 않았지만 정말~ 간단히 요약하자면 각각의 행을 아래와 같이 데이터프레임의 행 형태에서 아래와 같은 형태로 변환된다고 보면 되겠습니다.
['Afghanistan', 0, 0, 0, 0.0, 'AS']
변환한 데이터의 첫번째 행을 출력해볼까요? 이는 변환 후 0번 인덱스로 접근하면 됩니다.
drink_df.values[0]
#
# array(['Afghanistan', 0, 0, 0, 0.0, 'AS'], dtype=object)
위와 같은 형태로 변환이 되었다는 것은 다음과 같이 for
문 접근도 가능하다는 의미입니다.
for element in drink_df.values[0]:
print(element)
#
# Afghanistan
# 0
# 0
# 0
# 0.0
# AS
데이터프레임의 info
info()
는 데이터프레임의 전반적인 정보를 보여줍니다. info()
사용하고, info()
를 통해 알 수 있는 정보들을 최대한 정리해봅시다.
drink_df.info()
#
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 193 entries, 0 to 192
# Data columns (total 6 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 country 193 non-null object
# 1 beer_servings 193 non-null int64
# 2 spirit_servings 193 non-null int64
# 3 wine_servings 193 non-null int64
# 4 total_litres_of_pure_alcohol 193 non-null float64
# 5 continent 170 non-null object
# dtypes: float64(1), int64(3), object(2)
# memory usage: 9.2+ KB
info()
를 통해 다음과 같은 정보를 알 수 있습니다.
해당 데이터는 다음과 같은 열들을 가지고 있습니다. 데이터 분석 용어로는 데이터를 파악하기 위한 이러한 열을 특성(feature
) 이라고 부릅니다. 총 6개의 특성이 있는 셈입니다.
country
: 국가beer_servings
: 맥주 소비량spiti_servings
: spirit 소비량wine_servings
: wine 소비량total_litres_of_pure_alcohol
: 총 알코올 소비량continent
: 대륙 정보
info()
의 출력 결과로부터 총 193개의 데이터가 존재하며, contry
, continet
데이터의 경우에는 object
. 데이터프레임에서 object
는 문자열을 의미합니다. 그 외에는 정수형 데이터(int64
) 또는 실수형 데이터(float64
)로 구성되어져 있음을 알 수 있습니다.
또 하나 주목할 점은 데이터의 총 개수는 193개인데, Non-Null
Count를 보면 continent
의 경우에만 170개가 있습니다.
이는 Null
데이터. 다시 말해 결측 데이터가 23개 존재한다는 것을 의미합니다.
데이터프레임의 결측값(Null)
결측값이란 정상적으로 존재하지 않는 경우의 값을 의미합니다. Null
값이라고도 표현합니다. isnull().sum()
은 해당 데이터프레임의 각 열에서 Null
데이터가 총 몇 개인지를 출력합니다.
print(drink_df.isnull().sum())
#
# country 0
# beer_servings 0
# spirit_servings 0
# wine_servings 0
# total_litres_of_pure_alcohol 0
# continent 23
# dtype: int64
continent
라는 열에서 총 23개의 Null`(결측) 데이터가 있음을 확인할 수 있습니다.
02. 데이터프레임의 열 접근
앞서 사용했던 동일한 데이터프레임인 drink_df를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
데이터프레임의 열 접근
데이터프레임의 특정 열에 접근하는 가장 쉬운 방법은
데이터프레임의 이름.열의 이름
과 같은 방식으로 접근하는 것입니다. 다시 말해, 데이터프레임의 이름을 적고, 온점을 찍은 후에 열의 이름을 적으면 해당 열만을 불러옵니다.
drink_df.beer_servings
#
# 0 0
# 1 89
# 2 25
# 3 245
# 4 217
# ...
# 188 333
# 189 111
# 190 6
# 191 32
# 192 64
# Name: beer_servings, Length: 193, dtype: int64
또 다른 방법은 데이터프레임의 이름['해당 열의 이름']
과 같은 방법입니다. 이는 위와 동일한 결과를 출력합니다.
# 또 다른 방법
drink_df['beer_servings']
#
# 0 0
# 1 89
# 2 25
# 3 245
# 4 217
# ...
# 188 333
# 189 111
# 190 6
# 191 32
# 192 64
# Name: beer_servings, Length: 193, dtype: int64
2차원 테이블 형태를 데이터프레임이라고 부르지만, 이렇게 특정 하나의 열만을 불러올 경우에는 데이터프레임이 아니라 데이터 타입이 판다스(Pandas)에서 제공하는 또 다른 데이터 타입인 '시리즈(Series)'가 됩니다. 실제로 특정 열만 불러오고 타입을 확인해볼까요?
# 컬럼의 타입 확인
type(drink_df.beer_servings)
#
# pandas.core.series.Series
만약 하나의 열이 아니라 선택적으로 다수의 열에 접근려면 어떻게 하면 될까요?
데이터프레임의 이름[['특정열의 이름1', '특정열의 이름2']]
와 같은 방식으로 접근하면 됩니다. 예를 들어서 beer_servings
과 wine_servings
2개의 열만을 불러오고 싶다고 해봅시다.
drink_df[['beer_servings','wine_servings']]
두 개의 열만이 뽑힌 데이터프레임이 출력됩니다.
['특정 열의 이름1', '특정 열의 이름2']
은 파이썬 리스트 형태입니다.
다시 말해 파이썬 리스트의 형태로 열의 이름을 나열한 뒤에
데이터프레임의 이름[열의 이름이 나열된 파이썬 리스트]
를 사용해도 동일한 결과를 출력합니다.
cols = ['beer_servings','wine_servings']
drink_df[cols]
03. 특성의 수치 정보 파악하기
앞서 사용했던 동일한 데이터프레임인 drink_df를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
특성(features)
앞서 데이터프레임의 info()
를 설명한 적이 있습니다. info()
는 데이터프레임의 전반적인 정보를 보여줍니다.
drink_df.info()
#
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 193 entries, 0 to 192
# Data columns (total 6 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 country 193 non-null object
# 1 beer_servings 193 non-null int64
# 2 spirit_servings 193 non-null int64
# 3 wine_servings 193 non-null int64
# 4 total_litres_of_pure_alcohol 193 non-null float64
# 5 continent 170 non-null object
# dtypes: float64(1), int64(3), object(2)
# memory usage: 9.2+ KB
info()
를 통해 다음과 같은 정보를 알 수 있습니다.
해당 데이터는 다음과 같은 열들을 가지고 있습니다. 데이터 분석 용어로는 데이터를 파악하기 위한 이러한 열을 특성(feature) 이라고 부릅니다. 총 6개의 특성이 있는 셈입니다.
특성의 수치 정보 파악하기
이제 본격적으로 데이터프레임을 통해서 데이터를 파악해가는 과정을 시작해볼텐데요. 숫자와 같은 수치 데이터를 다루고 있다면, 해당 데이터의 최솟값, 최댓값, 평균값 등을 파악하는 것은 데이터 파악의 가장 첫 걸음입니다.
데이터가 데이터프레임 형태로 저장된 상황에서 이를 가장 빠르게 파악할 수 있는 방법은
describe()
를 사용하는 것입니다.
drink_df.describe()
describe()
는 데이터프레임의 총 데이터의 수(count), 평균(mean), 표준편차(std), 분위수(25%, 50%, 75%)를 파악하여 출력합니다. 하지만 이는 숫자. 즉, 수치 정보에 국한되어서 계산하므로 문자열 타입의 데이터였던 country 열과 continent 열은 제외되었습니다. 특정 열에 대해서만 출력해볼 수도 있습니다.
drink_df.beer_servings.describe()
#
# count 193.000000
# mean 106.160622
# std 101.143103
# min 0.000000
# 25% 20.000000
# 50% 76.000000
# 75% 188.000000
# max 376.000000
# Name: beer_servings, dtype: float64
특정 열의 최대값, 최소값, 평균값, 합계, 카운트도 계산가능합니다.
drink_df.beer_servings.mean()
#
# 106.16062176165804
drink_df.beer_servings.max()
#
# 376
drink_df.beer_servings.min()
#
# 0
drink_df.beer_servings.sum()
#
# 20489
drink_df.beer_servings.count()
#
# 193
이렇게 바로 계산한 수치를 뽑을 수 있다면, 이 수치를 가지고 계산도 가능하겠죠? mean()
으로 평균을 바로 구할 수 있긴 하지만, sum()
과 count()
를 이용하여 평균을 계산해봅시다.
float(drink_df.beer_servings.sum())/drink_df.beer_servings.count()
#
# 106.16062176165804
04. 조건부 로직
앞서 사용했던 동일한 데이터프레임인 drink_df
를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
조건부 로직
데이터프레임에 우리가 원하는 조건을 걸어서 해당 조건을 충족하는 값들만을 뽑아오는 것도 가능합니다. 우선 특정 열에 대해서 조건을 걸었을 때 어떤 값을 반환하는지를 봅시다.
데이터프레임의 이름.특정 열의 이름 == '특정값'
이라는 코드는 각 행에서 해당 조건을 만족하는지를 판단하여 만족한다면 True
, 아니라면 False
의 값을 가지는 시리즈(Series)를 리턴합니다.
drink_df.continent=='EU'
#
# 0 False
# 1 True
# 2 False# 3 True
# 4 False
# ...
# 188 False
# 189 False
# 190 False
# 191 False
# 192 False
# Name: continent, Length: 193, dtype: bool
하지만 일반적으로 저런 조건을 걸었을 때 우리가 원하는 건 True
와 False
로 구성된 시리즈(Series)가 아니라 continent의 값이 'EU'일 때의 데이터프레임 값을 뽑아내는 것일텐데요.
이 경우에는 저렇게 True
와 False
로 구성된 Series를 다음과 같이 사용해주면 됩니다.
데이터프레임의 이름[True와 False로 구성된 시리즈]
일단 drink_df.continent=='EU'
의 타입이 시리즈임을 확인합니다.
type(drink_df.continent=='EU')
#
# pandas.core.series.Series
이를 데이터프레임의 이름[ ]
의 대괄호 안에 넣어봅시다. head(20)
을 이용하여 상위 20개만 출력합니다.
drink_df[drink_df.continent=='EU'].head(20)
그렇다면 만약 beer_servings
의 값이 158보다 큰 경우만을 필터링하고 싶다면 어떨까요?
앞서 데이터프레임의 열을 호출하는 방법으로 다음과 같이 두 가지를 소개했습니다.
데이터프레임의 이름.열의 이름
데이터프레임의 이름['열의 이름']
다시 말해 beer_servings
의 값이 158보다 큰 경우로 필터링하는 코드는 아래의 두 가지 방법이 있습니다.
drink_df[drink_df.beer_servings > 158]
drink_df[drink_df['beer_servings'] > 158]
# 아래 두 가지는 동일함
# drink_df[drink_df.beer_servings > 158]
# drink_df[drink_df['beer_servings'] > 158]
drink_df[drink_df['beer_servings'] > 158].head(20)
그렇다면 조건을 걸되, 특정 열 몇 개만 출력하고 싶다면 어떨까요?
앞서 특정 열들만을 뽑아서 출력하는 방법을 아래와 같이 소개했었습니다.
데이터프레임의 이름[['특정 열의 이름1, '특정 열의 이름2']]
이를 응용하여 beer_servings
의 값이 10 이하이면서 country
, beer_servings
의 두 개의 열만을 뽑아내는 방법은 다음과 같습니다.
drink_df[drink_df.beer_servings <= 10][['country','beer_servings']]
우선, drink_df[drink_df.beer_servings <= 10]
로 조건에 맞는 데이터프레임을 뽑아낸 뒤에 이 데이터프레임에 [['country','beer_servings']]
를 붙여서 2개의 열만을 뽑아내는 것이죠.
# drink_df[drinks.beer_servings <= 10]는 데이터프레임이다.
drink_df[drink_df.beer_servings <= 10][['country','beer_servings']].head(20)
이번에는 조건으로 필터링하여 시리즈(Series)를 얻어낸 뒤에 평균(mean)을 얻어보겠습니다.
drink_df[drink_df.continent=='EU'].beer_servings.mean()
#
# 193.77777777777777
위 코드는 continent의 값이 'EU'인 경우로 데이터프레임을 뽑아내지만, 그 중 beer_serving
s라는 하나의 열만을 뽑아내므로 (앞서 열을 하나만 뽑을 경우 데이터프레임이 아니라 시리즈가 된다고 언급한 바 있습니다.) 시리즈로 변환되며, 시리즈에 대해서 mean()
을 사용하여 평균값을 뽑아내게 됩니다. 결과적으로 continent의 값이 EU인 beer_servings
열의 평균값(mean)을 구하게 되는 것이죠.
이번에는 beer_servings
열의 평균값을 조건에 넣어보겠습니다.
drink_df[drink_df.beer_servings > drink_df.beer_servings.mean()]
해당 열에 null
값(결측값)이 존재하는지 유무에 대한 True와 False의 시리즈(Series)로 얻어내는 방법은 해당 열의 isnull()
을 하면 알 수 있습니다. isnull()
은 해당 행에 Null 값이 들어있는 경우만 True
를 리턴하고, 아니라면 False
를 리턴합니다.
drink_df.continent.isnull()
#
# 0 False
# 1 False
# 2 False
# 3 False
# 4 False
# ...
# 188 False
# 189 False
# 190 False
# 191 False
# 192 False
# Name: continent, Length: 193, dtype: bool
#
drink_df.continent.isnull().sum()
#
# 23
이를 이용하면 데이터프레임에서 특정 열에 Null 값이 위치한 경우만 뽑을 수 있습니다.
drink_df[drink_df.continent.isnull()]
len(drink_df[drink_df.continent.isnull()])
#
# 23
continent
의 값에 NaN
값(결측값을 의미)인 경우만 출력되는 것을 확인할 수 있습니다. 즉, 이 데이터들은 다른 열에는 값이 다 존재하지만 continent
의 열에는 실질적으로 값이 존재하지 않는다고 해석할 수 있습니다.
05. AND
, OR
, NOT
연산자 사용하기
앞서 사용했던 동일한 데이터프레임인 drink_df
를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
AND
, OR
, NOT
연산자 사용하기
AND
나 OR
또는 NOT
과 같은 연산자를 사용하여 여러 개의 조건을 동시에 사용하거나, 조건문 자체를 반대로 해석하도록 할 수도 있습니다.
데이터프레임에서는 AND, OR, NOT은 각각 &
, |
, ~
로 표현합니다.
&
: AND|
: OR~
: NOT
다시 말해 다음과 같이 사용할 수 있습니다.
A조건 & B조건
: A조건과 B조건 모두 만족하는 경우A조건 | B조건
: A조건 또는 B조건 둘 중 하나를 만족하는 경우~A조건
: A조건을 만족하는 경우의 반대. 즉, A조건을 만족하지 않는 경우.
물론 실제로는 조건이 2개 이상일 수도 있고, 이들을 섞어서 사용하는 것도 가능하므로 위 케이스보다 다양하게 사용할 수 있습니다. 여기서는 가장 기본적인 위의 세 가지 경우에 대해서만 다뤄보겠습니다.
우선 NOT을 사용한 경우를 봅시다.
drink_df[~(drink_df.continent=='EU')]
위 코드는 continent의 값이 'EU'가 아닌 경우만을 필터링하는 코드입니다.
이번에는 두 개의 조건 모두가 참인 경우만을 필터링하는 AND를 사용해봅시다.
drink_df[(drink_df.continent=='EU') & (drink_df.wine_servings > 300)]
위 코드는 continent
의 값이 'EU'이면서 wine_servings
의 값이 300보다 큰 경우만을 필터링합니다. 두 개의 조건을 만족하는 행은 193개의 행 중 단 3개뿐입니다.
하지만 두 가지 조건을 모두 만족하는 것이 아니라 단, 한 개만 만족하더라도 데이터를 뽑으려고 한다면 AND
를 단지 OR
로 바꿔주기만 하면 됩니다. 두 개의 조건은 같지만 AND
를 단순히 OR
로 바꾼 경우에는 데이터가 몇 개나 뽑히는지 확인해봅시다.
len(입력)
을 사용하면 입력의 길이를 계산하여 알려줍니다. 데이터프레임의 이름을 입력으로 할 경우에는 데이터프레임의 행의 길이를 출력합니다.
# OR 조건
len(drink_df[(drink_df.continent=='EU') | (drink_df.wine_servings > 300)])
#
# 45
AND
조건을 사용하였을 경우에는 행이 3개밖에 없었지만, 이번에는 45개로 훨씬 많습니다.
06. 로직과 수치 정보의 결합 & 정렬
앞서 사용했던 동일한 데이터프레임인 drink_df
를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
로직과 수치 정보의 결합
drink_df
에서 total_litres_of_pure_alchohol
의 값이 최대값인 경우의 country
열을 출력한다면?
- 특정 열의 최대값을 구하는 방법이 무엇이었는지 생각해봅시다.
- 특정 열만을 출력하는 방법이 무엇이었는지 생각해봅시다.
# 어떤 나라가 total litres of pure alcohol의 값이 가장 클까요?
drink_df[drink_df.total_litres_of_pure_alcohol == drink_df.total_litres_of_pure_alcohol.max()]['country']
#
# 15 Belarus
# Name: country, dtype: object
우선 drink_df.total_litres_of_pure_alcohol.max()
를 통해서 total_litres_of_pure_alcohol
열의 최댓값을 구하고, 이를 로직에 적용하여 출력할 수 있습니다.
이번에는 drink_df
에서 wine_servings
의 값이 300보다 크거나, beer_servings
의 값이 300보다 크거나, spirit_servings
의 값이 300보다 큰 경우의 country
열의 데이터를 모두 카운트하였을 때의 숫자를 출력해봅시다. 이를 구현하기 위해서 고려해야할 것은 총 두 가지입니다.
- 다수의 조건을 '또는'으로 한 번에 사용하는 방법이 무엇이었는지 생각해봅시다.
- 숫자를 '카운트' 하는 방법이 무엇이었는지 생각해봅시다.
# 이번에는 drink_df에서 wine_servings의 값이 300보다 크거나,
# beer_servings의 값이 300보다 크거나,
# spirit_servings의 값이 300보다 큰 경우를 모두 카운트하였을 때의 숫자를 출력해봅시다.
drink_df[(drink_df.wine_servings > 300) | (drink_df.beer_servings > 300) | (drink_df.spirit_servings > 300)].country.count()
#
# 18
정렬
데이터를 특정 기준으로 정렬해서 볼 수도 있습니다.
정렬해서 보는 방법은 다음과 같습니다.
데이터프레임의 이름.sort_values('정렬 기준이 되는 열의 이름')
drink_df.sort_values('beer_servings') # 특정 컬럼을 기준으로 정렬
beer_servings
의 열을 보면 오름차순을 기준으로 정렬된 것을 확인할 수 있습니다. 기본적으로는 오름차순으로 정렬되지만, 만약 내림차순으로 정렬하고 싶다면 데이터프레임의 이름.sort_values('정렬 기준이 되는 열의 이름', ascending=False)
sort_values
의 인자로 ascending=False
를 추가해주면 됩니다.
# 내림차순으로 정렬
drink_df.sort_values('beer_servings', ascending=False)
정렬의 기준은 하나의 열이 아니라 다수의 열일 수 있습니다. 열의 이름들을 원소로하는 리스트를 sort_values
의 입력으로 사용하면 해당 열들을 기준으로 정렬을 수행합니다.
# 2개의 컬럼 기준으로 정렬
drink_df.sort_values(['beer_servings', 'wine_servings'])
07. 상관 관계 분석
상관 분석 이란 두 변수 간의 선형적 관계를 상관 계수로 표현하는 것을 말합니다. 상관 계수를 구하는 것은 공분산의 개념을 포함하는데, 공분산은 2개의 변수에 대한 상관 정도. 2개의 변수 중 하나의 값이 상승하는 경향을 보이면 다른 값도 상승하는 경향을 수치로 표현한 것입니다. 하지만 공분산만으로 두 확률 변수의 상관 관계를 구한다면 두 변수의 단위 크기에 영향을 받을 수 있습니다. 따라서 이를 -1과 1 사이 값으로 변환합니다. 이를 상관 계수라 합니다.
만약 상관 계수가 1에 가깝다면 서로 강한 양의 상관 관계가 있는 것이고, -1에 가깝다면 음의 상관 관계가 있는 것입니다. 0이면 상관 관계가 없습니다.
Matplotlib
는 파이썬에서 자료를 차트나 플롯으로 시각화하는 패키지입니다. Seaborn
은 Matplotlib
을 기반으로 다양한 테마와 기능을 추가한 시각화 패키지입니다. 데이터를 시각화하기위해서 Matplotlib만을 사용할 수도 있고, Seaborn을 사용할 수도 있으며, 이 두 가지를 함께 사용할 수도 있습니다.
상관관계 분석
상관관계 분석 실습을 위해 임의로 데이터프레임을 하나 만들어봅시다.
import pandas as pd # 판다스 임포트.
import matplotlib.pyplot as plt # 맷플롯립 임포트.
import seaborn as sns # 시본 임포트
다수의 파이썬 리스트를 통해 데이터프레임을 만드는 방법은 다음과 같습니다.
pd.DataFrame({"열이름1":리스트1, "열이름2":리스트2, "열이름3":리스트3})
test_df = pd.DataFrame({"v1":[100,200,300,400], "v2":[400,200,100,250], "v3":[40,60,60,100]})
test_df
| . | v1 | v2 | v3 | | :--- | ---: | ---: ---: | | 0 | 100 | 400 | 40 | | 1 | 200 | 200 | 60 | | 2 | 300 | 100 | 60 | | 3 | 400 | 250 | 10 |
데이터의 상관계수를 구하는 방법은 다음과 같습니다.
데이터프레임의 이름.corr(method='pearson')
corr = test_df.corr(method = 'pearson')
corr
| . | v1 | v2 | v3 | | :--- | ---: | ---: ---: | | v1 | 1.000000 | -0.568038 | 0.923381 | | v2 | -0.568038 | 1.000000 | -0.291397 | | v3 | 0.923381 | -0.291397 | 1.000000 |
히트맵 그리기
데이터프레임에서 .values
를 하게되면 데이터프레임의 각 행이 마치 리스트 형태로 변환되어서 출력이 됩니다. 이를 뒤에 차트를 그릴 때, 입력으로 사용할 것입니다.
corr.values
#
# array([[ 1. , -0.56803756, 0.92338052],
# [-0.56803756, 1. , -0.29139712],
# [ 0.92338052, -0.29139712, 1. ]])
차트에 이름을 입력하기 위해서 다음과 같이 column_names
라는 리스트를 만듭니다.
column_names = ['ver1', 'ver2', 'ver3']
일반적으로 상관계수 차트를 그릴 때는 seaborn
에서 제공하는 heatmap()
을 주로 사용합니다. seaborn
을 sns
이라는 이름으로 임포트하였다면, 기본적인 사용 방법은 다음과 같습니다.
sns.heatmap(데이터프레임의 상관계수 데이터)
아래 코드에 heatmap
에서 사용하는 각종 추가적인 설정값에 대해서 주석을 달아두었습니다. 이 설정값들은 필수적인 입력이 아니며, 좀 더 예쁘게 차트를 그리기 위해서 여러분들이 추가적으로 조작하는 것들입니다.
# 레이블의 폰트 사이즈를 조정
sns.set(font_scale=2.0)
test_heatmap = sns.heatmap(corr.values, # 데이터
cbar = False, # 오른쪽 컬러 막대 출력 여부
annot = True, # 차트에 숫자를 보여줄 것인지 여부
annot_kws={'size' : 20}, # 숫자 출력 시 숫자 크기 조절
fmt = '.2f', # 숫자의 출력 소수점자리 개수 조절
square = 'True', # 차트를 정사각형으로 할 것인지
yticklabels=column_names, # y축에 컬럼명 출력
xticklabels=column_names) # x축에 컬럼명 출력
plt.tight_layout()
plt.show()
이번에는 cbar
를 True
로 해봅시다.
# 레이블의 폰트 사이즈를 조정
sns.set(font_scale=2.0)
test_heatmap = sns.heatmap(corr.values,
cbar = True,
annot = True,
annot_kws={'size' : 20},
fmt = '.2f',
square = True,
yticklabels=column_names,
xticklabels=column_names)
plt.tight_layout()
plt.show()
각종 설정값이 어떻게 동작하는지 가장 쉽게 알 수 있는 방법은 해당 설정값을 지워서 재출력해보는 것입니다! 예를 들어서 fmt = '.2f'
의 값을 fmt = '.3f'
으로 바꿔서 재실행해보세요. 그러면 차트 위의 숫자가 소수점 셋 째자리까지 출력 될 것입니다.
다른 설정값에 대해서도 여러분들이 자유자재로 바꿔서 출력해보세요.
상관 분석을 시각화 할 수 있는 또 다른 방법을 산점도(scatter plot) 를 그리는 것입니다. 산점도는 좌표상에 점들을 표시하는 방법으로 두 개 변수 간의 관계를 나타내는 그래프 방법입니다. 변수 A가 증가할 때 변수 B 또한 증가하는 어떤 상관 관계가 있는지, 아니면 아무런 관계가 없는지 산점도를 통해서 확인해 볼 수 있습니다.
seaborn
을 sns
란 이름으로 임포트하였다면, 산점도를 그리는 기본 방법은 다음과 같습니다. pairplot
은 각 열의 조합에 대해서 산점도를 그리고, 같은 데이터가 만나는 대각선 영역에는 해당 데이터의 히스토그램을 그립니다.
sns.pairplot(데이터프레임)
# whitegrid = 배경에 하얀 선을 넣는다.
sns.set(style='whitegrid')
sns.pairplot(test_df)
plt.show()
지금은 데이터가 너무 적어서 산점도를 해석하기에는 무리가 있습니다. 산점도의 해석은 주류 데이터에 대해서 진행해보겠습니다!
08. 응용! 상관 관계 분석과 산점도
앞서 사용했던 동일한 데이터프레임인 drink_df
를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
주류 데이터의 상관 계수
주류 데이터의 각 특성(feature
)에 대해서 상관계수를 계산하고, 이를 seaborn
의 heatmap
과 pairplot
을 통해서 시각화해봅시다! 우선, 'beer_servings
', 'wine_servings
' 두 특성 간의 상관계수를 계산해보겠습니다.
# 'beer_servings', 'wine_servings' 두 피처간의 상관계수를 계산합니다.
# pearson은 상관계수를 구하는 계산 방법 중 하나를 의미하며, 가장 널리 쓰이는 방법입니다.
corr = drink_df[['beer_servings', 'wine_servings']].corr(method = 'pearson')
corr
주류 데이터의 각 특성(feature
)에 대해서 상관계수를 계산하고, 이를 seaborn
의 heatmap과 pairplot
을 통해서 시각화해봅시다! 우선, 'beer_servings
', 'wine_servings
' 두 특성 간의 상관계수를 계산해보겠습니다.
# 피처간의 상관계수 행렬을 구합니다.
cols = ['beer_servings', 'spirit_servings', 'wine_servings', 'total_litres_of_pure_alcohol']
corr = drink_df[cols].corr(method = 'pearson')
corr
가장 상관계수 값이 높은 경우는 beer_servings
와 total_litres_of_pure_alcohold
의 상관계수 값으로 0.835839에 해당됩니다. 이를 seaborn
의 heatmap
을 통해서 시각화해봅시다. 우선 heatmap
의 입력을 만들기 위해서 상관계수값에 .values
를 적용시켜줍니다.
corr.values
#
# array([[1. , 0.45881887, 0.52717169, 0.83583863],
# [0.45881887, 1. , 0.19479705, 0.65496818],
# [0.52717169, 0.19479705, 1. , 0.66759834],
# [0.83583863, 0.65496818, 0.66759834, 1. ]])
히트맵 차트의 x
축과 y
축에 각각의 레이블을 달아주기 위해서 다음의 리스트를 만들어줍니다.
column_names = ['beer', 'spirit', 'wine', 'alcohol']
seaborn
의 heatmap
시각화를 진행해봅시다!
import seaborn as sns
import matplotlib.pyplot as plt
# 레이블의 폰트 사이즈를 조정
sns.set(font_scale=1.5)
hm = sns.heatmap(corr.values,
cbar=True,
annot=True,
square=True,
fmt='.2f',
annot_kws={'size': 15},
yticklabels=column_names,
xticklabels=column_names)
plt.tight_layout()
plt.show()
cbar
는 오른쪽에 있는 막대(범주)를 표시하는 것입니다.annot
는 상관계수를 표시합니다.square
는 정사각형으로 지정하는 것이며, False로 설정 시 직사각형이 됩니다.fmt
는 상관계수의 소수점 자리수를 지정합니다.annot_kws
는 상관계수의 글자 크기를 지정합니다.yticklabels
는 y축 레이블 값을 의미합니다.xticklabels
는 x축 레이블 값을 의미합니다.
alcohol
은 대체적으로 다른 특성들과 모두 상관 계수가 높습니다. 이미 시각화를 진행하기 전에도 확인한 내용이기는 하지만, 그 중에서도 0.84로 beer
와 상관 계수값이 가장 높습니다. 앞서 확인하였을 때는 0.835839였지만, 여기서는 소수점 두 자리까지만 출력하면서 반올림되어 0.84로 출력된 것이라 보면 됩니다.
피어슨의 상관계수는 일반적으로,
- 값이 -1.0 ~ -0.7 이면, 강한 음적 상관관계 값이 -0.7 ~ -0.3 이면, 뚜렷한 음적 상관관계
- 값이 -0.3 ~ -0.1 이면, 약한 음적 상관관계
- 값이 -0.1 ~ +0.1 이면, 없다고 할 수 있는 상관관계
- 값이 +0.1 ~ +0.3 이면, 약한 양적 상관관계
- 값이 +0.3 ~ +0.7 이면, 뚜렷한 양적 상관관계
- 값이 +0.7 ~ +1.0 이면, 강한 양적 상관관계 로 해석됩니다.
산점도
주류 데이터의 산점도. 즉, pairplot
을 확인해봅시다. pairplot
은 데이터프레임을 인수로 받아 그리드(grid
) 형태로 각 데이터 열의 조합에 대해 산점도를 그립니다. 같은 데이터가 만나는 대각선 영역에는 해당 데이터의 히스토그램을 그립니다.
# 시각화 라이브러리를 이용한 피처간의 scatter plot을 출력합니다.
sns.set(style='whitegrid')
sns.pairplot(drink_df[['beer_servings', 'spirit_servings',
'wine_servings', 'total_litres_of_pure_alcohol']])
plt.show()
09. 데이터 탐색하기 (결측값 제거, 시각화, 통계 확인)
앞서 사용했던 동일한 데이터프레임인 drink_df를 사용합니다.
데이터 로드
import pandas as pd
url = 'https://raw.githubusercontent.com/justmarkham/DAT8/master/data/drinks.csv'
drink_df = pd.read_csv(url, ',')
drink_df
결측값 제거
결측값은 탐색적 데이터 분석에서도, 그 후 더 나아가 머신 러닝 알고리즘을 통해 분석을 할 때에도 성능에 영향을 줄 수 있는 값입니다. 결측값은 아예 제거를 해주거나, 특정 값으로 채워주거나 크게 두 가지 선택을 해줄 수 있습니다.
제거할 때는 주로 dropna()
를 쓰는 반면, 채워줄 때는 주로 fillna()
를 사용합니다. 앞서 continent
열에 결측값이 23개가 있는 것을 확인했었습니다. 여기서는 특정값으로 채우는 fillna()
를 사용해보겠습니다.
# 결측 데이터를 특정값으로 채우는 방법은 .fillna()를 사용하는 것이다.
# 이 경우 기타 값이라는 의미에서 'ETC'를 넣어준다.
drink_df['continent'] = drink_df['continent'].fillna('ETC')
drink_df.sample(10)
continent
열에 결측값이 있을 경우 'ETC'라는 값으로 채우도록 했습니다. 채우고 나서 랜덤으로 10개를 출력하도록 해보았는데, ETC란 값이 중간에 보입니다. 이제 결측값이 사라졌는지 앞서 배운 코드인 isnull().sum()
으로 확인해봅시다.
drink_df.isnull().sum()
#
# country 0
# beer_servings 0
# spirit_servings 0
# wine_servings 0
# total_litres_of_pure_alcohol 0
# continent 0
# dtype: int64
이제 continent
열에는 결측값이 존재하지 않습니다.
파이차트 그리기
파이 차트를 그리려면
- 파이 차트로 사용할 데이터의 이름이 담긴 리스트,
- 그리고 해당 이름에 해당하는 데이터의 값이 담긴 리스트
이 두 가지가 필요합니다. 여기서는 continent
에 대해서 파이차트를 그려보고자 합니다. 파이 차트의 값으로는 continent
에 있는 각 값들의 개수를 사용하고자 하구요. continent
열에 대해서 value_counts()
를 사용하였을 때의 결과를 출력해보고, 타입을 확인해봅시다.
drink_df['continent'].value_counts()
#
# AF 53
# EU 45
# AS 44
# ETC 23
# OC 16
# SA 12
# Name: continent, dtype: int64
type(drink_df['continent'].value_counts())
#
# pandas.core.series.Series
인덱스에는 continents
의 이름, 그리고 실제 값 부분에는 해당 continents
에 있는 값들을 각각 카운트한 값이 들어가있습니다. 인덱스는 index
로 접근하고 값에 대해서는 values
로 접근하므로 각각 접근 후에 리스트로 값을 변환하는 tolist()
를 사용해줍니다.
pie_labels = drink_df['continent'].value_counts().index.tolist()
pie_values = drink_df['continent'].value_counts().values.tolist()
print(pie_labels)
print(pie_values)
#
# ['AF', 'EU', 'AS', 'ETC', 'OC', 'SA']
# [53, 45, 44, 23, 16, 12]
이제 파이 차트를 그리기 위해 필요한 두 가지를 얻었습니다. 파이 차트를 그리는 방법은 plt.pie()
를 사용합니다. autopct
는 소수점을 몇 개까지 출력할지를 정하기 위한 값입니다.
사용 방법은 다음과 같습니다.
plt.pie(데이터의 실질적인 값, labels=데이터의 레이블 리스트)
autopct
는 소수점을 몇 개까지 출력할지를 정하기 위한 값입니다.
plt.pie(pie_values, labels=pie_labels, autopct='%.02f%%')
plt.title('Percentage of each continent')
plt.show()
GroupBy
를 이용한 통계 확인
GroupBy
는 특정 값을 기준으로 그룹핑한 후에 그룹 별로 통계적인 수치 정보를 구할 수 있도록 하는 방법입니다. 사용 방법은 다음과 같습니다.
데이터프레임의 이름.groupby('그룹핑 기준이 되는 열')['보고자 하는 열'].통계 함수
GroupBy
를 통해서 수치 정보를 계산해봅시다.
- 어떤 대륙이 평균적으로 맥주를 더 먹을까?
대륙에 대한 열은 continent
에 있습니다. 그리고 맥주의 소비량은 beer_servings
열입니다.
평균을 구하는 함수는 mean()
이므로 다음과 같이 사용합니다.
어떤 대륙이 평균적으로 맥주를 더 먹을까요?
drink_df.groupby('continent')['beer_servings'].mean()
#
# continent
# AF 61.471698
# AS 37.045455
# ETC 145.434783
# EU 193.777778
# OC 89.687500
# SA 175.083333
# Name: beer_servings, dtype: float64
type(drink_df.groupby('continent')['beer_servings'].mean())
#
# pandas.core.series.Series
각 대륙 별로 와인 소비에 대한 통계 정보를 출력해볼까요?
drink_df.groupby('continent')['wine_servings'].describe()
모든 컬럼에 대해서 대륙별로 평균 알콜 소비량을 출력해볼까요
drink_df.groupby('continent').mean()
전체 평균보다 많은 알코올을 섭취하는 대륙을 구해봅시다.
total_mean = drink_df.total_litres_of_pure_alcohol.mean()
continent_mean = drink_df.groupby('continent')['total_litres_of_pure_alcohol'].mean()
continent_over_mean = continent_mean[continent_mean >= total_mean]
print(continent_over_mean)
#
# continent
# ETC 5.995652
# EU 8.617778
# SA 6.308333
# Name: total_litres_of_pure_alcohol, dtype: float64
평균 wine_servings
이 가장 높은 대륙을 구해봅시다.
beer_continent = drink_df.groupby('continent').wine_servings.mean().idxmax()
print(beer_continent)
10. Quiz
Quiz 1
sklearn
은 머신 러닝 패키지로 각종 머신 러닝 데이터셋도 제공하고 있습니다. 이번 퀴즈에서 사용할 머신 러닝 데이터셋은 iris data(붓꽃 데이터)입니다.
# 퀴즈에 필요한 패키지는 아래의 두 가지가 전부가 아닙니다.
# 여러분들의 필요에 따라서 패키지를 지속 추가하셔도 됩니다.
import pandas as pd
from sklearn import datasets
# 붓꽃 데이터 로드
iris_data = datasets.load_iris()
iris
데이터셋의 각 열은 다음과 같습니다.
- Sepal Length : 꽃받침의 길이 정보
- Sepal Width : 꽃받침의 너비 정보
- Petal Length : 꽃잎의 길이 정보
- Petal Width : 꽃잎의 너비 정보
이러한 4개의 특성을 알려줄테니
꽃의 종류를 예측해보라는 것이 iris 데이터셋이 제시하는 머신러닝 문제입니다.
- Species 꽃의 종류 정보 : setosa / versicolor / virginica 의 3종류.
우선 4개의 특성에 해당되는 데이터를 df_data에 로드합니다.
df_data = pd.DataFrame(iris_data['data'], columns=iris_data['feature_names'])
df_data.sample(5)
4개의 열이 출력되는데, 각각 꽃받침의 길이 정보, 꽃받침의 너비 정보, 꽃잎의 길이 정보, 꽃잎의 너비 정보에 해당됩니다. iris 데이터(붓꽃 데이터)는 이 4개의 특성만 보고 이 꽃이 어떤 품종의 붓꽃인지를 예측해야하는 머신 러닝 문제를 위한 데이터셋입니다. 정답에 해당되는 레이블을 df_target에 로드합니다.
df_target = pd.DataFrame(iris_data['target'], columns=['species'])
df_target.sample(5)
이 시리즈에는 어떤 값들이 있는지 출력해봅시다.
# 값의 종류를 전부 출력
df_target['species'].unique()
array([0, 1, 2])
숫자가 0, 1, 2가 나오는데 각각이 의미하는 품종은 다음과 같습니다.
- 0 = setosa
- 1 = versicolor
- 2 = virginica
pd.concat()
을 사용하면 위 데이터프레임과 시리즈를 하나의 데이터프레임으로 합치는 것이 가능합니다.
df = pd.concat([df_data, df_target], axis=1)
df
이렇게 150개의 행과 5개의 열을 가진 붓꽃 데이터가 데이터프레임에 로드된 상태입니다.
총 150개의 붓꽃이 있는데, 종류는 3개였고,
각 붓꽃의 꽃받침의 길이와 너비, 꽃잎의 길이와 너비를 기록한 데이터셋인 것이죠.
여러분들의 퀴즈는 이 붓꽃 데이터를 가지고 데이터프레임의 기능(조건부 필터링, 상관 계수 분석 등), Matplotlib, Seaborn을 사용하여 각종 통계 정보를 구하고, 시각화를 해보는 것입니다. 단 하나의 정답이 존재하는 것은 아닙니다. 여러분들이 배운 내용들을 복습하여 최대한 성실하게 퀴즈를 수행해보세요.