이번 포스팅에서는 통계학에서 사용하는 p-값(p-value)이란 무엇이고, 어떻게 해석하는지 소개하겠습니다. 

 

1. 통계학에서 p-value 는 무엇이며, 무엇에 사용하는가? 

2. 독립된 두 표본 간 평균 차이 t-검정에서 p-value 해석 예시

3. Python으로 독립 두 표본 t-검정 (independent two-sample t-test)

4. 빈도론자(Frequentist)와 베이지안(Bayesian) 간 p-value 해석의 차이점은 무엇인가?

 

 

 

1. 통계학에서 p-value 는 무엇이며, 무엇에 사용하는가? 


통계학에서 p-값(확률 값)은 귀무가설(null hypothesis, H0)에 대한 증거를 평가하는 데 도움이 되는 지표입니다. 귀무가설은 차이나 효과가 없다는 것을 나타내는 명제로, 연구자들은 이 귀무가설을 기각할 충분한 증거가 있는지를 판단하기 위해 통계 검정을 사용합니다. 

p-값은 귀무가설이 참일 때 관측 결과나 그보다 더 극단적인 결과가 나올 확률을 나타냅니다. 다시 말해, 이는 귀무가설에 대한 증거의 강도를 측정하는 것입니다.

 


주로 다음과 같은 방식으로 사용합니다. 

(1) 만약 p-값이 작다면(일반적으로 미리 정의된 유의수준, 예를 들어 0.05보다 낮다면), 이는 관측 결과가 무작위로 발생할 가능성이 적다는 것을 나타내며, 이는 귀무가설(null hypothesis, H0)을 기각하고 대립가설(alternative hypothesis, H1)을 채택합니다.

(2) 만약 p-값이 크다면, 이는 관측 결과가 귀무가설과 상당히 일치한다는 것을 나타내며, 귀무가설을 기각할 충분한 증거가 없다는 것을 의미합니다.

 


p-값이 작다고 해서 특정 가설이 참임을 증명하는 것은 아닙니다. 이는 단순히 데이터가 귀무가설과 일치하지 않는다는 것을 나타냅니다. 또한, 유의수준(예: 0.05)의 선택은 어느 정도 임의적이며 연구자의 판단이나 해당 분야의 관례에 따라 달라집니다.

연구자들은 p-값을 신중하게 해석하고 귀무가설 검정에 관한 결정을 내릴 때 다른 관련 정보와 함께 고려해야 합니다. p-값은 효과의 크기나 실제적인 중요성에 대한 정보를 제공하지 않으며, 단지 귀무가설에 대한 증거의 강도(the evidence against a null hypothesis)를 나타냅니다

 



2. 독립된 두 표본 간 평균 차이 t-검정에서 p-value 해석 예시

 

예를 들어 혈압을 낮추는 새로운 약물에 대한 임상시험을 다루는 예를 살펴보겠습니다. 연구자들은 새로운 약물이 플라시보에 비해 혈압을 낮추는 데 효과적인지를 테스트하고자 합니다. 이 맥락에서 p-값의 사용과 해석은 다음과 같을 수 있습니다.

시나리오:

- 귀무가설 (H0): 새로운 약물은 혈압에 아무런 영향을 미치지 않으며, 관측된 차이는 무작위로 발생한 것이다.
- 대립가설 (H1): 새로운 약물은 플라시보에 비해 혈압을 낮추는 데 효과적이다. 즉, 플라시보 그룹보다 신약 그룹의 혈압이 더 낮다.


실험 설계:

- 연구자들은 참가자들을 새로운 약물을 받는 그룹과 플라시보를 받는 그룹으로 무작위로 배정합니다.
- 특정한 치료 기간 이후 각 참가자의 혈압 변화를 측정합니다.


데이터 분석:

- 각 10개의 샘플 데이터를 수집하고 분석한 후, 연구자들은 두 그룹 간의 혈압 변화의 평균을 비교하기 위해 통계적 검정(예: t-검정)을 수행합니다.
- 예를 들어, 검정 결과로 얻은 t-통계량이 3.372, p-값이 0.0017인 경우를 가정해 봅시다.


해석:

- 얻은 p-값(0.0034)이 선택된 유의수준(예: alpah=0.05)보다 작습니다.  
- 빈도주의적 해석: 이는 만약 귀무가설이 사실이라면(즉, 새로운 약물이 효과가 없다면), 실제로 관측된 데이터보다 더 극단적인 데이터를 관측할 확률이 0.17%밖에 되지 않는다는 것을 나타냅니다. 0.0017이 유의수준 0.05 보다 작으므로 연구자들은 귀무가설을 기각하기로 결정할 수 있습니다.
- 결정: 연구자들은 귀무가설을 기각하고 새로운 약물이 플라시보에 비해 혈압을 낮추는 데 효과적임을 시사하는 충분한 증거가 있다고 결론지을 수 있습니다. 

 

아래에는 t-분포표를 수작업으로 해석하는 방법입니다. 

t-분포표의 행은 자유도(degree of freedom) 으로서, 만약 샘플의 관측치 개수가 주어지면 (자유도 =  n-1) 로 계산해줍니다. t-분포표의 열은 유의수준 알파(alpha)이고, 표의 안에 있는 숫자는 가설 검정에 사용하는 t-통계량입니다. (정규분포표에서는 행과 열이 z-통계량이고, 표의 가운데 값이 p-value인데요, t-분포표는 이와 달라서 좀 헷갈리는 면이 있습니다.) 샘플로 부터 자유도와 t-통계량를 계산하면, 아래의 t-분포표를 보고 상단 열에서 p-값을 찾으면 됩니다.  

 

이번 예에서는 가령, 관측치가 각 그룹별로 10개라고 하면, 자유도 = n-1 = 9 가 됩니다. 

그리고 t-통계량을 계산했더니 3.372가 나왔다고 했을 때, 아래 표에서 t-통계량 3.372 는 자유도 9인 행에서 보면 3.250보다는 크고, 3.690보다는 작은 값입니다. 따라서 상단의 알파(alpha) 값은 0.005와 0.0025 사이의 값이라고 추정할 수 있습니다. 그런데 대립가설이 "H1: 새로운 약물은 플라시보에 비해 혈압을 낮추는 데 효과적이다. 즉, 플라시보 그룹보다 신약 그룹의 혈압이 더 낮다." 이므로 단측검정 (alternative = 'greater') 을 사용하므로, p-value=0.005/2=0.0025 보다 작고 ~ p-value=0.0025/2=0.00125 보다는 큰 값으로 볼 수 있습니다. 

 

유의수준 0.05보다는 훨씬 작은 값이므로, 귀무가설을 기각하고 대립가설을 채택합니다. 즉, 신약이 혈압을 낮추는데 효과가 있다고 판단할 수 있습니다. 

 

 

t-분포표 해석하기

 


p-값 자체만으로는 효과의 크기나 임상적 중요성에 대한 정보를 제공하지 않습니다. 연구자들은 통계 검정을 기반으로 의사결정을 내릴 때 효과 크기, 신뢰구간 및 실제적인 중요성도 고려해야 합니다. 또한 베이지안 프레임워크에서는 관측된 데이터를 기반으로 사전 신념을 업데이트하는 것이 포함될 수 있습니다.

 

 

3. Python으로 독립 두 표본 t-검정 (independent two-sample t-test)

 

scipy 모듈의 scipy.stats.stats.ttest_ind() 메소드를 사용해서 독립된 두 표본의 평균에 차이가 있는지를 t-검정해 보겠습니다.

 

- 귀무가설(H0): Group 1과 Group 2의 평균에는 차이가 없다. (mu_g1 = mu_g2)

- 대립가설(H1): Group 1가 Group 2의 평균보다 크다. (mu_g1 > mu_g2)

 

t 통계량이 3.37이고 p-값(p-value)이 0.0034 로서, 유의수준(alpha) 0.05보다 p-값이 더 작으므로 귀무가설을 기각(reject null hypothesis)하고, 대립가설을 채택(accept alternative hypothesis)합니다. 

 

## independent two-sample t-test
import numpy as np
import scipy.stats as stats

# Example data for two independent samples
group1 = [25, 30, 32, 28, 34, 26, 30, 29, 31, 27]
group2 = [21, 24, 28, 25, 30, 22, 26, 23, 27, 24]

print('Mean of Group 1:', np.mean(group1))
print('Mean of Group 2:', np.mean(group2))

# Perform independent two-sample t-test
t_statistic, p_value = stats.ttest_ind(
    group1, group2, 
    equal_var=True,
    alternative='greater', # alternative hypothesis: mu_1 is greater than mu_2
)

# Print the results
print(f'T-statistic: {t_statistic}')
print(f'P-value: {p_value}')

# Check the significance level (e.g., 0.05)
alpha = 0.05
if p_value < alpha:
    print('Reject the null hypothesis. \
There is a significant difference between the groups.')
else:
    print('Fail to reject the null hypothesis. \
There is no significant difference between the groups.')
    
# Mean of Group 1: 29.2
# Mean of Group 2: 25.0
# T-statistic: 3.3723126837047914
# P-value: 0.0016966232763530164
# Reject the null hypothesis. There is a significant difference between the groups.

 




4. 빈도론자(Frequentist)와 베이지안(Bayesian) 간 p-value 해석의 차이점은 무엇인가? 

 

빈도론자(Frequentist)와 베이지안(Bayesian) 간의 통계학적 접근 방식에서 p-값의 해석은 다릅니다. 각 접근 방식이 p-값을 어떻게 해석하는지 간략하게 살펴보겠습니다.

(1) 빈도주의적 해석 (Frequentist Interpretation):

- 빈도주의 통계학에서 p-값은 귀무가설이 참일 때 관측된 데이터나 그보다 더 극단적인 데이터가 나올 확률로 간주됩니다.
- 의사결정 과정은 일반적으로 특정한 유의수준(alpha), 보통 0.05,을 기준으로 귀무가설을 기각하거나 기각하지 않는 것으로 구성됩니다. 
- 만약 p-값이 선택된 유의수준보다 작거나 같으면 귀무가설이 기각됩니다. p-값이 유의수준보다 크면 귀무가설이 기각되지 않습니다.
- 빈도주의 통계학에서는 확률을 가설에 할당하지 않으며 대신 특정 가설 하에서 데이터의 확률에 중점을 둡니다.


(2) 베이지안 해석 (Bayesian Interpretation):

- 베이지안 통계학에서는 관측된 데이터를 기반으로 가설에 대한 사전 신념(prior beliefs)을 업데이트하는 것에 중점을 둡니다.
- 베이지안 분석은 가설에 대한 사전 신념과 관측된 데이터 모두를 고려하여 가설에 대한 사후 확률 분포(posterior probability distribution for the hypothesis)를 제공합니다.
- 베이지안은 의사결정을 위해 일반적으로 p-값 만을 고려하지 않습니다. 대신 전체 사후 분포를 고려하며 이전 정보를 통합합니다. 
- 베이지안 분석은 데이터가 주어졌을 때 가설이 참일 확률을 직접 계산합니다. 이는 빈도주의적 접근과 달리 가설이 주어진 데이터에 대한 확률만을 고려하는 것이 아닙니다.
- 가설 검정을 위해 때로는 베이지안 분석에서는 가설 간의 가능도 비율(ratio of the likehoods under different hypotheses)인 베이즈 팩터(Bayes factor)를 사용하기도 합니다.

요약하면 핵심적인 차이점은 확률 해석에 있습니다. 빈도주의자들은 확률을 사건의 장기적 빈도로 간주하며, 베이지안은 확률을 신념이나 불확실성의 척도로 해석합니다. 빈도주의자들은 p-값을 가설에 대한 의사결정에 사용하며, 베이지안안은 베이지안 추론을 사용하여 직접적으로 신념을 업데이트하고 표현합니다.

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 SciPy 모듈을 사용해서 각 원소 간 짝을 이루어서 유클리디언 거리를 계산(calculating pair-wise distances)하는 방법을 소개하겠습니다. 본문에서 scipy 의 거리 계산함수로서 pdist()와 cdist()를 소개할건데요, 반환하는 결과물의 형태에 따라 적절한 것을 선택해서 사용하면 되겠습니다. 마지막으로 scipy 에서 제공하는 거리를 계산하는 기준(distance metric)에 대해서 알아보겠습니다. 

(Scikit-Learn 모듈에도 거리 계산 함수가 있는데요, SciPy 모듈에 거리 측정 기준이 더 많아서 SciPy 모듈로 소개합니다.) 

 

(1) scipy.spatial.distance.pdist(): returns condensed distance matrix Y.

(2) scipy.spatial.distance.cdist(): returns M by N distance matrix.

(3) 거리 측정 기준(distance metric)

 

 

calculating pair-wise distances using python SciPy

 

 

먼저, 예제로 사용할 간단한 샘플 데이터셋으로서 x와 y의 두 개의 칼럼에 대해 4개의 원소를 가지는 numpy array 로 만들어보겠습니다. 그리고 matplotlib 으로 산점도를 그려서 4개의 원소에 대해 x와 y의 좌표에 산점도를 그려서 확인해보겠습니다. 

 

ID x y
1 3 2
2 3 3
3 5 5
4 6 7

 

import numpy as np
import matplotlib.pyplot as plt

X = np.array([
    # x, y
    [3, 2], # ID 1
    [3, 3], # ID 2
    [5, 5], # ID 3
    [6, 7], # ID 4
])

plt.plot(X[:, 0], X[:, 1], 'o')
plt.show()

scatter plot

 

 

(1) scipy.spatial.distance.pdist(X, metric='euclidean'): returns condensed distance matrix Y.

 

scipy.spatial.distance.pdist(X, metric='euclidean') 함수는 X 벡터 또는 행렬에서 각 원소(관측치) 간 짝을 이루어서 유클리디언 거리를 계산해줍니다. 그리고 응축된 형태의 거리 행렬(condensed distance matrix)을 반환합니다. 아래의 (1)번 pdist() 계산 결과가 반환된 형태를 (2) cdist() 로 계산 결과가 반환된 형태와 비교해보면 이해가 쉬울 거예요. 

 

pdist(X, metric='eudlidean') 계산 결과는 각 관측치 간 짝을 이룬 거리 계산이므로 아래 값들의 계산 결과입니다. 거리 계산 결과는 자기 자신과의 거리인 대각행렬 부분은 제외하고, 각 원소 간 쌍을 이룬 부분에 대해서만 관측치의 순서에 따라서 반환되었습니다. (예: ID1 : ID2, ID1 : ID3, ID1 : ID4, ID2 : ID3, ID2 : ID4, ID3 : ID4)

 

   * DISTANCE_euclidean(ID 1, ID 2) = sqrt((3-3)^2 + (2-3)^2) = 1.00

   * DISTANCE_euclidean(ID 1, ID 3) = sqrt((3-5)^2 + (2-5)^2) = 3.60

   * DISTANCE_euclidean(ID 1, ID 4) = sqrt((3-6)^2 - (2-7)^2) = 5.83

   * DISTANCE_euclidean(ID 2, ID 3) = sqrt((3-5)^2 - (3-5)^2) = 2.82

   * DISTANCE_euclidean(ID 2, ID 4) = sqrt((3-6)^2 - (3-7)^2) = 5.00

   * DISTANCE_euclidean(ID 3, ID 4) = sqrt((5-6)^2 - (5-7)^2) = 2.23

 

# scipy.spatial.distance.pdist(X, metric='euclidean', *, out=None, **kwargs)
# : Pairwise distances between observations in n-dimensional space.
# : Returns a condensed distance matrix Y.

from scipy.spatial import distance

X_pdist = distance.pdist(X, metric='euclidean')

X_pdist
# array([1.        , 3.60555128, 5.83095189, 2.82842712, 5.        ,
#        2.23606798])

 

 

 

(2) scipy.spatial.distance.cdist(): returns M by N distance matrix.

 

scipy.spatial.distance.cdist(XA, XB, metric='euclidean') 함수는 원소(관측치) 간 쌍을 이루어 유클리디언 거리를 계산합니다만, 위의 (1) pdist() 함수와는 달리, 

 

  - (a) input 으로 XA, XB 의 두 개의 행렬 (혹은 벡터)를 받으며, (vs. pdist() 는 X 행렬 한 개만 받음)

  - (b) output 으로 M by N 거리 행렬을 반환합니다. (vs. pdist() 는 condensed distance matrix 를 반환)

 

하는 차이점이 있습니다.  위의 (1)번 scipy.spatial.distance.pdist() 함수와 (2) scipy.spatial.distance.cdist() 함수의 input, output을 비교해보면 이해가 쉬울 거예요. 

 

# scipy.spatial.distance.cdist(XA, XB, metric='euclidean', *, out=None, **kwargs)
# : Compute distance between each pair of the two collections of inputs.
# : A M by N distance matrix is returned.(M for X, N for Y)

from scipy.spatial import distance

X_cdist = distance.cdist(X, X, metric='euclidean')
X_cdist
# array([[0.        , 1.        , 5.83095189, 3.60555128],
#        [1.        , 0.        , 5.        , 2.82842712],
#        [5.83095189, 5.        , 0.        , 2.23606798],
#        [3.60555128, 2.82842712, 2.23606798, 0.        ]])

 

 

 

(3) 거리 측정 기준(distance metric)

 

위의 (1), (2)번에서는 거리 측정 기준으로 디폴트 옵션인 '유클리디언 거리(metric='euclidean distance') 를 사용해서 원소 간 쌍을 이룬 거리를 계산하였습니다. 

 

scipy 모듈은 유클리디어 거리 외에도 다양한 거리 측정 기준을 제공합니다. (맨하탄 거리, 표준화 거리, 마할라노비스 거리, 자카드 거리, 코사인 거리, 편집 거리에 대해서는 아래의 Reference 의 링크를 참고하세요.)

 

[ scipy.spatial.distance.pdist(), cdist() 함수의 metric options (알파벳 순서) ]

‘braycurtis’, ‘canberra’, ‘chebyshev’, ‘cityblock’, ‘correlation’, ‘cosine’, ‘dice’, ‘euclidean’, ‘hamming’, ‘jaccard’, ‘jensenshannon’, ‘kulsinski’, ‘kulczynski1’, ‘mahalanobis’, ‘matching’, ‘minkowski’, ‘rogerstanimoto’, ‘russellrao’, ‘seuclidean’, ‘sokalmichener’, ‘sokalsneath’, ‘sqeuclidean’, ‘yule’.

 

아래에는 metric='cityblcok' 매개변수 설정을 통해서 '맨하탄 거리(Manhattan distance)'를 계산하여 보았습니다. cdist() 함수를 사용하였으므로 4 by 4 행렬의 형태로 관측치 간 쌍을 이룬 거리 계산 결과를 반환합니다. 

 

   * DISTANCE_manhattan(ID 1, ID 2) = |3-3|+|2-3| = 1

   * DISTANCE_manhattan(ID 1, ID 3) = |3-5|+|2-5| = 5

   * DISTANCE_manhattan(ID 1, ID 4) = |3-6|+|2-7| = 8

   * DISTANCE_manhattan(ID 2, ID 3) = |3-5|+|3-5| = 4

   * DISTANCE_manhattan(ID 2, ID 4) = |3-6|+|3-7| = 7

   * DISTANCE_manhattan(ID 3, ID 4) = |5-6|+|5-7| = 3

 

X
# array([[3, 2],
#        [3, 3],
#        [5, 5],
#        [6, 7]])


## Manhattan Distance
from scipy.spatial import distance

X_cdist_cityblock = distance.cdist(X, X, metric='cityblock')

X_cdist_cityblock
# array([[0., 1., 5., 8.],
#        [1., 0., 4., 7.],
#        [5., 4., 0., 3.],
#        [8., 7., 3., 0.]])

 

 

 

[ Reference ]

 

[1] scipy.spatial.distance.pdist
https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html#scipy.spatial.distance.pdist
[2] scipy.spatial.distance.cdist
https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html#scipy.spatial.distance.cdist
[3] 맨하탄 거리(Manhattan distance), 유클리드 거리(Euclidean distance), 표준화 거리(), 마할라노비스 거리
https://rfriend.tistory.com/199
[4] 자카드 거리 (Jaccard distance): https://rfriend.tistory.com/318
[5] 코사인 거리 (cosine distance): https://rfriend.tistory.com/319
[6] 편집거리 (edit distance, Levenshtein metric): https://rfriend.tistory.com/320

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

시계열 데이터를 분석할 때 꼭 확인하고 처리해야 하게 있는데요, 바로 결측값 여부 확인과 결측값 처리입니다. 

 

시계열 데이터의 결측값을 처리하는 방법에는

   (1) 보간 (Interpolation)

   (2) 이전 값 또는 다음 값 이용 (previous/next value)

   (3) 이동 평균 (Moving average)

등의 여러가지 방법이 있습니다. 

 

 

[ 시계열 데이터 결측값 처리 방법 (How to handle the time series missing data) ]

 

 

아래의 보간(Interpolation)에 대한 내용은 Wikipedia 의 내용을 번역하여 소개합니다. 

 

데이터 분석의 수학 분야에서는 "보간법(Interpolation)을 이미 알려진 데이터 포인트들의 이산형 집합의 범위에 기반해서 새로운 데이터 포인트들을 만들거나 찾는 추정(estimation)의 한 유형"으로 봅니다. 

 

공학과 과학 분야에서는 종종 샘플링이나 실험을 통해서 많은 수의 데이터 포인트들을 획득하는데요, 이들 데이터는 어떤 함수(a function)의 값이나 독립변수(independent variable)의 제한적인 수의 값을 표현한 것입니다. 종종 독립변수의 중간 사이의 값을 위한 함수의 값을 추정(estimate the value of that function for an intermediat value of the independent variable)하는 보간이 필요합니다.  

 

밀접하게 관련된 문제로서 복잡한 함수를 간단한 함수로 근사하게 추정(the approximation of a complicated function by a simple function)하는 것이 있습니다. 어떤 주어진 함수의 공식이 알려져있지만, 너무 복잡해서 효율적으로 평가하기가 어렵다고 가정해봅시다. 원래의 함수로부터 적은 수의 새로운 데이터 포인트는 원래의 값과 상당히 근접한 간단한 함수를 생성해서 보간할 수 있습니다. 단순성(simplicity)으로부터 얻을 수 있는 이득이 보간에 의한 오차라는 손실보다 크고, 연산 프로세스면서도 더 좋은 성능(better performance in calculation process)을 낼 수도 있습니다.   

 

 

이번 포스팅에서는 Python scipy 모듈을 이용해서 시계열 데이터 결측값을 보간(Interpolation)하는 방법을 소개하겠습니다. 

 

1. 이전 값/ 이후 값을 이용하여 결측값 채우기 (Imputation using the previous/next values)

2. Piecewise Constant Interpolation

3. 선형 보간법 (Linear Interpolation)

4. 스플라인 보간법 (Spline Interpolation)

 

 

[ Python scipy 모듈을 이용한 결측값 보간 (Interpolation using Python scipy module)  ]

 

 

먼저 '0.5'로 동일한 간격을 가지는 x 값들에 대한 사인 함수 (sine function) 의 y값을 계산해서  예제 데이터로 사용하겠습니다. 아래 예졔의 점과 점 사이의 값들이 비어있는 결측값이라고 간주하고, 이들 값을 채워보겠습니다. 

 

import numpy as np
from scipy import interpolate
import matplotlib.pyplot as plt

## generating the original data with missing values
x = np.arange(0, 4*np.pi, 0.5)
y = np.sin(x)

plt.plot(x, y, "o")
plt.show()

original data with missing values

 

 

1. 이전 값/ 다음 값을 이용하여 결측값 채우기 (Imputation using the previous/next values)

 

데이터 포인트 사이의 값을 채우는 가장 간단한 방법은 이전 값(previous value) 나 또는 다음 값(next value)을 이용하는 것입니다. 함수를 추정하는 절차가 필요없으므로 연산 상 부담이 적지만, 데이터 추정 오차는 단점이 될 수 있습니다. 

 

## Interpolation using the previous value
f_prev = interpolate.interp1d(
    x, y, kind='previous') # next
y_new_prev = f_prev(xnew)

plt.plot(x, y, "o", xnew, y_new_prev, '-')
plt.show()

interpolation using the previous value

 

 

 

2. Piecewise Constant Interpolation

 

위 1번의 이전 값 또는 다음 값을 이용한 사이값 채우기를 합쳐놓은 방법입니다. Piecewise Constant Interpolation은 특정 데이터 포인트를 기준으로 가장 가까운 값 (nearest value) 을 가져다가 사이값을 보간합니다. ("최근접 이웃 보간"이라고도 함)

 

간단한 문제에서는 아래 3번에서 소개하는 Linear Interpolation 이 주로 사용되고, Piecewise Constant Interpolation 은 잘 사용되지 않는 편입니다. 하지만 다차원의 다변량 보간 (in higher-dimensional multivariate interpolation)의 경우, 속도와 단순성(speed and simplicity) 측면에서 선호하는 선택이 될 수 있습니다. 

 

## Piecewise Constant Interpolation
f_nearest = interpolate.interp1d(
    x, y, kind='nearest')

y_new_nearest = f_nearest(xnew)

plt.plot(x, y, "o", xnew, y_new_nearest)
plt.show()

Piecewise constant interpolation

 

 

 

3. Linear Interpolation

 

선형 보간법은 가장 쉬운 보간법 중의 하나로서, 연산이 빠르고 쉽습니다. 하지만 추정값이 정확한 편은 아니며, 데이터 포인트 Xk 에서 미분 가능하지 않다는 단점도 있습니다. 

 

일반적으로, 선형 보간법은 두 개의 데이터 포인트, 가령 (Xa, Ya)와 (Xb, Yb), 를 사용해서 다음의 공식으로 두 값 사이의 값을 보간합니다. 

 

Y = Ya + (Yb - Ya) * (X - Xa) / (Xb- Xa)    at the point (x, y)

 

## Linear Interpolation
f_linear = interpolate.interp1d(
    x, y, kind='linear')

y_new_linear = f_linear(xnew)

plt.plot(x, y, "o", xnew, y_new_linear, '-')
plt.show()

Linear interpolation

 

 

 

4. Spline Interpolation

 

다항식 보간법(Polynomial Interpolation)은 선형 보간법을 일반화(generalization of linear interpolation)한 것입니다. 선형 보간법에서는 선형 함수를 사용했다면, 다항식 보간법에서는 더 높은 차수의 다항식 함수를 사용해서 보간하는 것으로 대체한 것입니다. 

일반적으로, 만약 우리가 n개의 데이터 포인트를 가지고 있다면 모든 데이터 포인트를 통과하는 n-1 차수의 다차항 함수가 존재합니다. 보간 오차는 데이터 포인트 간의 거리의 n 차승에 비례(interpolation error is proportional to the distance between the data points to the power n)하며, 다차항 함수는 미분가능합니다. 따라서 선형 보간법의 대부분의 문제를 다항식 보간법은 극복합니다. 하지만 다항식 보간법은 선형 보간법에 비해 복잡하고 연산에 많은 비용이 소요됩니다. 그리고 끝 점(end point) 에서는 진동하면서 변동성이 큰 값을 추정하는 문제가 있습니다. 

 

스플라인 보간법은 각 데이터 포인트 구간별로 낮은 수준의 다항식 보간을 사용 (Spline interpolation uses low-degree polynomials in each of the intervals) 합니다. 그리고 이들이 함께 부드럽게 연결되어서 적합될 수 있도록 다항식 항목을 선택(, and chooses the polynomial pieces such that they fit smoothly together)합니다. 이렇게 적합된 함수를 스플라인(Spline) 이라고 합니다. 

 

스플라인 보간법(Spline Interpolation)은 다항식 보간법의 장점은 살리고 단점은 피해간 보간법입니다. 스플라인 보간법은 다항식 보간법처럼 선형 보간법보다 보간 오차가 더 작은 반면에, 고차항의 다항식 보간법보다는 보간 함수가 부드럽고 평가하기가 쉽습니다.  

 

## Spline Interpolation
f_quadr = interpolate.interp1d(
    x, y, kind='quadratic') # cubic

y_new_quadr = f_quadr(xnew)

plt.plot(x, y, "o", xnew, y_new_quadr)
plt.show()

Polynomial interpolation

 

 

[ Reference ]

1. 보간법(interpolation): https://en.wikipedia.org/wiki/Interpolation

2. scipy 모듈: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다.

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

2개의 모집단에 대한 평균을 비교, 분석하는 통계적 기법으로 t-Test를 활용하였다면, 비교하고자 하는 집단이 2개 이상일 경우에는 분산분석 (ANOVA : Analysis Of Variance)를 이용합니다. 

 

설명변수는 범주형 자료(categorical data)이어야 하며, 종속변수는 연속형 자료(continuous data) 일 때 2개 이상 집단 간 평균 비교분석에 분산분석(ANOVA) 을 사용하게 됩니다.

 

분산분석(ANOVA)은 기본적으로 분산의 개념을 이용하여 분석하는 방법으로서, 분산을 계산할 때처럼 편차의 각각의 제곱합을 해당 자유도로 나누어서 얻게 되는 값을 이용하여 수준평균들간의 차이가 존재하는 지를 판단하게 됩니다.  이론적인 부분에 대한 좀더 자세한 내용은 https://rfriend.tistory.com/131 를 참고하세요. 

 

one-way ANOVA  일원분산분석

 

이번 포스팅에서는 Python의  scipy 모듈의 stats.f_oneway() 메소드를 사용하여 샘플의 크기가 서로 다른 3개 그룹 간 평균에 차이가 존재하는지 여부를 일원분산분석(one-way ANOVA)으로 분석하는 방법을 소개하겠습니다. 

 

분산분석(Analysis Of Variance) 검정의 3가지 가정사항을 고려해서, 샘플 크기가 서로 다른 가상의 3개 그룹의 예제 데이터셋을 만들어보겠습니다. 

 

[ 분산분석  검정의 가정사항 (assumptions of ANOVA test) ]

  (1) 독립성: 각 샘플 데이터는 서로 독립이다. 
  (2) 정규성: 각 샘플 데이터는 정규분포를 따르는 모집단으로 부터 추출되었다. 
  (3) 등분산성: 그룹들의 모집단의 분산은 모두 동일하다. 

 

먼저, 아래의 예제 샘플 데이터셋은 그룹1과 그룹2의 평균은 '0'으로 같고, 그룹3의 평균은 '5'로서 나머지 두 그룹과 다르게 난수를 발생시켜 가상으로 만든 것입니다. 

 

# 3 groups of dataset with different sized samples 
import numpy as np
import pandas as pd
np.random.seed(1004)

data1 = np.random.normal(0, 1, 50)
data2 = np.random.normal(0, 1, 40)
data3 = np.random.normal(5, 1, 30) # different mean

data123 = [data1, data2, data3]


print(data123)
[Out]
[array([ 0.59440307,  0.40260871, -0.80516223,  0.1151257 , -0.75306522,
       -0.7841178 ,  1.46157577,  1.57607553, -0.17131776, -0.91448182,
        0.86013945,  0.35880192,  1.72965706, -0.49764822,  1.7618699 ,
        0.16901308, -1.08523701, -0.01065175,  1.11579838, -1.26497153,
       -1.02072516, -0.71342119,  0.57412224, -0.45455422, -1.15656742,
        1.29721355, -1.3833716 ,  0.3205909 , -0.59086187, -1.43420648,
        0.60998011,  0.51266756,  1.9965168 ,  1.42945668,  1.82880165,
       -1.40997132,  0.49433367,  0.9482873 , -0.35274099, -0.15359935,
       -1.18356064, -0.75440273, -0.85981073,  1.14256322, -2.21331694,
        0.90651805,  2.23629   ,  1.00743665,  1.30584548,  0.46669171]), array([-0.49206651, -0.08727244, -0.34919043, -1.11363541, -1.71982966,
       -0.14033817,  0.90928317, -0.60012686,  1.03906073, -0.03332287,
       -1.03424396,  0.15929405,  0.33053582,  0.02563551, -0.09213904,
       -0.91851177,  0.3099129 , -1.24211638, -0.33113027, -1.64086666,
       -0.27539834, -0.05489003,  1.50604364, -1.37756156, -1.25561652,
        0.16120867, -0.42121705,  0.2341905 , -1.20155195,  1.48131392,
        0.29105321,  0.4022031 , -0.41466037,  1.00502917,  1.45376705,
       -0.07038153,  0.52897801, -2.37895295, -0.75054747,  1.10641762]), array([5.91098191, 4.14583073, 4.71456131, 3.88528941, 5.23020779,
       5.12814125, 3.44610618, 5.36308351, 4.69463298, 7.49521024,
       5.41246681, 3.8724271 , 4.60265531, 4.60082925, 4.9074518 ,
       3.8141367 , 6.4457503 , 4.27553455, 3.63173152, 6.25540542,
       3.77536981, 7.19435668, 6.25339789, 6.43469547, 4.27431061,
       5.16694916, 7.21065725, 5.68274021, 4.81732021, 3.81650656])]

 

 

 

위의 3개 그룹의 커널밀도추정 그래프 (Kernel Density Estimates Plot)를 겹쳐서 그려보면, 아래와 같이 2개 집단은 서로 평균이 비슷하고 나머지 1개 집단은 평균이 확연히 다르다는 것을 직관적으로 알 수 있습니다. 

# Kernel Density Estimate Plot
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['figure.figsize'] = [10, 6]

sns.kdeplot(data1)
sns.kdeplot(data2)
sns.kdeplot(data3)
plt.show()

 

 

상자 그래프 (Box Plot) 으로 3개 집단 간 평균의 위치와 퍼짐 정도를 비교해보면, 역시 아래와 같이 그룹1과 그룹2는 서로 중심위치가 서로 비슷하고 그룹3만 중심위치가 확연히 다름을 알 수 있습니다. 

 

# Boxplot
sns.boxplot(data=data123)
plt.xlabel("Group", fontsize=14)
plt.ylabel("Value", fontsize=14)
plt.show()

 

 

이제 scipy 모듈의 scipy.stats.f_oneway() 메소드를 사용해서 서로 다른 개수의 샘플을 가진 3개 집단에 대해 평균의 차이가 존재하는지 여부를 일원분산분석을 통해 검정을 해보겠습니다. 

 

  - 귀무가설(H0): 3 집단의 평균은 모두 같다. (mu1 = mu2 = m3)

  - 대립가설(H1):  3 집단의 평균은 같지 않다.(적어도 1개 집단의 평균은 같지 않다) (Not H0)

 

F통계량은 매우 큰 값이고 p-value가 매우 작은 값이 나왔으므로 유의수준 5% 하에서 귀무가설을 기각하고 대립가설을 채택합니다. 즉, 3개 집단 간 평균의 차이가 존재한다고 평가할 수 있습니다

# ANOVA with different sized samples using scipy
from scipy import stats

stats.f_oneway(data1, data2, data3)
[Out] 
F_onewayResult(statistic=262.7129127080777, pvalue=5.385523527223916e-44)

 

 

F통계량과 p-value 를 각각 따로 따로 반환받을 수도 있습니다. 

# returning f-statistic and p-value
f_val, p_val = stats.f_oneway(*data123)

print('f-statistic:', f_val)
print('p-vale:', p_val)
[Out]
f-statistic: 262.7129127080777
p-vale: 5.385523527223916e-44

 

 

scipy 모듈의  stats.f_oneway() 메소드를 사용할 때 만약 데이터에 결측값(NAN)이 포함되어 있으면  'NAN'을 반환합니다. 위에서 만들었던 'data1'  numpy array 의 첫번째 값을  np.nan 으로 대체한 후에 scipy.stats.f_oneway() 로 일원분산분석 검정을 해보면  'NAN'(Not A Number)을 반환한 것을 볼 수 있습니다. 

 

# if there is 'NAN', then returns 'NAN'
data1[0] = np.nan
print(data1)
[Out]
array([        nan,  0.40260871, -0.80516223,  0.1151257 , -0.75306522,
       -0.7841178 ,  1.46157577,  1.57607553, -0.17131776, -0.91448182,
        0.86013945,  0.35880192,  1.72965706, -0.49764822,  1.7618699 ,
        0.16901308, -1.08523701, -0.01065175,  1.11579838, -1.26497153,
       -1.02072516, -0.71342119,  0.57412224, -0.45455422, -1.15656742,
        1.29721355, -1.3833716 ,  0.3205909 , -0.59086187, -1.43420648,
        0.60998011,  0.51266756,  1.9965168 ,  1.42945668,  1.82880165,
       -1.40997132,  0.49433367,  0.9482873 , -0.35274099, -0.15359935,
       -1.18356064, -0.75440273, -0.85981073,  1.14256322, -2.21331694,
        0.90651805,  2.23629   ,  1.00743665,  1.30584548,  0.46669171])

# returns 'nan'
stats.f_oneway(data1, data2, data3)
[Out] 
F_onewayResult(statistic=nan, pvalue=nan)

 

 

샘플 데이터에 결측값이 포함되어 있는 경우, 결측값을 먼저 제거해주고 일원분산분석 검정을 실시해주시기 바랍니다. 

 

# get rid of the missing values before applying ANOVA test
stats.f_oneway(data1[~np.isnan(data1)], data2, data3)
[Out]
F_onewayResult(statistic=260.766426640122, pvalue=1.1951277551195217e-43)

 

 

위의  일원분산분석(one-way ANOVA) 는 2개 이상의 그룹 간 평균의 차이가 존재하는지만을 검정할 뿐이며, 집단 간 평균의 차이가 존재한다는 대립가설을 채택하게 된 경우 어느 그룹 간 차이가 존재하는지는 사후검정 다중비교(post-hoc pair-wise multiple comparisons)를 통해서 알 수 있습니다. (rfriend.tistory.com/133)

 

pandas DataFrame 데이터셋에서 여러개의 숫자형 변수에 대해 for loop 순환문을 사용하여 집단 간 평균 차이 여부를 검정하는 방법은 rfriend.tistory.com/639 포스팅을 참고하세요. 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요. 

 

[reference]

* scipy.stats.f_oneway : https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.f_oneway.html

 

scipy.stats.f_oneway — SciPy v1.6.3 Reference Guide

G.W. Heiman, “Understanding research methods and statistics: An integrated introduction for psychology”, Houghton, Mifflin and Company, 2001.

docs.scipy.org

728x90
반응형
Posted by Rfriend
,