← 이전 글: 센서 데이터는 어디서 오염되는가 — 제조 현장의 3가지 함정
#1에서 드리프트, 전환점, 누락 — 세 가지 오염 패턴을 봤습니다. 이번 글에서는 그것을 직접 찾아봅니다.
우리 공장 데이터에도 이런 패턴이 있을까요?
있다면 — 언제부터, 어느 구간에서 시작됐을까요?
3라인 온도 센서 3개월치 데이터 — 세 패턴이 모두 들어있습니다. 코드로 읽고, 각각이 현장에서 무엇을 의미하는지 함께 확인합니다.
탐지 순서에 대해 — 먼저 짚고 갑니다
#1 현장 관찰에서 확인한 것처럼 — 세 패턴이 동시에 존재할 때는 순서가 있습니다.
1단계 누락부터 확인합니다 — 없는 데이터를 먼저 파악해야 있는 데이터를 올바르게 해석할 수 있습니다
2단계 전환점을 확인합니다 — 기준 자체가 이동했다면, 이전과 이후를 같은 기준으로 비교할 수 없습니다
3단계 드리프트 추세를 봅니다 — 누락과 전환점을 제거한 구간에서 방향성이 있는 변화를 읽습니다
이 순서대로 코드를 실행합니다.
탐지 1 — 누락 시간을 찾습니다
데이터를 열었을 때 행이 몇 개인지 세는 것만으로는 부족합니다. 중요한 것은 타임스탬프 사이의 간격입니다. 정상 수집 주기보다 긴 간격이 있다면 — 그 구간에 데이터가 없는 것입니다.
df = pd.read_csv('sensor_temp_line3_2024Q1.csv', parse_dates=['timestamp'])
df = df.sort_values('timestamp').reset_index(drop=True)
# 타임스탬프 간격 계산 (분 단위)
df['gap_min'] = df['timestamp'].diff().dt.total_seconds() / 60
# 정상 수집 주기: 1분. 2배 이상이면 누락 의심
threshold = 2.0
missing_gaps = df[df['gap_min'] > threshold][['timestamp', 'gap_min']]
print(f"누락 의심 구간: {len(missing_gaps)}건")
print(missing_gaps)
코드가 보여주는 것은 간단합니다 — 타임스탬프 간격이 비정상적으로 긴 지점의 목록입니다.
현장에서 읽는 법
간격이 30분이면 — 설비 정지였을 수 있습니다. 5분이면 — 네트워크 순단이었을 수 있습니다. 코드는 시점과 길이를 알려줍니다. 그 시간대에 무슨 일이 있었는지는 — 현장 기록(작업일보, 정지이력)과 대조해 볼 수 있습니다.
누락 구간을 파악하면 — 이후 분석에서 그 구간을 제외하거나 별도로 표시할 수 있습니다. 없는 데이터를 모르고 분석하는 것과, 알고 처리하는 것은 다릅니다.

린코더 범위 밖: 누락 원인 제거 (네트워크 인프라, 설비 하드웨어)
수집 주기가 설비마다 다르다면 — threshold를 설비별로 따로 정의하는 것이 좋습니다.
탐지 2 — 기준 이동 (Changepoint)을 찾습니다
기준 이동은 교대 교체, 원자재 로트 변경, 설비 교체처럼 — 기준 자체가 이동하는 순간입니다. 값의 수준이 갑자기 달라집니다. 개별 값은 이상하지 않지만, 이전 구간과 이후 구간의 평균이 다릅니다.
window = 30 # 30분 이동 윈도우
df['rolling_mean'] = df['temperature'].rolling(window=window, center=True).mean()
df['rolling_std'] = df['temperature'].rolling(window=window, center=True).std()
# 이동 평균의 급격한 변화 탐지
df['mean_change'] = df['rolling_mean'].diff().abs()
change_threshold = df['rolling_std'].mean() * 2
changepoints = df[df['mean_change'] > change_threshold][['timestamp', 'temperature', 'mean_change']]
print(f"전환점 의심 구간: {len(changepoints)}건")
print(changepoints.head(10))
현장에서 읽는 법
이동평균 방식은 변화가 크고 명확할 때 잘 작동합니다. 변화가 완만하거나 노이즈가 많으면 놓칠 수 있습니다. 출발점으로 사용합니다.
기준 이동이 감지된 시점이 교대 교체 시간과 일치한다면 — 작업자 간 측정 방식 차이를 의심합니다. 원자재 입고 기록과 일치한다면 — 로트 변경이 원인일 수 있습니다. 코드는 시점을 알려줍니다. 원인은 현장 기록과 대조해 볼 수 있습니다.
기준 이동이 확인되면 — 이전 구간과 이후 구간을 별도로 분석하는 것이 좋습니다. 기준 이동을 무시하고 전체 구간을 하나로 보면 — 평균과 추세가 모두 왜곡됩니다.

린코더 범위 밖: 기준 이동 원인 제거 (작업 표준화, 공급망 관리)
탐지 3 — 드리프트 (Drift) 추세를 읽습니다
누락 구간을 제외하고, 전환점으로 구간을 나눈 다음 — 각 구간 안에서 값이 한 방향으로 지속적으로 이동하고 있는지 확인합니다. 드리프트는 추세입니다. 하루 이틀의 변동이 아니라, 주 단위로 같은 방향으로 움직이는 경향입니다.
# sensor_temp_line3_2024Q1.csv 로드
df = pd.read_csv('sensor_temp_line3_2024Q1.csv', parse_dates=['timestamp'])
df = df.sort_values('timestamp').reset_index(drop=True)
# CP2(03/01) 이후 구간 — 누락 제외
segment = df[(df['timestamp'] >= '2024-03-01') & df['temperature'].notna()].copy()
# 선형 회귀로 추세 방향과 기울기 확인
x = (segment['timestamp'] - segment['timestamp'].min()).dt.total_seconds()
slope, intercept, r_value, p_value, std_err = stats.linregress(x, segment['temperature'])
print(f"기울기: {slope * 3600:.4f} °C/시간")
print(f"R²: {r_value**2:.3f}")
print(f"p-value: {p_value:.4f}")
if p_value < 0.05 and abs(r_value) > 0.3:
direction = "상승" if slope > 0 else "하락"
print(f"→ 드리프트 감지: {direction} 추세")
else:
print(f"→ 유의미한 추세 없음")
# 출력 결과
# 기울기: +0.0120 °C/시간
# R²: 0.986
# p-value: 0.0000
# → 드리프트 감지: 상승 추세
현장에서 읽는 법
기울기 +0.0120°C/시간 — 하루 0.29°C, 한 달이면 약 8.6°C 상승합니다. R² 0.986은 온도 변화의 98.6%가 시간 흐름으로 설명된다는 뜻입니다. 추세가 매우 일관됩니다. p-value 0.0000은 이 추세가 우연이 아닐 가능성이 매우 높다는 의미입니다. 세 숫자가 동시에 이 방향을 가리킨다면 — Calibration 점검 주기를 재검토하는 근거가 될 수 있습니다.

린코더 범위 밖: 센서 Calibration 실행 (측정 담당자, 설비 엔지니어)
전환점이 여러 개라면 — 각 구간마다 반복 실행합니다. 전환점을 확정하는 방법은 다음 글에서 다룹니다.
코드는 완전한 답을 주지 않습니다.
어디를 봐야 하는지 — 방향을 가리킵니다.
그것만으로도 현장에서는 충분한 출발점이 됩니다.
드리프트가 멈추지 않으면 — 기준값 자체가 밀립니다. 그 순간부터 센서는 정상이라고 말하지만, 공정은 이미 다른 곳에 있습니다. 다음 글에서 확인합니다.