scipy 기초
학습 내용
result.statistic,result.pvalue를 읽는 기본 습관 익히기stats.shapiro(),stats.ttest_1samp(),stats.ttest_ind(),stats.ttest_rel(),stats.f_oneway()의 차이 이해하기alternative='two-sided','greater','less'가 무엇을 뜻하는지 배우기- pvalue를 바탕으로 결과를 짧은 결론 문장으로 바꾸는 방법 익히기
scipy.stats는 통계검정을 할 때 가장 자주 쓰는 도구 중 하나입니다. 이 페이지에서는 검정 공식을 외우기보다, 어떤 상황에서 어떤 함수를 쓰고 결과를 어떻게 읽는지에 집중합니다.
처음에는 아래 흐름만 익혀도 충분합니다. 검정 함수를 실행하고, result.statistic과 result.pvalue를 보고, 보통 0.05 기준으로 결론을 내리면 됩니다.
통계검정을 실행하면 보통 결과 객체가 돌아오고, 그 안에서 가장 먼저 보는 값이 statistic과 pvalue입니다.
from scipy import stats sample = [52, 54, 51, 53, 55] result = stats.ttest_1samp(sample, popmean=50) print(round(result.statistic, 3)) print(round(result.pvalue, 3)) print(result.pvalue < 0.05)
실행 결과:
4.243 0.013 True
여기서 statistic은 검정통계량이고, pvalue는 귀무가설이 맞다고 가정했을 때 지금 같은 차이가 우연히 나올 가능성을 보여 주는 값입니다. 초보자 단계에서는 아래처럼 먼저 읽으면 됩니다.
pvalue < 0.05: 차이가 있다고 보는 쪽pvalue >= 0.05: 뚜렷한 차이가 없다고 보는 쪽
이 예시는 평균이 50이라고 보기 어렵다는 쪽으로 읽을 수 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
정규성 검정은 “이 데이터가 정규분포를 크게 벗어나지 않는가”를 확인할 때 자주 사용합니다. 가장 많이 쓰는 함수 중 하나가 stats.shapiro()입니다.
import pandas as pd from scipy import stats data_path = "/data/body.csv" df = pd.read_csv(data_path) sample = df.loc[df["측정회원성별"] == "M", "신장 : cm"].sample(120, random_state=42) result = stats.shapiro(sample) print(len(sample)) print(round(result.statistic, 3)) print(round(result.pvalue, 3)) print(result.pvalue > 0.05)
실행 결과:
120 0.994 0.856 True
이 예시에서는 pvalue가 0.856으로 아주 큽니다. 즉, 정규분포에서 크게 벗어났다고 볼 만한 강한 근거가 없다는 뜻입니다. 정규성 검정은 “정규분포다”를 증명하는 검정이 아니라, “정규분포가 아니다”라고 강하게 말할 근거가 있는지를 보는 검정이라는 점이 중요합니다.
처음 배울 때는 아래처럼 기억하면 충분합니다.
pvalue > 0.05: 정규성에서 큰 문제를 발견하지 못함pvalue <= 0.05: 정규성 가정을 다시 점검할 필요가 있음
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
한 집단의 평균이 어떤 기준값과 다른지 보고 싶을 때 stats.ttest_1samp()를 사용합니다. popmean= 자리에 비교 기준값을 넣습니다.
import pandas as pd from scipy import stats data_path = "/data/body.csv" df = pd.read_csv(data_path) sample = df["악력D : kg"].sample(150, random_state=42) two_sided = stats.ttest_1samp(sample, popmean=30) greater = stats.ttest_1samp(sample, popmean=30, alternative="greater") less = stats.ttest_1samp(sample, popmean=30, alternative="less") print(round(two_sided.statistic, 3)) print(format(two_sided.pvalue, ".3e")) print(format(greater.pvalue, ".3e")) print(round(less.pvalue, 1))
실행 결과:
9.055 7.071e-16 3.536e-16 1.0
양측검정인 two_sided는 “다르다”를, greater는 “평균이 더 크다”를, less는 “평균이 더 작다”를 보는 방향입니다. 이 예시는 표본 평균이 30보다 큰 쪽이므로 greater의 pvalue가 아주 작고, 반대 방향인 less의 pvalue는 1.0에 가깝게 나옵니다.
alternative 파라미터는 초보자가 특히 자주 헷갈리는 부분입니다.
two-sided: 크든 작든 다르면 본다greater: 표본 평균이 기준값보다 큰지 본다less: 표본 평균이 기준값보다 작은지 본다
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
서로 독립인 두 집단의 평균 차이를 보고 싶을 때는 stats.ttest_ind()를 씁니다. 예를 들어 남성과 여성의 키 평균이 다른지 보고 싶다면 이 검정을 사용할 수 있습니다.
import pandas as pd from scipy import stats data_path = "/data/body.csv" df = pd.read_csv(data_path) male_height = df.loc[df["측정회원성별"] == "M", "신장 : cm"].sample(120, random_state=42) female_height = df.loc[df["측정회원성별"] == "F", "신장 : cm"].sample(120, random_state=42) result = stats.ttest_ind(male_height, female_height, equal_var=False) print(round(result.statistic, 3)) print(format(result.pvalue, ".3e")) print(result.pvalue < 0.05)
실행 결과:
17.726 3.539e-45 True
여기서는 pvalue가 매우 작아서 두 집단 평균 차이가 우연이라고 보기 어렵다는 뜻입니다. equal_var=False는 두 집단 분산이 같다고 강하게 가정하지 않는 Welch t-test 방식이라, 초보자가 안전하게 시작하기 좋습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
같은 대상에서 전후를 비교하거나, 시점이 서로 짝지어진 두 값을 비교할 때는 stats.ttest_rel()을 사용합니다. 독립표본이 아니라 “쌍이 맞는 데이터”라는 점이 중요합니다.
import pandas as pd from scipy import stats data_path = "/data/weather.csv" df = pd.read_csv(data_path) paired = df[["이화동기온", "수영동기온"]].dropna().head(200) two_sided = stats.ttest_rel(paired["이화동기온"], paired["수영동기온"]) less = stats.ttest_rel(paired["이화동기온"], paired["수영동기온"], alternative="less") greater = stats.ttest_rel(paired["이화동기온"], paired["수영동기온"], alternative="greater") print(round(two_sided.statistic, 2)) print(format(less.pvalue, ".3e")) print(round(greater.pvalue, 1))
실행 결과:
-41.52 2.839e-100 1.0
통계량이 음수라는 것은 첫 번째 값인 이화동기온이 두 번째 값인 수영동기온보다 작은 방향이라는 뜻입니다. 그래서 alternative="less"의 pvalue는 아주 작고, 반대 방향인 greater의 pvalue는 1.0이 됩니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
세 집단 이상 평균을 한 번에 비교할 때는 stats.f_oneway()를 사용합니다. 이것이 일원분산분석(ANOVA)입니다.
import pandas as pd from scipy import stats data_path = "/data/body.csv" df = pd.read_csv(data_path) group_a = df.loc[df["등급"] == "A", "앉아윗몸앞으로굽히기 : cm"].dropna().sample(200, random_state=42) group_b = df.loc[df["등급"] == "B", "앉아윗몸앞으로굽히기 : cm"].dropna().sample(200, random_state=42) group_c = df.loc[df["등급"] == "C", "앉아윗몸앞으로굽히기 : cm"].dropna().sample(200, random_state=42) result = stats.f_oneway(group_a, group_b, group_c) print(round(result.statistic, 3)) print(format(result.pvalue, ".3e")) print(result.pvalue < 0.05)
실행 결과:
96.893 3.612e-37 True
이 결과는 세 집단 평균이 모두 같다고 보기 어렵다는 뜻입니다. 다만 ANOVA는 “어느 집단끼리 다른지”까지는 바로 알려 주지 않습니다. 그것까지 보려면 사후검정이 필요합니다. 초보자 단계에서는 “세 집단 이상이면 먼저 ANOVA를 생각한다” 정도로 익히면 좋습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
초보자가 가장 자주 어려워하는 부분은 숫자를 결론 문장으로 바꾸는 단계입니다. 처음에는 아래처럼 아주 단순한 문장부터 만들면 됩니다.
import pandas as pd from scipy import stats data_path = "/data/body.csv" df = pd.read_csv(data_path) male_height = df.loc[df["측정회원성별"] == "M", "신장 : cm"].sample(120, random_state=42) female_height = df.loc[df["측정회원성별"] == "F", "신장 : cm"].sample(120, random_state=42) normality_result = stats.shapiro(male_height) ttest_result = stats.ttest_ind(male_height, female_height, equal_var=False) print("정규성 문제 없음" if normality_result.pvalue > 0.05 else "정규성 다시 확인") print("차이가 있다" if ttest_result.pvalue < 0.05 else "차이가 없다")
실행 결과:
정규성 문제 없음 차이가 있다
이렇게 숫자를 바로 문장으로 바꾸는 연습을 해 두면, 이후 문제풀이와 보고서 작성이 훨씬 쉬워집니다. 물론 실제 실무에서는 더 조심스럽게 쓰지만, 입문 단계에서는 먼저 짧고 분명한 문장으로 바꾸는 연습이 중요합니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.
에디터 로딩 중...
코드 입력 환경을 준비하고 있습니다.