60 lines
2.1 KiB
Python
60 lines
2.1 KiB
Python
"""CSV 读取模块:读 timestamp,value,label 三列,返回归一化元组。"""
|
||
|
||
from pathlib import Path
|
||
import pandas as pd
|
||
|
||
|
||
def read_csv(path: str | Path, value_col: str = "value") -> tuple[list[int], list[float], list[int]]:
|
||
"""读 timestamp,value,label CSV 文件。
|
||
|
||
Args:
|
||
path: CSV 文件路径
|
||
value_col: 数值列名 (default: "value")
|
||
|
||
Returns:
|
||
(timestamps_ms, values, labels) 三元组
|
||
- timestamps_ms: 毫秒精度 Unix 时间戳
|
||
- values: 浮点数值列表
|
||
- labels: 整数标签列表(0=正常, 1=异常)
|
||
|
||
Raises:
|
||
FileNotFoundError: 文件不存在
|
||
ValueError: 缺少 timestamp 或 value 列
|
||
"""
|
||
df = pd.read_csv(path)
|
||
|
||
if "timestamp" not in df.columns or value_col not in df.columns:
|
||
raise ValueError(f"CSV 缺少 timestamp 或 {value_col} 列: {path}")
|
||
|
||
timestamps = pd.to_datetime(df["timestamp"], format="mixed", errors="coerce")
|
||
if timestamps.isna().any():
|
||
# 对无法解析的行尝试修正(如 00:60:00 → 01:00:00)
|
||
import re
|
||
na_mask = timestamps.isna()
|
||
raw_strs = df.loc[na_mask, "timestamp"].astype(str)
|
||
|
||
def _fix_hour60(s: str) -> str:
|
||
def _repl(m):
|
||
h = (int(m.group(1)) + 1) % 24
|
||
return f"{h:02d}:00:00"
|
||
return re.sub(r"(\d{2}):60:00", _repl, s)
|
||
|
||
fixed = raw_strs.apply(_fix_hour60)
|
||
timestamps.loc[na_mask] = pd.to_datetime(fixed, errors="coerce")
|
||
# 丢弃时间戳仍为 NaN 的行(coerce 后无法恢复的异常时间戳)
|
||
valid_mask = timestamps.notna()
|
||
if not valid_mask.all():
|
||
n_drop = (~valid_mask).sum()
|
||
import logging
|
||
logging.getLogger(__name__).warning("dropping %d rows with invalid timestamps", n_drop)
|
||
timestamps = timestamps[valid_mask].reset_index(drop=True)
|
||
df = df[valid_mask].reset_index(drop=True)
|
||
|
||
timestamps_ms = (timestamps.astype("int64") // 10**6).tolist()
|
||
|
||
values = df[value_col].astype(float).tolist()
|
||
|
||
labels = df["label"].fillna(0).astype(int).tolist() if "label" in df.columns else [0] * len(df)
|
||
|
||
return timestamps_ms, values, labels
|