
本教程详细讲解如何使用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 pdimport 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)# 预处理:将非数值转换为NaNcandy['Q3: AGE_numeric'] = pd.to_numeric(candy['Q3: AGE'], errors='coerce')print("n预处理后的数值列:")print(candy[['Q3: AGE', 'Q3: AGE_numeric']])
输出:
原始数据: Q3: AGE0 45-551 202 563 354 sixty-nine5 NaN6 157 60 on the day after Halloween8 40预处理后的数值列: Q3: AGE Q3: AGE_numeric0 45-55 NaN1 20 20.02 56 56.03 35 35.04 sixty-nine NaN5 NaN NaN6 15 15.07 60 on the day after Halloween NaN8 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:
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转换后的NaNcandy['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_cat0 45-55 NaN unknown1 20 20.0 18-252 56 56.0 56+3 35 35.0 26-354 sixty-nine NaN unknown5 NaN NaN unknown6 15 15.0 17 and under7 60 on the day after Halloween NaN unknown8 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_cat0 45-55 unknown1 20 18-252 56 56+3 35 26-354 sixty-nine unknown5 NaN unknown6 15 17 and under7 60 on the day after Halloween unknown8 40 36-45age_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 pdimport 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设为NaNcandy['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而产生的NaNcandy['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:如何将数值数据精确分箱并处理非数值与缺失值的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1373719.html
微信扫一扫
支付宝扫一扫