- 4.1 The NumPy ndarray 多维数组对象
- 4.2 Universal Functions 通用函数
- 4.3 Array-Oriented Programming with Arrays 数组导向编程
- 5.1 Introduction to pandas Data Structures pandas 的数据结构
- 5.2 Essential Functionality 主要功能
- 5.3 Summarizing and Computing Descriptive Statistics 汇总和描述性统计
- 7.1 Handling Missing Data 处理缺失数据
- 7.2 Data Transformation 数据变换
- 7.3 String Manipulation 字符串处理
- 11.1 Date and Time Data Types and Tools 日期和时间数据类型及其工具
- 11.2 Time Series Basics 时间序列基础
- 11.3 Date Ranges, Frequencies, and Shifting 日期范围,频度,和位移
- 12.1 Categorical Data 类别数据
- 14.1 USA.gov Data from Bitly USA.gov 数据集
- 14.2 MovieLens 1M Dataset MovieLens 1M 数据集
- 14.3 US Baby Names 1880–2010 1880年至2010年美国婴儿姓名
12.1 Categorical Data 类别数据
这一届会介绍 pandas 的 Categorical 类型。
1 Background and Motivation(背景和动力)
表格中的列克可能会有重复的部分。我们可以用 unique 和 value_counts,从一个数组从提取不同的值,并计算频度:
import numpy as np import pandas as pd
values = pd.Series(['apple', 'orange', 'apple', 'apple'] * 2) values
0 apple 1 orange 2 apple 3 apple 4 apple 5 orange 6 apple 7 apple dtype: object
pd.unique(values)
array(['apple', 'orange'], dtype=object)
pd.value_counts(values)
apple 6 orange 2 dtype: int64
对于不同的类型数据值,一个更好的方法是用维度表(dimension table)来表示,然后用整数键(integer keys)来指代维度表:
values = pd.Series([0, 1, 0, 0] * 2) values
0 0 1 1 2 0 3 0 4 0 5 1 6 0 7 0 dtype: int64
dim = pd.Series(['apple', 'orange']) dim
0 apple 1 orange dtype: object
用 take 方法来重新存储原始的,由字符串构成的 Series:
dim.take(values)
0 apple 1 orange 0 apple 0 apple 0 apple 1 orange 0 apple 0 apple dtype: object
这种用整数表示的方法叫做类别(categorical)或字典编码(dictionary-encoded)表示法。表示不同类别值的数组,被称作类别,字典,或层级。本书中我们将使用类别(categorical and categories)来称呼。表示类别的整数值被叫做,类别编码(category code),或编码(code)。
2 Categorical Type in pandas(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 | 5 | 2.255245 |
1 | 1 | orange | 8 | 1.309949 |
2 | 2 | apple | 6 | 2.330312 |
3 | 3 | apple | 3 | 2.927920 |
4 | 4 | apple | 13 | 1.322311 |
5 | 5 | orange | 10 | 0.474809 |
6 | 6 | apple | 4 | 0.827271 |
7 | 7 | apple | 8 | 2.480494 |
这里,df['fruit']是一个 python 的字符串对象。我们将其转换为类型对象:
fruits_cat = df['fruit'].astype('category') fruits_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]
fruits_cat 的值并不是一个 numpy 数组,而是一个 pandas.Categorical 实例:
c = fruits_cat.values type(c)
pandas.core.categorical.Categorical
这个 Categorical 对象有 categories 和 codes 属性:
c.categories
Index(['apple', 'orange'], dtype='object')
c.codes
array([0, 1, 0, 0, 0, 1, 0, 0], dtype=int8)
可以把转换的结果变为 DataFrame 列:
df['fruit'] = df['fruit'].astype('category') df.fruit
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]
也可以直接把其他的 python 序列变为 pandas.Categorical 类型:
my_categories = pd.Categorical(['foo', 'bar', 'baz', 'foo', 'bar']) my_categories
[foo, bar, baz, foo, bar] Categories (3, object): [bar, baz, foo]
如果已经得到了分类编码数据(categorical encoded data),我们可以使用 from_codes 构造器:
categories = ['foo', 'bar', 'baz']
codes = [0, 1, 2, 0, 0, 1]
my_cats_2 = pd.Categorical.from_codes(codes, categories) 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]
输出的结果中, [foo < bar < baz]
表示 foo 在 bar 之间,以此类推。一个没有顺序的类型实例(unordered categorical instance)可以通过 as_ordered 来排序:
my_cats_2.as_ordered()
[foo, bar, baz, foo, foo, bar] Categories (3, object): [foo < bar < baz]
最后一点需要注意的,类型数据没必要一定是字符串,它可以是任何不可变的值类型 (any immutable value types)。
3 Computations with Categoricals(类型计算)
Categorical 类型和其他类型差不多,不过对于某些函数,比如 groupby 函数,在 Categorical 数据上会有更好的效果。很多函数可以利用 ordered 标记。
假设有一些随机的数字,用 pandas.quct 进行分箱(binning)。得到的类型是 pandas.Categorical;虽然之前用到过 pandas.cut,但是没有具体介绍里面的细节:
np.random.seed(12345)
draws = np.random.randn(1000)
draws[:5]
array([-0.20470766, 0.47894334, -0.51943872, -0.5557303 , 1.96578057])
计算分箱后的分位数:
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.95, -0.684], (-0.0101, 0.63], (0.63, 3.928]] Length: 1000 Categories (4, interval[float64]): [(-2.95, -0.684] < (-0.684, -0.0101] < (-0.0101, 0.63] < (0.63, 3.928]]
具体分位数并不如季度的名字直观,我们直接在 qcut 中设定 labels:
bins = pd.qcut(draws, 4, labels=['Q1', 'Q2', 'Q3', 'Q4']) bins
[Q2, Q3, Q2, Q2, Q4, ..., Q3, Q2, Q1, Q3, Q4] Length: 1000 Categories (4, object): [Q1 < Q2 < Q3 < Q4]
bins.codes[:10]
array([1, 2, 1, 1, 3, 3, 2, 2, 3, 3], dtype=int8)
bins caetegorical 并没有包含边界星系,我们可以用 groupby 来提取:
bins = pd.Series(bins, name='quartile')
results = (pd.Series(draws) .groupby(bins) .agg(['count', 'min', 'max']) .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 |
quartile 列包含了原始的类别信息,包含 bins 中的顺序:
results['quartile']
0 Q1 1 Q2 2 Q3 3 Q4 Name: quartile, dtype: category Categories (4, object): [Q1 < Q2 < Q3 < Q4]
Better performance with categoricals (使用 categoricals 得到更好的效果)
使用 categorical 能让效果提高。如果一个 DataFrame 的列是 categorical 类型,使用的时候会减少很多内存的使用。假设我们有一个一千万的元素和一个类别:
N = 10000000 draws = pd.Series(np.random.randn(N)) labels = pd.Series(['foo', 'bar', 'baz', 'qux'] * (N // 4))
把 labels 变为 categorical:
categories = labels.astype('category')
可以看到 labels 会比 categories 使用更多的内存:
labels.memory_usage()
80000080
categories.memory_usage()
10000272
当然,转换成 category 也是要消耗计算的,不过这种消耗是一次性的:
%time _ = labels.astype('category')
CPU times: user 303 ms, sys: 70.1 ms, total: 373 ms Wall time: 385 ms
在 categories 上使用 groupby 会非常快,因为用的是基于整数的编码,而不是由字符串组成的数组。
4 Categorical Methods(类别方法)
如果是包含 categorical 数据的 Series 数据,有和 Series.str 类似的一些比较特殊的方法。对于访问 categories 和 code 很方便:
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 可以访问 categorical 方法:
cat_s.cat.codes
0 0 1 1 2 2 3 3 4 0 5 1 6 2 7 3 dtype: int8
cat_s.cat.categories
Index(['a', 'b', 'c', 'd'], dtype='object')
假设我们知道实际的类别超过了当前观测到的四个类别,那么我们可以使用 set_categories 方法来扩展:
actual_categories = ['a', 'b', 'c', 'd', 'e'] cat_s2 = cat_s.cat.set_categories(actual_categories) cat_s2
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]
数据本身似乎没有改变,不过在对其进行操作的时候会反应出来。例如,value_counts:
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
在大型数据集,categoricals 经常用来作为省内存和提高效果的工具。在对一个很大的 DataFrame 或 Series 进行过滤后,很多类型可能不会出现在数据中。我们用 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]
Creating dummy variables for modeling(为建模创建哑变量)
在使用机器学习的一些工具时,经常要转变类型数据为哑变量(dummy variables ),也被称作是独热编码(one-hot encoding)。即在 DataFrame 中,给一列中不同的类别创建不同的列,用 1 表示出现,用 0 表示未出现。
例子:
cat_s = pd.Series(['a', 'b', 'c', 'd'] * 2, dtype='category')
在第七章也介绍过,pandas.get_dummies 函数会把一维的类型数据变为包含哑变量的 DataFrame:
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 |
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论