第1章:数据读取——用Pandas搞定BMS数据文件
各位同行,咱们直接进入正题。
做储能系统数据分析,第一步就是读数据。这一步看似简单,但我在项目里见过太多人栽在这上面——文件路径写错、编码不对、时间格式乱掉……说白了,数据还没进门就卡住了。
今天我就带大家把「数据读取」这个基本功练扎实。咱们用Pandas,把CSV、Excel、BMS典型数据文件统统拿下。
1.1 准备工作:导入Pandas
先别急着读文件,把工具准备好。我个人习惯在代码开头这样写:
import pandas as pd
import numpy as np
import os
# 设置显示选项,方便查看数据
pd.set_option('display.max_columns', None) # 显示所有列
pd.set_option('display.width', 1000) # 显示宽度
嗯,这里有个小细节——display.max_columns设为None,能避免数据列太多时被截断。我在调试一个200多列的电芯数据时,就靠这个选项一眼发现了某列全是空值。
1.2 读取CSV文件
CSV是储能数据最常见的格式。BMS导出的历史数据,十有八九是CSV。基本用法:
# 最简单的读取
df = pd.read_csv('bms_data_20240101.csv')
# 实际项目中更常用的写法
df = pd.read_csv(
'bms_data_20240101.csv',
encoding='utf-8', # 指定编码,避免乱码
parse_dates=['timestamp'], # 自动解析时间列
low_memory=False # 避免大文件分块读取时的类型推断问题
)
⚠️ 避坑指南:我曾经因为没指定
encoding,读出来的中文全是乱码。后来发现BMS厂家导出的文件,有的用utf-8,有的用gbk。我的经验是:先试utf-8,报错就换gbk。
1.3 读取Excel文件
有些厂家喜欢用Excel存配置参数或日报数据。Excel文件可能包含多个sheet,读取时要注意:
# 读取第一个sheet
df = pd.read_excel('bms_config.xlsx')
# 读取指定sheet
df = pd.read_excel('bms_config.xlsx', sheet_name='参数设置')
# 读取所有sheet,返回一个字典
sheets_dict = pd.read_excel('bms_config.xlsx', sheet_name=None)
你想想看,如果不知道Excel里有几个sheet,直接用sheet_name=None就能全部读进来,然后遍历字典处理。这招我在处理厂家提供的「参数配置表」时屡试不爽。
1.4 读取BMS典型数据文件
BMS数据文件有个特点——列名往往很长,比如单体电压_01、单体温度_01,而且时间戳格式五花八门。我给大家看一个真实案例:
# 读取BMS典型数据
df = pd.read_csv(
'bms_typical_data.csv',
encoding='utf-8',
parse_dates=['采集时间'],
dtype={
'总电压': float,
'总电流': float,
'SOC': float
}
)
💡 经验之谈:用
dtype指定数据类型,能避免Pandas把数字读成字符串。我遇到过SOC列因为混入了一个"N/A"字符,整列变成object类型,后续计算全崩了。
1.5 查看数据基本信息
数据读进来了,第一件事就是「看看它长什么样」。我一般三步走:
1.5.1 head()——看前几行
# 查看前5行
print(df.head())
# 查看前10行
print(df.head(10))
这一步能快速确认数据是否读对了,列名对不对,时间格式正不正常。
1.5.2 info()——看整体结构
print(df.info())
输出示例:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 86400 entries, 0 to 86399
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 采集时间 86400 non-null datetime64[ns]
1 总电压 86400 non-null float64
2 总电流 86400 non-null float64
3 SOC 86400 non-null float64
4 单体电压_01 86400 non-null float64
5 单体电压_02 86400 non-null float64
6 单体温度_01 86400 non-null float64
7 单体温度_02 86400 non-null float64
8 绝缘电阻 86000 non-null float64
9 状态标志 86400 non-null int64
10 报警代码 85000 non-null float64
11 备注 1200 non-null object
dtypes: datetime64[ns](1), float64(9), int64(1), object(1)
memory usage: 7.9 MB
info()是我最常用的函数。它能告诉我:
- 有多少行、多少列
- 每列的数据类型
- 有没有缺失值(Non-Null Count)
- 内存占用情况
🔍 注意看:上面例子中「绝缘电阻」只有86000条非空数据,比总行数86400少了400条。这说明有缺失值,后面清洗时就要处理。我一般看到这种差异,就会标记一下,后续做插值或删除。
1.5.3 describe()——看统计信息
print(df.describe())
输出示例:
| 指标 | 总电压 | 总电流 | SOC | 单体电压_01 |
|---|---|---|---|---|
| count | 86400.0 | 86400.0 | 86400.0 | 86400.0 |
| mean | 768.5 | 120.3 | 55.2 | 3.215 |
| std | 12.4 | 85.6 | 28.7 | 0.052 |
| min | 720.0 | -200.0 | 0.0 | 2.800 |
| 25% | 760.0 | 45.0 | 30.0 | 3.180 |
| 50% | 770.0 | 110.0 | 55.0 | 3.215 |
| 75% | 778.0 | 195.0 | 80.0 | 3.250 |
| max | 800.0 | 350.0 | 100.0 | 3.650 |
describe()能快速给出数值列的统计特征。我特别关注min和max——如果单体电压最小值低于2.8V,那电池可能已经过放了,需要立刻报警。
1.6 本章知识体系
下面这张图,是我梳理的数据读取核心流程:
1.7 实战小贴士
最后分享几个我踩过的坑:
- 文件路径别用中文:我曾经把文件放在「桌面/储能数据」文件夹里,结果Pandas死活读不出来。后来改成英文路径,一次通过。
- 大文件分块读:如果文件超过1GB,用
chunksize参数分块读取,避免内存爆炸。 - 时间列统一格式:BMS数据的时间戳可能是
2024-01-01 00:00:00,也可能是2024/01/01 00:00:00。用parse_dates配合format参数,能保证解析正确。
📌 核心要点:数据读取不是简单的「读进来就完事」。head()看长相,info()看结构,describe()看统计——这三板斧抡完,你对数据就有了基本判断。后面清洗、分析才能有的放矢。