数据清洗工具:Python Pandas库入门、NumPy基础、数据清洗常用函数
说实话,做仿真脚本数据录制这么多年,我踩过最大的坑就是——数据本身是脏的。你辛辛苦苦跑了一夜的仿真,结果录出来的数据有空值、有重复、有格式错误,那真是欲哭无泪。所以今天咱们聊聊数据清洗,这是每个数据工程师的必修课。
Pandas库入门:你的瑞士军刀
Pandas,说白了就是Python里的Excel。但它比Excel强太多了。我刚开始用的时候,觉得这玩意儿不就是个表格工具吗?后来才发现,处理百万级数据时,Excel直接卡死,Pandas却稳如老狗。
先看个最简单的例子:
import pandas as pd
# 读取CSV文件
df = pd.read_csv('simulation_data.csv')
print(df.head()) # 看看前5行数据
嗯,就这么简单。你可能会问:「那我直接用Excel打开不就行了?」问题是,仿真脚本录出来的数据,动辄几十万行,Excel根本扛不住。
核心概念:DataFrame是Pandas的核心数据结构。你可以把它想象成一个带标签的二维表格,每一列可以有不同的数据类型(整数、浮点数、字符串等)。
我个人习惯,拿到数据后第一件事就是看基本信息:
# 查看数据概览
print(df.info()) # 列名、非空值数量、数据类型
print(df.describe()) # 数值列的统计信息(均值、标准差、分位数等)
我在项目中遇到过一件事:有次录制的仿真数据里,时间戳列居然混了两种格式,一种是"2024-01-15 10:30:00",另一种是"01/15/2024 10:30 AM"。用info()一看,这列被识别成了object类型,而不是datetime。这就是脏数据的典型表现。
NumPy基础:Pandas背后的引擎
很多人学Pandas时忽略了NumPy,这是个误区。Pandas的底层就是NumPy,很多操作其实是在调用NumPy的函数。
举个例子:
import numpy as np
# 创建一个数组
arr = np.array([1, 2, 3, 4, 5])
print(arr * 2) # 输出 [2, 4, 6, 8, 10]
你想想看,如果不用NumPy,用Python原生的列表做这种操作,你得写个循环。但NumPy直接向量化运算,速度能快几十倍。
为什么Pandas快?因为它的核心运算都是向量化的。比如:
# Pandas的向量化操作
df['value'] = df['value'] * 2 # 整列乘以2,一行代码搞定
这背后就是NumPy在发力。所以学Pandas之前,先了解NumPy的几个核心概念:
- ndarray:多维数组,Pandas的DataFrame底层就是用它存储数据
- 向量化运算:不用写循环,直接对整个数组做运算
- 广播机制:不同形状的数组也能做运算,Pandas自动帮你处理
小技巧:如果你发现Pandas操作特别慢,试试把数据转成NumPy数组再处理。有时候能快10倍以上。
数据清洗常用函数:dropna、fillna、drop_duplicates
好了,铺垫完了,咱们进入正题。数据清洗最常用的三个函数,我一个个说。
1. dropna:扔掉空值
空值,也就是NaN,是数据里最烦人的东西。仿真脚本偶尔会漏录数据,或者传感器没采到值,就会产生NaN。
# 删除所有包含NaN的行
df_clean = df.dropna()
# 只删除某列为空的行
df_clean = df.dropna(subset=['timestamp', 'value'])
# 删除所有列都为空的行
df_clean = df.dropna(how='all')
我曾经犯过一个错误:直接用了dropna(),结果删掉了80%的数据。为什么?因为有一列是备注信息,大部分行都是空的。所以用dropna之前,一定要搞清楚哪些列是必须非空的。
注意:dropna默认是删除行。如果你想删除列,用axis=1参数:df.dropna(axis=1)。但一般不建议这么干,除非整列都是空值。
2. fillna:填充空值
有时候不能直接删数据,比如时间序列数据,删掉一行会导致时间不连续。这时候就得填充。
# 用固定值填充
df['value'] = df['value'].fillna(0)
# 用前一个值填充(向前填充)
df['value'] = df['value'].fillna(method='ffill')
# 用后一个值填充(向后填充)
df['value'] = df['value'].fillna(method='bfill')
# 用均值填充
df['value'] = df['value'].fillna(df['value'].mean())
我个人习惯,对于仿真数据,优先用向前填充。因为仿真过程是连续的,前一个时刻的值往往比均值更靠谱。但要注意边界情况——如果第一行就是空值,向前填充就失效了,这时候得用向后填充兜底。
3. drop_duplicates:去重
仿真脚本有时候会重复录制数据,比如网络延迟导致同一帧数据被录了两次。这时候就得去重。
# 删除完全重复的行
df_clean = df.drop_duplicates()
# 根据指定列去重
df_clean = df.drop_duplicates(subset=['timestamp'])
# 保留最后出现的重复行
df_clean = df.drop_duplicates(subset=['timestamp'], keep='last')
这里有个坑:默认keep='first',也就是保留第一次出现的行。但有时候我们需要保留最后一次出现的,比如数据是实时更新的,最后一次才是最新状态。
实战经验:我建议去重之前,先看看数据里到底有多少重复:
# 查看重复行数
print(df.duplicated().sum())
# 查看哪些列有重复
print(df.duplicated(subset=['timestamp']).sum())
这样心里有数,不会盲目去重。
综合案例:清洗仿真数据
咱们来个完整的例子,把上面学的串起来:
import pandas as pd
import numpy as np
# 模拟脏数据
data = {
'timestamp': ['2024-01-15 10:00:00', '2024-01-15 10:00:01',
'2024-01-15 10:00:01', None],
'sensor_id': [1, 2, 2, 3],
'value': [23.5, np.nan, 24.1, 25.0]
}
df = pd.DataFrame(data)
# 1. 处理时间戳空值
df = df.dropna(subset=['timestamp'])
# 2. 填充传感器数值的空值(用均值)
df['value'] = df['value'].fillna(df['value'].mean())
# 3. 删除重复的时间戳(保留第一次)
df = df.drop_duplicates(subset=['timestamp'])
print(df)
你看,三步下来,数据就干净了。实际项目中,你可能需要根据业务逻辑调整顺序。比如先填充再删除,还是先删除再填充?这得看具体情况。
知识体系总览
下面这张图,是我自己总结的数据清洗知识体系,你可以照着这个思路学:
这张图把今天讲的内容串起来了。你想想看,Pandas是工具,NumPy是引擎,三个函数是招式。三者结合,才能应对各种脏数据。
我的建议:刚开始学的时候,别急着背函数参数。先拿一份真实数据,用info()和describe()看一遍,心里有数了再动手清洗。数据清洗,七分靠理解数据,三分靠写代码。
好了,今天就聊到这儿。记住,数据清洗不是一次性工作,而是迭代的过程。你可能需要反复查看数据、调整清洗策略。别怕麻烦,数据干净了,后面的分析才能站得住脚。
可可精品资料库