자동차·데이터 실무

시험 CSV의 시간축과 단위를 먼저 확인하기

설명용 합성 데이터에서 단위, 누락값, 유한수, 시간 중복·역행을 확인하고 점검 결과와 그래프를 저장합니다.

목차 보기

이런 분께자동차 시험 CSV를 처음 파이썬으로 읽어 보는 엔지니어와 입문자

준비사항
  • Python 3.12와 터미널을 준비합니다. python --version으로 버전을 확인합니다.
  • 다운로드한 ZIP을 모두 풀고 example.py가 있는 폴더에서 터미널을 엽니다.
  • 처음에는 동봉된 연습 데이터로 실행한 뒤 결과와 원본을 비교합니다.
  • 동봉된 synthetic_test.csv는 설명용 합성 데이터입니다. 시간 단위 s, 속도 단위 km/h를 사용합니다.

01그래프 전에 확인할 네 가지

설명용 합성 데이터의 파일 구조, 숫자, 단위, 시간 순서를 먼저 검사합니다. 값이 읽힌다는 사실만으로 단위가 맞거나 시간이 올바르다고 알 수는 없습니다. 이 예제는 모든 입력 행에서 s와 km/h가 명시되어 있는지 확인하고 원본 행 순서대로 시간이 증가하는지 검사합니다.

02입력 CSV 읽기

동봉 값과 의미
dataset_note설명용 합성 데이터
time0.0부터 1.0까지 경과 시간
time_units
speed0부터 22까지 속도 값
speed_unitkm/h

숫자 칸에는 단위 문자열을 붙이지 않습니다. 단위는 별도 열에 기록합니다. time은 음수가 될 수 없고, 최소 두 개의 표본이 필요합니다. speed의 물리적 정상 범위는 이 코드에서 정하지 않습니다.

03파일을 준비하고 실행하기

  1. ZIP을 풀고 README.txt에 적힌 입력 파일과 example.py가 같은 위치인지 확인합니다. ZIP 안에서 바로 실행하지 않습니다.
  2. example.py가 있는 폴더에서 아래 명령을 한 줄씩 실행합니다. Windows에서 python 명령이 없고 py가 있으면 각 명령의 python을 py -3.12로 바꿉니다.
  3. 완료 메시지가 나온 뒤 새 outputs 폴더를 엽니다. 기존 outputs가 있으면 먼저 다른 이름으로 보관하고 다시 실행합니다.
bash
python --version
python -m pip install -r requirements.txt
python example.py

설치는 인터넷 연결이 필요합니다. 코드의 BASE는 현재 터미널 위치가 아니라 example.py의 위치를 기준으로 입력과 결과 경로를 정합니다.

04전체 코드와 중지 조건

CSV의 각 행을 읽으면서 누락값과 단위를 확인합니다. math.isfinite로 NaN과 무한대를 거부합니다. 현재 시간이 직전 시간보다 작거나 같으면 중지하므로, 정렬해서 문제를 숨기거나 중복 표본을 임의로 지우지 않습니다. 입력 검사가 끝난 후에만 outputs를 만듭니다.

example.py
"""Check time order and declared units in explanatory synthetic test data.

This demonstrates file checks only. It makes no vehicle safety assessment.
"""
import csv
import json
import math
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

BASE = Path(__file__).resolve().parent
HEADERS = ["dataset_note", "time", "time_unit", "speed", "speed_unit"]
NOTE = "설명용 합성 데이터"


def read_samples():
    samples = []
    with (BASE / "synthetic_test.csv").open(encoding="utf-8-sig", newline="") as file:
        reader = csv.DictReader(file)
        if reader.fieldnames != HEADERS:
            raise ValueError(f"Expected headers: {HEADERS}")
        for line, row in enumerate(reader, start=2):
            if None in row or any(value is None or not value.strip() for value in row.values()):
                raise ValueError(f"Row {line}: missing or extra field.")
            if row["dataset_note"] != NOTE:
                raise ValueError(f"Row {line}: explanatory synthetic-data label is required.")
            if row["time_unit"] != "s" or row["speed_unit"] != "km/h":
                raise ValueError(f"Row {line}: expected units s and km/h; no automatic conversion.")
            time, speed = float(row["time"]), float(row["speed"])
            if not math.isfinite(time) or not math.isfinite(speed):
                raise ValueError(f"Row {line}: NaN and infinite values are not supported.")
            if time < 0:
                raise ValueError(f"Row {line}: elapsed time must be nonnegative.")
            if samples and time <= samples[-1][0]:
                raise ValueError(f"Row {line}: duplicate or decreasing time; check the source.")
            samples.append((time, speed))
    if len(samples) < 2:
        raise ValueError("At least two samples are required.")
    return samples


def main():
    destination = BASE / "outputs"
    if destination.exists():
        raise ValueError("outputs already exists; rename it before running again.")
    samples = read_samples()
    times, speeds = zip(*samples)
    intervals = [right - left for left, right in zip(times, times[1:])]
    report = {
        "dataset_note": NOTE,
        "scope": "File structure and numeric checks only; no vehicle safety assessment.",
        "sample_count": len(samples), "time_unit": "s", "speed_unit": "km/h",
        "start_time": times[0], "end_time": times[-1],
        "min_interval_s": min(intervals), "max_interval_s": max(intervals),
        "min_speed_kmh": min(speeds), "max_speed_kmh": max(speeds),
        "time_strictly_increasing": True,
    }
    figure, axis = plt.subplots(figsize=(8, 4.6), layout="constrained")
    axis.plot(times, speeds, "o-", color="#2B6578", linewidth=2)
    axis.set(title="Speed trace | Explanatory synthetic data", xlabel="Elapsed time (s)", ylabel="Speed (km/h)")
    axis.spines[["top", "right"]].set_visible(False)
    axis.grid(alpha=0.2)
    destination.mkdir(exist_ok=False)
    with (destination / "data_check.json").open("x", encoding="utf-8") as file:
        json.dump(report, file, ensure_ascii=False, indent=2)
    figure.savefig(destination / "synthetic_speed.png", dpi=160)
    plt.close(figure)
    print(f"Explanatory synthetic data: {len(samples)} samples, {times[0]:g}-{times[-1]:g} s.")
    print(f"Sample interval: {min(intervals):g}-{max(intervals):g} s; units: s, km/h.")
    print("Created outputs/data_check.json and outputs/synthetic_speed.png.")


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError, csv.Error) as error:
        raise SystemExit(f"Stopped: {error}") from error

05점검 결과와 그래프 확인하기

outputs/data_check.json에는 단위와 표본 수, 시간 범위, 표본 간격의 최솟값·최댓값이 기록됩니다. outputs/synthetic_speed.png는 같은 설명용 합성 데이터를 그린 결과입니다.

항목동봉 설명용 합성 데이터 결과
표본 수6
시간 범위0.0~1.0 s
표본 간격약 0.2 s
속도 최솟값 / 최댓값0 / 22 km/h
시간 증가 여부true
설명용 합성 데이터의 속도 곡선. 시간 0~1초, 속도 0~22 km/h.
설명용 합성 데이터의 속도 곡선. 시간 0~1초, 속도 0~22 km/h.

JSON의 표본 간격은 부동소수점 표현 때문에 0.19999999999999996처럼 보일 수 있습니다. 입력 값의 정확도나 계측 분해능을 추가로 주장하는 숫자는 아닙니다.

06오류를 직접 넣어 확인하기

  1. 정상 결과의 sample_count가 6이고 time_unit이 s인지 확인합니다.
  2. 새로 압축을 푼 연습 폴더에서 세 번째 데이터의 time을 앞선 값 0.2와 같게 바꿉니다. 실행하면 duplicate or decreasing time 메시지와 함께 중지해야 합니다.
  3. 다른 새 복사본에서 speed_unit 하나를 m/s로 바꿉니다. expected units 메시지가 나오고 outputs가 생성되지 않는지 확인합니다.
  4. 원래 입력 파일은 보관하고, 오류 실험은 복사본에서만 진행합니다.

07점검이 중지되면 무엇을 볼까

메시지원본에서 확인할 것
expected units s and km/h내보내기 설정과 각 행의 단위가 일치하는지 확인합니다. 코드가 자동 환산하지 않습니다.
duplicate or decreasing time재시작·표본 중복·파일 합치기 과정에서 시간 순서가 달라졌는지 확인합니다.
NaN and infinite values누락 표본이나 계산 오류를 원본에서 확인합니다.
synthetic-data label is required이 튜토리얼 전용 설명용 합성 데이터 표시를 유지합니다.
missing or extra field쉼표 수, 빈 칸, 열 이름을 확인합니다.

08점검이 끝나도 남는 확인

  • 시간 간격이 일정해야 한다는 규칙은 강제하지 않습니다. min_interval_s와 max_interval_s가 다르면 원본의 표본 주기를 따로 확인합니다.
  • 필터링, 보간, 단위 환산, 센서 교정, 시간 동기화, 노이즈 판단을 하지 않습니다.
  • 단위 열의 표기만 검사합니다. 실제 값이 그 단위로 측정되었는지, 센서가 올바른 채널인지 확인할 수 없습니다.
  • 동봉 데이터에 한정된 파일 처리 연습입니다. 실제 차량 시험 절차나 안전 판단에 사용할 수 있는 검증 도구로 해석하지 않습니다.

실행·검증 기록

Windows 11 (10.0.26200), CPython 3.12.14 (64비트). openpyxl 3.1.5, Matplotlib 3.10.8. 예제별 사용 라이브러리는 requirements.txt에 고정했습니다.

  • 설명용 합성 데이터 6개 표본·시간 범위·단위·간격 확인
  • 단위 불일치·중복/역행 시간·NaN·누락값 오류 검사
  • 원본 해시 불변, 기존 결과 거부, PNG 생성 확인
검증 범위의 한계
  • 설명용 합성 데이터만 검사했습니다. 실제 계측 데이터 및 차량 안전성은 평가하지 않았습니다.

사이트 전체의 작성·검증 원칙

직접 실행할 예제 파일

코드·입력 데이터·실행 안내가 포함되어 있습니다. 압축을 풀고 README.txt부터 읽어 주세요.

예제 ZIP 다운로드

직접 작성한 연습 자료 · 원본을 따로 보관한 뒤 실행하세요.

참고 출처

설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.