- 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年美国婴儿姓名
7.2 Data Transformation 数据变换
1 删除重复值
import pandas as pd import numpy as np
data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'], 'k2': [1, 1, 2, 3, 3, 4, 4]}) data
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
6 | two | 4 |
DataFrame 方法 duplicated 返回的是一个 boolean Series,表示一个 row 是否是重复的(根据前一行来判断):
data.duplicated()
0 False 1 False 2 False 3 False 4 False 5 False 6 True dtype: bool
drop_duplicateds 返回一个 DataFrame,会删除重复的部分:
data.drop_duplicates()
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
上面两种方法都默认考虑所有列;另外,我们可以指定一部分来检测重复值。假设我们只想检测'k1'列的重复值:
data['v1'] = range(7) data
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
2 | one | 2 | 2 |
3 | two | 3 | 3 |
4 | one | 3 | 4 |
5 | two | 4 | 5 |
6 | two | 4 | 6 |
data.drop_duplicates(['k1'])
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
duplicated 和 drop_duplicated 默认保留第一次观测到的数值组合。设置 keep='last'
能返回最后一个:
data.drop_duplicates(['k1', 'k2'], keep='last')
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
2 | one | 2 | 2 |
3 | two | 3 | 3 |
4 | one | 3 | 4 |
6 | two | 4 | 6 |
2 Transforming Data Using a Function or Mapping(用函数和映射来转换数据)
有时候我们可能希望做一些数据转换。比如下面一个例子,有不同种类的肉:
data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'pastrami', 'honey ham', 'nova lox'], 'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]}) data
food | ounces | |
---|---|---|
0 | bacon | 4.0 |
1 | pulled pork | 3.0 |
2 | bacon | 12.0 |
3 | Pastrami | 6.0 |
4 | corned beef | 7.5 |
5 | Bacon | 8.0 |
6 | pastrami | 3.0 |
7 | honey ham | 5.0 |
8 | nova lox | 6.0 |
假设你想加一列,表明每种肉来源的动物是什么。我们可以写一个映射:
meat_to_animal = { 'bacon': 'pig', 'pulled pork': 'pig', 'pastrami': 'cow', 'corned beef': 'cow', 'honey ham': 'pig', 'nova lox': 'salmon' }
用于 series 的 map 方法接受一个函数,或是一个字典,包含着映射关系,但这里有一个小问题,有些肉是大写,有些是小写。因此,我们先用 str.lower 把所有的值变为小写:
lowercased = data['food'].str.lower() lowercased
0 bacon 1 pulled pork 2 bacon 3 pastrami 4 corned beef 5 bacon 6 pastrami 7 honey ham 8 nova lox Name: food, dtype: object
data['animal'] = lowercased.map(meat_to_animal) data
food | ounces | animal | |
---|---|---|---|
0 | bacon | 4.0 | pig |
1 | pulled pork | 3.0 | pig |
2 | bacon | 12.0 | pig |
3 | Pastrami | 6.0 | cow |
4 | corned beef | 7.5 | cow |
5 | Bacon | 8.0 | pig |
6 | pastrami | 3.0 | cow |
7 | honey ham | 5.0 | pig |
8 | nova lox | 6.0 | salmon |
我们也可以用一个函数解决上面的问题:
data['food'].map(lambda x: meat_to_animal[x.lower()])
0 pig 1 pig 2 pig 3 cow 4 cow 5 pig 6 cow 7 pig 8 salmon Name: food, dtype: object
使用 map 是一个很简便的方法,用于 element-wise 转换和其他一些数据清洗操作。
3 Replacing Values(替换值)
其实 fillna 是一个特殊换的替换操作。map 可以用于修改一个 object 里的部分值,但是 replace 能提供一个更简单和更灵活的方法做到这点。下面是一个 series:
data = pd.Series([1., -999., 2., -999., -1000., 3.]) data
0 1.0 1 -999.0 2 2.0 3 -999.0 4 -1000.0 5 3.0 dtype: float64
这里-999 可能是用来表示缺失值的标识符。用 NA 来替代的话,用 replace,会产生一个新 series(除非使用 inplace=True):
data.replace(-999, np.nan)
0 1.0 1 NaN 2 2.0 3 NaN 4 -1000.0 5 3.0 dtype: float64
如果想要一次替换多个值,直接用一个 list 即可:
data.replace([-999, -1000], np.nan)
0 1.0 1 NaN 2 2.0 3 NaN 4 NaN 5 3.0 dtype: float64
对于不同的值用不同的替换值,也是导入一个 list:
data.replace([-999, -1000], [np.nan, 0])
0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64
参数也可以是一个 dict:
data.replace({-999: np.nan, -1000: 0})
0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64
注意:data.replace 方法和 data.str.replace 方法是不同的,后者会对 string 进行 element-wise 替换。
4 Renaming Axis Indexes(重命名 Axis Indexes)
像是 series 里的 value 一样,axis label 也能类似地是函数或映射来转换,产生一个新的 object。当然也可以设置 in-place 不产生新的数据:
data = pd.DataFrame(np.arange(12).reshape((3, 4)), index=['Ohio', 'Colorado', 'New York'], columns=['one', 'two', 'three', 'four']) data
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colorado | 4 | 5 | 6 | 7 |
New York | 8 | 9 | 10 | 11 |
与 series 相同,axis index 有一个 map 方法:
transform = lambda x: x[:4].upper() transform
<function __main__.<lambda>>
data.index
Index(['Ohio', 'Colorado', 'New York'], dtype='object')
data.index.map(transform)
array(['OHIO', 'COLO', 'NEW '], dtype=object)
可以赋值给 index,以 in-place 的方式修改 DataFrame:
data.index = data.index.map(transform) data
one | two | three | four | |
---|---|---|---|---|
OHIO | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
如果你想要创建一个转换后的版本,而且不用修改原始的数据,可以用 rename:
data.rename(index=str.title, columns=str.upper)
ONE | TWO | THREE | FOUR | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colo | 4 | 5 | 6 | 7 |
New | 8 | 9 | 10 | 11 |
注意,rename 能用于 dict 一样的 oject,
data.rename(index={'OHIO': 'INDIANA'}, columns={'three': 'pekaboo'})
one | two | pekaboo | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
rename 能让你避免陷入手动赋值给 index 和 columns 的杂务中。可以用 inplace 直接修改原始数据:
data.rename(index={'OHIO': 'INDIANA'}, inplace=True) data
one | two | three | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
5 Discretization and Binning(离散化和装箱)
连续型数据经常被离散化或分散成 bins(分箱)来分析。假设你有一组数据,你想把人分到不同的年龄组里:
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
我们把这些分到四个 bin 里,19~25, 26~35, 36~60, >60。可以用 pandas 里的 cut:
bins = [18, 25, 35, 60, 100] cats = pd.cut(ages, bins) cats
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]] Length: 12 Categories (4, object): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
返回的是一个特殊的 Categorical object。我们看到的结果描述了 pandas.cut 如何得到 bins。可以看作是一个 string 数组用来表示 bin 的名字,它内部包含了一个 categories 数组,用来记录不同类别的名字,并伴有表示 ages 的 label(可以通过 codes 属性查看):
cats.codes
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
cats.categories
Index(['(18, 25]', '(25, 35]', '(35, 60]', '(60, 100]'], dtype='object')
pd.value_counts(cats)
(18, 25] 5 (35, 60] 3 (25, 35] 3 (60, 100] 1 dtype: int64
这里 pd.value_counts(cats)是 pandas.cut 后 bin 的数量。
这里我们注意一下区间。括号表示不包含,方括号表示包含。你可以自己设定哪一边关闭(right=False):
pd.cut(ages, [18, 26, 36, 61, 100], right=False)
[[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)] Length: 12 Categories (4, object): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]
你也可以用一个 list 或数组给 labels 选项来设定 bin 的名字:
group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
pd.cut(ages, bins, labels=group_names)
[Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, MiddleAged, YoungAdult] Length: 12 Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]
如果你只是给一个 bins 的数量来 cut,而不是自己设定每个 bind 的范围,cut 会根据最大值和最小值来计算等长的 bins。比如下面我们想要做一个均匀分布的四个 bins:
data = np.random.rand(20)
pd.cut(data, 4, precision=2)
[(0.77, 0.98], (0.33, 0.55], (0.77, 0.98], (0.55, 0.77], (0.55, 0.77], ..., (0.77, 0.98], (0.11, 0.33], (0.11, 0.33], (0.33, 0.55], (0.11, 0.33]] Length: 20 Categories (4, object): [(0.11, 0.33] < (0.33, 0.55] < (0.55, 0.77] < (0.77, 0.98]]
precision=2 选项表示精确到小数点后两位。
一个近似的函数,qcut,会按照数据的分位数来分箱。取决于数据的分布,用 cut 通常不能保证每一个 bin 有一个相同数量的数据点。而 qcut 是按百分比来切的,所以可以得到等数量的 bins:
data = np.random.randn(1000) # Normally distributed
cats = pd.qcut(data, 4) # Cut into quartiles cats
[(-0.717, -0.0981], (-0.717, -0.0981], (-0.0981, 0.639], (0.639, 3.434], [-2.86, -0.717], ..., (-0.0981, 0.639], (-0.717, -0.0981], (-0.0981, 0.639], (0.639, 3.434], (-0.0981, 0.639]] Length: 1000 Categories (4, object): [[-2.86, -0.717] < (-0.717, -0.0981] < (-0.0981, 0.639] < (0.639, 3.434]]
pd.value_counts(cats)
(0.639, 3.434] 250 (-0.0981, 0.639] 250 (-0.717, -0.0981] 250 [-2.86, -0.717] 250 dtype: int64
类似的,在 cut 中我们可以自己指定百分比:
cats2 = pd.cut(data, [0, 0.1, 0.5, 0.9, 1.]) # 累进的百分比 cats2
[NaN, NaN, (0.1, 0.5], NaN, NaN, ..., (0.1, 0.5], NaN, (0.5, 0.9], NaN, (0.5, 0.9]] Length: 1000 Categories (4, object): [(0, 0.1] < (0.1, 0.5] < (0.5, 0.9] < (0.9, 1]]
pd.value_counts(cats2)
(0.1, 0.5] 135 (0.5, 0.9] 124 (0, 0.1] 40 (0.9, 1] 21 dtype: int64
在之后的章节我们还会用到 cut 和 qcut,这些离散函数对于量化和群聚分析很有用。
6 Detecting and Filtering Outliers(检测和过滤异常值)
过滤或转换异常值是数组操作的一个重头戏。下面的 DataFrame 有正态分布的数据:
data = pd.DataFrame(np.random.randn(1000, 4)) data.describe()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | 0.010953 | 0.012928 | 0.033165 | -0.031257 |
std | 1.011621 | 1.013341 | 1.004356 | 0.996333 |
min | -2.994342 | -4.328036 | -3.303616 | -3.133495 |
25% | -0.654483 | -0.662177 | -0.644982 | -0.670813 |
50% | -0.000637 | -0.033241 | 0.050481 | -0.074641 |
75% | 0.723100 | 0.725839 | 0.708452 | 0.643418 |
max | 3.318499 | 3.353001 | 3.002853 | 3.002868 |
假设我们想要找一个列中,绝对值大于 3 的数字:
data.head()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.123766 | 0.933920 | 0.494755 | 0.690507 |
1 | 2.513636 | 0.575393 | -0.323590 | 0.586833 |
2 | -0.335958 | -0.843735 | 0.302201 | -0.490675 |
3 | -1.307658 | -0.485670 | 1.612787 | 0.210169 |
4 | -0.793757 | -0.693757 | -1.718367 | 0.515088 |
col = data[2] col.head()
0 0.494755 1 -0.323590 2 0.302201 3 1.612787 4 -1.718367 Name: 2, dtype: float64
col[np.abs(col) > 3]
339 -3.303616 932 3.002853 Name: 2, dtype: float64
选中所有绝对值大于 3 的行,可以用 any 方法在一个 boolean DataFrame 上:
data[(np.abs(data) > 3)].head()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | NaN | NaN | NaN | NaN |
1 | NaN | NaN | NaN | NaN |
2 | NaN | NaN | NaN | NaN |
3 | NaN | NaN | NaN | NaN |
4 | NaN | NaN | NaN | NaN |
data[(np.abs(data) > 3).any(1)] # any 中 axis=1 表示 column
0 | 1 | 2 | 3 | |
---|---|---|---|---|
22 | 1.075728 | 0.250000 | 0.951303 | -3.133495 |
155 | 0.064837 | -4.328036 | 1.121061 | -0.574203 |
224 | -0.289148 | -2.912116 | 0.332218 | -3.129604 |
339 | -0.098352 | -0.610929 | -3.303616 | -2.072304 |
609 | 0.983240 | 1.372633 | 0.018172 | 3.002868 |
735 | 3.318499 | -2.573122 | -1.515901 | -1.204596 |
822 | 1.810396 | 3.353001 | -1.283856 | -1.166749 |
856 | -0.795070 | 3.204789 | -0.211642 | 1.278828 |
932 | 0.898147 | -0.961850 | 3.002853 | -0.128494 |
下面是把绝对值大于 3 的数字直接变成-3 或 3:
data[np.abs(data) > 3] = np.sign(data) * 3
data[21:23]
0 | 1 | 2 | 3 | |
---|---|---|---|---|
21 | -0.066111 | -1.159064 | 0.518720 | -0.284596 |
22 | 1.075728 | 0.250000 | 0.951303 | -3.000000 |
data.describe()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | 0.010634 | 0.013698 | 0.033466 | -0.030997 |
std | 1.010629 | 1.006768 | 1.003383 | 0.995521 |
min | -2.994342 | -3.000000 | -3.000000 | -3.000000 |
25% | -0.654483 | -0.662177 | -0.644982 | -0.670813 |
50% | -0.000637 | -0.033241 | 0.050481 | -0.074641 |
75% | 0.723100 | 0.725839 | 0.708452 | 0.643418 |
max | 3.000000 | 3.000000 | 3.000000 | 3.000000 |
np.sign(data)会根据值的正负号来得到 1 或-1:
np.sign(data).head()
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.0 | 1.0 | 1.0 | 1.0 |
1 | 1.0 | 1.0 | -1.0 | 1.0 |
2 | -1.0 | -1.0 | 1.0 | -1.0 |
3 | -1.0 | -1.0 | 1.0 | 1.0 |
4 | -1.0 | -1.0 | -1.0 | 1.0 |
7 Permutation and Random Sampling(排列和随机采样)
排列(随机排序)一个 series 或 DataFrame 中的 row,用 numpy.random.permutation 函数很容易就能做到。调用 permutation 的时候设定好你想要进行排列的 axis,会产生一个整数数组表示新的顺序:
df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4))) df
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
4 | 16 | 17 | 18 | 19 |
sampler = np.random.permutation(5) sampler
array([4, 3, 2, 1, 0])
这个数组能被用在基于 iloc 上的 indexing 或 take 函数:
df.take(sampler)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
4 | 16 | 17 | 18 | 19 |
3 | 12 | 13 | 14 | 15 |
2 | 8 | 9 | 10 | 11 |
1 | 4 | 5 | 6 | 7 |
0 | 0 | 1 | 2 | 3 |
为了选中一个随机的子集,而且没有代替功能(既不影响原来的值,返回一个新的 series 或 DataFrame),可以用 sample 方法:
df.sample(n=3)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
3 | 12 | 13 | 14 | 15 |
4 | 16 | 17 | 18 | 19 |
如果想要生成的样本带有替代功能(即允许重复),给 sample 中设定 replace=True:
choices = pd.Series([5, 7, -1, 6, 4]) draws = choices.sample(n=10, replace=True) draws
4 4 4 4 1 7 1 7 1 7 1 7 4 4 3 6 1 7 1 7 dtype: int64
8 Computing Indicator/Dummy Variables(计算指示器/虚拟变量)
Dummy Variables:虚拟变量,又称虚设变量、名义变量或哑变量,用以反映质的属性的一个人工变量,是量化了的自变量,通常取值为 0 或 1。
另一种在统计模型上的转换或机器学习应用是把一个 categorical variable(类别变量)变为一个 dummy or indicator matrix(虚拟或指示器矩阵)。如果 DataFrame 中的一列有 k 个不同的值,我们可以用一个矩阵或 DataFrame 用 k 列来表示,1 或 0。pandas 有一个 get_dummies 函数实现这个工作,当然,你自己设计一个其实也不难。这里举个例子:
df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)}) df
data1 | key | |
---|---|---|
0 | 0 | b |
1 | 1 | b |
2 | 2 | a |
3 | 3 | c |
4 | 4 | a |
5 | 5 | b |
pd.get_dummies(df['key'])
a | b | c | |
---|---|---|---|
0 | 0.0 | 1.0 | 0.0 |
1 | 0.0 | 1.0 | 0.0 |
2 | 1.0 | 0.0 | 0.0 |
3 | 0.0 | 0.0 | 1.0 |
4 | 1.0 | 0.0 | 0.0 |
5 | 0.0 | 1.0 | 0.0 |
在一些情况里,如果我们想要给 column 加一个 prefix, 可以用 data.get_dummies 里的 prefix 参数来实现:
dummies = pd.get_dummies(df['key'], prefix='key')
df_with_dummy = df[['data1']].join(dummies) df_with_dummy
data1 | key_a | key_b | key_c | |
---|---|---|---|---|
0 | 0 | 0.0 | 1.0 | 0.0 |
1 | 1 | 0.0 | 1.0 | 0.0 |
2 | 2 | 1.0 | 0.0 | 0.0 |
3 | 3 | 0.0 | 0.0 | 1.0 |
4 | 4 | 1.0 | 0.0 | 0.0 |
5 | 5 | 0.0 | 1.0 | 0.0 |
如果 DataFrame 中的 a row 属于多个类别,事情会变得复杂一些。我们来看一下 MoviesLens 1M 数据集:
mnames = ['movie_id', 'title', 'genres']
movies = pd.read_table('../datasets/movielens/movies.dat', sep='::', header=None, names=mnames, engine='python') movies[:10]
movie_id | title | genres | |
---|---|---|---|
0 | 1 | Toy Story (1995) | Animation|Children's|Comedy |
1 | 2 | Jumanji (1995) | Adventure|Children's|Fantasy |
2 | 3 | Grumpier Old Men (1995) | Comedy|Romance |
3 | 4 | Waiting to Exhale (1995) | Comedy|Drama |
4 | 5 | Father of the Bride Part II (1995) | Comedy |
5 | 6 | Heat (1995) | Action|Crime|Thriller |
6 | 7 | Sabrina (1995) | Comedy|Romance |
7 | 8 | Tom and Huck (1995) | Adventure|Children's |
8 | 9 | Sudden Death (1995) | Action |
9 | 10 | GoldenEye (1995) | Action|Adventure|Thriller |
给每个 genre 添加一个指示变量比较麻烦。首先我们先取出所有不同的类别:
all_genres = [] for x in movies.genres: all_genres.extend(x.split('|')) genres = pd.unique(all_genres) genres
array(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy', 'Romance', 'Drama', 'Action', 'Crime', 'Thriller', 'Horror', 'Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir', 'Western'], dtype=object)
一种构建 indicator dataframe 的方法是先构建一个全是 0 的 DataFrame:
zero_matrix = np.zeros((len(movies), len(genres))) zero_matrix.shape
(3883, 18)
dummies = pd.DataFrame(zero_matrix, columns=genres) dummies.head()
Animation | Children's | Comedy | Adventure | Fantasy | Romance | Drama | Action | Crime | Thriller | Horror | Sci-Fi | Documentary | War | Musical | Mystery | Film-Noir | Western | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
然后迭代每一部 movie,并设置每一行中的 dummies 为 1。使用 dummies.columns 来计算每一列的 genre 的指示器:
gen = movies.genres[0] gen.split('|')
['Animation', "Children's", 'Comedy']
dummies.columns.get_indexer(gen.split('|'))
array([0, 1, 2])
然后,使用.iloc,根据索引来设定值:
for i, gen in enumerate(movies.genres): indices = dummies.columns.get_indexer(gen.split('|')) dummies.iloc[i, indices] = 1
dummies.head()
Animation | Children's | Comedy | Adventure | Fantasy | Romance | Drama | Action | Crime | Thriller | Horror | Sci-Fi | Documentary | War | Musical | Mystery | Film-Noir | Western | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
1 | 0.0 | 1.0 | 0.0 | 1.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
然后,我们可以结合这个和 movies:
movies_windic = movies.join(dummies.add_prefix('Genre_')) movies_windic.iloc[0]
movie_id 1 title Toy Story (1995) genres Animation|Children's|Comedy Genre_Animation 1 Genre_Children's 1 Genre_Comedy 1 Genre_Adventure 0 Genre_Fantasy 0 Genre_Romance 0 Genre_Drama 0 Genre_Action 0 Genre_Crime 0 Genre_Thriller 0 Genre_Horror 0 Genre_Sci-Fi 0 Genre_Documentary 0 Genre_War 0 Genre_Musical 0 Genre_Mystery 0 Genre_Film-Noir 0 Genre_Western 0 Name: 0, dtype: object
对于一个很大的数据集,这种构建多个成员指示变量的方法并不会加快速度。写一个低层级的函数来直接写一个 numpy array,并把写过整合到 DataFrame 会更快一些。
一个有用的 recipe 诀窍是把 get_dummies 和离散函数(比如 cut)结合起来:
np.random.seed(12345)
values = np.random.rand(10) values
array([ 0.92961609, 0.31637555, 0.18391881, 0.20456028, 0.56772503, 0.5955447 , 0.96451452, 0.6531771 , 0.74890664, 0.65356987])
bins = [0, 0.2, 0.4, 0.6, 0.8, 1.]
pd.cut(values, bins)
[(0.8, 1], (0.2, 0.4], (0, 0.2], (0.2, 0.4], (0.4, 0.6], (0.4, 0.6], (0.8, 1], (0.6, 0.8], (0.6, 0.8], (0.6, 0.8]] Categories (5, object): [(0, 0.2] < (0.2, 0.4] < (0.4, 0.6] < (0.6, 0.8] < (0.8, 1]]
pd.get_dummies(pd.cut(values, bins))
(0, 0.2] | (0.2, 0.4] | (0.4, 0.6] | (0.6, 0.8] | (0.8, 1] | |
---|---|---|---|---|---|
0 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 |
1 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 |
2 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3 | 0.0 | 1.0 | 0.0 | 0.0 | 0.0 |
4 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 |
5 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 |
6 | 0.0 | 0.0 | 0.0 | 0.0 | 1.0 |
7 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 |
8 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 |
9 | 0.0 | 0.0 | 0.0 | 1.0 | 0.0 |
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论