第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_percent、cell_temp_c。中文列名在跨平台传输时容易出乱码,我吃过这个亏。
1.2 数据类型检查
数据类型不对,后面全白费。我见过最典型的问题:时间列被读成了 object 类型,电压列被读成了字符串。你想想看,字符串怎么算平均值?
# 查看每列的数据类型
print("数据类型检查:")
print(df.dtypes)
# 重点关注
# - object: 可能是文本,也可能是时间戳读错了
# - float64/int64: 数值型,正常
# - datetime64: 时间类型,需要转换
嗯,这里要注意。有时候数据看起来是数字,但实际上是字符串。比如电压值“48.5”,如果里面有不可见字符(空格、换行符),pandas 就会把它当成 object。我曾经排查一个 bug 排查了整整一下午,最后发现是数据文件里多了个空格。
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%。这些明显不符合物理规律。
我常用的方法有两种:
- 统计法:用3σ原则,超出均值±3倍标准差的值视为异常。
- 业务法:根据设备参数设定合理范围。比如锂电池电压通常在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()}")
知识体系总览
下面这张图,是我自己总结的数据探索流程。每次拿到新数据,我都会按这个步骤走一遍。
小结
数据探索这一步,说白了就是给数据做“体检”。维度、列名、类型、缺失、异常——这五个维度检查完,你心里就有底了。
我个人习惯,每次做完数据探索,都会写一个简单的报告,记录发现的问题。这样后续处理时,能快速定位问题根源。你也不妨试试。
explore_data() 函数,省了不少事。