0

0

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

DDD

DDD

发布时间:2025-09-25 15:41:01

|

286人浏览过

|

来源于php中文网

原创

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:

Tellers AI
Tellers AI

Tellers是一款自动视频编辑工具,可以将文本、文章或故事转换为视频。

下载
  • 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开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

760

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

639

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

762

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

618

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1265

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

549

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

579

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

709

2023.08.11

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

72

2026.01.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 4.6万人学习

Django 教程
Django 教程

共28课时 | 3.2万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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