分类类型
类别类型可谓是非常常用的一种类型,其具有如下特征:
- 取固定几种值;
- 可以定义序,序的形式与实数序或字典序可以都不同;
- 即使是数值表示,数值运算可能也无意义,与离散数值型不一定相同。
import numpy as np
import pandas as pd
2
# 创建
# 创建 Category 的类
pd.Categorical(values, categories=None, ordered=False)
- values: 类别序列;
- categories:自定义的类别序列;
- ordered:类别是否定义顺序,默认增序。
c = pd.Categorical([2,1,1,3], ordered = True )
c
# 不提供categories,则用values去重后的值作为类别
# 若ordered = True,顺序则按照字典序升序给定
2
3
4
[2, 1, 1, 3]
Categories (3, int64): [1 < 2 < 3]
c = pd.Categorical([1,2,3], categories = [3,2], ordered = True )
c
# 提供 categories(类别不能有重复,否则报错),若values的值不在categories中,则用NaN替换
# 若ordered = True,顺序则按照类别顺序升序给定
2
3
4
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
类别的两个重要属性
c.categories # 类别
Int64Index([3, 2], dtype='int64')
c.ordered # 是否有序
True
# 创建分类的 Series
pd.Series(c)
2
0 NaN
1 2
2 3
dtype: category
Categories (2, int64): [3 < 2]
# 需要注意的是:Categories必须是unique,并且不能为NaN,否则会报ValueError
try:
# c.categories = [1, 1]
c.categories = [1, np.NaN]
except ValueError as e:
print(e)
2
3
4
5
6
Categorical categories cannot be null
# 转换为类别类型
s = pd.Series([2,1,1,3], dtype='category') # 当然也可以通过 astype('category') 将其它 Series 或 DataFrame 进行转换
s
2
0 2
1 1
2 1
3 3
dtype: category
Categories (3, int64): [1, 2, 3]
Series 查看类型属性需要通过.cat
s.cat.categories
Int64Index([1, 2, 3], dtype='int64')
s.cat.ordered
False
# 查、改、增、删
# 查
[]
四种查看方式
类别类型是序列形式,可以采用[]
来查看,不过.loc[]
和.iloc[]
都是不支持的。
c[0]
nan
c[0:2]
[NaN, 2]
Categories (2, int64): [3 < 2]
c[[0,2]]
[NaN, 3]
Categories (2, int64): [3 < 2]
mask = [True, False, True]
c[mask]
2
[NaN, 3]
Categories (2, int64): [3 < 2]
# 改
改类别值这个功能用得会比较多,将字符串类别映射为数值类别。
直接修改
c1 = c.copy()
c1.categories = ['5','6'] # 这种改法,新的类别序列与旧类别序列长度必须相同,实质为将值和类型依次替换
c1
2
[NaN, '6', '5']
Categories (2, object): ['5' < '6']
s1= s.copy()
s1
2
0 2
1 1
2 1
3 3
dtype: category
Categories (3, int64): [1, 2, 3]
s1.cat.categories = [6,5,7]
s1 # 对Series来说,用.cat操作改法是相同的
2
0 5
1 6
2 6
3 7
dtype: category
Categories (3, int64): [6, 5, 7]
函数改
categories.rename_categories(cat , inplace=False)
- cat:新的类别,必须和旧类别长度相同;
- inplace:True or False,是否原地修改。
c1 = c.copy()
c1.rename_categories(['5','6'], inplace=True) #和上面完全相同
c1
2
3
[NaN, '6', '5']
Categories (2, object): ['5' < '6']
s1 = s.copy()
s1.cat.rename_categories(['6','5','7'], inplace=True) # 和上面完全相同
s1
2
3
0 5
1 6
2 6
3 7
dtype: category
Categories (3, object): ['6', '5', '7']
# 也可以传入一个字典
c1.rename_categories({'5': 'a', '6':'b'})
2
[NaN, 'b', 'a']
Categories (2, object): ['a' < 'b']
# 通过传入一个字典
s1.cat.rename_categories({'5':'A', '6':'B', '7':'C'})
2
0 A
1 B
2 B
3 C
dtype: category
Categories (3, object): ['B', 'A', 'C']
# 有序、无序转变
categories.as_ordered(inplace = False)
categories.as_unordered(inplace = False)
- inplace:True or False,是否原地修改。
c1 = c.copy()
c1
2
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
c1.as_unordered(inplace = True)
c1
2
[NaN, 2, 3]
Categories (2, int64): [3, 2]
c1.as_ordered()
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
# 有序改变顺序
categories.reorder_categories(cat , ordered = False,inplace = False)
- cat:只能是旧类别改变顺序后的序列,不能增减类别;
- ordered:True or False,类别是否有序
- inplace:True or False,是否原地修改。
c1 = c.copy()
c1
2
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
c1.reorder_categories([2,3],ordered = True,inplace = True)
c1
2
[NaN, 2, 3]
Categories (2, int64): [2 < 3]
# 增
categories.add_categories(cat,inplace = False)
- cat:想要新增的类别,必须不在旧类别中;
- inplace:True or False,是否原地修改。
c1 = c.copy()
c1
2
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
c1.add_categories([4,5], inplace = True)
c1
2
[NaN, 2, 3]
Categories (4, int64): [3 < 2 < 4 < 5]
# 删
# 删除任意不需要的类别
categories.remove_categories(cat,inplace = False)
- cat:想要删除的类别,必须在旧类别中;
- inplace:True or False,是否原地修改。
c1.remove_categories([4],inplace = True)
c1
2
[NaN, 2, 3]
Categories (3, int64): [3 < 2 < 5]
# 去除没有使用的类别
categories.remove_unused_categories(inplace = False)
- inplace:True or False,是否原地修改。
c1.remove_unused_categories(inplace=True) # 类别 5 被去除
c1
2
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
# 改增删三合一
categories.set_categories(cat , ordered=False,rename=False, inplace=False)
- cat:只能是旧类别改变顺序后的序列,不能增减类别;
- ordered:True or False,改序,如果提供这一项,保持原来属性,最好明确给出;
- rename:True or False,改名,这个参数我发现没啥用(?);
- inplace:True or False,是否原地修改。
c1 = c.copy()
c1
2
[NaN, 2, 3]
Categories (2, int64): [3 < 2]
c1.set_categories([2,4,5], ordered = True, inplace = True) # 删除了旧类别 1,增加新类别4、5,
c1
2
[NaN, 2, NaN]
Categories (3, int64): [2 < 4 < 5]
# cut() 和 qcut()
这俩函数用于将连续型变量分割为类别变量。
# cut()
pd.cut(x, bins, right = False,include_lowest=False, labels=None, retbins=False)
- x:待分割的 Series 或序列;
- bins:如果是 int,那么将 Series 的进行等分,并在最大最小值的基础上外延 1%作为区间边界;如果是序列,那么将序列值作为分隔点;
- right:True or False,分隔区间默认为左闭右开;
- include_lowest:True or False,将最左侧区间的左值外延 1%,试图去包含最小值;
- labels:分隔后是区间,可以用 label 来替换为想要的类别形式;
- retbins:是否返回分隔点;
s = pd.Series( range(0,5))
s
2
0 0
1 1
2 2
3 3
4 4
dtype: int64
pd.cut( s, 3) # 可以看到一共3个类别,类别形式为区间形式(]
0 (-0.004, 1.333]
1 (-0.004, 1.333]
2 (1.333, 2.667]
3 (2.667, 4.0]
4 (2.667, 4.0]
dtype: category
Categories (3, interval[float64]): [(-0.004, 1.333] < (1.333, 2.667] < (2.667, 4.0]]
pd.cut( s, 3, labels = ['a','b','c']) # 这样就清晰多了
0 a
1 a
2 b
3 c
4 c
dtype: category
Categories (3, object): ['a' < 'b' < 'c']
pd.cut( s, 3, labels = ['a','b','c'], retbins = True) # 分隔点也返回
(0 a
1 a
2 b
3 c
4 c
dtype: category
Categories (3, object): ['a' < 'b' < 'c'],
array([-0.004 , 1.33333333, 2.66666667, 4. ]))
pd.cut(s,[0,2.5,4], right = False) # 左闭右开,不包括4,所以4不属于任何一类别
0 [0.0, 2.5)
1 [0.0, 2.5)
2 [0.0, 2.5)
3 [2.5, 4.0)
4 NaN
dtype: category
Categories (2, interval[float64]): [[0.0, 2.5) < [2.5, 4.0)]
pd.cut(s,[0,2.5,4], right = True) # 左开右闭,不包括0,所以0不属于任何一类别
0 NaN
1 (0.0, 2.5]
2 (0.0, 2.5]
3 (2.5, 4.0]
4 (2.5, 4.0]
dtype: category
Categories (2, interval[float64]): [(0.0, 2.5] < (2.5, 4.0]]
pd.cut(s,[0,2.5,4], right = True, include_lowest = True) # 最左侧值被包含
0 (-0.001, 2.5]
1 (-0.001, 2.5]
2 (-0.001, 2.5]
3 (2.5, 4.0]
4 (2.5, 4.0]
dtype: category
Categories (2, interval[float64]): [(-0.001, 2.5] < (2.5, 4.0]]
# qcut()
pd.qcut(x, q, labels=None, retbins=False)
- x:待分割的 Series 或序列;
- q:安装分位数也来定义分隔点,而不是按照给定值;
- labels:分隔后是区间,可以用 label 来替换为想要的类别形式;
- retbins:是否返回分隔点;
pd.qcut(s, q = [0.0, 0.25, 0.5,0.75, 1.0], labels =['a','b','c','d'])
# 5个分位点,形成 4 个区间。看来默认参数是right =True, include_lowest = True
2
0 a
1 a
2 b
3 c
4 d
dtype: category
Categories (4, object): ['a' < 'b' < 'c' < 'd']