T
traeai
登录
返回首页
KDnuggets

3 Pandas Tricks for Data Cleaning & Preparation

8.5Score

TL;DR · AI 摘要

Pandas 的三种高效数据清洗技巧可显著提升数据准备效率,包括声明式方法链、内存和速度优化、分组感知插补。

核心要点

  • 使用 .assign()、.query() 和 .pipe() 实现声明式方法链,提升代码可读性和安全性。
  • 通过 categoricals 和 vectorized string accessors 优化内存和速度。
  • 使用 .transform() 进行分组感知插补,提高数据完整性。

结构提纲

按章节快速跳转。

  1. 数据清洗和准备占数据科学家日常工作的 80%,使用 Pandas 可提高效率。

  2. 使用 .assign()、.query() 和 .pipe() 实现更安全、可读性强的数据处理流程。

  3. 通过 categoricals 和 vectorized string accessors 优化内存使用和执行速度。

  4. 使用 .transform() 进行分组感知插补,提高数据完整性。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Pandas 数据清洗技巧
    • 声明式方法链
      • .assign()
      • .query()
      • .pipe()
    • 内存和速度优化
      • categoricals
      • vectorized string accessors
    • 分组感知插补
      • .transform()

金句 / Highlights

值得收藏与分享的关键句。

#Python#Pandas#数据清洗#数据科学
打开原文

3 个 Pandas 技巧用于数据清洗与准备 - KDnuggets

publ: 2026年6月15日

  • 博客热门文章
  • 主题 人工智能 职业建议 计算机视觉 数据工程 数据科学 语言模型 机器学习 MLOps 自然语言处理 编程 Python SQL
  • 数据集
  • 活动
  • 资源 快速参考指南 推荐 技术简报
  • 广告

加入新闻通讯

#header end

/ad_wrapper

3 个 Pandas 技巧用于数据清洗与准备

在本文中,我们将介绍三个关键的 Pandas 技巧,以高效地清洗和准备数据:声明式方法链、通过分类和向量化字符串访问器进行内存和速度优化,以及使用 .transform() 进行组感知插补。

作者:

Matthew Mayo

KDnuggets 主编,2026年6月15日,在

Python

<div class="addthis_native_toolbox"></div>

# 引言

据估计,数据清洗和准备占据了数据科学家日常工作中多达80%的时间。由于 Pandas 是 Python 中的标准数据操作库,您的操作效率直接决定了您从原始、脏数据集到模型就绪特征的转换速度。而且,有充分的理由希望增加清洗和准备时间:这直接转化为可用于建模、分析和传达洞察的时间。

然而,许多开发人员编写的 Pandas 代码模仿了标准的 Python 循环结构,或者使用了命令式、状态突变的更新。这些方法存在几个问题:它们可能引发令人困惑的 SettingWithCopyWarning,通过冗余的复制增加 RAM 使用量,并且由于避免向量化而降低执行速度。

要编写生产级别的数据管道,您需要从基本语法过渡到惯用的 Pandas 设计模式。在本文中,我们将介绍三个关键的 Pandas 技巧,以高效地清洗和准备数据:

  • 使用 .assign()、.query() 和 .pipe() 的声明式方法链
  • 通过分类和向量化字符串访问器进行内存和速度优化
  • 使用 .transform() 进行组感知插补

# 1. 使用 .assign()、.query() 和 .pipe() 的声明式方法链

在准备数据时,通常会执行一系列修改:清理字符串值、创建新的数学列、过滤异常值、重命名字段等。

一种天真的方法是按顺序编写这些操作,就地修改 DataFrame 或反复将其重新分配给相同的变量。这不仅使代码难以阅读和调试,而且修改切片后的 DataFrame 通常会频繁触发臭名昭著的 SettingWithCopyWarning。这个警告是 Pandas 告诉您它无法保证您是在修改副本还是内存中的原始数组缓冲区。

通过将数据清洗管道用括号括起来,您可以按顺序链接 Pandas 方法。使用 .assign() 声明新列,使用 .query() 进行行过滤,并使用 .pipe() 应用自定义函数,可以保持您的操作线性、可读,并且免受副作用的影响。

这种命令式风格逐步修改 DataFrame,存在警告警报的风险,并且使中间阶段难以隔离:

code
import pandas as pd
import numpy as np

# 示例原始销售数据
data = {
    'sale_date': ['2026-01-01', '2026-01-02', 'invalid_date', '2026-01-04'],
    'item_code': ['  PROD_A ', ' PROD_B', 'PROD_C  ', '  PROD_D '],
    'price': [100.0, 250.0, -99.0, 150.0],
    'quantity': [2, 1, 5, 3]
}
df = pd.DataFrame(data)

# 简单的多步骤清理
df['sale_date'] = pd.to_datetime(df['sale_date'], errors='coerce')
df['item_code'] = df['item_code'].str.strip()
df['total_revenue'] = df['price'] * df['quantity']

# 过滤掉无效日期和价格
df = df[df['sale_date'].notna()]
df = df[df['price'] > 0]

# 重命名列以保持一致性
df.rename(columns={'item_code': 'product_id'}, inplace=True)

print(df)

在这里,我们将相同的逻辑重新组织为一个单一、连贯、从上到下的流程。我们使用自定义的辅助函数和 .pipe() 来处理自定义异常:

code
import pandas as pd
import numpy as np

data = {
    'sale_date': ['2026-01-01', '2026-01-02', 'invalid_date', '2026-01-04'],
    'item_code': ['  PROD_A ', ' PROD_B', 'PROD_C  ', '  PROD_D '],
    'price': [100.0, 250.0, -99.0, 150.0],
    'quantity': [2, 1, 5, 3]
}
df_raw = pd.DataFrame(data)

# 自定义模块化清理步骤
def clean_item_codes(df):
    df['item_code'] = df['item_code'].str.strip()
    return df

# 方法链式流程
cleaned_df = (
    df_raw
    .copy()  # 防止修改原始数据
    .assign(
        sale_date=lambda d: pd.to_datetime(d['sale_date'], errors='coerce'),
        total_revenue=lambda d: d['price'] * d['quantity']
    )
    .pipe(clean_item_codes)
    .query("sale_date.notna() and price > 0")
    .rename(columns={'item_code': 'product_id'})
)

print(cleaned_df)

输出:

code
sale_date product_id  price  quantity  total_revenue
0 2026-01-01     PROD_A  100.0         2          200.0
1 2026-01-02     PROD_B  250.0         1          250.0
3 2026-01-04     PROD_D  150.0         3          450.0

通过将表达式包裹在 ( ... ) 中,Python 允许在不使用反斜杠的情况下进行多行链式操作。

  • .assign() 接受关键字参数,其中 lambdas 接收 DataFrame 的当前状态 (d),使您能够按顺序创建或修改多个列。
  • .pipe() 将中间 DataFrame 传递给外部函数。这将可重用的清理逻辑与主链分开。
  • .query() 接受一个布尔表达式字符串。它比嵌套括号 (df[(df[a] > 0) & (df[b].notna())]) 更整洁,并且在内部使用 NumPy 的快速数值表达式评估器 NumExpr,运行速度更快。

这种函数模式避免了 SettingWithCopyWarning,因为它从未修改中间切片。

# 2. 使用分类和向量化字符串方法进行内存和速度优化

默认情况下,Pandas 会将包含文本的列分配为通用对象数据类型。对象列存储指向堆内存中字符串的 Python 指针,而不是连续的打包值。对于包含低基数字符串(如状态标志、城市名称或性别等重复类别的列)的大数据集,这会导致显而易见的内存占用。

此外,开发人员经常通过将 Python lambda 表达式传递给 .apply() 来应用自定义字符串修改。这迫使 Pandas 以缓慢的 Python 解释器速度逐行循环。

我们可以通过以下方式优化内存使用和执行时间:

  • 将低基数字符串列转换为原生的类别数据类型 通过使用 .str 访问器,用优化的向量化字符串方法替换缓慢的 .apply() 循环

让我们通过保持文本作为对象列并使用 .apply() 清理空格来模拟清理一个大型数据集(1,000,000 行):

code
import pandas as pd
import numpy as np
import time

# 创建一个包含100万行低基数字符串数据的模拟数据集
n_rows = 1000000
categories = [' PENDING ', ' COMPLETED ', ' FAILED ', ' SHIPPED ']
df = pd.DataFrame({
    'status': np.random.choice(categories, size=n_rows),
    'val': np.random.rand(n_rows)
})

# 清理前的内存使用情况基准测试
mem_before = df['status'].memory_usage(deep=True) / (1024 ** 2)

start_time = time.time()

# 朴素清理:缓慢的Python apply循环
df['status'] = df['status'].apply(lambda x: x.strip().upper())
duration_apply = time.time() - start_time

mem_after = df['status'].memory_usage(deep=True) / (1024 ** 2)

print(f"Apply清理完成时间: {duration_apply:.4f} 秒")
print(f"状态列内存使用情况: {mem_after:.2f} MB(原始为 {mem_before:.2f} MB)")

通过首先将状态列转换为类别类型,并使用向量化的 .str 访问器,我们实现了即时的速度提升并节省了大量内存:

code
import pandas as pd
import numpy as np
import time

n_rows = 1000000
categories = [' PENDING ', ' COMPLETED ', ' FAILED ', ' SHIPPED ']
df = pd.DataFrame({
    'status': np.random.choice(categories, size=n_rows),
    'val': np.random.rand(n_rows)
})

# 转换为类别类型
df['status'] = df['status'].astype('category')

# 内存使用情况基准测试
mem_category = df['status'].memory_usage(deep=True) / (1024 ** 2)

start_time = time.time()

# 直接在类别上进行向量化字符串清理
df['status'] = df['status'].cat.rename_categories(lambda x: x.strip().upper())
duration_vectorized = time.time() - start_time

print(f"向量化类别清理完成时间: {duration_vectorized:.4f} 秒")
print(f"类别状态列内存使用情况: {mem_category:.2f} MB")
print(f"加速比: {duration_apply / duration_vectorized:.2f} 倍更快")

综合输出:

code
Apply清理完成时间: 0.1213 秒
状态列内存使用情况: 53.64 MB(原始为 55.55 MB)

向量化类别清理完成时间: 0.0003 秒
类别状态列内存使用情况: 0.95 MB
加速比: 407.83 倍更快

我们将这些性能提升视为胜利。

当一个列被转换为类别类型时,Pandas 在内部将字符串编码为整数键(例如,PENDING -> 0,COMPLETED -> 1)。

  • 与其存储1,000,000个字符串,Pandas存储1,000,000个小整数和一个很小的映射表,其中包含4个实际字符串类别。这将内存占用从约56 MB减少到不到1 MB。通过直接使用 .cat.rename_categories() 清理标签,Pandas只在4个唯一类别上执行字符串操作,而不是遍历1,000,000行。执行时间几乎降为零。

注意:如果你处理的是高基数文本(其中值很少重复),将其保留为类别类型不会节省内存。在这种情况下,你仍然应避免使用 .apply(),并直接在对象列上使用向量化字符串方法:df['status'].str.strip().str.upper(),这在编译的C代码中执行,而不是在Python中。

# 3. 使用 groupby() 和 .transform() 进行组感知的插值和插值

处理缺失数据是数据清洗的基本步骤。在许多情况下,用全局平均值或常数替换缺失值会引入统计偏差。例如,如果你要填补缺失的产品价格,使用所有商店产品的全局平均价格是不准确的。使用该特定产品类别的平均价格进行填补会更加精确。

一种简单的方法是遍历产品类别,计算组平均值,过滤 DataFrame,填充缺失值,然后将各组重新拼接在一起。另一种方法是使用自定义函数在 groupby().apply() 中进行操作,这会触发效率较低的 split-apply-combine 循环,扩展性较差。

优化的解决方案是将 groupby() 与 .transform() 方法结合使用。

在这里,我们使用循环或传递给 .apply() 的自定义函数来模拟填补缺失的数值价格(用 NaN 表示):

code
import pandas as pd
import numpy as np
import time

# 创建一个包含 100,000 个物品的模拟目录,按类别分组
n_items = 100000
categories = [f"CAT_{i}" for i in range(100)]

df = pd.DataFrame({
    'category': np.random.choice(categories, size=n_items),
    'price': np.random.uniform(10.0, 500.0, size=n_items)
})

# 引入 10% 的缺失价格(NaN)
nan_mask = np.random.rand(n_items) < 0.1
df.loc[nan_mask, 'price'] = np.nan

df_clunky = df.copy()

start_time = time.time()

# 使用 apply() 和自定义 lambda 进行 split-apply-combine
df_clunky['price'] = df_clunky.groupby('category')['price'].apply(lambda x: x.fillna(x.mean())).reset_index(level=0, drop=True)
duration_clunky = time.time() - start_time

print(f"基于 apply 的组填补耗时: {duration_clunky:.4f} 秒")

通过利用 .transform(),我们绕过了自定义 lambda 循环,允许 Pandas 本地处理索引对齐和矢量化操作:

code
import pandas as pd
import numpy as np
import time

# 使用相同的设置
df_optimized = df.copy()

start_time = time.time()

# 使用 transform 的优化方法
group_means = df_optimized.groupby('category')['price'].transform('mean')
df_optimized['price'] = df_optimized['price'].fillna(group_means)
duration_opt = time.time() - start_time

print(f"基于 transform 的组填补耗时: {duration_opt:.4f} 秒")
print(f"加速比: {duration_clunky / duration_opt:.2f} 倍更快")
code
基于 apply 的组填补耗时: 0.0224 秒
基于 transform 的组填补耗时: 0.0032 秒
加速比: 7.04 倍更快

理解 .transform() 的工作原理对于编写高性能的 Pandas 代码至关重要:

  • 当你运行 df.groupby('category')['price'].transform('mean') 时,Pandas 会计算每个类别的平均价格。与返回一个较小的分组汇总表不同,.transform() 会将计算的值广播回原始 DataFrame 的大小和对齐方式。它输出一个与原始数据集长度相同的序列,其中索引 i 包含该行所属组的平均值。然后我们可以使用 df['price'].fillna(group_means)。这通过干净、矢量化、索引对齐的赋值来填补缺失值。

这种模式非常灵活。你可以使用它来进行组级别的标准化(例如,减去组平均值),或者使用 df.groupby('group')['val'].transform('ffill') 按组进行前向填充缺失值。

# 总结

通过超越基础的、简单的循环结构,采用符合 Pandas 风格的设计模式,你可以构建出能够无缝从本地原型扩展到生产环境的数据准备管道。

让我们回顾一下:

  • 方法链式调用用可读的、声明式的处理序列取代了脆弱的、多行的命令式修改,完全避免了 SettingWithCopyWarning。分类转换和向量化字符串方法优化了内存布局,并将字符串转换操作卸载到 C 语言速度的执行中,在低基数数据上可将 RAM 使用量减少高达 98%。使用 .transform() 的分组感知插补方法计算分组级别的统计信息,并原生地将它们重新对齐到原始索引形状,避免了缓慢的自定义分组循环。

将这些模式融入到你的日常工作中,将使你的特征工程和数据清洗过程变得快速、整洁且高度可维护。

Matthew Mayo(@mattmayo13)拥有计算机科学硕士学位和数据挖掘研究生文凭。作为 KDnuggets & Statology 的总编辑,以及 Machine Learning Mastery 的特约编辑,Matthew 致力于使复杂的数据科学概念变得易于理解。他的专业兴趣包括自然语言处理、语言模型、机器学习算法以及探索新兴的人工智能。他致力于推动数据科学社区知识的民主化。Matthew 从六岁起就开始编程了。

更多相关内容

  • 365 Data Science 提供的免费 AI 驱动的面试准备工具
  • 7 个 Pandas 技巧,节省你的时间
  • 掌握使用 Python 和 Pandas 进行数据清洗的 7 个步骤
  • 使用 Pandas 进行 NLP 任务的文本数据清洗和预处理
  • 使用 Python 和 Pandas 创建自动化的数据清洗管道
  • 10 个 Pandas 一行代码实现的数据清洗技巧

<hr class="grey-line"><br> <div><h3>我们推荐的 5 门免费课程</h3><br> </div>

Mailchimp for WordPress v4.13.0 - https://wordpress.org/plugins/mailchimp-for-wp/

/ Mailchimp for WordPress 插件

你可以从这里开始编辑。

如果评论已关闭。

<= 上一篇

下一篇 =>

#content end

<script type="text/javascript">kda_sid_write(kda_sid_n);</script>

最新文章

  • 2026 年成为 LLM 工程师的路线图 停止在 Pandas 中编写循环:尝试 7 个更快的替代方法 使用 sktime 在 Python 中构建时间序列机器学习模型 Pandas 数据清洗与准备的 3 个技巧 将 Claude Code 与本地模型配对 3 个 NumPy 技巧提升数值性能

热门文章

  • 低成本实现本地代理编程:Claude Code + Ollama + Gemma4
  • 将 Claude Code 与本地模型配对
  • Anthropic 的 Claude 技能构建完整指南
  • 5 个有用的 Python 脚本,自动化无聊的 PDF 任务
  • 7 个获取创业点子资金的最佳方式
  • 10 个用于 Python Web 开发的 GitHub 仓库
  • AI 工程师必须知道的 5 个 Python 概念
  • 使用 sktime 在 Python 中构建时间序列机器学习模型
  • 5 篇有趣论文清晰解释 LLMs
  • 数据清洗与准备的 3 个 Pandas 技巧

#content_wrapper end

© 2026

Guiding Tech Media

|

关于

联系我们

广告合作

隐私政策

服务条款

发布于 2026 年 6 月 15 日

blank

不,谢谢!

/.main_wrapper

<script defer type="text/javascript" src="https://s7.addthis.com/js/300/addthis_widget.js#pubid=gpsaddthis"></script>

noptimize

/noptimize

AI 可能会生成不准确的信息,请核实重要内容

3 Pandas Tricks for Data Cleaning & Preparation | KDnuggets | traeai