深入理解 Pandas cut() 方法:将连续数据转化为洞察力

在数据科学和分析的日常工作中,我们经常遇到这样的情况:手头有一堆连续的数值数据,比如年龄、收入、房价或者成绩,虽然这些数据很精确,但直接面对成千上万个具体的数字往往让我们难以抓住重点。这时候,我们往往不关心具体的数值,而是更想知道这些数据落在哪个区间里。

这就是我们今天要深入探讨的核心话题——数据分箱(Binning),或者叫离散化。而在 Python 的 Pandas 库中,INLINECODE98133273 方法正是我们完成这一任务的“瑞士军刀”。在这篇文章中,我们将像解剖一只麻雀一样,从原理到实战,全方位地掌握 INLINECODE48b45175 的用法。我们不仅会学习它的基本语法,还会深入探讨它处理边缘情况的机制、如何自定义标签,以及在实际项目中如何避免那些常见的坑。

准备好了吗?让我们开始这段将数据转化为洞察力的旅程吧。

什么是数据分箱?

简单来说,数据分箱就是将一系列连续的数值“切”成几个片段,并把每个数值分配到对应的片段中。这就像我们把一堆散落的硬币分类存放到不同的储钱罐里一样。

#### 为什么我们需要这样做?

  • 降噪与简化:原始数据往往包含噪声。将“25岁”和“26岁”都视为“20-30岁”年龄段,可以帮助我们忽略细微的差别,捕捉更宏观的模式。
  • 建立非线性关系:在机器学习中,某些特征(如收入)与目标变量之间可能不是线性的。分箱可以帮助模型更好地理解这些非线性关系。
  • 可视化友好:与其绘制一条杂乱的折线图,通常绘制一个清晰的直方图或柱状图来展示分布情况会更直观。

pandas.cut() 核心概念解析

pandas.cut() 函数的核心目的是将一维数组进行分箱操作。它最基本的工作原理是:你给它一列数据和几个“刀口”,它就帮你把数据切开。

#### 基本语法

pd.cut(x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates=‘raise‘)

让我们来逐一拆解这些参数,看看我们手里有哪些控制权:

  • x:这是我们要处理的目标数据,通常是 DataFrame 的某一列,或者一个 NumPy 数组/列表。
  • bins:这是定义切分规则的关键。它可以是一个整数(表示要切成几份),也可以是一个序列(具体的切分点数值列表)。
  • INLINECODEb8da5dc9 (默认: True):这是一个非常关键的布尔值。它决定了区间是左开右闭 INLINECODEdd9ff61a 还是左闭右开 [a, b)。默认情况下,区间包含右边的数字,不包含左边的数字。
  • INLINECODEaff8730b:用来给切出来的箱子贴上标签。如果不指定,Pandas 会默认显示区间范围(如 INLINECODE3c6d9636)。如果你指定了标签(如“青年”、“中年”),输出就会变得更加人性化。
  • INLINECODE2fb727d6 (默认: False):通常第一个区间是不包含左边界的。如果你的数据刚好等于最小边界值,设置 INLINECODEee3f6a70 可以将其包含进来(即把第一个区间变为 [min, max])。
  • INLINECODEc309fed2 (默认: False):如果你不仅想要分类结果,还想看看具体的边界值是多少,可以把它设为 INLINECODEe698f46c,它会帮你把边界也一起返回。
  • duplicates:如果我们的切分点里有重复的值,这会导致区间长度为0。该参数决定是报错(‘raise‘)还是丢弃重复值(‘drop‘)。

实战演练:从基础到进阶

为了更好地理解,让我们通过几个具体的例子来实际操作一下。在这个过程中,我们会遇到各种真实场景,并看看如何用代码解决它们。

#### 场景一:学生成绩分段处理

这是最经典的入门案例。假设我们是一所学校的数据管理员,有一份学生的成绩单。我们不想盯着具体的“72分”看,而是想知道这是“低分”、“中等”还是“高分”。

import pandas as pd

# 1. 准备数据:创建一个包含学生姓名和成绩的 DataFrame
data = {
    ‘Student‘: [‘Aryan‘, ‘Prajjwal‘, ‘Vishakshi‘, ‘Brijkant‘, ‘Kareena‘],
    ‘Marks‘: [77, 72, 19, 68, 45]
}
df = pd.DataFrame(data)

# 2. 定义分箱的“切分点”和“标签”
# 我们定义三个区间:
# 0-50 分 -> Low
# 51-75 分 -> Average
# 76-100 分 -> High
bins = [0, 50, 75, 100]
labels = [‘Low‘, ‘Average‘, ‘High‘]

# 3. 应用 pd.cut
# 注意:这里我们使用 include_lowest=True,确保正好是 50 分的同学也能被分到 ‘Low‘ 组(默认情况下第一个区间是左开的)
df[‘Category‘] = pd.cut(df[‘Marks‘], bins=bins, labels=labels, include_lowest=True)

print("学生成绩分类结果:")
print(df)

输出结果:

    Student  Marks Category
0    Aryan     77     High
1 Prajjwal     72  Average
2Vishakshi     19      Low
3 Brijkant     68  Average
4  Kareena     45      Low

深度解析:

在这里,Aryan 的 77 分被分到了 INLINECODEc88e492f 组。为什么?因为我们定义的 bins 是 INLINECODEf70c19eb,对应的区间其实是 INLINECODE3e63dec3, INLINECODEd4d74209, INLINECODE6e8e8be0(默认 INLINECODEb5a6cca6)。77 落在了 INLINECODE51ceb836 里面。这就是 INLINECODE066cac02 方法的直观之处——它严格按照数学区间来划分数据。

#### 场景二:将随机数据分组(无标签版)

有时候,我们只是在做数据探索,不需要具体的标签,只想看看数据是如何分布的。这时候,我们可以让 cut() 直接返回数学区间。

import pandas as pd
import numpy as np

# 为了结果可复现,我们设置一个随机种子
np.random.seed(42)

# 创建一个包含 10 个 1 到 100 之间随机数的 DataFrame
df = pd.DataFrame({‘number‘: np.random.randint(1, 100, 10)})

# 我们使用自定义的 bins 列表来切分,这次我们不指定 labels
# 区间划分为:(1, 20], (20, 40], (40, 60], (60, 80], (80, 100]
bin_edges = [1, 20, 40, 60, 80, 100]

df[‘bins‘] = pd.cut(df[‘number‘], bins=bin_edges)

print("数据分组详情:")
print(df)
print("
存在的唯一分组:")
print(df[‘bins‘].unique())

输出结果:

    number          bins
0       52   (40, 60]
1       93   (80, 100]
2       15    (1, 20]
3       60   (40, 60]
4       81   (80, 100]
5       62   (60, 80]
...

注意到了吗? 输出的 INLINECODE8dfc0a69 列显示的是像 INLINECODE5eb134a1 这样的 Interval 对象。Pandas 非常聪明,它把这些区间当作一个独立的类别数据类型(Categorical),这比单纯的字符串要高效得多,而且支持排序和比较。

#### 场景三:使用 include_lowest 处理边界值

在实际业务中,边界值经常让人头疼。比如,如果你的及格线是 60 分,那么“刚好考了 60 分”的同学算及格还是不及格?这取决于你的区间定义。

让我们看看 include_lowest 如何影响结果。

import pandas as pd

# 假设有一组包含边界值的数据
scores = pd.Series([10, 20, 30, 40, 50])

# 定义 bins
bins = [10, 20, 30, 40, 50]

# 情况 A:默认 include_lowest=False (默认)
# 结果将全是 NaN,因为:
# (10, 20] 不包含 10 (如果不特殊处理), 且通常左开
# 实际上,如果只有离散的边界,默认行为可能不包括最左侧的边界点,除非显式设定
cut_default = pd.cut(scores, bins=bins)

# 情况 B:设置 include_lowest=True
# 这会让第一个区间变为 [10, 20],从而包含 10 这个值
cut_inclusive = pd.cut(scores, bins=bins, include_lowest=True)

print("原始值:", scores.tolist())
print("
默认结果 (包含最左侧边界=False):")
print(cut_default)
print("
开启包含最左侧边界=True:")
print(cut_inclusive)

关键点: 当你定义的 bins 恰好覆盖了数据的上下限时,一定要记得检查 INLINECODEda36ada5,否则你的最小值数据可能会变成 INLINECODEfffc3c7b(缺失值)。

#### 场景四:直接处理 NumPy 数组

Pandas 的强大之处在于它能无缝衔接 NumPy。我们不一定非要有一个 DataFrame 才能使用 cut()

import pandas as pd
import numpy as np

# 创建一个简单的 NumPy 数组
arr = np.array([10, 25, 45, 68, 90])

# 定义区间
bins = [0, 20, 50, 100]

# 直接对数组进行 cut
result = pd.cut(arr, bins=bins)

print("分类结果:")
print(result)
print("
结果类型:", type(result))

输出:

[(0, 20], (20, 50], (20, 50], (50, 100], (50, 100]]
Categories (3, interval[int64, right]): [(0, 20] < (20, 50] < (50, 100]]
结果类型: 

这展示了 pd.cut 的灵活性,你可以把它用在数据清洗的任何环节。

进阶技巧与最佳实践

掌握了基础用法后,让我们来看看一些在实战中非常有用的进阶技巧。

#### 1. 自动分箱:指定数量而非边界

有时候我们不想手动计算具体的边界值(比如 0-50, 50-70 等),我们只想说“把数据平均分成 4 份”。这时候,我们可以直接给 bins 参数传一个整数。

import pandas as pd

# 生成一些随机数据
data = pd.DataFrame({‘values‘: range(100)})

# 将数据均匀分成 4 个区间,即每个区间包含 25% 的数据
# Pandas 会自动计算最大值和最小值,然后四等分
data[‘quartile_category‘] = pd.cut(data[‘values‘], bins=4)

# 查看分类计数
print(data[‘quartile_category‘].value_counts())

这在做分位数分析时非常方便。

#### 2. 处理重复的边界值

如果你尝试传入 INLINECODEc106f018 这样的 bins,Pandas 默认会报错,因为它无法定义一个宽度为 0 的区间。但在某些清洗数据的场景下,你可能想忽略这些无效边界。此时可以设置 INLINECODE14b5d574。

bins_with_duplicates = [0, 10, 10, 20]
try:
    # 默认情况会报 ValueError
    result = pd.cut([5, 15], bins=bins_with_duplicates)
except ValueError as e:
    print(f"默认报错: {e}")

# 丢弃重复值
result_safe = pd.cut([5, 15], bins=bins_with_duplicates, duplicates=‘drop‘)
print("处理后的结果:", result_safe)

#### 3. 添加自定义排序(让“高”排在“低”前面)

Categorical 数据类型的一个巨大优势是可以自定义排序顺序。默认情况下,Pandas 按照字母顺序排序字符串,这在处理“High”、“Medium”、“Low”时会导致顺序错误。我们可以结合 cut 和 Categorical 类型来修正这个问题。

import pandas as pd

df = pd.DataFrame({
    ‘score‘: [88, 50, 60, 90],
    ‘grade‘: pd.cut([88, 50, 60, 90], bins=[0, 60, 80, 100], labels=[‘Low‘, ‘Medium‘, ‘High‘])
})

# 如果直接 sort_values(‘grade‘),High 可能会排在 Low 前面 (字母序 H < L)
# 但 pd.cut 生成的 Categorical 默认是保留 bins 顺序的,这通常是我们想要的
print(df['grade'].sort_values())

常见错误与避坑指南

在使用 cut() 时,新手(甚至老手)经常会遇到以下几个问题,这里我们一并解决:

  • 数据变成了 NaN

* 原因:你的数据超出了你定义的 INLINECODE937a81bb 范围。比如 bins 定义是 INLINECODE3e37637c,但数据里有个 150,或者是负数。

* 解决:在切分前,检查 INLINECODE8809670c 和 INLINECODE190b7db9,确保 bins 覆盖了所有数据范围。或者使用 pd.cut(..., include_lowest=True) 来包含左边界。

  • 区间方向搞反了?

* 原因:如果你希望区间包含左边的数(比如 INLINECODE63381509),记得设置 INLINECODE893197cd。这对于处理年龄(如 0岁 [0, 1), 1岁 [1, 2))等场景至关重要。

  • 标签数量不匹配?

* 原因:如果你定义了 INLINECODE90322614 个 bins 边界(比如 INLINECODE6475a6ea),那么实际上会产生 INLINECODEefae6cc9 个区间。因此,你的 INLINECODE0a316dbe 列表长度必须是 N-1。如果标签多了或少了,Pandas 会直接报错。

总结

在这篇文章中,我们深入探讨了 Pandas 中的 cut() 方法。从最基本的语法概念,到如何处理复杂的边界情况,再到处理 NumPy 数组和自定义排序,我们覆盖了数据分析中关于数据分箱的绝大多数常见需求。

核心要点回顾:

  • cut() 是将连续数值离散化的利器。
  • 一定要分清 INLINECODE94cd0912(左开右闭)和 INLINECODE8793b450 的配合使用,这直接决定了边界值的归属。
  • 合理使用 labels 可以极大提升数据的可读性,让你的报表更加直观。

下一步建议:

现在你已经掌握了如何把数据“切开”,但在某些场景下,你可能希望每个箱子里的数据数量是相等的(比如前 10% 的人是 VIP),这时候单纯靠数值切分就不够了。建议你接下来去探索一下 Pandas 的另一个函数 INLINECODE3ebab4db,它是专门基于分位数进行切分的,是 INLINECODE96d8066e 的强力补充。

希望这篇文章能帮助你在数据处理的道路上更进一步!如果你在实操中遇到了什么有趣的问题,欢迎继续探索。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/34723.html
点赞
0.00 平均评分 (0% 分数) - 0