선형 회귀(Linear Regression Model)

선형 회귀가 뭐지?

통계학에서, 선형 회귀(線型回歸, 영어: linear regression)는 종속 변수 y와 한 개 이상의 독립 변수 (또는 설명 변수) X와의 선형 상관 관계를 모델링하는 회귀분석 기법이다. 한 개의 설명 변수에 기반한 경우에는 단순 선형 회귀(simple linear regression), 둘 이상의 설명 변수에 기반한 경우에는 다중 선형 회귀라고 한다.

선형 회귀는 선형 예측 함수를 사용해 회귀식을 모델링하며, 알려지지 않은 파라미터는 데이터로부터 추정한다. 이렇게 만들어진 회귀식을 선형 모델이라고 한다.

일반적으로 최소제곱법(least square method)을 사용해 선형 회귀 모델을 세운다. 최소제곱법 외에 다른 기법으로도 선형 회귀 모델을 세울 수 있다. 손실 함수(loss fuction)를 최소화 하는 방식으로 선형 회귀 모델을 세울 수도 있다. 최소제곱법은 선형 회귀 모델 뿐 아니라, 비선형 회귀 모델에도 적용할 수 있다. 최소제곱법과 선형 회귀는 가깝게 연관되어 있지만, 그렇다고 해서 동의어는 아니다.
- 위키백과

선형 회귀란 쉽게 생각하면 어떠한 현상 A와 이와 관련되어 보이는 한 개 이상의 현상 B가 있을 때, B가 변화함에 따라 A가 어떻게 변하는지 도형(직선, 평면 등) 하나를 통해 나타낸 것이다. 여기서 B가 1개라면 단순 선형 회귀(Simple Linear Regression), 2개 이상이면 다중 선형 회귀(Multiple Linear Regression)라고 한다.


선형 회귀에서의 가정(Assumptions of Linear Regression)

선형 회귀가 성립되기 위한 가정에는 다음 4가지가 있다.

  1. 선형성(Linearity) : 독립 변수와 종속 변수의 관계는 선형적이어야 한다. 즉, 그래프로 표현했을 때 끊어지지 않고 쭈욱 이어지는 선으로 표현 가능해야 한다.
  2. 정규성(Normality) : 잔차(종속 변수에 대한 예측 값과 실제 값의 차이)$^*$$^1$가 정규 분포여야 한다.
  3. 등분산성(Homoscedasticity, Constant Variance) : 독립 변수의 모든 값에 대하여 잔차의 분산이 같아야 한다.
  4. 독립성(Independence) : 잔차는 서로 독립적이어야 한다.

단순 선형 회귀(Simple Linear Regression)

위에서 간단하게 언급한 바와 같이 단순 선형 회귀는 독립 변수 1개와 종속 변수 1개의 관계를 직선으로 나타낸 것이다.
이를 통해 독립 변수의 값에 따라 이에 해당하는 종속 변수의 값을 구할 수 있다. 즉, 기존의 데이터를 바탕으로 실제 데이터에는 존재하지 않는 값을 예측할 수 있다.

예를 들면 어느 중학교 학생들의 공부 시간과 시험 성적을 조사한 결과가 아래와 같다고 해보자.

학생 공부 시간 시험 성적
A 3 30
B 5 50
C 2 20
D 6 60
E 4 40

이를 그래프로 나타내면 아래처럼 보일 것이다.

그리고 이 그래프 위의 점들에 최대한 가깝게 지나가는 직선을 그리면 아래와 같다.

실제 데이터에는 공부 시간이 2, 3, 4, 5, 6 뿐이다. 여기서 만약 8시간 공부한 학생의 시험 성적이 궁금하다면 x값(공부 시간)이 8일 때 직선 상의 y값(시험 성적)이 무엇인지 알아보면 된다.

굳이 별도의 계산이나 그래프가 필요할 만한 예시는 아니었지만, 단순 선형 회귀를 통해 예측 값을 얻는 과정이 이와 같다.

그렇다면 실제로 저 직선은 어떻게 구하는 것일까?


최소제곱법(Least Squares Method = Ordinary Least Squares, OLS)

최소제곱법이란 예측 값과 실제 값의 차이인 잔차들의 제곱의 합을 최소화하는 직선을 찾아내는 방법이다. 이 직선을 최소 제곱 직선이라고 한다.

※ 왜 제곱을 하지?

제곱하는 이유는 실제 값이 예측 값보다 클 수도, 작을 수도 있기 때문이다.
제곱을 안 하고서 그냥 합하면 양수와 음수가 서로 상쇄되어 결과가 왜곡될 수 있다(분산 구할 때 제곱해주던 것과 마찬가지).

<예시>
아래와 같이 x와 y가 있다고 해보자.

x y
1 2
2 4
3 5
4 4
5 5
평균 3 평균 4

이를 그래프로 나타내면 아래와 같다.

여기서 직선 $\hat{y} = \beta_0 + \beta_1x$ 가 예측 값을 얻는데 쓰일 최소 제곱 직선(다른 말로는 선형 회귀선)이다.

직선 $\hat{y} = \beta_0 + \beta_1x$는 $x$와 $y$의 관계를 나타낸다($\hat{y}$는 $y$의 예측값(추정치)라는 의미).
$x$와 $y$의 관계를 최대한 잘 나타내려면 각각의 $x$값에 대하여 실제 $y$값에 최대한 가까운 예측 값을 가져야 한다.
즉, 직선 상의 $y$값(= $\hat{y}$, 예측 값)과 점으로 표시된 $y$값(실제 값)의 차이(잔차)를 최소화해야 한다.

그럼 잔차를 최소화하는 직선 $\hat{y} = \beta_0 + \beta_1x$의 기울기와 $y$ 절편을 구해보자.


1) 기울기($\beta_1$) 구하기

직선의 기울기를 구하는 식은 다음과 같다.
$$\beta_1 = \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})} {\sum_{i=1}^{n} (x_i - \bar{x})^2}$$
아래 테이블에 계산되어 있는 값을 식에 그대로 넣으면 $\beta_1 = \frac{6}{10} = 0.6$ 이다.

$x$ $y$ $x - \bar{x}$ $y - \bar{y}$ $(x - \bar{x})^2$ $(x - \bar{x})(y - \bar{y})$
1 2 -2 -2 4 4
2 4 -1 0 1 0
3 5 0 1 0 0
4 4 1 0 1 0
5 5 2 1 4 2
평균 3 평균 4     합계 10 합계 6

2) $y$ 절편($\beta_0$) 구하기

이제 기울기를 알고 있으니 $y$ 절편을 구하는건 간단하다.
직선 $\hat{y} = \beta_0 + 0.6x$에 ($\bar{x}, \bar{y}$)를 넣어주면 된다.
$∴ \beta_0 = \bar{y} - 0.6\bar{x} = 4 - 0.6*3 = 2.2$

※ $\beta_1$을 구하는 식은 왜 저렇게 되고, $\beta_0$는 왜 저렇게 구할까? (참고 자료)
최소제곱법 - 네이버캐스트 수학 산책
최소 제곱법 (Least Square Method = OLS)
#선형회귀 의 기초인 #최소제곱법 ? 공식 유도를 해보자~! (영상)


기준 모델(Baseline Model)

위처럼 예측 값을 잘 뽑아줄 직선을 구했으니 무언가 할 일은 다 한 것 같다.
그러나 사실 그 전에 해야 할 일이 있었으니, 바로 기준 모델을 세우는 것이다.

기준 모델이란 예측 모델(위에서 만든 직선 식)을 구체적으로 만들기 전에 가장 간단하면서도 직관적이면서 최소한의 성능을 나타내는 기준이 되는 모델이다.
쉽게 말하면 예측 모델을 뚝딱 만들고 봤을 때 최소한 이 기준 모델이라는 친구보다는 일을 잘 해야 한다는 것이다.

기준 모델은 문제의 유형에 따라 보통 다음과 같이 설정한다.

  • 분류 문제: 타겟의 최빈 클래스
  • 회귀 문제: 타겟의 평균값 -> 평균 절대 오차(MAE, Mean Absolute Error)로 평가
  • 시계열 회귀 문제: 이전 타임 스탬프의 값

 


scikit-learn 활용하여 단순 선형 회귀 모델 만들기

  1. 데이터 불러오기
# 예시 데이터 불러오기 (House Sales in King County, USA)
import pandas as pd
df = pd.read_csv('https://ds-lecture-data.s3.ap-northeast-2.amazonaws.com/kc_house_data/kc_house_data.csv')

  1. 타겟과 이와 관련된 특성 찾아서 정하기
# 여기서는 타겟(price)과 가장 상관 관계가 높은 특성을 찾겠습니다
df.corr().price.sort_values(ascending=False)

price 1.000000
sqft_living 0.702035
grade 0.667434
sqft_above 0.605567
sqft_living15 0.585379
bathrooms 0.525138
view 0.397293
sqft_basement 0.323816
bedrooms 0.308350
lat 0.307003
waterfront 0.266369
floors 0.256794
yr_renovated 0.126434
sqft_lot 0.089661
sqft_lot15 0.082447
yr_built 0.054012
condition 0.036362
long 0.021626
id -0.016762
zipcode -0.053203
Name: price, dtype: float64

∴ 타겟과 상관 관계가 가장 높은 특성의 이름 : sqft_living


  1. 기준 모델 만들기 및 평가
predict = df.price.mean() # 기준 모델 : 타겟(price)의 평균값
print(int(predict))

sns.scatterplot(x=df.sqft_living, y=df.price)
sns.lineplot(x=df.sqft_living, y=predict, color='red'); # 기준 모델 시각화

 

# 평균값으로 예측할 때 샘플 별 평균값과의 차이(error)를 저장
errors = predict - df.price
errors

0 318188.141767
1 2088.141767
2 360088.141767
3 -63911.858233
4 30088.141767
...
21608 180088.141767
21609 140088.141767
21610 137987.141767
21611 140088.141767
21612 215088.141767
Name: price, Length: 21613, dtype: float64

# mean_absolute_error(MAE), error에 절대값을 취한 후 평균을 계산
mean_absolute_error = errors.abs().mean()
print(f'예측한 주택 가격이 ${predict:,.0f}이며 절대 평균 에러가 ${mean_absolute_error:,.0f}임을 확인할 수 있습니다.')

예측한 주택 가격이 $540,088이며 절대 평균 에러가 $233,942임을 확인할 수 있습니다.


  1. 예측 모델(선형 회귀 모델) 만들기
from sklearn.linear_model import LinearRegression

model = LinearRegression()
feature = ['sqft_living']
target = ['price']
X_train = df[feature]
y_train = df[target]
model.fit(X_train, y_train)

LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

# 예측을 잘 하는지 값 하나 던져서 테스트 해보기
X_test = [[15000]]
y_pred = model.predict(X_test)

print(f'{X_test[0][0]} sqft GrLivArea를 가지는 주택의 예상 가격은 ${round(float(y_pred))} 입니다.')

15000 sqft GrLivArea를 가지는 주택의 예상 가격은 $4165773 입니다.


  1. 예측 값과 실제 값을 그래프로 표현
X_test  =  [[x]  for  x  in  df['sqft_living']]  
y_pred  =  model.predict(X_test)

y_pred

array([[287555.06702451],
[677621.82640197],
[172499.40418656],
...,
[242655.29616092],
[405416.96554144],
[242655.29616092]])

plt.scatter(X_train, y_train, color='black', linewidth=1)
plt.scatter(X_test, y_pred, color='blue', linewidth=1);

 


  1. 예측 모델의 회귀 계수(직선의 기울기)와 y 절편 확인
print(model.coef_) # 회귀 계수
print(model.intercept_) # y 절편

[[280.6235679]]
[-43580.74309447]

$*1$ 오차(error)와 잔차(residual)의 차이가 뭐지?
오차 : 모집단이 기준
잔차 : 표본 집단이 기준
실제 데이터를 토대로 선형 회귀 등의 방식으로 회귀식을 구하면 예측 값을 얻을 수 있다. 여기서 실제 데이터가 모집단인지, 표본 집단인지에 따라 실제 값과 예측 값의 차이를 각각 오차와 잔차라고 한다.

 

<참고 자료>

● 선형 회귀

● 선형 회귀에서의 가정

● 최소제곱법

● 기준 모델

● 오차와 잔차

● 시각화

중심 극한 정리(Central Limit Theorem, CLT)

중심 극한 정리가 뭐지?

확률론과 통계학에서, 중심 극한 정리(中心 極限 定理, 영어: central limit theorem, 약자 CLT)는 동일한 확률분포를 가진 독립 확률 변수 n개의 평균의 분포는 n이 적당히 크다면 정규분포에 가까워진다는 정리이다.
- 위키백과

중심 극한 정리란 어떤 모집단 하나로부터 표본 집단을 여러 개 가져온 후 이들의 평균을 구해놓고서 분포를 보니 가져온 갯수가 많으면 많을수록 정규 분포와 비슷해지더라, 하는 내용이다.

중심 극한 정리는 모집단이 어떻게 생겼는지에 상관 없이 적용된다.
즉, 표본의 분포가 원래 무엇이었는지에 상관 없이 표본 평균이 정규 분포를 따르므로 이를 통해 신뢰 구간을 만들거나 T-Test, ANOVA 등의 시행이 가능하다.

표본을 한 번 가져올 때는 보통 30개 이상을 가져온다.


중심 극한 정리는 어떻게 하는거지?

모집단으로부터 가져올 표본 집단의 갯수와 한 집단에 들어갈 표본의 갯수를 정한다. 그리고 각 표본 집단의 평균값을 구한 후 이들의 분포가 어떻게 생겼는지 본다.

<예시 - 중심 극한 정리 구현>

import pandas as pd  

def  clt(data, num):
    sample_means = []
    for x in  range(0, num):
        sample = np.random.choice(data, 30) # 표본 갯수 30개씩 가져오기
        sample_means.append(sample.mean()) # 표본 집단의 평균값 저장

    pd.DataFrame(sample_means).hist(color = '#4000c7');

<예시 - 이항분포>

import numpy as np
import matplotlib.pyplot as plt  

n, p = 1, 0.5
bino = np.random.binomial(n, p, 100)  

plt.hist(bino)
plt.show()

clt(bino, 100) # 표본 집단을 100개 가져온 경우
clt(bino, 1000) # 표본 집단을 1000개 가져온 경우


<예시 - 지수 분포>

expo = np.random.exponential(1, 1000)

plt.hist(expo)
plt.show()

clt(expo, 100)
clt(expo, 1000)


<예시 - 포아송 분포>

poi = np.random.poisson(5, 1000)  

plt.hist(poi)
plt.show()

clt(poi, 100)
clt(poi, 1000)


위 예시들을 통해 알 수 있듯이, 모집단이 어떻게 생겼든 표본 평균의 분포는 정규 분포와 비슷해진다.
또한 표본 평균의 갯수가 많을 수록 정규 분포에 보다 가까워진다.

<참고 자료>
중심 극한 정리 - 위키백과
[개념 통계 17] 중심극한 정리는 무엇이고 왜 중요한가?
중심극한정리에 대한 오해, 많으면 무조건 정규분포 OK???
[파이썬 데이터 사이언스] 중심극한정리(CLT, central limit theorem) 시뮬레이션

[확률과 통계] 파이썬으로 이항 분포 그리기
[확률과 통계] 파이썬으로 지수 분포 그리기
[Python NumPy] 무작위 표본 추출, 난수 만들기 (random sampling, random number generation)
numpy.random.choice
numpy.random.binomial
numpy.random.exponential
numpy.random.poisson

 

'Statistics > Confidence Interval' 카테고리의 다른 글

[Stats] 신뢰 구간(Confidence Interval)  (0) 2021.10.17

신뢰 구간(Confidence Intervals)

신뢰 구간이 뭐지?

통계학에서 신뢰 구간(信賴區間, 영어: confidence interval)은 모수가 어느 범위 안에 있는지를 확률적으로 보여주는 방법이다.
신뢰 구간은 보통 표본에서 산출된 통계와 함께 제공된다. 예를 들어, "신뢰수준 95%에서 투표자의 35% ~ 45%가 A후보를 지지하고 있다."라고 할 때 95%는 신뢰수준이고 35% ~ 45%는 신뢰구간이며 θ는 A후보의 지지율이다.
- 위키백과

신뢰 구간을 쉽게 표현하자면 무언가에 대해서 말할 때 그 무언가의 일부를 보고서 "이 정도면 어지간해선 들어맞겠지" 할 만한 범위이다.

위 예에서는 A 후보의 지지율 신뢰 구간이 35% ~ 45%라고 하는데, 이는 투표 마친 사람들한테 설문조사한 결과를 토대로 실제 A 후보의 지지율 결과가 35% ~ 45% 사이에 있을 거라고 보는 것이다.
그리고 신뢰수준 95%의 의미는 이 말이 진짜로 맞을 확률이 95% 라는 것이다.


신뢰 구간은 어떻게 구하는거지?

아래는 표본 평균을 이용하여 모평균이 들어와 있을 만한 구간, 즉 '신뢰 구간'을 구하는 과정과 이를 Python으로 표현한 것이다.

신뢰 구간은 표본 평균을 기준으로 좌우로 2 × 표준 오차(Standard Error of Mean, SEM)$^1$ 만큼 더하고 뺀 범위이다(신뢰 수준 95% 기준$^2$).

import numpy as np
from scipy import stats  

def  confidence_interval(data, confidence = 0.95):
    """
    주어진 데이터의 표본 평균에 대한 신뢰구간을 계산.
    기본 값으로 t-분포와 양방향 (two-tailed), 95%의 신뢰도를 사용합니다.

    입력 값 :
    data - 여러 개로 이루어진 (list 혹은 numpy 배열) 표본 관측치
    confidence - 신뢰구간을 위한 신뢰도

    반환 되는 값:
    (평균, 하한, 상한구간)으로 이루어진 tuple
    """

    data = np.array(data)
    mean = np.mean(data)
    n = len(data)

    # Standard Error of Mean (https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.sem.html)
    stderr = stats.sem(data)

    # length_of_one_interval
    interval = stderr * stats.t.ppf( (1 + confidence) / 2 , n-1) # ppf : inverse of cdf

    return (mean, mean - interval, mean + interval)

    # cdf -> t 를 넣으면 %
    # ppf -> % 를 넣으면 t

    # 1 + 0.95 / 2 -> 0.975
    # (1 - 0.95) / 2 -> 0.025
np.random.seed(123)  

data = np.random.normal(50, 10, 1000)  
sample = np.random.choice(data, 10)  

confidence_interval(sample)

(44.28501220284126, 37.93312500671013, 50.63689939897239)

# 다른 방법
from scipy.stats import t

# 표본의 크기
n = len(sample)
# 자유도
dof = n-1
# 평균의 평균
mean = np.mean(sample)
# 표본의 표준편차
sample_std = np.std(sample, ddof = 1)
# 표준 오차
std_err = sample_std / n ** 0.5 # sample_std / sqrt(n)

CI = t.interval(.95, dof, loc = mean, scale = std_err) # https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.t.html
print("95% 신뢰구간: ", CI)

95% 신뢰구간: (37.93312500671013, 50.63689939897239)


*1 : 표준 오차란 표본 평균의 표준 편차이며, 구하는 방법은 아래와 같다. $$SEM=\frac{σ}{√n}$$ 여기서 $σ$는 모집단의 표준편차이고 $n$은 표본의 크기이다.
*2 신뢰 수준에 따라 표준 오차에 몇을 곱해주는지가 달라진다. 참고 : Z-Score in Statistics

 

<참고 자료>
신뢰 구간 - 위키백과
신뢰 구간의 의미 - 공돌이의 수학정리노트
신뢰구간 (Confidence Interval) - BioinformaticsAndMe
표준 점수 - 위키백과
scipy.stats.sem - Standard Error of Mean, SEM
scipy.stats.t
Compute a confidence interval from sample data - Stack Overflow

분산 분석(ANOVA, analysis of variance)

분산 분석이 뭐지?

분산 분석(分散分析, analysis of variance, ANOVA, 또는 변량 분석)은 통계학에서 두 개 이상 다수의 집단을 서로 비교하고자 할 때 집단 내의 분산, 총평균 그리고 각 집단의 평균의 차이에 의해 생긴 집단 간 분산의 비교를 통해 만들어진 F분포를 이용하여 가설검정을 하는 방법이다.
- 위키백과

보통 세 개 이상의 집단 간 평균이 유의미한 차이가 있는지 알아보기 위한 방법이다.

❓ 이름은 분산 분석인데 평균을 비교하네? 뭐지?
'집단 간 분산'과 '집단 내 분산'을 이용하여 분석하기 때문에 '분산 분석'이라고 함.
∵ 평균값이 같아도 분산에 따라 데이터의 모양이 완전히 달라질 수 있다
$\rightarrow$ 분산이 클수록 집단 간 평균값의 차이가 무의미해짐

  • 여러 집단 간의 평균이 비슷하다 = 여러 집단 간의 분산이 작다
  • 여러 집단 간의 평균이 다르다 = 여러 집단 간의 분산이 크다

∴ '분산'이라는 단어 안에 '평균'의 의미가 내포되어 있다
∴ '분산 분석 = 평균 간 차이 분석' 이라고 할 수 있다

<예시>

import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt

mean = [4.8, 5.2, 5]
std = 0.1 # 표준편차(-> 분산)
color = 'rgb'

data = []
for i in  range(len(mean)):
    data.append(stats.norm(mean[i], std).rvs(100))
    plt.plot(np.arange(len(data[i]))+i*len(data[0]), data[i], '.', color=color[i])

std2 = 1 # 표준편차(분산)를 기존 0.1에서 1로 변경

data2 = []
for i in  range(len(mean)):
    data2.append(stats.norm(mean[i], std2).rvs(100))
    plt.plot(np.arange(len(data2[i]))+i*len(data2[0]), data2[i], '.', color=color[i])
# 각 데이터별 평균값은 이전과 동일하나, 분산에 따라서 전체적인 분포 모양의 차이가 있음

정리해보면
$$집단\ 간\ 평균\ 차이 = \frac{집단\ 간\ 분산}{집단\ 내\ 분산}$$
$$= {집단\ 간\ 평균\ 차이가\ 큰가?\over애초에\ 집단이라고\ 할\ 수\ 있나?}$$
$$= F\ 값(F-statistic)$$
이라고 할 수 있다.
- 집단 간 분산 : 커야 함 (집단 간의 평균값 차이가 뚜렷하다는 것)
- 집단 내 분산 : 작아야 함 (집단 내 분산이 크면(=집단 내 데이터 간의 차이가 크면) 이 안에 속한 것들이 진짜 한 집단이라고 하기 애매함)

완전한 분산 분석은 세 단계로 이루어진다.

  1. 가정 검정(등분산성, 정규성, 독립성) (여기서는 '등분산성'만 언급)
    1) 등분산성 검정 : 집단 간 비교가 가능한가?
    • 집단 내 분산이 서로 다르다 $\rightarrow$ 집단들을 동등하게 비교하기 어려움
    • $H_0$ : 모든 집단의 '집단 내 분산'이 같다
    • $H_a$ : 모든 집단의 '집단 내 분산'이 같지 않다
  2. 분산 분석의 가설 검정 (Omnibus test) : 집단 간 평균이 다른가?
    • $H_0$ : 모든 집단의 평균이 같다
    • $H_a$ : 모든 집단의 평균이 같지 않다
    • 한계점 : 구체적으로 어떻게 같지 않은 것인지, 어떤 집단의 평균이 다른 것인지 알 수 없음
  3. 사후분석 (post-hoc test) : 어느 집단의 평균이 다른가?
    • 분산 분석에서 F값이 충분히 큰 경우(평균이 같지 않은 경우) 사후분석 시행
    • 어떻게? 집단 2개씩 묶어서 T-test? 아니다
    • ∵ 1종 오류의 증가alpha=0.05로 설정, 3번 시행하면 0.95 * 0.95 * 0.95 = 0.86으로 1종 오류를 저지르지 않을 확률이 낮아짐
    • 이를 방지하기 위해 Bonferroni Correction 등을 사용함

분산 분석에는 일원 분산 분석(One-way ANOVA)과 이원 분산 분석(Two-way ANOVA)이 있다.


분산 분석은 어떻게 하는거지?

일원 분산 분석(One-way ANOVA)

  • 독립 변수와 종속 변수 모두 1개인 경우
  • 한 변수의 변화가 결과에 미치는 영향을 알아보기 위해 사용

<예시 - scipy 이용>

import numpy as np
from scipy.stats import f_oneway

g1 = np.array([0, 31, 6, 26, 40])
g2 = np.array([24, 15, 12, 22, 5])
g3 = np.array([32, 52, 30, 18, 36])

f_oneway(g1, g2, g3)

F_onewayResult(statistic=2.6009238802972483, pvalue=0.11524892355706169)


이원 분산 분석(Two-way ANOVA)

  • 독립 변수가 2개 이상인 경우
  • 두 요인이 서로 영향을 미치는지의 여부를 알아보기 위해 사용

<예시 - statsmodel 이용>

import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols

df = pd.DataFrame({'water': np.repeat(['daily', 'weekly'], 15),
                   'sun': np.tile(np.repeat(['low', 'med', 'high'], 5), 2),
                   'height': [6, 6, 6, 5, 6, 5, 5, 6, 4, 5,
                              6, 6, 7, 8, 7, 3, 4, 4, 4, 5,
                              4, 4, 4, 4, 4, 5, 6, 6, 7, 8]})  

#perform two-way ANOVA
model = ols('height ~ C(water) + C(sun) + C(water):C(sun)', data=df).fit()
sm.stats.anova_lm(model, typ=2)
  sum_sq df F PR(>F)
C(water) 8.533333 1.0 16.0000 0.000527
C(sun) 24.866667 2.0 23.3125 0.000002
C(water):C(sun) 2.466667 2.0 2.3125 0.120667
Residua 12.800000 24.0 NaN NaN

 

<참고 자료>
분산 분석 - 위키백과
분산 분석 - 나무위키
파이썬으로 분산분석(ANOVA)
분산 분석(ANOVA)이란 무엇인가요?
ANOVA using Python (with examples)
수식없는 분산분석 1 - ASDF 오터의 통계
수식없는 분산분석 1 - ASDF 오터의 통계
F값 (통계) - 위키백과
F-value의 의미와 분산분석 - 공돌이의 수학정리노트
통계 F 값 (F-ratio) 을 가장 쉽게 설명해 보자 - 엘트리고의 농업저널
scipy.stats.f_oneway - One-way ANOVA
statsmodels.stats.anova.anova_lm - Two-way ANOVA
How to Perform a Two-Way ANOVA in Python

카이제곱 검정(chi-squared test)

카이제곱 검정이 뭐지?

카이제곱 검정(chi-squared test) 또는 $χ^2$ 검정은 카이제곱 분포에 기초한 통계적 방법으로, 관찰된 빈도가 기대되는 빈도와 의미있게 다른지의 여부를 검정하기 위해 사용되는 검정방법이다. 자료가 빈도로 주어졌을 때, 특히 명목척도 자료의 분석에 이용된다.
카이제곱 값은 χ2 = Σ (관측값 - 기댓값)$^2$ / 기댓값 으로 계산한다.
- 위키백과

카이제곱 검정은 주어진 데이터의 분포가 예상한 분포와 동일한지 알아보는 방법이다.
예를 들어 주사위를 60번 굴려서 1부터 6까지의 값이 각각 10번 나온다고 예상했고 실제로는 각각 5, 10, 15, 12, 8, 10번 나왔다고 했을 때 두 분포가 동일한지를 알아보는 식이다.

주사위 1 2 3 4 5 6
예상 10 10 10 10 10 10
실제 5 10 15 12 8 10

카이제곱 검정은 범주형 데이터(값이 연속적이지 않은 데이터)에 대해서만 사용 가능하다.

수치형(numerical) $\rightarrow$ 범주형(categorical)

형변환(Type casting)
numerical 이지만 연속적(continuous)이지 않아 바로 category로 사용할 수 있는 경우
ex) 1, 2, 3 -> 1, 2, 3

  • 구간화(Binning)
    numerical 이지만, continuous 해서 구간별로 나누어 사용할 수 있는 경우
    ex) 1.4, 2, 3.1, 2.8, 1.1, 2.5 -> A : 1 ~ 2, B : 2 ~ 3, C : 3 ~ 4

카이제곱 검정은 주어진 변수의 갯수에 따라 One Sample 카이제곱 검정과 Two Sample 카이제곱 검정으로 나눌 수 있다.

  • One Sample Chi-squared test
    • 적합도 검정 : 주어진 한 변수의 분포가 예상한 분포와 같은지 알아보기
  • Two Sample Chi-squared test
    • 동질성 검정 : 주어진 두 변수의 분포가 같은지 알아보기
    • 독립성 검정 : 주어진 두 변수가 관련이 있는지 알아보기

카이제곱 검정은 어떻게 하는거지?

계산식

$$X^2 = \sum_{i=1}^{k} \frac{(observed_i - expected_i)^2}{expected_i}$$

Scipy를 이용한 카이제곱 검정

One Sample Chi-squared test - 적합도 검정

  • 귀무가설($H_0$) : 주어진 한 변수의 분포가 기대와 동일할 것이다
  • 대안가설($H_a$) : 주어진 한 변수의 분포가 기대와 동일하지 않을 것이다

<예시>

from scipy.stats import chisquare

dice_obs = [5, 10, 15, 12, 8, 10]

# chisquare 함수의 파라미터 1) 관측값, 2) 예상값, 3) 자유도 4) 축 방향(행/열)
chisquare(dice_obs)
# 관측값만 넣어줄 경우 관측값의 평균을 예상값으로 하여 결과를 보여준다

Power_divergenceResult(statistic=7.111111111111111, pvalue=0.2125071271925751)

# 아래와 같이 별도로 예상값을 넣어줄 수도 있다
chisquare(dice_obs, 14)

Power_divergenceResult(statistic=11.0, pvalue=0.05137998348306955)


Two Sample Chi-squared test - 동질성 검정

  • 귀무가설($H_0$) : 두 변수의 분포가 동일할 것이다
  • 대안가설($H_a$) : 두 변수의 분포가 동일하지 않을 것이다
from scipy.stats import chisquare

obs1 = [5, 4, 10, 12, 8, 6]
obs2 = [9, 9, 9, 9, 9, 9]

chisquare(obs1, obs2)

Power_divergenceResult(statistic=6.777777777777777, pvalue=0.23769876659731384)


Two Sample Chi-squared test - 독립성 검정

  • 귀무가설($H_0$) : 두 변수는 서로 독립이다 (= 관련이 없을 것이다)
  • 대안가설($H_a$) : 두 변수는 서로 독립이 아니다 (= 관련이 있을 것이다)
import numpy as np

from scipy.stats import chi2_contingency

obs3 = np.array([[10, 10, 20], [20, 20, 20]])
chi2_contingency(obs3)

(2.7777777777777777, 0.24935220877729622, 2, array([[12., 12., 16.], [18., 18., 24.]]))
$\rightarrow$ 결과 해석 : ($x^2$ 통계치, p-value, 자유도(degree of freedom), Expected value for Observed)


※ One Sample $x^2$ test(적합도 검정)의 자유도와 Two Sample $x^2$ test(독립성 검정)의 자유도 차이

  • 1-sample(적합도 검정) 자유도 = #categories-1
  • 2-sample(독립성 검정) 자유도 = (#행 - 1) * (#열 - 1)
    위 예시에서 obs3의 자유도는 (3-1) * (2-1) = 2 이다

※ Two Sample $x^2$ test(독립성 검정)의 예측값 계산 과정
A, B, C, D라는 2x2 table을 예로 들면

       
  A B A+B
  C D C+D
  A+C B+D A+B+C+D==T

위 테이블의 Expected value는

       
  (A+B)*(A+C) / T (A+B)*(B+D) / T A+B
  (A+C)*(C+D) / T (B+D)*(C+D) / T C+D
  A+C B+D T

∴ Expected = (rowsum * colsum) / totalsum

※ $x^2$ 통계치를 P-value로 바꾸는 방법

import numpy as np
from scipy.stats import chi2_contingency, chi2

obs3 = np.array([[10, 10, 20], [20, 20, 20]])
x2, p, dof, _ = chi2_contingency(obs3)
print(x2, p, dof)

1 - chi2.cdf(x2, df = dof)

2.7777777777777777 0.24935220877729622 2
0.24935220877729625

*cdf = cumulative distribution function(누적 분포 함수)

 


<참고 자료>
카이제곱 검정 - 위키백과
Chi-squared test - Wikipedia
scipy.stats.chisquare - One Sample Chi-squared test
scipy.stats.chi2_contingency - Two Sample Chi-squared test
# scipy.stats.chi2
카이제곱 검정 - jmp.com
카이제곱 검정을 위한 카이제곱분포 가정과 공식 : 범주형 자료
데이터 분석 초보자를 위한 T-test & Chi-squared test
카이제곱 검정이 세 종류나 있었어?(적합도, 독립성, 동질성)
누적 분포 함수 - 위키백과

P-값 (유의 확률, P-value)

P-값이 뭐지?

유의 확률(有意 確率, 영어: significance probability, asymptotic significance) 또는 p-값(영어: p-value, probability value)은 귀무가설이 맞다고 가정할 때 얻은 결과보다 극단적인 결과가 실제로 관측될 확률이다. 실험의 유의확률은 실험의 표본 공간에서 정의되는 확률변수로서, 0~1 사이의 값을 가진다.
p-값(p-value)은 귀무 가설(null hypothesis)이 맞다는 전제 하에, 표본에서 실제로 관측된 통계치와 '같거나 더 극단적인' 통계치가 관측될 확률이다. 여기서 말하는 확률은 '빈도주의' (frequentist) 확률이다.
- 위키백과

모르고 보면 말이 헷갈리는데, 한 마디로 '귀무가설이 맞다고 볼 수 있는 확률'이다.
조금 더 구체적으로 표현하면 '귀무가설이 참이라는 것을 표본이 나타낼 확률'이다

단순하게 '귀무가설이 맞을 확률'이라고 단정지을 수 있을 것 같은데, 이는 옳지 못하다.
왜냐하면 뽑아낸 표본이 어쩌다 보니 귀무가설이 틀렸다고 말할 수도 있는데 실제로는 맞을 수도 있고(제1종 오류),
반대로 표본이 귀무가설을 맞다고 하는데 실제로는 틀릴 수도 있기 때문이다(제2종 오류).

P-값이 0.05보다 크면 귀무가설이 맞다고 볼 수 있으며(귀무가설 채택, 대안가설 기각),
반대로 0.05보다 작으면 귀무가설이 틀렸다고 볼 수 있다(귀무가설 기각, 대안가설 채택).

<참고 자료>
유의 확률 - 위키백과
1종 오류와 2종 오류 - 위키백과
P값을 올바르게 해석하는 방법 How to Correctly Interpret P Values
p-value의 의미
1종 오류와 2종 오류

T-테스트(Student's T-test)

T-테스트가 뭐지?

t-테스트(t-test) 또는 t-검정 또는 스튜던트 t-테스트(Student's t-test)는 검정통계량이 귀무가설 하에서 t-분포를 따르는 통계적 가설 검정법이다.
- 위키백과

단순하게 말하면 '두 집단의 평균값이 유의미한 차이가 있는지 알아보는 방법'이다.
T-테스트는 One Sample T-test와 Two Sample T-test가 있다.
테스트 결과 두 값이 같을지 다를지 혹은 둘 중 하나가 더 큰지 알아보려면 각각 One-tailed T-test와 Two-tailed T-test를 사용한다.


T-테스트는 어떻게 하는거지?

과정

  1. 귀무가설 설정
  2. 대안가설 설정
  3. 신뢰도 설정 : 모수가 신뢰구간 안에 포함될 확률 (보통 95, 99% 등을 사용)※ 신뢰도 95%의 의미
    = 모수가 신뢰 구간 안에 포함될 확률이 95%
    = 귀무가설이 틀렸지만 우연히 성립할 확률이 5%
  4. P-value 확인P-value는 주어진 가설에 대해서 "얼마나 근거가 있는지"에 대한 값을 0과 1사이의 값으로 나타낸 지표이며
    P-value가 낮다는 것은, 귀무가설이 틀렸을 확률이 높다는 뜻이다.
  5. 이후 P-value를 바탕으로 가설에 대해 결론을 내림

One Sample T-test

  • 한 표본 집단의 평균값을 특정값(주로 모집단의 평균)과 비교하는 것
  • Two-tailed(Two-sided) T-test인 경우
    • 귀무가설(H_0) : 두 값이 같을 것이다 $\rightarrow \overline{x} = \mu$
    • 대안가설(H_a) : 두 값이 같지 않을 것이다 $\rightarrow \overline{x} \neq \mu$
  • One-tailed(One-sided) T-test인 경우
    • 귀무가설(H_0) : 두 값이 같을 것이다 $\rightarrow \overline{x} = \mu$
    • 대안가설(H_a) : 둘 중 하나가 더 클 것이다 $\rightarrow \overline{x} > \mu$ 또는 $\overline{x} < \mu$

<예시>

# Two-tailed T-test
import numpy as np

np.random.seed(1111)
coinflips = np.random.binomial(n = 1, p = 0.5, size = 1000)

print(np.mean(coinflips))

0.509

from scipy import stats

# ttest_1samp 함수의 파라미터 1) Sample 데이터, 2) 비교하려는 값
stats.ttest_1samp(coinflips, 0.5) # coinflips의 평균값이 0.5와 같은가?

Ttest_1sampResult(statistic=0.5690174909554405, pvalue=0.5694721717152109)
∴ p값(p-value)이 0.05보다 크기 때문에 coinflips의 평균은 0.5와 같다고 볼 수 있다.

※ 참고 - One Sample One-tailed T-test를 하려면? (A와 특정값 비교 시)

  • p값을 2로 나누어 준 값을 alpha(0.05)와 비교
  • p값이 0.05보다 작다면 둘 중 하나가 더 크다고 볼 수 있다
  • T-test 통계치가 양수라면 A가 더 크다고 볼 수 있고, 음수라면 작다고 볼 수 있다

※ 내용 추가 : ttest_1samp() 함수의 alternavive 옵션을 사용하여 One Sample One-tailed T-test 가능

# One-tailed T-test
stats.ttest_1samp(coinflips, 0.3, alternative='greater') # 작은지 알고 싶으면 'greater'를 'less'로 설정

Ttest_1sampResult(statistic=13.213850623298551, pvalue=3.7058897137725e-37)
∴ p값(p-value)이 0.05보다 작고, 통계치가 양수이기 때문에 coinflips의 평균은 0.3보다 크다고 볼 수 있다.


Two Sample T-test

  • 두 표본 집단의 평균값을 비교하는 것
  • Two-tailed(Two-sided) T-test인 경우
    • 귀무가설(H_0) : 두 값이 같을 것이다 $\rightarrow \overline{x}_1 = \overline{x}_2$
    • 대안가설(H_a) : 두 값이 같지 않을 것이다 $\rightarrow \overline{x}_1 \neq \overline{x}_2$
  • One-tailed(One-sided) T-test인 경우
    • 귀무가설(H_0) : 두 값이 같을 것이다 $\rightarrow \overline{x}_1 = \overline{x}_2$
    • 대안가설(H_a) : 둘 중 하나가 더 클 것이다 $\rightarrow \overline{x}_1 > \overline{x}_2$ 또는 $\overline{x}_1 < \overline{x}_2$

<예시>

# Two-tailed T-test
np.random.seed(111)

coin1 = np.random.binomial(n = 1, p = 0.5, size = 500)
coin2 = np.random.binomial(n = 1, p = 0.5, size = 200)

print(np.mean(coin1))
print(np.mean(coin2))

stats.ttest_ind(coin1, coin2)

0.476
0.5
Ttest_indResult(statistic=-0.5733615498088787, pvalue=0.5665845925197331)
∴ p값(p-value)이 0.05보다 크기 때문에 두 집단의 평균은 같다고 볼 수 있다.

np.random.seed(111)

coin1 = np.random.binomial(n = 1, p = 0.6, size = 500)
coin2 = np.random.binomial(n = 1, p = 0.5, size = 200)

print(np.mean(coin1))
print(np.mean(coin2))

stats.ttest_ind(coin1, coin2)

0.604
0.5
Ttest_indResult(statistic=2.5217925052968604, pvalue=0.011897284906103034)
∴ p값(p-value)이 0.05보다 작기 때문에 두 집단의 평균은 같다고 볼 수 없다.

# One-tailed T-test
stats.ttest_ind(coin1, coin2, alternative='greater')

Ttest_indResult(statistic=2.5217925052968604, pvalue=0.005948642453051517)
∴ p값(p-value)이 0.05보다 작고 통계치가 양수이기 때문에 coin1의 평균값이 coin2의 평균값보다 크다고 볼 수 있다.


<참고 자료>
t-테스트 - 위키백과
단측-양측 검정 - 위키백과
[통계교육] 풀어쓰는 통계 - t 검정(t-test)이란?
[ 통계, 처음입니다] 헷갈리는 양측검정, 단측검정 (one-tailed, two-tailed)
Hypothesis Test - T-Test
[부트캠프 #3] Statistics (1) T-test
scipy.stats.ttest_1samp - One Sample T-test
scipy.stats.mstats.ttest_ind - Two Sample T-test

평균(mean, average)

  • 데이터의 총합을 그 갯수로 나눈 것
  • 기대값(expected value)이라고도 함(관점과 맥락에 따라 다르게 부르는 듯)
    ! 중앙값(median) : 어떤 주어진 값들을 크기의 순서대로 정렬했을 때 가장 중앙에 위치하는 값

분산(variance)

  • 데이터가 퍼진 정도를 나타낸 것
  • 평균에 대한 편차 제곱의 평균
  • 확률변수 $X$의 분산은 $X$의 기댓값 ${\displaystyle \mu =\operatorname {E} [X]}$로부터 확률변수가 얼마나 떨어져있는지 그 정도를 제곱한 것의 기댓값과 같다.
  • ${\displaystyle \operatorname {Var} (X)=\operatorname {E} \left[(X-\mu )^{2}\right]}$

표준 편차(standard deviation)

  • 분산의 양의 제곱근
  • 분산이 편차를 제곱하면서 숫자가 커지게 되면서 값의 의미 파악이 어려워지는 문제가 있음
  • 표준 편차는 이를 해소하기 위해 분산에 제곱근을 씌워 본래의 단위로 맞춰줌

<예시>

성인 남성 5명의 몸무게 = [165, 181, 178, 170, 173]

평균 = (165+181+178+170+173) / 5 = 173.4

분산 = ((165-173.4) + (181-173.4) + (178-173.4) + (170-173.4) + (173-173.4)) / 5 = 32.24 (소수점 둘째 자리까지만)

표준 편차 = √분산 = 5.678 (소수점 셋째 자리까지만)

import numpy as np
weights = np.array([165, 181, 178, 170, 173])
print(f'평균 : {weights.mean()}')
print(f'분산 : {weights.var()}')
print(f'표준 편차 : {weights.std()}')

평균 : 173.4
분산 : 32.239999999999995
표준 편차 : 5.6780278266313555

 

<참고>
[기초통계] 평균과 분산의 의미, 개념
통계의 기초인 평균, 분산, 표준편차

+ Recent posts