高阶pandas : 分类数据

12.1 分类数据

介绍pandas 的Categorical类型,展示使用pandas进行操作时如何获得更好的性能和内存使用。并介绍一些在统计和机器学习应用中使用的分类数据。。。

12.1.1 背景和目标

一个列经常会包含重复值,这些重复值时一个小型的不同值的集合。可以使用unique 和 value_counts这样的函数从一个数组中提取出不同值并分别计数这些的频率。

values=pd.Series(['apple','orange','apple','apple']*2)

pd.unique(values)
array(['apple', 'orange'], dtype=object)

pd.value_counts(values)
apple     6
orange    2
dtype: int64


values = pd.Series([0, 1, 0, 0] * 2)
dim = pd.Series(['apple', 'orange'])         # 用重复的值表示数据
dim.take(values)
0     apple
1    orange
0     apple
0     apple
0     apple
1    orange
0     apple          # 这个take 不是索引嘛,,怎么就分类了
0     apple
dtype: object

这种按照整数展现的方式称为分类或字典编码展现,不同值的数组可以被称为数据的类别,字典或层级。

在数据分析中,分类展示可以显著的性能提升。。。额,哪里分类了我怎么看不出来。。。

12.1.2 pandas中的Categorical 类型

pandas有特殊的Categorical类型,用于承载基于整数的类别显示或编码的数据。

fruits = ['apple', 'orange', 'apple', 'apple'] * 2
N = len(fruits)
df = pd.DataFrame({'fruit': fruits,
                   'basket_id': np.arange(N),
                   'count': np.random.randint(3, 15, size=N),
                   'weight': np.random.uniform(0, 4, size=N)},
                  columns=['basket_id', 'fruit', 'count', 'weight'])
df
	basket_id	fruit	count	weight
0	0	apple	13	2.595562
1	1	orange	13	3.613305
2	2	apple	8	0.822683
3	3	apple	5	3.966781
4	4	apple	9	3.545468
5	5	orange	4	3.816133
6	6	apple	5	2.302654
7	7	apple	8	2.171438


fruit_cat = df['fruit'].astype('category')   # df['fruit']是一个python字符串对象组成的数组。可以调用函数转换为Categorical对象。
fruit_cat
0     apple
1    orange        # 这就是分类类型吗,,,,
2     apple
3     apple
4     apple
5    orange
6     apple
7     apple
Name: fruit, dtype: category
Categories (2, object): [apple, orange]

c = fruit_cat.values
type(c)                    # fruit_cat 的值并不是numpy数组,而是pandas.Categorical 实例。
pandas.core.arrays.categorical.Categorical


c.categories,c.codes                   # 分类对象的两个属性。
(Index(['apple', 'orange'], dtype='object'),
 array([0, 1, 0, 0, 0, 1, 0, 0], dtype=int8))
 
 df['fruit'] = df['fruit'].astype('category') # 将已存在的转换为一个分类对象。

my_categories = pd.Categorical(['foo', 'bar', 'baz', 'foo', 'bar'])   # 从其他数据类型直接生成分类对象。


categories = ['foo', 'bar', 'baz']
codes = [0, 1, 2, 0, 0, 1]
my_cats_2 = pd.Categorical.from_codes(codes, categories)   # 从另一个数据源获得了分类编码数据,可以使用cfrom_codes构造函数
my_cats_2
[foo, bar, baz, foo, foo, bar]
Categories (3, object): [foo, bar, baz]

一般分类转换时不会指定类别的顺序的,所有categories数组可能会与输入数据的顺序不同。但使用from_codes或其他任意构造函数时,就可以为类别指定一个有意义的顺序。

ordered_cat = pd.Categorical.from_codes(codes, categories,
                                        ordered=True)
ordered_cat
[foo, bar, baz, foo, foo, bar]
Categories (3, object): [foo < bar < baz]  # 就这顺序

my_cats_2.as_ordered()  # 对于未排序的可以使用as_ordered()来排序。

注意分类数据可以不仅是字符串,可以是任何不可变的值类型。

12.1.3 使用Categorical 对象进行计算

np.random.seed(12345)
draws = np.random.randn(1000)   # 经过分箱得到的是categories对象
bins=pd.qcut(draws,4)
bins
[(-0.684, -0.0101], (-0.0101, 0.63], (-0.684, -0.0101], (-0.684, -0.0101], (0.63, 3.928], ..., (-0.0101, 0.63], (-0.684, -0.0101], (-2.9499999999999997, -0.684], (-0.0101, 0.63], (0.63, 3.928]]
Length: 1000
Categories (4, interval[float64]): [(-2.9499999999999997, -0.684] < (-0.684, -0.0101] < (-0.0101, 0.63] < (0.63, 3.928]]

bins=pd.qcut(draws,4,labels=['Q1','Q2','Q3','Q4'])   # 给各个分组取个名更加好用。
bins.codes[:10]
array([1, 2, 1, 1, 3, 3, 2, 2, 3, 3], dtype=int8)

bins = pd.Series(bins, name='quartile')
results = (pd.Series(draws)
           .groupby(bins)
           .agg(['count', 'min', 'max'])          # 使用groupby方法来提取一些汇总值
           .reset_index())
results
quartile	count	min	max
0	Q1	250	-2.949343	-0.685484
1	Q2	250	-0.683066	-0.010115
2	Q3	250	-0.010032	0.628894
3	Q4	250	0.634238	3.927528


results['quartile']
0    Q1
1    Q2
2    Q3
3    Q4
Name: quartile, dtype: category
Categories (4, object): [Q1 < Q2 < Q3 < Q4]           # 这一列保留了bins中原始的分类信息,包括顺序。
12.1.3.1 使用分类获得更高性能

如果在特定的数据集上做了大量的分析,将数据转换为分类数据可以产生大幅度的性能提升。

N = 10000000
draws = pd.Series(np.random.randn(N))
labels = pd.Series(['foo', 'bar', 'baz', 'qux'] * (N // 4))
categories = labels.astype('category')              # 转换成categories对象
labels.memory_usage(),categories.memory_usage()

(80000128, 10000320)              # 可以看到省了很多内存

使用分类对象进行GroupBy操作明显更快,这是因为底层算法使用了基于整数代码的数组而不是字符串数组。

12.1.4 分类方法

series 包含的分类数据有一些特殊的方法,类似于Series.str 的特殊字符串方法。这些方法提供了快捷访问类别和代码的方式。

s = pd.Series(['a', 'b', 'c', 'd'] * 2)
cat_s = s.astype('category')
cat_s
0    a
1    b
2    c
3    d
4    a
5    b
6    c
7    d
dtype: category
Categories (4, object): [a, b, c, d]

cat_s.cat.codes            # 特殊属性cat提供了对分类方法的访问。
0    0
1    1
2    2
3    3
4    0
5    1
6    2
7    3
dtype: int8

actual_categories = ['a', 'b', 'c', 'd', 'e']
cat_s2 = cat_s.cat.set_categories(actual_categories)   # 假设我们知道该数据的实际类别集合超出了数据中观察到的四个值
cat_s2 												# 使用set_categories方法改变类别。
0    a
1    b
2    c
3    d
4    a
5    b
6    c
7    d
dtype: category
Categories (5, object): [a, b, c, d, e]      # 虽然返回的结果差不多,但新类别会反应到他们的操作中。

cat_s.value_counts()
d    2
c    2
b    2
a    2
dtype: int64

cat_s2.value_counts()
d    2
c    2
b    2
a    2
e    0
dtype: int64


# 在大型数据中,分类数据经常被用于节省内存和更高性能的便捷工具,在你过滤了一个大型数据后,很多类别不会出现在数据中
# 可以使用remove_unused_categories 方法来去除观察到的类别。
cat_s3 = cat_s[cat_s.isin(['a', 'b'])]
cat_s3
0    a
1    b
4    a
5    b
dtype: category
Categories (4, object): [a, b, c, d]

cat_s3.cat.remove_unused_categories()
0    a
1    b
4    a
5    b
dtype: category
Categories (2, object): [a, b]        # 真是神奇啊。。。。。

在这里插入图片描述

12.1.4.1 创建用于建模的虚拟变量

就是pd.get_dummies啦,将数据转换成虚拟变量

cat_s = pd.Series(['a', 'b', 'c', 'd'] * 2, dtype='category')
pd.get_dummies(cat_s)
   a	b	c	d
0	1	0	0	0
1	0	1	0	0
2	0	0	1	0
3	0	0	0	1
4	1	0	0	0
5	0	1	0	0
6	0	0	1	0
7	0	0	0	1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值