数据清洗工具: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基础 清洗函数 DataFrame / Series 读取数据(read_csv等) 数据概览(info/describe) ndarray数组 向量化运算 广播机制 dropna(删除空值) fillna(填充空值) drop_duplicates(去重) 核心思路:先了解数据 → 再处理空值 → 最后去重

这张图把今天讲的内容串起来了。你想想看,Pandas是工具,NumPy是引擎,三个函数是招式。三者结合,才能应对各种脏数据。

我的建议:刚开始学的时候,别急着背函数参数。先拿一份真实数据,用info()和describe()看一遍,心里有数了再动手清洗。数据清洗,七分靠理解数据,三分靠写代码。

好了,今天就聊到这儿。记住,数据清洗不是一次性工作,而是迭代的过程。你可能需要反复查看数据、调整清洗策略。别怕麻烦,数据干净了,后面的分析才能站得住脚。

可可精品资料库