07-1 통계적으로 추론하기
모수검정이란
: 모집단에 대한 파라미터를 추정하는 방법
*파라미터: 평균, 분산 *모집단: 관심 대상이 되는 전체 데이터
*표본: 모집단에서 선택한 일부 샘플
표준점수 구하기
*표준점수(z 점수): 데이터가 정규분포를 따른다고 가정하고, 각 값이 평균에서 얼마나 떨어져 있는지 표준편차를 사용해 변환한 점수
*z 점수: 평균까지 거릴르 표준편차로 나눈 것
- z 점수 구하기
넘파이로 먼저 표준편차와 평균 계산
import numpy as np
x = [0, 3, 5, 7, 10]
s = np.std(x) //표준편차
m = np.mean(x) //평균
z = (7 - m) / s
print(z)
사이파이의 stats 모듈을 임포트한 다음 zscore() 함수를 호출하여 배열 x에 대한 모든 z 점수 계산 가능
from scipy import stats
stats.zscore(x)
- 누적분포 이해하기
표준정규분포: 평균이 0이고 표준편차가 1인 정규분포
*z 점수를 사용해 전체 데이터가 어떻게 분포되어 있는지 나타낼 수 있다.
- 누적분포 구하기
norm.cdf() 메서드에 평균 0을 전달하여 0까지의 비율 구하기
stats.norm.cdf(0)
*norm.cdf() 메서드: 누적된 분포 반환
z 점수 1 이내의 비율을 구하려면 z 점수 1까지 누적분포에서 z 점수 -1까지 누적분포를 뺀다.
stats.norm.cdf(1.0) - stats.norm.cdf(-1.0)
표준편차 2 이내의 비율
stats.norm.cdf(2.0) - stats.norm.cdf(-2.0)
90% 누적분포에 해당하는 z 점수 구하기
stats.norm.ppf(0.9)
*norm.ppf() 메서드: 전체에서 특정 비율에 해당하는 z 점수 구할 수 있다.
중심극한정리 알아보기
: 무작위로 샘플을 뽑아 만든 표본의 평균은 정규분포에 가깝다
ns_book7.csv 파일을 구글 드라이브에서 다운로드하여 판다스 데이터프레임으로 불러오기
import gdown
gdown.download('https://bit.ly/3pK7iuu', 'ns_book7.csv', quiet=False)
import pandas as pd
ns_book7 = pd.read_csv('ns_book7.csv', low_memory=False)
ns_book7.head()
ns_book7 데이터프레임의 '대출건수'열로 히스토그램 그리기
import matplotlib.pyplot as plt
plt.hist(ns_book7['대출건수'], bins=50)
plt.yscale('log')
plt.show()
- 샘플링하기
무작위로 1,000개의 표본을 샘플링하여 각 평균을 리스트로 저장
np.random.seed(42)
sample_means = []
for _ in range(1000):
m = ns_book7['대출건수'].sample(30).mean()
sample_means.append(m)
*sample() 메서드 첫 번째 매개변수에는 샘플링할 개수 지정 → mean() 메서드 호출하여 샘플링 결과의 평균 계산 → 평균 sample_means 리스트에 추가 → for 문으로 1,000번 반복
sample_means 히스토그램 살펴보기
plt.hist(sample_means, bins=30)
plt.show()
- 샘플링 크기와 정확도
sample_means 배열의 평균 확인
np.mean(sample_means) //무작위로 뽑은 표본의 통계량
ns_book7 데이터프레임에 있는 전체 대출건수의 평균 확인
ns_book7['대출건수'].mean() //실제 모집단의 통계량
샘플링 크기 20으로 낮추어 평균 계산
np.random.seed(42)
sample_means = []
for _ in range(1000):
m = ns_book7['대출건수'].sample(20).mean()
sample_means.append(m)
np.mean(sample_means)
*일반적으로 중심극한정리를 따르려면 샘플링의 크기가 30보다 클수록 좋다.
*30은 일종의 매직넘버 → 정규분포가 아니더라도 표본 크기가 30보다 크면 정규분포에 가까워지기 때문
샘플링 크기를 40으로 높여서 평균 구하기
np.random.seed(42)
sample_means = []
for _ in range(1000):
m = ns_book7['대출건수'].sample(40).mean()
sample_means.append(m)
np.mean(sample_means)
넘파이 std() 함수로 sample_means의 표준편차 구하기
np.std(sample_means)
전체 대출건수의 표준편차를 샘플링 개수 40의 제곱근으로 나누기
np.std(ns_book7['대출건수']) / np.sqrt(40)
→ 이렇게 구한 표본평균의 오차: 표준오차
모집단의 평균 범위 추정하기: 신뢰구간
*신뢰구간: 표본의 파라미터(여기에서는 평균)가 속할 것이라고 믿는 모집단의 파라미터 범위
주제분류번호가 '00'으로 시작하고 도서명에 '파이썬'이 포함된 행을 불리언 배열로 인덱스 python_books_index를 만들어 도서 추출
python_books_index = ns_book7['주제분류번호'].str.startswith('00') & \
ns_book7['도서명'].str.contains('파이썬')
python_books = ns_book7[python_books_index]
python_books.head()
len() 함수를 사용해 도서가 몇 권인지 확인
len(python_books)
파이썬 도서의 대출건수 평균 계산
python_mean = np.mean(python_books['대출건수'])
python_mean
남산도서관의 파이썬 도서 대출건수로 표준편차 구한 다음, 표준오차 계산
python_std = np.std(python_books['대출건수'])
python_se = python_std / np.sqrt(len(python_books))
python_se
95% 비율에 해당하는 z 점수를 알기 위해 norm.ppf() 메서드에 각각 0.975와 0.025 입력
stats.norm.ppf(0.975)
stats.norm.ppf(0.025)
*정규분포는 대칭이기 때문에 두 z 점수가 부호만 다르고 같다.
표준오차 python_se와 z 점수를 곱하여 파이썬 도서 대출건수 평균인 python_mean 데이터프레임이 속할 범위 구하기
print(python_mean-1.96*python_se, python_mean+1.96*python_se)
통계적 의미 확인하기: 가설검정
*가설검정: 표본에 대한 정보를 사용해 모집단의 파라미터에 대한 가정을 검정하는 것
예) 파이썬과 C++ 도서의 평균 대출건수가 같다고 가정했을 때, 파이썬과 C++ 도서의 표본을 각각 추출하여 이 가정이 맞는지 검정
영가설(귀무가설): 파이썬과 C++ 도서의 평균 대출건수가 같다.
대립가설: 파이썬과 C++ 도서의 평균 대출건수가 같지 않다.
*영가설: 표본 사이에 통계적으로 의미가 없다고 예상되는 가설
*대립가설: 표본 사이에 통계적인 차이가 있다는 가설
- z 점수로 가설 검증하기
C++ 도서에 대한 불리언 배열의 인덱스를 만들어 cplus_books 데이터프레임 만들기
cplus_books_index = ns_book7['주제분류번호'].str.startswith('00') & \
ns_book7['도서명'].str.contains('C++', regex=False)
cplus_books = ns_book7[cplus_books_index]
cplus_books.head()
C++ 도서권수 확인
len(cplus_books)
C++ 평균 대출건수 확인
cplus_mean = np.mean(cplus_books['대출건수'])
cplus_mean
C++ 도서에 대한 표준오차 계산
cplus_se = np.std(cplus_books['대출건수'])/ np.sqrt(len(cplus_books))
cplus_se
가설검정 공식에 대입해서 계산
(python_mean - cplus_mean) / np.sqrt(python_se**2 + cplus_se**2)
계산된 z 점수를 사용해 누적분포 확인
stats.norm.cdf(2.50)
- t-검정으로 가설 검증하기
*ttest_ind() 함수: t-분포인 두 표본을 비교하는 t-검정 수행
*t-분포: 정규분포와 비슷하지만, 중앙은 조금 더 낮고 꼬리가 더 두꺼운 분포
*표본의 크기가 30이하일 때 t-분포를 사용하는 것이 좋다.
*표본의 크기가 30보다 크면 t-분포는 정규분포와 매우 비슷해진다.
파이썬 도서와 C++ 도서의 데이터를 ttest_ind() 함수에 전달하면 t 점수와 p-값 반환
t, pvalue = stats.ttest_ind(python_books['대출건수'], cplus_books['대출건수'])
print(t, pvalue)
정규분포가 아닐 때 가설 검증하기: 순열검정
*순열검정: 모집단의 분포가 정규분포를 따르지 않거나 모집단의 분포를 알 수 없을 때 사용할 수 있는 방법
*모집단의 파라미터를 추정하지 않기 때문에 비모수검정 방법 중 하나이다.
*순열검정 방법
두 표본의 평균의 차이를 계산한 후 두 표본을 섞고 무작위로 두 그룹으로 나눈다. (이 때 두 그룹은 원래 표본의 크기와 동일하게 만든다.)
→ 나눈 두 그룹에서 다시 평균의 차이 계산
→ 이런 과정을 여러 번 반복해서 원래 표본의 평균 차이가 무작위로 나눈 그룹의 평균 차이보다 크거나 작은 경우를 헤아려 p-값 계산
- 도서 대출건수 평균 비교하기(1): 파이썬 vs C++
두 개의 배열을 받아 평균을 구하는 statistic() 함수 만들기
def statistic(x, y):
return np.mean(x) - np.mean(y)
순열검정 실행하는 permutatuion_test() 함수 구현
*permutatuion() 함수: 두 배열을 넘파이 append() 함수로 합친 후 무작위로 추출
→ 인덱스로 x_, y_ 두 그룹을 나눈 후 그룹 사이의 평균 차이 계산 → 1,000번 수행
def permutation_test(x, y):
# 표본의 평균 차이를 계산합니다.
obs_diff = statistic(x, y)
# 두 표본을 합칩니다.
all = np.append(x, y)
diffs = []
np.random.seed(42)
# 순열 검정을 1000번 반복합니다.
for _ in range(1000):
# 전체 인덱스를 섞습니다.
idx = np.random.permutation(len(all))
# 랜덤하게 두 그룹으로 나눈 다음 평균 차이를 계산합니다.
x_ = all[idx[:len(x)]]
y_ = all[idx[len(x):]]
diffs.append(statistic(x_, y_))
# 원본 표본보다 작거나 큰 경우의 p-값을 계산합니다.
less_pvalue = np.sum(diffs < obs_diff)/1000
greater_pvalue = np.sum(diffs > obs_diff)/1000
# 둘 중 작은 p-값을 선택해 2를 곱하여 최종 p-값을 반환합니다.
return obs_diff, np.minimum(less_pvalue, greater_pvalue) * 2
파이썬 도서와 C++ 도서의 대출건수를 전달하여 순열검정 결과 확인
permutation_test(python_books['대출건수'], cplus_books['대출건수'])
- 도서 대출건수 평균 비교하기(2): 파이썬 vs 자바스크립트
도서 제목에 '자바스크립트' 키워드가 들어간 행 추출
java_books_indx = ns_book7['주제분류번호'].str.startswith('00') & \
ns_book7['도서명'].str.contains('자바스크립트')
java_books = ns_book7[java_books_indx]
java_books.head()
자바스크립트의 도서권수와 평균 대출건수 확인
print(len(java_books), np.mean(java_books['대출건수']))
순열검정 확인
permutation_test(python_books['대출건수'], java_books['대출건수'])
정리
함수/메서드 | 기능 |
scipy.stats.zscore() | z 점수 계산 |
scipy.stats.norm.cdf() | 정규분포의 누적분포 비율 계산 |
scipy.stats.norm.ppf() | cdf() 메서드와 반대로 분포의 비율을 입력하면 해당 z 점수 반환 |
Series.sample() | 데이터프레임에서 무작위로 값 추출 |
scipy.stats.ttest_ind() | 두 표본의 평균에 대한 t-검정 수행 |
scipy.stats.permutation_test() | 두 표본에 대한 순열검정 수행 |
07-2 머신러닝으로 예측하기
알아두면 좋은 머신러닝 용어
*머신러닝: 컴퓨터 프로그램을 사용해 데이터에서 패턴을 학습하는 방법
→ 학습된 패턴을 사용하여 새로운 데이터에 대해 빠르고 유용한 결정 내릴 수 있다.
예) 구글 지메일의 스팸 메일 감지기: 스팸 메일과 스팸이 아닌 메일로 훈련한 다음, 예측
*머신러닝은 인공지능의 하위분야이고, 지도 학습과 비지도 학습으로 나눈다.
*사이킷런: 파이썬의 대표적인 머신러닝 패키지
- 모델
*머신러닝으로 학습된 패턴을 저장하는 소프트웨어 객체 의미
*사이킷런을 사용한다면 사이킷런에 있는 어떤 클래스의 인스턱스 객체
- 지도 학습과 비지도 학습
*지도 학습: 데이터에 있는 각 샘플에 대한 정답(타깃)을 알고 있는 경우
*입력(input): 타깃을 맞추기 위해 모델이 재료로 사용하는 데이터
→ 입력과 타깃이 들어있는 데이터를 사용해 어떤 머신러닝 모델을 훈련한 후, 타깃을 모르는 새로운 입력에 모델을 적용하여 타깃을 예측
비지고 학습: 입력 데이터는 있지만 타깃이 없는 경우
모델 훈련하기
*일반적으로 훈련 데이터가 테스트 데이터보다 크다.(보통 테스트 데이터는 전체 데이터의 20~25% 정도를 사용)
- 훈련 세트와 테스트 세트로 나누기
ns_book7 데이터를 다운로드하고 데이터프레임으로 불러오기
import gdown
gdown.download('https://bit.ly/3pK7iuu', 'ns_book7.csv', quiet=False)
import pandas as pd
ns_book7 = pd.read_csv('ns_book7.csv', low_memory=False)
ns_book7.head()
train_test_split() 함수를 임포트한 다음 ns_book7 데이터를 훈련 세트와 테스트 세트로 나누기
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(ns_book7, random_state=42)
* train_test_split() 함수는 기본적으로 입력된 데이터를 무작위로 섞은 후 75%를 훈련 세트로, 25%를 테스트 세트로 나눈다.
*분할된 훈련 세트와 테스트 세트를 리스트로 반환하기 때문에 리스트 개수에 맞는 변수를 왼쪽에 나열하여 반환되는 값 각각 저장 가능
반환된 훈련 세트와 테스트 세트를 저장한 train_set, test_set 크기 확인
print(len(train_set), len(test_set))
train_set에서 '도서권수'열과 '대출건수'열을 각각 X-train와 y_train 변수에 저장하고 크기 확인
X_train = train_set[['도서권수']]
y_train = train_set['대출건수']
print(X_train.shape, y_train.shape)
*'도서권수'열 하나를 리스트로 감싸서 입력 X-train을 만들었다.
*특성: 2차원 배열인 입력은 행 방향으로 샘플이 나열되고 열 방향으로 샘플의 속성이 나열
- 선형 회귀 모델 훈련하기
선형 회귀 알고리즘인 LinearRegression 클래스의 객체 lr을 만들고 이 객체의 fit() 메서드를 호출하며 모델 훈련
→ fit() 메서드를 호출할 때 앞서 만들었던 입력 X-train와 타깃 y_train을 전달
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X_train, y_train)
훈련된 모델을 평가하기: 결정계수
X_test와 y_test를 만들어 score()메서드 호출
X_test = test_set[['도서권수']]
y_test = test_set['대출건수']
lr.score(X_test, y_test)
y_train을 입력으로 사용해 모델 훈련
*y_train은 시리즈 객체이므로 fit() 메서드의 입력으로 사용하려면 2차원 배열 형태여야 한다.
→ to_frame() 메서드를 사용해 데이터프레임으로 만든다.
lr.fit(y_train.to_frame(), y_train)
lr.score(y_test.to_frame(), y_test)
연속적인 값 예측하기: 선형 회귀
*선형 회귀: 선형 함수를 사용해 모델을 만드는 알고리즘
*y = ax + b
*x는 입력, y는 타깃, 입력에 기울기 a를 곱하고 y축과 만나는 절편 b를 더하여 예측을 만드는 것
lr 객체의 coef_ 속성과 intercept_ 속성에 학습된 기울기와 절편 각각 저장
print(lr.coef_, lr.intercept_)
→ 기울기는 1이고 절편은 0에 가까운 매우 작은 음수: y = x + 0
카테고리 예측하기: 로지스틱 회귀
*회귀: 지도 학습 중에서 타깃이 실수인 문제
*이진 분류: 2개의 카테고리로 분류
*다중 분류: 3개 이상의 카테고리로 분류
*클래스: 분류 알고리즘에서 타깃 카테고리
*일반적으로 이진 분류의 타깃은 0 또는 1이다.
*음성 클래스: 0일 때
*양성 클래스: 1일 때
*로지스틱 회귀: 선형 회귀처럼 선형 함수를 사용하지만, 예측을 만들기 전에 로지스틱 함수 거친다.
- 로지스틱 회귀 모델 훈련하기
도서권수로 대출건수가 평균보다 높은지 아닌지 예측하는 이진 분류 문제
borrow_mean = ns_book7['대출건수'].mean()
y_train_c = y_train > borrow_mean
y_test_c = y_test > borrow_mean
사이킷런의 linear_model 모듈에 있는 LogisticRegression 클래스를 임포트하여 훈련 세트로 fit() 메서드 호출
→ 테스트 세트로 score() 메서드 호출
from sklearn.linear_model import LogisticRegression
logr = LogisticRegression()
logr.fit(X_train, y_train_c)
logr.score(X_test, y_test_c)
*사이킷런의 분류 모델의 경우 score() 메서드가 출력하는 점수는 정확도이다.
*정확도: 입력 데이터 중 정답을 맞힌 비율
- 양성 클래스와 음성 클래스 분포 확인하기
시리즈 객체인 y_test_c에서 value_counts() 메서드를 호출
y_test_c.value_counts()
DummyClassifier()를 사용해 score() 메서드 결과 확인
from sklearn.dummy import DummyClassifier
dc = DummyClassifier()
dc.fit(X_train, y_train_c)
dc.score(X_test, y_test_c)
*사이킷런은 가장 많은 클래스로 무조건 예측을 수행하는 더미 모델 제공
*회귀일 경우 DummyRegressor 모델이 있으며 무조건 타깃의 평균을 예측
*분류일 경우 DummyClassifier 모델이 기본적으로 가장 많은 클래스 예측
정리
함수/메서드 | 기능 |
sklearn.model_selection.train_test_split() | 입력된 데이터를 훈련 세트와 테스트 세트로 나눈다. |
sklearn.linear_model.LinearRegression | 선형 회귀 수행 |
LinearRegression.fit() | 모델 훈련 |
LinearRegression.score() | 모델의 성능 평가 |
LinearRegression.predict() | 샘플에 대한 예측 만들기 |
sklearn.linear_model.LogisticRegression | 로지스틱 회귀 수행 |
sklearn.dummy.DummyClassifier | 입력값을 고려하는 대신 타깃에서 다수의 클래스를 예측으로 사용 |
'혼자 공부하는 데이터 분석' 카테고리의 다른 글
6장 복잡한 데이터 표현하기 (0) | 2023.06.08 |
---|---|
5장 데이터 시각화하기 (1) | 2023.05.29 |
4장 데이터 요약하기 (0) | 2023.05.29 |
3장 데이터 정제하기 (0) | 2023.05.22 |
2장 데이터 수집하기 (0) | 2023.05.14 |
댓글