Chapter 6. 결측 데이터 처리
결측 데이터를 처리하는 것은 매우 중요하다. 일반적으로 결측이 존재하는 데이터를 모두 삭제하는 경우도 있는데 이럴 경우에 데이터에 큰 편향이 생기게 된다. 예시로 보면 전자기기에 친숙하지 않은 노년층의 유저들이 정보를 잘 안채우는 문제로 결측치가 생겼고 이를 모두 제거하게 되면 결론적으로 젊은 사람들을 위한 모델이나 분석을 해서 전체 유저에 대한 편향이 발생할 수 있다.
어떤 기준으로 삭제를 해야할지?
가장 간단한 전략으로 이런 방법을 제안한다.
1. 결측값이 가장 많은 변수를 가지고 하나는 모든 결측값을 해당 변수의 최솟값으로 대체하고, 나머지 하나는 최대값으로 대체한다.
2. 원본 데이터셋과 1에 만들어진 2개 데이터셋을 활용해서 해당 변수의 가장 중요한 관계의 회귀분석을 진행한다.
3. 이 세 회귀에서 큰 차이가 없으면 결측값의 양은 삭제하기 적합한 양이라고 볼 수 있다.
결측 데이터의 상관관계
결측값이 있는 변수 사이의 결측이 얼마나 관련되어 있는지 살펴본다. 이를 통해 상관관계가 높게 다온다면 한 변수의 누락이 다른 변수의 누락을 의미할 수도 있다는 인사이트를 얻게 된다.
결측 데이터 진단
우리가 앞에서 배운 결측치는 일종의 우리가 알고 싶지만 알 수 없는 관측할 수 없는 변수에 대해 힌트를 얻을 수 있는 변수라고 볼 수도 있다. 보통 일부만 관찰된 변수를 아래와 같이 점선으로 표시한다.
하지만 이런 결측에 대해서도 여러가지 원인이 있다.
결측의 요인
- 루빈의 분류
1. 변수의 결측이 무작위 요인과 같이 데이터 외부의 변수에만 영향을 받는 경우 해당 변수는 완전 무작위 결측(MCAR)이라고 한다.
2. 데이터의 변수 중 하나라도 변수의 결측에 영향을 미치는 경우 해당 변수는 무작위 결측(MAR)이라고 한다. (예를 들면 구매한 사람에 대해서 선호도를 물어봤다면 이 결측에 영향을 주는 것이기 때문)
3. 변수의 값이 자신의 결측에 영향을 미치는 변수는 무작위가 아닌 결측(Missing not at random)이라고 한다.
- 결측 요인 검증
확인하려는 변수에 대해서 Isnull을 적용해주고 이를 목적변수로 활용. 그리고 나머지 다른 변수들에 대해서 로지스틱 회귀분석을 했을 때 유의미한 계수를 갖지 않으면 이를 MCAR로 판단할 수 있다. 유의미한 계수를 가질 경우 중에 해당 변수의 자식 변수까지도 상관관계가 많을 경우에 우리는 MNAR이라고 하고 특정 변수만 유의미한 경우를 MAR이라고 한다.
- 결측의 스펙트럼
우리가 어떤 시험 데이터를 가지고 있다고 하자. 그리고 1차 면접 점수에 따라서 합격컷이 있고 이 이상인 사람만 2차 면접을 본다고 하면 의도적으로 특정 임곗값에 대해서 결측이 발생하는데 이는 그래프를 그려서, 확인할 수 있고 이건 일종의 비즈니스 로직을 잘 알고 있으면 굳이 분석 없이도 이해할 수 있다. (X축에는 임계값으로 활용할 변수, y축은 missing value가 일어날 확률을 그려보면 파악할 수 있다.)
결측 데이터 처리
이 절에서 결측 데이터를 처리하는 이유는 결측 데이터 자체를 처리하는 것이 목표가 아니라, 데이터에서 인과관계에 대해 편향되지 않고 정확한 추정치를 얻는 것이 핵심이다. 그에 맞춰서 결측 데이터를 처리하려고 한다.
다중 대체법(multiple imputation)
서로 다른 대체값을 사용해서 데이터의 복사본을 여러개 생성하는 방법을 사용하는 것으로 결측된 밥의 나이는 42세입니다라고 하지 않고 42세, 44세 또는 38세 일 수 있다고 말하는 것과 같습니다.
즉, 그렇게 해서 회귀분석을 넣어서 각 변수가 가지는 회귀계수의 유의성을 확보하는 것입니다. 그래서, 구체적으로 이게 어떤 값이 들어갔는지 확인하는게 아니라 회귀 분석 결과를 살펴보는게 중요하다.
이게 잘 작동하는 이유는 일종의 앙상블 모델을 생각하면 될것 같다. 우리가 임의로 하나의 값으로 바꿔버리게 될 경우에는 통계적으로 많은 불확실성을 가질 수 있다. 하지만 이 불확실성을 여러개의 모델로 합침으로써 이 편차를 낮춰주는 역할을 한다고 생각하면 된다.
MI_data_df = mice.MICEData(available_data_df)
fit = mice.MICE(model_formula = 'bkg_amt ~ age + open + extra + neuro + gender_M + gender_F',
model_class = sm.OLS, data = MI_data_df)
MI_summ = fit.fit(n_imputations = 20).summary()
print(MI_summ)
예측값 대입 방법 : 예측 평균 매칭
앞절에서는 대체 방법을 지정하지 않고 MICE의 디폴트 값을 적용했습니다. 파이썬에서는 그 기본 방법이 예측평균 매칭이라서 더 작업할게 없지만 R에서는 다양한 방법들을 적용할 수 있습니다.
이 방법은 흡사 kNN과 비슷한데, 예측값이 있는 데이터와 가장 가까운 데이터를 찾고 그 데이터의 변수 값으로 결측치를 채우는 것이다. 그러다 보니 생각보다 속도가 좀 느려서 대규모 데이터셋에 적용하기가 어렵다.
예측값 대입 방법 : 정규분포 대체법
변수가 만일 정규분포를 따른다는 가정하에서 변수들 간의 공분산 구조를 활용하여 확률적으로 대체 값을 넣어주는 작업이다.
보조 변수 추가
결측치를 채우려는 변수와 매우 상관관계가 있지만 실제 회귀분석에는 안들어가는 변수도 함께 넣어서 MICE를 돌릴 수 있다. 하지만 이렇게 될 경우에는 보조변수가 다른 변수들에 영향을 줄 수도 있어서 R에서는 특정 변수를 대체할때만 이 변수를 사용할 수 있도록 지정할 수 있다.(파이썬은 해당 설정이 없음)
Chapter 7. 부트스트랩을 활용한 불확실성 측정
아무리 좋은 모델과 컴퓨팅 능력이더라도 데이터가 이상하거나 적을 경우에는 무용지물이며 이에 대한 대안으로 부트스트랩을 활용할 수 있습니다.
부트스트랩은 난수를 기반으로 서로 조금씩 다른 버전의 데이터를 생성하고 분석하는 방식으로 작동합니다.
1. 개요
다음의 패키지를 사용한다.
import statsmodels.api as sm
import statsmodels.stats.outliers_influence as st_inf
만일 정규분포를 따른다고 생각하고 신뢰구간을 추정해서 데이터를 추출할 경우에는 0보다 작은 값이 추출될 수 있다.(분산이 크다는 가정 하에) 그렇게 되면 현실에서 나올 수 없는 숫자이기 때문에 좋은 방법이 아니다.
부트스트랩을 사용하면 사용 가능한 데이터를 최대한 활용하고 표본 크기나 데이터 형태 문제와 상관없이 합리적인 결론을 도출할 수 있습니다. 사용 가능한 데이터를 기반으로 여러 개의 가상 데이터셋을 생성해서 그걸 기준으로 비교하기 때문입니다.
부트스트랩 과정은 아래와 같은 방식을 따른다.
1단계 : 관찰한 표본에서 복원 추출하여 동일한 크기의 새로운 표본을 생성한다.
2단계 : 생성된 각 표본에 대해 관심있는 통계(평균)를 계산한다.
3단계 : 2단계에서 얻은 값의 백분위수를 확인하여 신뢰 구간을 구축합니다.
2. 부트스트랩의 사용 사례
평균에 대한 신뢰구간 추정
우리가 얻고 싶은 건 현재 우리가 가지고 있는 평균에 대한 신뢰구간을 얻기 위함입니다. 여러번의 추출을 통해서 평균에 대한 집합을 구하고 신뢰구간 0.95이면, 전체 분포의 2.5%, 97.5%에 있는 값을 뽑는다.
import numpy as np
import pandas as pd
# 예시 데이터 세트
data = np.array([1, 2, 3, 4, 5])
# 부트스트랩 샘플 생성 및 통계량 계산
bootstrap_samples = 1000
bootstrap_means = np.empty(bootstrap_samples)
for i in range(bootstrap_samples):
bootstrap_sample = np.random.choice(data, size=len(data), replace=True)
bootstrap_means[i] = np.mean(bootstrap_sample)
# 신뢰구간 계산
confidence_level = 0.95
lower_bound = np.percentile(bootstrap_means, (1 - confidence_level) / 2 * 100)
upper_bound = np.percentile(bootstrap_means, (1 + confidence_level) / 2 * 100)
print(f"{confidence_level*100}% 신뢰구간: {lower_bound} ~ {upper_bound}")
회귀분석을 위한 부트스트랩
위 방식과 동일하게 표본을 추출하고 개별 표본에 대해서 회귀분석을 진행해서 회귀 계수들에 대해 히스토그램을 찍는 형태로 진행한다.
3. 부트스트랩의 사용 조건
부트스트랩은 전통적인 방법으로 얻은 것과 가장 가까운 중앙 추정치나 계수를 산출하기 때문에 전통적인 분석으로 계수를 뽑아낼 수 있다면 사실 굳이 이를 사용할 필요가 없다.
추가로 신뢰구간을 요구했다면 위의 음수 값 사례처럼 부트스트랩을 고려할 수 있습니다.
회귀 과정에서의 부트스트랩 사용 기준
- 해당 데이터를 삭제했을 때 회귀가 크게 변하는 데이터를 영향점이라고 하는데 이런 경우에는 부트스트랩을 사용하는 것이 좋다.
- 회귀 잔차를 그려봤을 때 정규 분포를 따르지 않으면 부트스트랩을 사용하는 것이 좋다.
부트스트랩 사용 Flow
1. 주요 추정치를 구할 때 우선적으로 전통적인 회귀분석에서 시작한다.
2. 데이터에 100개 미만의 점이 있는 경우 25 ~ 200개 사이를 표본으로 부트스트랩을 사용하여 해당 추정치 주변의 불확실성을 파악한다.
3. N > 100인 경우 쿡의 거리로 영향점인지 확인하고 잔차의 비정규성을 확인한다.
4. N 관계없이 정확한 신뢰 구간이 필요하거나 유의 수준을 달성한 경우 B를 1000~2000 사이의 값으로 설정하여 부트스트랩을 다시 수행한다.
4. 파이썬에서의 부트스트랩 사용
별도의 라이브러리는 없지만 부트스트랩 구현에 있어서 numpy로만 구현하면 계산 속도가 50배 이상 향상시킬 수 있습니다.
data_ar = data_df.to_numpy()
rng = np.random.default_rng()
np_lst = []
for i in range(B):
boot_ar = rng.choice(data_ar, size = N, replace = True)
X = boot_ar[:,1]
X = np.c_[X, np.ones(N)]
Y = boot_ar[:,0]
np_lst.append(np.linalg.lstsq(X, Y, rcond = -1)[0][0])
'데이터 분석, 통계 > 유튜브, 책, 아티클 정리' 카테고리의 다른 글
[Book] 행동 데이터 분석 8장 ~ 12장 : 실험 설계와 분석/분석 도구 (0) | 2023.12.16 |
---|---|
[Book] 행동 데이터 분석 4장 ~ 5장 : 인과관계 다이어그램과 교란 해소 (0) | 2023.12.14 |
[book] 행동 데이터 분석 정리 1~3장 (0) | 2023.12.12 |
[book] 데이터 분석과 비판적사고 (1) | 2023.11.04 |