第1章:数据探索——先摸清你家储能系统的“脾气”

各位同行,大家好。我是老张,在储能行业摸爬滚打了快十年。今天咱们开始聊《储能运行数据清洗与预处理实战》的第一章——数据探索。

说实话,我见过太多人拿到数据就急着建模、急着分析。结果呢?跑出来的结果自己都不敢信。为什么?因为数据本身就有问题。你想想看,一个储能系统每天成千上万条数据,传感器漂移、通信丢包、人为误操作……这些坑我全踩过。

所以,数据探索这一步,说白了就是先摸摸你家储能系统的“脾气”。别急着干活,先看看数据长什么样。

1.1 数据维度与列名分析

拿到数据的第一件事,我习惯先看数据框的 shape。这就好比你去仓库盘点,先要知道仓库有多大、有多少货架。

import pandas as pd

# 读取数据
df = pd.read_csv('bess_operation_data.csv')

# 查看数据维度
print(f"数据维度: {df.shape}")
print(f"行数: {df.shape[0]}, 列数: {df.shape[1]}")

# 查看所有列名
print("\n列名列表:")
for col in df.columns:
    print(f"  - {col}")

我在项目中遇到过,有一次数据明明有50列,但实际有用的只有20列。剩下的全是冗余字段,比如重复的时间戳、无意义的ID编号。所以,列名分析这一步,能帮你快速筛掉“垃圾信息”。

我的习惯:列名最好用英文+下划线命名,比如 soc_percentcell_temp_c。中文列名在跨平台传输时容易出乱码,我吃过这个亏。

1.2 数据类型检查

数据类型不对,后面全白费。我见过最典型的问题:时间列被读成了 object 类型,电压列被读成了字符串。你想想看,字符串怎么算平均值?

# 查看每列的数据类型
print("数据类型检查:")
print(df.dtypes)

# 重点关注
# - object: 可能是文本,也可能是时间戳读错了
# - float64/int64: 数值型,正常
# - datetime64: 时间类型,需要转换

嗯,这里要注意。有时候数据看起来是数字,但实际上是字符串。比如电压值“48.5”,如果里面有不可见字符(空格、换行符),pandas 就会把它当成 object。我曾经排查一个 bug 排查了整整一下午,最后发现是数据文件里多了个空格。

避坑指南:我曾经因为时间列没转成 datetime 类型,导致后续的时序分析全部错位。记住:时间列一定要用 pd.to_datetime() 显式转换。

1.3 缺失值初步统计

缺失值,说白了就是数据里的“窟窿”。储能系统的数据采集不是100%可靠的,通信中断、传感器故障都会导致缺失。

# 缺失值统计
missing_count = df.isnull().sum()
missing_percent = (df.isnull().sum() / len(df)) * 100

# 合并展示
missing_df = pd.DataFrame({
    '缺失数量': missing_count,
    '缺失比例(%)': missing_percent.round(2)
})
print(missing_df[missing_df['缺失数量'] > 0])

我个人习惯,先看缺失比例。如果某列缺失超过50%,我会直接考虑删除。如果缺失在5%以内,可以用插值填充。但要注意:SOC(荷电状态)这种关键参数,缺失了必须想办法补,不能随便删。

缺失比例 处理建议 我的经验
< 5% 插值填充或均值填充 线性插值对连续数据效果不错
5% - 20% 根据业务逻辑填充 比如用前后时刻的均值
20% - 50% 考虑是否保留该特征 如果特征重要,尝试模型预测填充
> 50% 建议删除该列 数据质量太差,留着也是噪声

1.4 异常值初步识别

异常值,就是那些“离谱”的数据。比如电池电压突然跳到1000V,或者SOC瞬间从50%变成-10%。这些明显不符合物理规律。

我常用的方法有两种:

  1. 统计法:用3σ原则,超出均值±3倍标准差的值视为异常。
  2. 业务法:根据设备参数设定合理范围。比如锂电池电压通常在2.5V-4.2V之间,超出这个范围就是异常。
# 统计法识别异常值
import numpy as np

def detect_outliers_zscore(data, threshold=3):
    z_scores = np.abs((data - data.mean()) / data.std())
    return z_scores > threshold

# 以电压列为例
voltage_col = 'cell_voltage_v'
outliers = detect_outliers_zscore(df[voltage_col].dropna())
print(f"电压列异常值数量: {outliers.sum()}")

# 业务法:设定合理范围
valid_range = (2.5, 4.2)
business_outliers = ~df[voltage_col].between(*valid_range)
print(f"业务规则异常值数量: {business_outliers.sum()}")
核心观点:异常值不一定是错误,也可能是真实事件。比如电网波动导致电压骤升,这反而是有价值的信息。所以,识别出来后先别急着删,要结合业务场景判断。

知识体系总览

下面这张图,是我自己总结的数据探索流程。每次拿到新数据,我都会按这个步骤走一遍。

数据探索核心流程 ① 数据加载 读取CSV/数据库 ② 维度与列名分析 shape / columns ③ 数据类型检查 dtypes ④ 缺失值统计 isnull().sum() ⑤ 异常值识别 3σ / 业务规则 每个步骤都对应一个核心函数,按顺序执行即可

小结

数据探索这一步,说白了就是给数据做“体检”。维度、列名、类型、缺失、异常——这五个维度检查完,你心里就有底了。

我个人习惯,每次做完数据探索,都会写一个简单的报告,记录发现的问题。这样后续处理时,能快速定位问题根源。你也不妨试试。

一个小技巧:把数据探索的代码封装成一个函数,每次拿到新数据直接调用。我自己的工具包里就有这样一个 explore_data() 函数,省了不少事。

专注资料整理