심화 프로젝트

Chapter.5 심화 프로젝트(2)

devjjun 2026. 4. 30. 20:52

TIL(Today I Learned)


1️⃣심화 프로젝트 

데이터 전처리 과정 

전체 변수 별 데이터 분포 확인

데이터들 사이 CO, HC등 불규칙하고 비정상적인 그래프 확인 

전체 변수 별 박스플롯


주요 변수 로그를 통한 박스플롯 확인

주요 변수 로그활용 박스플롯


타겟변수(Fault)별 분포 확인

주요 변수 별 타겟변수 로그활용 박스플롯

해석

CO

Fault 1에서 분산이 크고, 최댓값이 크다. 결함2,3에서는 중앙값은 낮지만 이상치 존재

해석 -> Fault 1은 CO 배출이 불안정하게 증가하는 경향, 연소 과정에서 연료 과다 또는 불완전 연소가 간헐적으로 발생가능성. Fault 2, 3은 평균적인 CO 수준은 낮지만 특정 상황에서 급격히 증가하는 이상 패턴을 보인다.

HC

Fault 1에서 HC가 압도적으로 높다.

해석 -> HC 농도는 Fault 1에서 비정상적으로 높은 값을 보이며, 이는 연료가 완전히 연소되지 않고 배출되는 현상일 가능성. Fault 1은 점화 문제 또는 심각한 연소 불량 상태와 강하게 연관된 결함으로 판단.

Lambda

Fault 0: ~0.95 근처 (정상 범위)/ Fault 2,3: 1에 가까움 (또는 초과)/ Fault 1: 분산 큼 (불안정)

해석 -> Fault 2와 3은 1에 근접하거나 초과하는 경향을 보이며 이는 공기 과잉 상태(희박 연소)를 의미한다.

Fault 1은 Lambda 분포가 넓게 퍼져 있어 연소 상태가 매우 불안정한 특징을 보인다.


이상치 판별 

HC

더보기
# IQR 기준 로그 전/후 비교로 이상치 판별하기
Q1 = df['HC'].quantile(0.25)
Q3 = df['HC'].quantile(0.75)
IQR = Q3 - Q1

lower_hc = Q1 - 1.5 * IQR

# 하단 이상치
df['HC_low_outlier'] = (df['HC'] < lower_hc).astype(int)

# 로그 변환
df['HC_log'] = np.log1p(df['HC'])

# 로그 기준 극단값 확인
Q1_log = df['HC_log'].quantile(0.25)
Q3_log = df['HC_log'].quantile(0.75)
IQR_log = Q3_log - Q1_log

lower_log = Q1_log - 1.5 * IQR_log

df['HC_log_low_outlier'] = (df['HC_log'] < lower_log).astype(int)

# 개수 비교
print(df['HC_low_outlier'].value_counts())
print(df['HC_log_low_outlier'].value_counts())

HC_low_outlier
0    52999
1     3000
Name: count, dtype: int64
HC_log_low_outlier
0    52999
1     3000
Name: count, dtype: int64
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=4, random_state=42)
df['cluster'] = kmeans.fit_predict(df[['HC']])

print(df.groupby('cluster')['HC'].describe())

  count        mean        std      min        25%       50%  \
cluster                                                                 
0        28268.0  161.886503  12.288312  133.087  152.28850  162.2215   
1         1000.0  919.664880  18.559689  855.126  907.45225  919.5590   
2        23731.0  212.815700  23.533307  187.293  197.94950  205.3570   
3         3000.0    2.196761   0.207901    1.787    1.93600    2.3080   

               75%      max  
cluster                      
0        170.63350  187.285  
1        932.01775  975.657  
2        218.01850  312.865  
3          2.35500    2.494
# HC 3 기준인 이유
per = [0.01, 0.05, 0.10]

for p in per:
    q = df['HC'].quantile(p)
    subset = df[df['HC'] <= q]
    
    print(f" 하위 {int(p*100)}% (기준값: {q:.4f})")
    print("개수:", len(subset))
    print("평균:", subset['HC'].mean())
    print("표준편차:", subset['HC'].std())
    print("최대값:", subset['HC'].max())
 
	하위 1% (기준값: 1.9140)
개수: 560
평균: 1.8829874999999998
표준편차: 0.0232895330125521
최대값: 1.913
 	하위 5% (기준값: 2.4010)
개수: 2804
평균: 2.1810263908701852
표준편차: 0.20598707312439438
최대값: 2.401
 	하위 10% (기준값: 143.9400)
개수: 5601
평균: 66.8179896447063
표준편차: 69.41941874145384
최대값: 143.94

CO

더보기
# CO IQR 기준
Q1 = df['CO'].quantile(0.25)
Q3 = df['CO'].quantile(0.75)
IQR = Q3 - Q1

upper_co = Q3 + 1.5 * IQR

# 이상치 기준 (상단)
df['CO_high_outlier'] = (df['CO'] > upper_co).astype(int)

# 전체 개수
print("총 이상치 개수:", df['CO_high_outlier'].sum())

# Fault별 분포
co_fault = df[df['CO_high_outlier'] == 1]['Fault'].value_counts(normalize=True) * 100
print(co_fault)

# Fault 1의 결함 신호일 수 있음

총 이상치 개수: 6000
Fault
1    50.000000
0    16.666667
2    16.666667
3    16.666667
Name: proportion, dtype: float64

CO2

더보기
# CO2 IQR 기준
Q1 = df['CO2'].quantile(0.25)
Q3 = df['CO2'].quantile(0.75)
IQR = Q3 - Q1

lower_co2 = Q1 - 1.5 * IQR

# 이상치 기준
df['CO2_low_outlier'] = (df['CO2'] < lower_co2).astype(int)

# 전체 개수
print("총 이상치 개수:", df['CO2_low_outlier'].sum())

# Fault별 분포
co2_fault = df[df['CO2_low_outlier'] == 1]['Fault'].value_counts(normalize=True) * 100
print(co2_fault)

# Fault 1의 결함 신호일 수 있음

총 이상치 개수: 5919
Fault
1    48.183815
0    18.009799
2    16.911640
3    16.894746
Name: proportion, dtype: float64

파생변수 생성 계획 

  • Fault 2, 3 구분용
    • O2_dev (정상 상태의 산소 농도를 기준으로 한 "이탈도”를 의미)
      • df['O2_dev'] = abs(df['O2'] - df[df['Fault']==0]['O2'].mean())
        • Fault 0 (정상): 이 값은 0에 매우 가깝습니다. (자신의 평균 근처에서 움직이기 때문)
        • Fault 1 (Rich): 연료가 너무 많아 산소가 거의 소모되므로, 정상치보다 훨씬 낮은 O2 값을 갖게 되어 O2_dev 수치가 커집니다.
        • Fault 2, 3 (Lean): 공기가 너무 많아 산소가 남게 되므로, 정상치보다 높은 O2 값을 갖게 되어 역시 O2_dev 수치가 커집니다.
    • 왜 lambda_dev와 함께 사용하나요?
      • 제어 결과 vs 물리적 성분 : lambda_dev는 ECU(자동차의 '두뇌' 역할을 하는 컴퓨터 제어 장치)가 계산한 제어 결과의 오차를 보는 것이라면, O2_dev는 배기가스에 남아있는 실제 물리적 성분의 변화를 직접 측정하는 것입니다.
      • 센서 신뢰성 검증 : 센서 신뢰성 검증: 만약 lambda_dev는 정상인데 O2_dev만 비정상적으로 높다면, 이는 산소 센서 자체의 노후화나 리딩 오류를 잡아내는 힌트가 될 수도 있습니다.
    • O2_RPM_Effect(보류)

💡 느낀점

도메인 지식이 많이 필요한 데이터셋이라 전처리 과정 시행 전 조사에 많은 시간이 필요했다. 파생변수 생성 계획 또한 결함 2와 3을 구분하는 주요 변수를 파악해 생성을 해 볼 예정인데, 그래프가 상당히 유사하게 나와 자세히 들여다 볼 필요성을 느꼈다. 내일은 결함에 따른 도메인 조사를 확실히 해 변수 구분을 하여 적절한 파생변수를 생성하고 검정을 진행 할 예정이다.

'심화 프로젝트' 카테고리의 다른 글

Chapter.5 심화 프로젝트(6)  (0) 2026.05.08
Chapter.5 심화 프로젝트(5)  (0) 2026.05.07
Chapter.5 심화 프로젝트(4)  (0) 2026.05.04
Chapter.5 심화 프로젝트(3)  (0) 2026.05.01
Chapter.5 심화 프로젝트(1)  (0) 2026.04.29