Python Pandas:如何将数值数据精确分箱并处理非数值与缺失值

DDD
发布: 2025-09-25 15:41:01
原创
248人浏览过

python pandas:如何将数值数据精确分箱并处理非数值与缺失值

本教程详细讲解如何使用Pandas将数值数据分箱到指定类别,同时有效处理非数值和缺失值。通过pd.cut结合pd.to_numeric和fillna,我们将演示如何解决“分箱标签数量必须比分箱边界少一个”的常见错误,并确保最终分类结果符合预期的类别顺序。

1. 引言:数据分箱与挑战

在数据分析中,将连续的数值数据划分到离散的类别(即分箱或离散化)是一种常见的预处理技术。例如,将年龄数据划分为“17岁以下”、“18-25岁”等类别,有助于简化数据、发现模式或满足业务需求。然而,在实际数据中,我们经常面临非数值数据、缺失值以及分箱逻辑的精确控制等挑战。本教程将以一个具体的年龄分箱场景为例,详细阐述如何使用Pandas库克服这些挑战。

我们的目标是将原始年龄数据(可能包含文本或缺失值)转换为以下七个精确定义的类别:unknown、17 and under、18-25、26-35、36-45、46-55、56+。其中,任何非数值或无法识别的年龄都应归入unknown类别。

2. 核心工具:pd.cut函数

pd.cut是Pandas中用于将数值数据分箱的关键函数。它允许我们根据提供的分箱边界(bins)将Series或DataFrame列中的值分配到不同的类别。

基本用法:

立即学习Python免费学习笔记(深入)”;

pd.cut(x, bins, labels=None, right=True, include_lowest=False)
登录后复制
  • x: 要分箱的Series或数组。
  • bins: 分箱的边界,可以是整数(等宽分箱)或列表(自定义分箱)。
  • labels: 分箱后的类别标签,其数量必须比bins少一个。
  • right: 布尔值,指示右侧边界是否包含在区间内(默认为True,即(a, b])。
  • include_lowest: 布尔值,指示第一个区间是否包含左侧边界(默认为False)。

3. 数据预处理:统一数值格式

在进行数值分箱之前,确保所有数据都是可处理的数值类型至关重要。原始数据可能包含非数字字符(如“sixty-nine”、“45-55”)或缺失值。pd.cut只能处理数值类型。

我们使用pd.to_numeric函数,配合errors='coerce'参数,将非数值数据转换为NaN(Not a Number)。这是一种简洁而强大的预处理方法。

示例数据准备:

import pandas as pd
import numpy as np

# 模拟原始数据
data = {'Q3: AGE': ['45-55', '20', '56', '35', 'sixty-nine', np.nan, '15', '60 on the day after Halloween', '40']}
candy = pd.DataFrame(data)

print("原始数据:")
print(candy)

# 预处理:将非数值转换为NaN
candy['Q3: AGE_numeric'] = pd.to_numeric(candy['Q3: AGE'], errors='coerce')

print("\n预处理后的数值列:")
print(candy[['Q3: AGE', 'Q3: AGE_numeric']])
登录后复制

输出:

原始数据:
                       Q3: AGE
0                        45-55
1                           20
2                           56
3                           35
4                   sixty-nine
5                          NaN
6                           15
7  60 on the day after Halloween
8                           40

预处理后的数值列:
                       Q3: AGE  Q3: AGE_numeric
0                        45-55              NaN
1                           20             20.0
2                           56             56.0
3                           35             35.0
4                   sixty-nine              NaN
5                          NaN              NaN
6                           15             15.0
7  60 on the day after Halloween              NaN
8                           40             40.0
登录后复制

可以看到,'45-55'、'sixty-nine'等非数字字符串以及原始NaN都被成功转换为了NaN。

4. 构建分箱逻辑:边界与标签的匹配

pd.cut函数的一个常见错误是“Bin labels must be one fewer than the number of bin edges”(分箱标签数量必须比分箱边界少一个)。这意味着,如果你有N个类别标签,你就需要提供N+1个分箱边界来定义这些区间。

为了实现我们所有类别,包括unknown,并解决上述错误,我们需要精心构造bins和labels:

怪兽AI数字人
怪兽AI数字人

数字人短视频创作,数字人直播,实时驱动数字人

怪兽AI数字人 44
查看详情 怪兽AI数字人
  • bins (分箱边界): 必须包含N+1个值,定义了N个区间。
  • labels (类别标签): 必须包含N个值,与bins定义的区间一一对应。

为了将unknown类别纳入pd.cut的直接处理范围,我们可以创建一个特殊的区间。例如,我们将所有小于等于-1的数值归为unknown。由于pd.to_numeric会将无效值转换为NaN,而NaN不会被pd.cut直接分箱,所以这个unknown区间主要用于捕获理论上可能存在的负数年龄(尽管在实际年龄数据中不常见)。更重要的是,后续我们会用fillna('unknown')来处理所有因预处理而产生的NaN。

# 定义分箱边界
# 注意:这里有8个边界,对应7个标签
bins = [-float('inf'), -1, 17, 25, 35, 45, 55, float('inf')]

# 定义类别标签
labels = ['unknown', '17 and under', '18-25', '26-35', '36-45', '46-55', '56+']

print(f"分箱边界数量: {len(bins)}")
print(f"类别标签数量: {len(labels)}")
登录后复制

这里,len(bins)是8,len(labels)是7,满足了len(bins) = len(labels) + 1的条件。

5. 处理缺失值与非数值数据

在pd.to_numeric步骤中,所有非数值数据都已转换为NaN。pd.cut在遇到NaN时,默认也会将其结果设为NaN。为了将这些NaN统一归类到unknown,我们需要在pd.cut操作之后使用fillna()方法。

# 应用pd.cut进行分箱
candy['age_cat'] = pd.cut(candy['Q3: AGE_numeric'], bins=bins, labels=labels, right=True)

# 填充所有剩余的NaN值为'unknown'
# 这些NaN可能来自原始数据中的NaN,或者pd.to_numeric转换后的NaN
candy['age_cat'] = candy['age_cat'].fillna('unknown')

print("\n初步分箱结果(包含unknown填充):")
print(candy[['Q3: AGE', 'Q3: AGE_numeric', 'age_cat']])
登录后复制

输出:

初步分箱结果(包含unknown填充):
                       Q3: AGE  Q3: AGE_numeric       age_cat
0                        45-55              NaN       unknown
1                           20             20.0         18-25
2                           56             56.0           56+
3                           35             35.0         26-35
4                   sixty-nine              NaN       unknown
5                          NaN              NaN       unknown
6                           15             15.0  17 and under
7  60 on the day after Halloween              NaN       unknown
8                           40             40.0         36-45
登录后复制

现在,所有非数值和缺失值都已正确地归类为unknown。

6. 规范化分类结果:Categorical类型

为了确保分类结果的数据类型是Pandas的Categorical类型,并且类别顺序严格按照要求排列,我们需要显式地进行转换。这有助于后续的数据分析和可视化,并确保类别顺序的稳定性。

# 定义最终的类别顺序
final_categories = ['unknown', '17 and under', '18-25', '26-35', '36-45', '46-55', '56+']

# 将age_cat列转换为Categorical类型,并指定类别顺序
candy['age_cat'] = pd.Categorical(candy['age_cat'], categories=final_categories, ordered=False)

print("\n最终分箱结果(Categorical类型及指定顺序):")
print(candy[['Q3: AGE', 'age_cat']])
print("\nage_cat列的类别信息:")
print(candy['age_cat'].cat.categories)
登录后复制

输出:

最终分箱结果(Categorical类型及指定顺序):
                       Q3: AGE       age_cat
0                        45-55       unknown
1                           20         18-25
2                           56           56+
3                           35         26-35
4                   sixty-nine       unknown
5                          NaN       unknown
6                           15  17 and under
7  60 on the day after Halloween       unknown
8                           40         36-45

age_cat列的类别信息:
Index(['unknown', '17 and under', '18-25', '26-35', '36-45', '46-55', '56+'], dtype='object')
登录后复制

可以看到,age_cat列现在是Categorical类型,并且其内部的类别顺序与final_categories完全一致。

7. 完整代码示例

将上述所有步骤整合,即可得到一个完整且健壮的解决方案:

import pandas as pd
import numpy as np

# 模拟原始数据
data = {'Q3: AGE': ['45-55', '20', '56', '35', 'sixty-nine', np.nan, '15', '60 on the day after Halloween', '40', '-5']}
candy = pd.DataFrame(data)

print("--- 原始数据 ---")
print(candy)

# 1. 数据预处理:将非数值转换为NaN
#    使用pd.to_numeric的errors='coerce'参数处理文本和无效值
candy['Q3: AGE_numeric'] = pd.to_numeric(candy['Q3: AGE'], errors='coerce')

# 2. 定义分箱边界和标签
#    注意:分箱边界数量必须比标签数量多一个
bins = [-float('inf'), -1, 17, 25, 35, 45, 55, float('inf')]
labels = ['unknown', '17 and under', '18-25', '26-35', '36-45', '46-55', '56+']

# 3. 应用pd.cut进行分箱
#    pd.cut会将Q3: AGE_numeric中的NaN值对应的age_cat设为NaN
candy['age_cat'] = pd.cut(candy['Q3: AGE_numeric'],
                          bins=bins,
                          labels=labels,
                          right=True) # right=True表示区间右侧闭合 (a, b]

# 4. 填充所有剩余的NaN值为'unknown'
#    这会捕获所有因pd.to_numeric转换失败或原始数据为NaN而产生的NaN
candy['age_cat'] = candy['age_cat'].fillna('unknown')

# 5. 规范化为Categorical类型并指定类别顺序
final_categories = ['unknown', '17 and under', '18-25', '26-35', '36-45', '46-55', '56+']
candy['age_cat'] = pd.Categorical(candy['age_cat'], categories=final_categories, ordered=False)

print("\n--- 最终处理结果 ---")
print(candy[['Q3: AGE', 'age_cat']])

print("\n--- age_cat列的类别顺序 ---")
print(candy['age_cat'].cat.categories)
登录后复制

8. 注意事项

  • bins与labels数量匹配: 始终牢记len(bins) = len(labels) + 1。这是pd.cut函数的核心要求。
  • errors='coerce'的重要性: 在pd.to_numeric中使用此参数是处理混合数据类型(数值和非数值)的优雅方式,它能将无法转换的值安全地变为NaN。
  • fillna()的必要性: pd.cut本身不会将NaN值分箱到特定类别。因此,在pd.cut之后使用fillna('unknown')是确保所有缺失或无效数据归入unknown的关键步骤。
  • right=True与include_lowest=True: pd.cut默认区间是右闭合的(例如(17, 25])。如果需要包含左侧边界,特别是第一个区间,可能需要设置include_lowest=True。在本例中,(-inf, -1]是第一个区间,默认right=True已经满足了包含-1的需求。
  • 明确Categorical类型: 即使pd.cut的结果已经是Categorical类型,显式地使用pd.Categorical并指定categories参数,可以确保类别顺序完全符合要求,尤其是在后续数据处理或可视化时保持一致性。

9. 总结

本教程详细演示了如何使用Pandas进行复杂的数据分箱操作。通过结合pd.to_numeric进行数据清洗、精确构造bins和labels解决pd.cut的常见错误、利用fillna处理缺失值,以及最终通过pd.Categorical规范化类别顺序,我们能够高效且准确地将原始数据转换为符合业务需求的分类数据。掌握这些技巧对于任何数据分析师来说都至关重要。

以上就是Python Pandas:如何将数值数据精确分箱并处理非数值与缺失值的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号