카테고리 없음

심화프로젝트 2일차 - 데이터 전처리 및 가설검증

iron-min 2025. 11. 7. 20:47

1. 이상치 확인 및 처리

프로젝트를 진행하면서 아마 가장 어려운 부분인것 같습니다.

 

이런식으로 원본데이터에 1.5XIQR을 넘는 데이터가 너무 많아서 어떻게 데이터를 처리해야할지 난제에 빠졌습니다.

우선 상식적으로 말이 안되는 수치를 제거했습니다.

 

#극단값 제거하기
df = df[df['Pixels_Areas'] < 150000]
df = df[df['X_Perimeter'] < 10000]
df = df[df['Y_Perimeter'] < 18000]

 

 

그리고 데이터에 로그를 씌워서 어느정도 정규성을 가지게 해보았습니다.

# 로그 변환 후 IQR 확인
import numpy as np

# df_log를 df의 복사본으로 초기화하여 원본 데이터프레임 df를 보호합니다.
df_log = df.copy()

# 로그 변환 대상 컬럼 이름 리스트
cols_to_log = ['Pixels_Areas', 'X_Perimeter', 'Y_Perimeter','X_Minimum','X_Maximum','Y_Minimum',
'Y_Maximum']

# 로그 변환 (0이 있으면 log(0) 에러 → +1 해서 회피)
for col_name in cols_to_log:
    # 컬럼이 존재하는지 확인 후 변환 적용
    if col_name in df_log.columns:
        df_log[f'{col_name}_log'] = np.log1p(df_log[col_name])
    else:
        print(f"경고: df_log에 '{col_name}' 컬럼이 없습니다. 이 컬럼에 대한 로그 변환을 건너뜁니다.")

# 변환된 데이터 확인
transformed_cols = [f'{c}_log' for c in cols_to_log if f'{c}_log' in df_log.columns]
if transformed_cols:
    display(df_log[transformed_cols].head())
else:
    print("로그 변환된 컬럼이 생성되지 않았습니다.")

 

 

그리고 해당 데이터를 기반으로 다시 이상치를 파악하니

# 위치 기반 컬럼 이상치제거(IQR,LOG 사용)
# 로그 변환 후 IQR 기반 이상치 카운트
import pandas as pd
import numpy as np

# df_log는 이전 셀에서 이미 DataFrame으로 생성되고 로그 변환이 적용되었다고 가정합니다.
# 확인할 컬럼 리스트 (로그 변환된 컬럼들)
cols_for_outliers = [f'{c}_log' for c in ['Pixels_Areas', 'X_Perimeter', 'Y_Perimeter','X_Minimum','X_Maximum','Y_Minimum',
'Y_Maximum']]

for col in cols_for_outliers:
    # 해당 컬럼이 df_log에 존재하는지 확인
    if col not in df_log.columns:
        print(f"경고: df_log에 '{col}' 컬럼이 없습니다. 다음 컬럼으로 넘어갑니다.")
        continue

    Q1 = df_log[col].quantile(0.25)
    Q3 = df_log[col].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    # 오류 수정: df[col] 대신 df_log[col] 사용
    outliers = df_log[(df_log[col] < lower_bound) | (df_log[col] > upper_bound)]

    print(f" [{col}] 이상치 요약")
    print(f" - Q1: {Q1:.2f}, Q3: {Q3:.2f}, IQR: {IQR:.2f}")
    print(f" - 하한: {lower_bound:.2f}, 상한: {upper_bound:.2f}")
    print(f" - 이상치 개수: {len(outliers)}개")
    print(outliers[[col]].head())

 

 

이런식으로 이상치가 확연히 줄어든것을 볼 수 있습니다.

 

그리고 저는 여기서 정규화된 데이터를 바이올린 플롯으로 그려보았습니다.

 

fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(14, 15))

sns.violinplot(df, y='Log_X_Index', x='Pastry', hue='Pastry', ax=axes[0][0])
axes[0][0].set_title('Log_X_Index', fontsize=14)

sns.violinplot(df, y='Log_Y_Index', x='Pastry', hue='Pastry', ax=axes[0][1])
axes[0][1].set_title('Log_Y_Index', fontsize=14)

sns.violinplot(df_log, y='Pixels_Areas_log', x='Pastry', hue='Pastry', ax=axes[1][0])
axes[1][0].set_title('Pixels_Areas_log', fontsize=14)

sns.violinplot(df_log, y='X_Perimeter_log', x='Pastry', hue='Pastry', ax=axes[1][1])
axes[1][1].set_title('X_Perimeter_log', fontsize=14)

sns.violinplot(df_log, y='Y_Perimeter_log', x='Pastry', hue='Pastry', ax=axes[2][0])
axes[2][0].set_title('Y_Perimeter_log', fontsize=14)

axes[2][1].set_visible(False)

plt.suptitle('Pastry결함에 대한 영향도', fontsize=18, y=1.00)
plt.tight_layout()
plt.show()

 

 

이렇게 보니 간단한 가설을 설정할 수 있었습니다.

 

1. Pastry 결함
pixels_areas - 평균 비슷, 상대적으로 좁은 범위에 분포
length of conveyer - 평균값 높음운송거리가 많을 수록 더 많은 결함 발생
철반두께 - 이상치 영향 있음, 평균값 높음두께는 근소하게 영향을 미침
방향도 - 양의 방향에 치중되어 있음  대체적으로 양의 방향일 때 결함발생

 

이런식으로 7가지 결함에 대해 간단한 가설을 세웠고 이를 검증하기 위해 t-test를 실시하기 위해 정규성을 검토했습니다.

 

 

아쉽게도 대부분의 데이터들이 정규성을 띄지 않아서 t-test 대신 Mann-Whitney U Test를 진행했습니다.

from scipy.stats import ttest_ind

t_state_pastry_Pixels, p_value_pastry_Pixels = ttest_ind(df_pastry['Pixels_Areas'], df_no_pastry['Pixels_Areas'])
t_state_pastry_XPerimeter, p_value_psstry_XPerimeter = ttest_ind(df_pastry['X_Perimeter'], df_no_pastry['X_Perimeter'])
t_state_pastry_YPerimeter, p_value_psstry_YPerimeter = ttest_ind(df_pastry['Y_Perimeter'], df_no_pastry['Y_Perimeter'])
t_state_pastry_Conveyer, p_value_psstry_Conveyer = ttest_ind(df_pastry['Length_of_Conveyer'], df_no_pastry['Length_of_Conveyer'])
t_state_pastry_Thickness, p_value_psstry_Thickness = ttest_ind(df_pastry['Steel_Plate_Thickness'], df_no_pastry['Steel_Plate_Thickness'])
t_state_pastry_Orientation, p_value_pastry_Orientation = ttest_ind(df_no_pastry['Orientation_Index'], df_pastry['Orientation_Index'])

print('Pastry 결함면적 검정')
print(f't_stat: {t_state_pastry_Pixels}, p_value: {p_value_pastry_Pixels}')
print('Pastry 결함 X_경계길이 검정')
print(f't_stat: {t_state_pastry_XPerimeter}, p_value: {p_value_psstry_XPerimeter}')
print('Pastry 결함 Y_경계길이 검정')
print(f't_stat: {t_state_pastry_YPerimeter}, p_value: {p_value_psstry_YPerimeter}')
print('Pastry 결함 이송거리 검정')
print(f't_stat: {t_state_pastry_Conveyer}, p_value: {p_value_psstry_Conveyer}')
print('Pastry 결함 철판두께 검정')
print(f't_stat: {t_state_pastry_Thickness}, p_value: {p_value_psstry_Thickness}')
print('Pastry 결함 방향성 검정')
print(f't_stat: {t_state_pastry_Orientation}, p_value: {p_value_pastry_Orientation}')

 

Pastry 결함면적 검정
t_stat: -4.267306179974067, p_value: 2.0738854609100897e-05
Pastry 결함 X_경계길이 검정
t_stat: -4.960117552235953, p_value: 7.659634338012978e-07
Pastry 결함 Y_경계길이 검정
t_stat: -1.7968736687700153, p_value: 0.07251133180811208
Pastry 결함 이송거리 검정
t_stat: 8.804720037422044, p_value: 2.846470783045391e-18
Pastry 결함 철판두께 검정
t_stat: 3.376867030394855, p_value: 0.0007476528286841543
Pastry 결함 방향성 검정
t_stat: -15.387029897600591, p_value: 1.7062078466951517e-50

 

이런식으로 결과가 나오게 되었고 기존 시각화로 세운 가설과 비교해 보니 일치하는것을 확인했습니다.

Pastry 결함(a=0.05)

H0 : 결함의 면적과 정상의 면적은 같다
H1 : 결함의 면적과 정상의 면적은 다르다
-> H0 채택 : 결함의 면적과 정상의 면적은 같다.

H0 : 결함과 정상의 X경계길이는 같다.
H1 : 결함의 정상의 X경계길이는 다르다.
-> H1 채택 : 결함의 정상의 X경계길이는 다르다.

H0 : 결함과 정상의 Y경계길이는 같다.
H1 : 결함의 정상의 Y경계길이는 다르다.
-> H1 채택 : 결함의 정상의 Y경계길이는 다르다.

H0 : 결함과 정상의 이송거리는 같다.
H1 : 결함과 정상의 이송거리는 다르다.
-> H1 채택 : 결함과 정상의 이송거리는 다르다.

H0 : 결함과 정상의 철판 두께는 같다.
H1 : 결함과 정상의 철판 두께는 다르다.
-> H1 채택 : 결함과 정상의 철판 두께는 다르다.

H0 : 결함과 정상의 방향성은 같다.
H1 : 결함과 정상의 방향성은 다르다.
-> H1 채택 : 결함과 정상의 방향성은 다르다.

 

2. 발생한 문제점

아직도 이상치를 어떤 기준으로 처리해야하는지에 대해 결정하지 못했습니다.

일단 머신러닝을 돌려보고 이상치를 제거한 데이터와 제거하지 않은 데이터의 정확도를 비교해보고 조원들끼리 토의 해보기로 했습니다.

튜터님께서도 이부분이 가장 큰 난제라고 하셨고 아마 여러번 코딩을 해봐야 어느정도 문제가 해결될 것 같습니다.

 

 

 

 

3. 느낀점

항상 느끼는 거지만 t-test 기사문제로 엄청 어렵게 나오는데 이렇게 버튼 몇개로 해결할 수 있다니... QA/QC 능력에 필수로 데이터 활용능력에 필수적인 것 같습니다. 더군다나 정규화 되지 않은 데이터도 비교검증을 하는 Mann-Whitney U Test 을 실제로 활용해보니 실무에서도 활용할 수 있다는 자신감을 얻었습니다.