数据准备:读取与清洗家庭用电数据
好,咱们正式开始动手了。上一章我们聊了电费账单分析的全局思路,这一章,我来带你搞定最基础也最关键的一步——把数据从CSV文件里读进来,然后洗干净。
你可能觉得这步没啥技术含量。嗯,我以前也这么想。直到有一次,我跑完整个分析模型,发现结果异常——回头一查,原来是数据里混了几条「2023年2月30日」的记录。你说气不气人?从那以后,数据清洗我从来不敢马虎。
2.1 读取CSV文件
家庭用电数据,最常见的就是CSV格式。每家每户的电表数据,通常包含这几列:时间戳、用电量(千瓦时)、电压、电流、功率因数等。
我习惯用 pandas 的 read_csv() 来读。为什么?因为它的参数设计太贴心了,几乎覆盖了所有现实中的坑。
import pandas as pd
# 读取家庭用电数据
df = pd.read_csv(
'household_electricity.csv',
parse_dates=['timestamp'], # 自动解析时间列
infer_datetime_format=True, # 自动推断时间格式
encoding='utf-8' # 避免中文乱码
)
print(f'数据加载完成,共 {df.shape[0]} 行,{df.shape[1]} 列')
df.head()
encoding='gbk' 或 encoding='gb2312'。我在南方某小区的数据里就遇到过这种情况,因为他们的电表系统是老款,导出的CSV是GBK编码。
2.2 初步探索数据
数据读进来了,别急着往下走。先看看它长什么样。我一般会做三件事:
- 看列名和类型——确认每一列是不是我们想要的
- 看缺失值——哪些列有空值,占比多少
- 看基本统计量——用电量的最小值、最大值、平均值,有没有异常
# 1. 查看数据基本信息
print('=== 数据概览 ===')
print(df.info())
print('\n=== 缺失值统计 ===')
print(df.isnull().sum())
print('\n=== 数值列统计描述 ===')
print(df.describe())
这里有个坑,我踩过好几次——describe() 默认只统计数值列。如果你的用电量列是字符串类型(比如带了单位「kWh」),它就不会被统计到。所以,记得先确认数据类型。
2.3 数据清洗四步走
数据清洗,说白了就是「去脏、补缺、纠错、去重」。我总结了一个四步流程,你照着做基本不会出问题。
2.3.1 处理缺失值
缺失值怎么处理?要看情况。如果缺失比例很小(比如不到5%),我倾向于直接删除。如果缺失比较多,就得考虑填充了。
# 删除缺失比例超过50%的列
threshold = len(df) * 0.5
df = df.dropna(thresh=threshold, axis=1)
# 对用电量列,用前后均值填充
df['power_consumption'] = df['power_consumption'].fillna(
df['power_consumption'].interpolate()
)
# 对时间戳列,直接删除缺失行
df = df.dropna(subset=['timestamp'])
2.3.2 处理重复数据
重复数据很常见。电表有时候会重复上报同一时刻的数据。我的做法是:
# 检查重复行
duplicates = df.duplicated(subset=['timestamp']).sum()
print(f'发现 {duplicates} 条重复的时间戳记录')
# 删除重复,保留第一条
df = df.drop_duplicates(subset=['timestamp'], keep='first')
2.3.3 处理异常值
异常值是最头疼的。比如用电量突然飙到10000千瓦时——这明显是电表故障。我一般用「四分位距法」来识别:
# 用电量列的异常值检测
Q1 = df['power_consumption'].quantile(0.25)
Q3 = df['power_consumption'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[
(df['power_consumption'] < lower_bound) |
(df['power_consumption'] > upper_bound)
]
print(f'检测到 {len(outliers)} 条异常用电记录')
# 将异常值替换为上下限
df['power_consumption'] = df['power_consumption'].clip(
lower=lower_bound, upper=upper_bound
)
2.3.4 时间序列对齐
家庭用电数据通常是15分钟或1小时一条。但实际数据里,时间间隔可能不均匀。我习惯把它重采样成固定频率:
# 设置时间戳为索引
df = df.set_index('timestamp')
# 重采样为1小时间隔,用均值填充
df_hourly = df.resample('1H').mean()
# 前向填充缺失的小时段
df_hourly = df_hourly.fillna(method='ffill')
print(f'重采样完成,共 {len(df_hourly)} 条小时级数据')
2.4 知识体系总览
下面这张图,是我自己总结的数据准备流程。你看一眼,心里就有谱了。
2.5 完整代码示例
最后,我把上面所有步骤串成一个完整的函数。你以后处理类似数据,直接复制改改就能用。
def load_and_clean_electricity_data(filepath):
"""
加载并清洗家庭用电数据
参数:
filepath: CSV文件路径
返回:
清洗后的DataFrame
"""
# 1. 读取
df = pd.read_csv(filepath, parse_dates=['timestamp'])
# 2. 删除缺失过多的列
df = df.dropna(thresh=len(df)*0.5, axis=1)
# 3. 填充用电量缺失值
df['power_consumption'] = df['power_consumption'].interpolate()
# 4. 删除重复时间戳
df = df.drop_duplicates(subset=['timestamp'])
# 5. 异常值处理
Q1 = df['power_consumption'].quantile(0.25)
Q3 = df['power_consumption'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
df['power_consumption'] = df['power_consumption'].clip(lower, upper)
# 6. 时间对齐
df = df.set_index('timestamp').resample('1H').mean().ffill()
return df
# 使用示例
clean_df = load_and_clean_electricity_data('household_electricity.csv')
print(f'清洗完成,剩余 {len(clean_df)} 条有效数据')
好了,数据准备好了。下一章,我们就可以开始真正的分析工作了。记住一句话:数据清洗花的时间,永远会在后续分析中加倍还给你。