기본 설정
numpy와 pandas 라이브러리를 각각 np와 pd로 불러온다.
import numpy as np
import pandas as pd데이터프레임의 chained indexing을 금지시키기 위한 설정을 지정한다. Pandas 3.0 버전부터는 기본 옵션으로 지정된다.
pd.options.mode.copy_on_write = True주피터 노트북을 사용하고 있다면 아래 명령어(파이썬 코드 아님)를 통해 부동소수점의 출력을 소수점 이하 6자리로 제한한다.
%precision 6'%.6f'아래 코드는 데이터프레임 내에서 부동소수점의 출력을 소수점 이하 6자리로 제한한다.
pd.set_option('display.precision', 6)앞으로 사용할 데이터셋들의 기본 저장소를 지정한다.
data_url = 'https://raw.githubusercontent.com/codingalzi/statsRev/refs/heads/master/data/'18.1데이터셋 불러오기¶
body_info = pd.read_csv("../data/body_info.csv", index_col='ID')body_infobody_info.info()<class 'pandas.core.frame.DataFrame'>
Index: 1000 entries, F001 to M500
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Sex 1000 non-null object
1 Age 1000 non-null float64
2 Height 1000 non-null float64
3 Weight 1000 non-null float64
4 Body fat percentage 1000 non-null float64
dtypes: float64(4), object(1)
memory usage: 46.9+ KB
body_info['Sex'].value_counts()Sex
F 500
M 500
Name: count, dtype: int6450명의 영어와 수학 점수를 담고 있는 csv 파일을 데이터프레임으로 불러온다. 이때 학생번호를 인덱스로 지정한다.
df = pd.read_csv(data_url+'ch02_scores_em.csv',
index_col='student number')
dfhead()/tail() 메서드
head()/tail() 메서드에 정수 인자를 지정하면 지정된 개수의 샘플로 구성된 데이터프레임이 생성된다. 인자를 입력하지 않으면 기본값인 5가 실행된다. 따라서 처음 5명과 마지막 5명의 점수는 다음과 같이 확인할 수 있다.
df.head()df.tail()18.2넘파이 어레이 활용¶
처음 열 명의 이름을 A, B, C, ..., J로 지정하면서 영어 점수로만 구성된 데이터프레임을 지정해 보자.
먼저 처음 10명의 영어 점수로 구성된 넘파이 어레이를 생성한다.
first10english = df['english'].head(10)
scores = np.array(first10english)
scoresarray([42, 69, 56, 41, 57, 48, 65, 49, 65, 58])그런 다음, 이 어레이를 이용해 열 명의 이름을 A, B, C, ..., J로 지정한 새로운 데이터프레임을 생성한다.
scores_df = pd.DataFrame({'score':scores},
index=pd.Index(['A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J'],
name='student'))
scores_df18.3데이터 대표값¶
주어진 데이터셋을 대표하는 값으로 평균값, 중앙값, 최빈값이 가장 많이 사용된다. 각 대표값들의 의미와 파이썬을 이용해 계산하는 여러가지 방법들에 대해서 살펴보자.
평균값
평균값은 데이터를 모두 더한 뒤, 데이터의 개수로 나눈 값을 말한다. 데이터셋이 개의 데이터 로 이루어져 있다면 이 데이터셋의 평균값 는 다음과 같이 구한다.
예를 들어, 처음 10명 학생의 영어 점수의 평균값은 다음과 같고, 위에서 생성한 넘파이 어레이 scores 또는 데이터프레임 scores_df를 이용해 3가지 방식으로 계산할 수 있다.
파이썬
mean = sum(scores) / len(scores)
print("평균값:", mean)평균값: 55.0
넘파이 어레이의
mean()메서드 활용
np.mean(scores)55.000000데이터프레임의
mean()메서드 활용
scores_df.mean()score 55.0
dtype: float64중앙값
중앙값은 데이터를 크기 순서대로 나열할 때 정확히 중앙에 위치한 값이다.
따라서 중앙값을 구하려면 먼저 값들을 크기순으로 정렬해야 한다. 이때 넘파이 어레이의 sort() 메서드를 사용하면 편리하다.
sorted_scores = np.sort(scores)
sorted_scoresarray([41, 42, 48, 49, 56, 57, 58, 65, 65, 69])정렬한 후에 데이터 개수 n이 짝수인지 홀수인지에 따라 중앙값을 지정한다.
n이 홀수일 때: (n+1)/2 번째 데이터
n이 짝수일 때: n/2 번째 데이터와 (n/2) + 1 번째 데이터의 평균값
따라서 처음 10명의 영어 점수의 중앙값을 계산하는 방법은 다음과 같다.
파이썬
n = len(sorted_scores)
if n % 2 == 0:
m0 = sorted_scores[n//2 - 1]
m1 = sorted_scores[n//2]
median = (m0 + m1) / 2
else:
median = sorted_scores[(n+1)//2 - 1]
print("중앙값:", median)중앙값: 56.5
넘파이 어레이의
median()메서드 활용
np.median(scores)56.500000데이터프레임의
median()메서드 활용
scores_df.median()score 56.5
dtype: float64최빈값
최빈값은 데이터에서 가장 많이 나타나는 값이다.
10명의 영어 점수에서 65가 두 번 나타나며 나머지 값은 한 번만 사용되었으므로, 65가 최빈값이다.
데이터프레임의 mode() 메서드를 이용하여 최빈값을 쉽게 확인할 수 있다.
scores_df.mode()18.4데이터 분포¶
편차
각 데이터가 평균으로부터 떨어져 있는 정도를 편차deviation라 한다. 즉, 편차는 데이터들이 평균값으로부터 떨어져 있는 정도를 나타내는 값이다.
앞에서 10명 학생의 영어 점수의 평균값을 구했으므로, 각 학생의 성적 편차는 다음과 같이 계산된다.
mean = np.mean(scores)
print('scores의 평균값:', mean)
deviation = scores - mean
deviationscores의 평균값: 55.0
array([-13., 14., 1., -14., 2., -7., 10., -6., 10., 3.])평균값이 같은 데이터라 하더라도 편차는 많이 다를 수 있다. 예를 들어 다음 10명의 편차는 이전 10명의 편차보다 상대적으로 많이 작다.
another_scores = [50, 60, 58, 54, 51, 56, 57, 53, 52, 59]
another_mean = np.mean(another_scores)
print('another_scores의 평균값:', another_mean)
another_deviation = another_scores - another_mean
another_deviationanother_scores의 평균값: 55.0
array([-5., 5., 3., -1., -4., 1., 2., -2., -3., 4.])두 데이터셋의 편차를 각각 그림으로 나타내면 another_scores에 포함된 데이터들의 편차가 훨씬 작음을
알 수 있다.
scores데이터들의 편차

another_scores데이터들의 편차

데이터프레임을 이용하여 동일한 결과를 확인할 수 있다.
아래 코드는 scores_df 데이터프레임을 복제해서 새로
생성한 데이터프레임에 편차 데이터를 추가하는 방법을 보여준다.
summary_df = scores_df.copy()
summary_df['deviation'] = deviation
summary_dfmean() 메서드를 사용하면 각 열의 평균값을 출력하므로, 영어 점수의 평균값과 편차들의 평균값을 확인할 수 있다. 편차의 평균값은 항상 0 임에 주의한다.
summary_df.mean()score 55.0
deviation 0.0
dtype: float64분산
데이터들의 편차는 항상 음수와 양수가 섞여 평균값이 0이 된다. 데이터들이 평균값을 기준으로 얼마나 떨어져 있는지를 나타내는 측정값으로는 적절치 않다. 이러한 용도로 쓰이는 값이 바로 지금부터 소개할 분산variance과 표준편차standard deviation이다.
분산variance는 편차의 제곱의 평균값이며, 계산식은 다음과 같다.
예를 들어 scores에 포함된 학생 10명의 영어 점수의 분산은 다음과 같고, 편차의 제곱에 mean() 메서드를 적용해 구할 수 있다.
np.mean(deviation ** 2)86.000000넘파이의 var()와 데이터프레임의 var() 메서드를 활용해 구해도 된다. 하지만 데이터프레임을 이용할 때는 ddof=0 키워드 인자를 사용해 (편향된) 표본분산sample variance을 계산토록 해야한다.
np.var(scores)86.000000scores_df.var(ddof=0)score 86.0
dtype: float64ddof 키워드 인자를 생략하면 ddof=1로 지정된 기본 옵션이 실행되며
불편분산unbiased variance, 즉 편향되지 않은 표본분산을 계산한다. 편향분산biased variance과 불편분산의 구분은 잠시 후에 설명한다.
scores_df.var()score 95.555556
dtype: float64scores_df.var(ddof=0)score 86.0
dtype: float64한편, np.var() 함수 또한 ddof 키워드 인자를 사용할 수 있는데, 기본값은 데이터프레임의 경우와는 달리 0으로
지정되어 있어서 기본적으로 편향분산을 계산한다.
np.var(scores, ddof=0)86.000000ddof=1로 지정하면 불편분산을 계산한다.
np.var(scores, ddof=1)95.555556아래 코드는 편차의 제곱을 새로운 변수 summary_df가 가리키는 데이터프레임의 새로운 특성(변인)으로 추가한다.
summary_df['square of deviation'] = np.square(deviation)
summary_df각 특성의 평균값을 계산하면 앞서 scores에 포함된 데이터들의 평균값, 편차 평균값, (편향)분산이 확인된다.
summary_df.mean()score 55.0
deviation 0.0
square of deviation 86.0
dtype: float64분산과 정사각형의 면적
편차 제곱을 한 변의 길이가 편차인 정사각형의 면적으로 간주할 수 있다. 그러면 분산은 정사각형 면적의 평균값이 된다. 아래 그림은 A, B, C, D의 편차의 제곱과 분산을 정사각형의 면적으로 표현한 것이다.
4개의 회색 정사각형의 면적: A, B, C, D의 편차의 제곱
옅은 하늘색 정사각형의 면적: 4개 데이터의 분산

실제로 A, B, C, D의 평균값은 52이며 분산은 다음과 같이 계산된다.
ABCD = scores[:4]
ABCDarray([42, 69, 56, 41])print("A, B, C, D의 평균값:", ABCD.mean())A, B, C, D의 평균값: 52.0
print("A, B, C, D의 분산:", ABCD.var())A, B, C, D의 분산: 131.5
표준편차
분산은 편차 제곱의 평균값이므로, 원래 데이터에 사용된 단위가 아닌 해당 단위의 제곱을 사용한다. 앞서 보았던 정사각형의 한 변의 길이와 면적은 단위가 다른 것과 같다. 따라서 동일한 단위로 데이터의 분포도를 측정하는 용도로 분산의 제곱근인 표준편차standard deviation를 활용한다.
10명 점수의 표준편차는 넘파이의 제곱근 함수인 np.sqrt()를 이용해 직접 계산하거나, 넘파이의 np.std() 함수를 바로 이용해도 된다.
np.sqrt(np.var(scores))9.273618np.std(scores)9.273618단, 데이터프레임의 std() 메서드를 사용할 때는 앞에서처럼 편향분산을 위한 ddof=0 옵션을 함께 지정해야 한다.
scores_df.std(ddof=0)score 9.273618
dtype: float64범위
범위range는 데이터의 최댓값과 최솟값의 차이를 가리킨다.
scores에 포함된 데이터의 범위는 다음과 같이 구하고, 그 값은 28 임을 알 수 있다.
np.max(scores) - np.min(scores)28범위는 최댓값과 최소값이 변하면 그에 따라 값이 크게 요동칠 수 있다. 따라서 데이터 분포를 이해하는 데는 범위 대신 데이터의 변화에 덜 민감한 사분위수와 사분범위가 주로 이용된다.
사분위수와 사분범위
데이터의 하위 25%, 50%, 75%에 위치하는 값을 각각 제1사분위수(Q1), 제2사분위수(Q2), 제3사분위수(Q3)라 한다. Q2는 앞서 살펴본 중앙값에 해당한다.
사분범위interquartile range는 제3사분위수에서 제1사분위수를 뺀 값으로서, 평균값을 중심으로 50%의 데이터가 모여있는 구간의 길이를 나타낸다.
아래 코드는 scores의 사분범위 scores_IQR을 계산하는데,
np.percentile() 함수는 지정된 %에 해당하는 값을 반환하는 함수이다.
scores_Q1 = np.percentile(scores, 25)
scores_Q3 = np.percentile(scores, 75)
scores_IQR = scores_Q3 - scores_Q1
scores_IQR15.000000상자 그림
상자 그림은 데이터의 범위, 사분위수, 사분범위를 동시에 보여준다. 상자 그림은 다음 값들을 점, 선분, 직사각형 등으로 표시하는데, 그림에 표시되는 값들을 위에서부터 나열하면 다음과 같다.
너무 큰 이상치outlier에 해당하는 점들
(Q3 + 1.5 * IQR)에 해당하는 값의 수평 선분
Q3, Q2, Q1에 해당하는 값을 이용한 직사각형
(Q1 - 1.5 * IQR)에 해당하는 값의 수평 선분
너무 작은 이상치outlier에 해당하는 점들

아래 코드는 scores에 포함된 값들의 분포도를 상자 그림으로 그리는 과정이다. 그래프를 그리기 위해서는 먼저 데이터 시각화 관련 라이브러리를 불러와야 한다. 보통 matplotlib.pyplot를 활용하므로, 여기서는 이 라이브러리를 plt라는 별칭으로 불러온다.
import matplotlib.pyplot as plt이제 데이터프레임 summary_df의 boxplot() 메서드를 이용하여 score 특성에 포함된 값들을 대상으로 상자 그림을 그린다.
summary_df.boxplot(column=['score'], grid=False)
plt.show()
데이터프레임의 describe() 메서드
데이터프레임의 describe() 메서드는
데이터의 개수, 평균값, 표준편차, 최댓값, 최소값, 사분위수에 대한 정보를 담고있는 데이터프레임을 생성한다.
scores_df = summary_df[['score']]
scores_df.describe()18.5데이터 정규화¶
데이터 분석을 진행할 때 경우에 따라 데이터셋의 특성값들의 크기scale를 비슷하게 조정할 필요가 있다. 이렇게 특성의 스케일을 조정하는 과정을 스케일링scaling이라고 부른다.
스케일링은 보통 표준화를 사용한다.아래 두 가지 방식을 사용한다.
min-max 스케일링
표준화
min-max 스케일링
min-max 스케일링은 아래 식을 이용하여 특성값 를 0에서 1 사이의 값으로 변환한다. 와 은 해당 특성값들의 최솟값과 최댓값을 가리키고, 이들의 변환값은 각각 0과 1이 된다.
아래 코드는 scores에 포함된 10개의 점수에 대해 min-max 스케일링을 적용한다.
minmax = (scores - np.min(scores)) / (np.max(scores) - np.min(scores))
minmaxarray([0.035714, 1. , 0.535714, 0. , 0.571429, 0.25 ,
0.857143, 0.285714, 0.857143, 0.607143])최솟값 41은 0으로, 최댓값 69는 1로 변환되었음을 알 수 있다.
np.min(minmax), np.max(minmax)(0.000000, 1.000000)표준화
표준화standardization는 아래식을 이용하여 특성값 를 변환한다. 와 는 각각 해당 특성값들의 평균값과 표준편차를 가리킨다.
표준화된 특성값은 평균값은 0, 표준편차는 1인 분포를 따르며, 편차를 표준편차로 나눈 값이다.
z = (scores - np.mean(scores)) / np.std(scores)
zarray([-1.401826, 1.509659, 0.107833, -1.509659, 0.215666, -0.754829,
1.078328, -0.646997, 1.078328, 0.323498])평균값과 표준편차는 다음과 같다.
np.mean(z), np.std(z)(-0.000000, 1.000000)-0.000000으로 표기되는 이유는 소수점 아래 어딘가에 0이 아닌 숫자가 있음을 의미한다.
즉, 평균값이 0이 아니다.
이는 앞서 소수점 이하 6자리까지만 출력하라고 했기 때문이다.
하지만 다음과 같이 소수점 이하 20째 자리까지 표현하라 하면 다르게 출력된다.
%precision 20
np.mean(z), np.std(z)(-0.00000000000000001665, 0.99999999999999988898)즉, 평균값은 거의 0, 표준편차는 거의 1로 계산된다. 하지만 이는 부동소수점 연산의 한계 때문이다.
아래 코드는 2가지 스케일링(min-max 스케일링과 표준화)을 통해 변환한 값들을 데이터프레임 scores_df에 새로운 특성들로 추가하는 과정이다.
scores_df.loc[:, 'score_minmax'] = minmaxscores_df.loc[:, 'score_standardized'] = zscores_df앞에서 살펴보았듯이, 데이터프레임의 describe() 메서드를 활용하면 각 특성값들의 분포를 한 눈에 살펴볼 수 있다.
scores_df.describe()18.6연습문제¶
참고: (연습) 1차원 데이터