카테고리 없음

실전프로젝트 2일차 - 데이터 EDA 및 가설검정

iron-min 2025. 12. 3. 20:51

1. 시각화 분석 및 EDA

 

 

시퀀스별로 전류 패턴이 있는걸 확인할 수 있습니다. 정상과 불량의 패턴이 다른지는 검증해봐야할것 같습니다.

 

 

시퀀스별로 전압 또한 패턴이 보여지는 모습입니다.

 

기초통계(불량, 양품으로만 나눠서 진행)

 

 

  • 핵심 불량 패턴은 전류의 극심한 불안정성온도의 상승으로 집중. 전압 데이터는 이 두 가지 현상의 결과 또는 반응으로 해석
  • 제 1원인: 전류의 통제불능

▶️ 분석: 표준편차 증가율에서 전류가 압도적인 1위(+72.5%)를 차지한다는 것은, 불량품 공정이 전류 제어에 가장 큰 문제를 겪었음을 의미. 불안정한 전류는 피막 파괴, 스파크 등을 유발하여 아노다이징 불량의 가장 직접적인 원인

  • 제 2원인: 온도의 상승 및 불안정

▶️ 분석:

  • 온도 표쥰편차(+61.4% 증가): 온도 변동성이 매우 높음
  • 온도 평균/중위값 상승 (+16.2% / +33.3%): 공정 자체가 고온 조건에서 진행되었음을 확인시켜 줌
  • 인과관계: 1차적으로 높은 전류(I)와 불안정한 저항(R)으로 인해 열이 발생했고, 이 열이 전해액의 온도를 상승 및 불안정하게 만들어 피막 용해 속도를 증가시켜 품질 저하를 초래
  • 제 1,2원인으로 인한 전압의 상태 반영

▶️ 분석:

  • 전압의 표준 편차 (+10.3% 증가): 전압의 변동성도 증가했지만, 전류의 변동성(+72.5%)와 비교하면 상대적으로 낮은 수준
  • 경과: 아노 다지징 공정은 보통 정전류 모드로 작동함. 전류를 목표치에 맞추기 위해 전압(V)은 저항(R)의 변화에 따라 자동으로 변함(V=IR). 전류가 불안정하거나 피막 저항이 급변할 때, 공정은 전류를 유지하기 위해 전압을 더 높게, 더 자주 변경해야 했었고, 이것이 전압의 중위값 및 표준편차의 상승으로 나타남

 

라는 가설을 세웠습니다.

 

 

2. 가설검정

시각화에서 세워본 가설을 Mann-Whitney U 검정을 실시해 보았습니다.

 

2-1. 정규성검정

 

양품/불량품 데이터 정규성 검정

from scipy.stats import shapiro

# 실패 / 성공 데이터 분리
df_fail = df_mil[df_mil['failure'] == -1]
df_success = df_mil[df_mil['failure'] == 1]

# 시퀀스별 평균
df_success_avg = df_success.groupby('sequence_index').mean()
df_fail_avg = df_fail.groupby('sequence_index').mean()

# 정규성 검정할 컬럼 리스트
test_columns = ['volt','ampere','temperature','시간변화량(초)','두께변화량']

results = []

for col in test_columns:
    fail_stat, fail_p = shapiro(df_fail_avg[col])
    success_stat, success_p = shapiro(df_success_avg[col])

    results.append({
        'Column': col,
        'Fail_statistic': f'{fail_stat:.4f}',
        'Fail_p-value': f'{fail_p:.4f}',
        'Success_statistic': f'{success_stat:.4f}',
        'Success_p-value': f'{success_p:.4f}'
    })

result_df = pd.DataFrame(results)
print(result_df)

# 정규성 만족 컬럼 volt,ampere
# 정규성 만족 X 컬럼 temperature, 시간변화량, 두께변화량(success 데이터는 만족함)

 

장비별 데이터 정규성 검정

# rec1 / rec2 데이터 분리
df_rec1 = df_mil[df_mil['rec_num'] == 1]
df_rec2 = df_mil[df_mil['rec_num'] == 2]

# 시퀀스별 평균
df_rec1_avg = df_rec1.groupby('sequence_index').mean(numeric_only=True)
df_rec2_avg = df_rec2.groupby('sequence_index').mean(numeric_only=True)

# 정규성 검정할 컬럼 리스트
test_columns_rec = ['volt','ampere','temperature','시간변화량(초)','두께변화량']

results = []

for col in test_columns_rec:
    rec1_stat, rec1_p = shapiro(df_rec1_avg[col].dropna())
    rec2_stat, rec2_p = shapiro(df_rec2_avg[col].dropna())

    results.append({
        'Column': col,
        'Rec1_statistic': f'{rec1_stat:.4f}',
        'Rec1_p-value': f'{rec1_p:.4f}',
        'Rec2_statistic': f'{rec2_stat:.4f}',
        'Rec2_p-value': f'{rec2_p:.4f}'
    })

result_df_rec = pd.DataFrame(results)
print(result_df_rec)

# 정규성 만족 컬럼 volt, 두께변화량
# 정규성 만족 X 컬럼 ampere,temperature,시간변화량(초)

 

일부 정규성이 만족하지 않는 컬럼들이 많아서 Mann-Whitney U 검정을 하기로 했습니다.

 

2-2. 양품/불량품간 비교

시각화

# subplot 설정
fig, axes = plt.subplots(nrows=1, ncols=len(test_columns), figsize=(4*len(test_columns), 5))

for i, col in enumerate(test_columns):
    sns.violinplot(data=df_mil, x='failure', y=col, ax=axes[i], inner='quartile',palette='Set2')
    axes[i].set_title(col)
    axes[i].set_xlabel('')
    axes[i].set_ylabel('')

plt.tight_layout()
plt.show()

검정

 

from scipy.stats import mannwhitneyu

mw_results_failure = []

for col in ['volt', 'ampere', 'temperature', '시간변화량(초)', '두께변화량']:
    stat, p = mannwhitneyu(df_fail_avg[col],
                           df_success_avg[col],
                           alternative='two-sided')

    mw_results_failure.append({
        'Column': col,
        'U-statistic': round(stat, 4),
        'p-value': round(p, 4),
        '유의성 (p < 0.05)': '차이가 있다' if p < 0.05 else '차이가 없다'
    })

mw_df_failure = pd.DataFrame(mw_results_failure)
print(mw_df_failure)

 

2-2. 장비별 특성치 비교

시각화

# subplot 설정

test_columns_rec = ['volt','ampere','temperature','시간변화량(초)','두께변화량']
fig, axes = plt.subplots(nrows=1, ncols=len(test_columns_rec), figsize=(4*len(test_columns_rec), 5))

for i, col in enumerate(test_columns_rec):
    sns.violinplot(data=df_mil, x='rec_num', y=col, ax=axes[i], inner='quartile',palette='Set2')
    axes[i].set_title(col)
    axes[i].set_xlabel('')
    axes[i].set_ylabel('')

plt.tight_layout()
plt.show()

 

검정

mw_results_rec = []

for col in ['volt', 'ampere', 'temperature', '시간변화량(초)', '두께변화량']:
    stat, p = mannwhitneyu(df_rec1_avg[col],
                           df_rec2_avg[col],
                           alternative='two-sided')

    mw_results_rec.append({
        'Column': col,
        'U-statistic': round(stat, 4),
        'p-value': round(p, 4),
        '유의성 (p < 0.05)': '차이가 있다' if p < 0.05 else '차이가 없다'
    })

mw_df_rec = pd.DataFrame(mw_results_rec)
print(mw_df_rec)

 

 

 

2-3. 양품/불량품 간 산포차이 유의성 검정

import numpy as np

cols = ['volt', 'ampere', 'temperature', '시간변화량(초)', '두께변화량']

mw_var_results = []

for col in cols:
    # 시퀀스별 분산 계산
    fail_var = df_fail.groupby('sequence_index')[col].var()
    success_var = df_success.groupby('sequence_index')[col].var()

    # Mann-Whitney U test (양측 검정)
    stat, p = mannwhitneyu(
        fail_var,
        success_var,
        alternative='two-sided'
    )

    mw_var_results.append({
        'Column': col,
        'U-statistic': round(stat, 4),
        'p-value': round(p, 4),
        '유의성 (p < 0.05)': '차이가 있다' if p < 0.05 else '차이가 없다'
    })

mw_var_df = pd.DataFrame(mw_var_results)
print(mw_var_df)

 

 

2-4. 양품/불량품 간 특성치 변화율 최대치 비교 검정

순간변화율 계산

# 1) 정렬
df_mil = df_mil.sort_values(['sequence_index', 'pk_datetime'])

# 2) ampere에 대한 dI/dt
df_mil['dI_ampere'] = df_mil.groupby('sequence_index')['ampere'].diff()
df_mil['dt_ampere'] = df_mil.groupby('sequence_index')['pk_datetime'].diff().dt.total_seconds()
df_mil['dI_dt_ampere'] = df_mil['dI_ampere'] / df_mil['dt_ampere']

# 3) volt에 대한 dV/dt
df_mil['d_volt'] = df_mil.groupby('sequence_index')['volt'].diff()
df_mil['dt_volt'] = df_mil.groupby('sequence_index')['pk_datetime'].diff().dt.total_seconds()
df_mil['dV_dt_volt'] = df_mil['d_volt'] / df_mil['dt_volt']

# 4) temperature에 대한 dT/dt
df_mil['d_temperature'] = df_mil.groupby('sequence_index')['temperature'].diff()
df_mil['dt_temperature'] = df_mil.groupby('sequence_index')['pk_datetime'].diff().dt.total_seconds()
df_mil['dT_dt_temperature'] = df_mil['d_temperature'] / df_mil['dt_temperature']

# 5) 두께변화량에 대한 d(thickness)/dt
df_mil['d_thickness'] = df_mil.groupby('sequence_index')['두께변화량'].diff()
df_mil['dt_thickness'] = df_mil.groupby('sequence_index')['pk_datetime'].diff().dt.total_seconds()
df_mil['dThickness_dt'] = df_mil['d_thickness'] / df_mil['dt_thickness']

# 6) 시퀀스별 최대 절대 변화율 계산 (ampere + volt + temperature + 두께변화량)
max_change_rate = (
    df_mil.groupby('sequence_index')
          .agg(
              max_abs_dI_dt_ampere   = ('dI_dt_ampere',    lambda x: np.nanmax(np.abs(x))),
              max_abs_dV_dt_volt     = ('dV_dt_volt',      lambda x: np.nanmax(np.abs(x))),
              max_abs_dT_dt_temp     = ('dT_dt_temperature', lambda x: np.nanmax(np.abs(x))),
              max_abs_dThickness_dt  = ('dThickness_dt',   lambda x: np.nanmax(np.abs(x)))
          )
          .reset_index()
)

max_fail_rate = max_change_rate.merge(df_fail_avg, on='sequence_index',how='left').dropna()
max_success_rate = max_change_rate.merge(df_success_avg, on='sequence_index',how='left').dropna()

 

양품 특성치 순간변화율

 

 

순간변화율 검정

mw_results_rate = []

for col in ['max_abs_dV_dt_volt', 'max_abs_dI_dt_ampere', 'max_abs_dT_dt_temp', 'max_abs_dThickness_dt']:
    stat, p = mannwhitneyu(max_success_rate[col],
                           max_fail_rate[col],
                           alternative='two-sided')

    mw_results_rate.append({
        'Column': col,
        'U-statistic': round(stat, 4),
        'p-value': round(p, 4),
        '유의성 (p < 0.05)': '차이가 있다' if p < 0.05 else '차이가 없다'
    })

mw_results_rate = pd.DataFrame(mw_results_rate)
print(mw_results_rate)

 

 

3. 정리

 

시각화를 했을때는 양품과 불량품간의 차이가 있을줄 알았는데 검정을 해보니 차이가 나지 않는다는것을 알았습니다.

데이터를 따로 계산해봐도 차이가 큰데 검정이 잘못될수도 있고 정말 유의하지 않을 수도 있을 것 같습니다.

 

추측하건데 아마 시계열 분석까지는 진행해야 불량품을 선별할 수 있지 않을까 생각합니다.