我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

继续python里面,对数字数据进行分类处理的方式方法。数字数据一般指的是:年龄、工资、金额 等数据。这类数据由数字组成,每一部分数据都可以使用文字来进行描述,全部数据就可以使用多个文字来进行描述。从而实现对数字类型数据的一个抽象分类化的过程。

苏南大叔:数据清洗,如何利用pd.cut()给数字数据进行分组处理? - 数字分组cut
数据清洗,如何利用pd.cut()给数字数据进行分组处理?(图1-1)

苏南大叔的“程序如此灵动”博客,这里讲述苏南大叔和计算机代码之间的故事。测试环境:python@3.12.3pandas@2.2.2numpy@1.26.4

前文回顾

本文所描述的分组过程,和前面描述过dtypecategory化是有类似的效果的。所以,可以把两者进行对比:

默认情况下,分组后的结果还具有排序功能。这部分内容,对比参考:

pandas.cut()原型

pandas.cut(x, bins, right=True, labels=None, retbins=False,precision=3, include_lowest=False, duplicates='raise', ordered=True)
参数默认值解释
x要切分的一维数组,可以是列表,也可以是dataFrame的一列
bins切片的方式,列表[a,b,c],表示按照a-b,b-c的区间来切分,也可以是数值n,直接指定分为n组
labels标签参数,比如[低、中、高]。数量上来说,是bins数组数量减一
rightTrueTrue/False,为True时,表示分组区间包含右边,不包含左边,即(]。False,代表[)
retbinsFalseTrue/False, True返回bin的具体范围值
precision3存储和显示标签的精度。
include_lowestFalseTrue/False, 第一个区间是否应该是左包含的
duplicatesraiseraise/drop, 如果bin列表里有重复,报错/直接删除至保留一个
orderedTrue标签是否有序

最简单用法【推荐】

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
c = pd.cut(df.kg, [0, 6, 9, 11], labels=['light(0,6]', 'normal(6,9]', 'heavy(9,11]'])
print(c)

输出:

0     light(0,6]
1    normal(6,9]
2     light(0,6]
3    normal(6,9]
4            NaN
Name: kg, dtype: category
Categories (3, object): ['light(0,6]' < 'normal(6,9]' < 'heavy(9,11]']

这里labels里面的左开右闭区间,是后面的主要内容。而且,要注意到:这个默认返回值是个带有顺序的series。但是,这里的结果其实并不是很好理解。所以,如果把返回的结果,放在原来的dataframe里面,是个更好的选择。例如:

df["w"] = pd.cut(df.kg, [0, 6, 9, 11], labels=['light(0,6]', 'normal(6,9]', 'heavy(9,11]'])
print(df)

输出:

  name  kg            w
0   老许   2   light(0,6]
1   小黑   9  normal(6,9]
2   虎子   6   light(0,6]
3   黑猫   9  normal(6,9]
4   老白  12          NaN

目前为止,就是最基础常见的用法了。还可以看到:bins设定,并不是一定会覆盖所有的数据的,允许产生遗漏的NaN数据。本文后面的内容,基本上也用不到。

极简不设置标签

如果不设置labels,也是可以的。但是,这么操作似乎没有什么意义。

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
c = pd.cut(df.kg, [0, 6, 9, 11])
print(c)

输出:

0    (0.0, 6.0]
1    (6.0, 9.0]
2    (0.0, 6.0]
3    (6.0, 9.0]
4           NaN
Name: kg, dtype: category
Categories (3, interval[int64, right]): [(0, 6] < (6, 9] < (9, 11]]

边界特殊设置

正如上面的例子所示,bins就是设置数字边界的,默认情况下是个list数组(必须从左到右依次增加,否则报错),而且范围是属于左开右闭。允许有不被这个bins覆盖到的数据。

特殊情况一

然而,bins也可以设置为一个数字。这种情况下,是自动平均划分范围的意思。这同时面临的问题是:均分的精度问题。可以使用参数precision设置。
例如:

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
c = pd.cut(df.kg, 3)
print(c)
c = pd.cut(df.kg, 3, precision=1)
print(c)

输出:

0     (1.99, 5.333]
1     (8.667, 12.0]
2    (5.333, 8.667]
3     (8.667, 12.0]
4     (8.667, 12.0]
Name: kg, dtype: category
Categories (3, interval[float64, right]): [(1.99, 5.333] < (5.333, 8.667] < (8.667, 12.0]]
0     (2.0, 5.3]
1    (8.7, 12.0]
2     (5.3, 8.7]
3    (8.7, 12.0]
4    (8.7, 12.0]
Name: kg, dtype: category
Categories (3, interval[float64, right]): [(2.0, 5.3] < (5.3, 8.7] < (8.7, 12.0]]

特殊情况二

retbins参数,返回真正的bins。这个情况很特殊,有两个返回值。一个是正常的分组后的数据,另外一个是分组的标准。为啥要再次返回分组标准?是因为确实有没有明确说明bins的情况,或者存在bins设置,被duplicates='drop'修改的情况。

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
c,r = pd.cut(df.kg, 3, retbins = True)
print(r)
c, r = pd.cut(df.kg, [0, 6, 6, 9, 12], retbins=True, duplicates="drop")
print(r)

输出:

[ 1.99        5.33333333  8.66666667 12.        ]
[ 0  6  9 12]

边界是否包含处理

这个小节的内容是:左开右闭区间的问题。有两个相关的参数,

  • 参数right,默认True,左开右闭。False则是左闭右开。
  • 参数include_lowest,默认False不包含最小值。改成True,则最左边的范围会变小一些[或者改成左闭右开],使得最小值被包含。值得特别注意的是:这个lowest指的是bins里面的最小值,并不是实际被切分的数据中的最小值。

为了更好的展示效果,这里不设置labels,直接看结果。

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
c = pd.cut(df.kg,  [0, 6, 9, 12], right = False)
print(c)
c = pd.cut(df.kg, [0, 6, 9, 12], include_lowest=True)
print(c)
c = pd.cut(df.kg, [3, 6, 9, 12], include_lowest=True)
print(c)
c = pd.cut(df.kg, [3, 6, 9, 12], include_lowest=True, right=False)
print(c)

输出:

0     [0.0, 6.0)
1    [9.0, 12.0)
2     [6.0, 9.0)
3    [9.0, 12.0)
4            NaN
Name: kg, dtype: category
Categories (3, interval[int64, left]): [[0, 6) < [6, 9) < [9, 12)]
0    (-0.001, 6.0]
1       (6.0, 9.0]
2    (-0.001, 6.0]
3       (6.0, 9.0]
4      (9.0, 12.0]
Name: kg, dtype: category
Categories (3, interval[float64, right]): [(-0.001, 6.0] < (6.0, 9.0] < (9.0, 12.0]]
0             NaN
1      (6.0, 9.0]
2    (2.999, 6.0]
3      (6.0, 9.0]
4     (9.0, 12.0]
Name: kg, dtype: category
Categories (3, interval[float64, right]): [(2.999, 6.0] < (6.0, 9.0] < (9.0, 12.0]]
0            NaN
1    [9.0, 12.0)
2     [6.0, 9.0)
3    [9.0, 12.0)
4            NaN
Name: kg, dtype: category
Categories (3, interval[int64, left]): [[3, 6) < [6, 9) < [9, 12)]

边界是否合理处理

这里指的就是参数duplicates,指的是bins里面如果写了重复的值,该如何处理。说实话,这个参数就是伪命题。bins这个人为设置的参数,为啥非要设置个非法的重复值呢?默认是raise报错。

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
c = pd.cut(df.kg, [0, 6, 6, 12])
print(c)

输出:

ValueError: Bin edges must be unique: Index([0, 6, 6, 12], dtype='int64').
You can drop duplicate edges by setting the 'duplicates' kwarg

是否排序处理

这个ordered参数就略微烧脑,因为从pd.cut()的返回值上来说,是不是ordered区别不大。返回值类型都是Series,其dtype都是category。区别仅仅是这个category是不是有序的。如何测试其有序性呢?参考文章:

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
df["w"] = pd.cut(df.kg, [0, 6, 9, 12], labels = ["轻", "不轻不重", "重"])
print(df["w"])
print(df["w"]>="不轻不重")

输出:

0       轻
1    不轻不重
2       轻
3    不轻不重
4       重
Name: w, dtype: category
Categories (3, object): ['轻' < '不轻不重' < '重']

0    False
1     True
2    False
3     True
4     True
Name: w, dtype: bool

如果改成无序的,就会得到报错信息。

import pandas as pd
df = pd.DataFrame({
            'name': ['老许', '小黑', '虎子', '黑猫', '老白'],
            'kg': [2, 9, 6, 9, 12]})
df["w"] = pd.cut(df.kg, [0, 6, 9, 12], labels = ["轻", "不轻不重", "重"], ordered = False)
print(df["w"])
print(df["w"]>="不轻不重")

输出:

0       轻
1    不轻不重
2       轻
3    不轻不重
4       重
Name: w, dtype: category
Categories (3, object): ['轻', '不轻不重', '重']
    raise TypeError(
TypeError: Unordered Categoricals can only compare equality or not

pd.cut()后续操作

效果对比:

  • pd.cut()的效果上来看,是大量数字变成了有限的category文字数组。
  • .astype('category')的效果,是大量的重复文本变成了有限的category文字数组。

所以,后续的操作,可以是df.groupby()。参考文章:

结语

数据清洗是对数据集进行预测的必要步骤。更多苏南大叔的python文章,请参考下面的链接:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   python