在数据分析和建模过程中,处理缺失数据很重要,另外对于数据的筛选、清理和转换同样很重要。
一、删除重复数据
删除重复的行。DataFrame 的方法 duplicated() 返回一个布尔序列,判断每一行是否重复(其列值与前一行中的列值完全相等则为重复)。另一个方法drop_duplicates() 返回一个去除重复数据后的DataFrame新对象。
import numpy as np
import pandas as pd
#构造一个DataFrame对象
data = pd.DataFrame({"k1": ["one", "two"] * 3 + ["two"], "k2": [1, 1, 2, 3, 3, 4, 4]})
a = data.duplicated()
b = data.drop_duplicates()
print(data)
print(a)
print(b)
data输出:
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
6 | two | 4 |
data.duplicated()输出:
0 False
1 False
2 False
3 False
4 False
5 False
6 True
dtype: bool
data.drop_duplicates()输出:
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
删除重复行的数据时,上面的函数drop_duplicates() 考虑所有列值都相等才会去重。如果我们想根据某个列或某几列的列值进行去重,可以给drop_duplicates()方法传递一个列参数或一个列名列表,并使用keep参数指定保留重复行的最后观测到的行(默认情况下,duplicated 和 drop_duplicates 保留第一个观测值组合。传递 keep=“last” 将返回最后一个)。
import numpy as np
import pandas as pd
#构造一个DataFrame对象
data = pd.DataFrame({"k1": ["one", "two"] * 3 + ["two"], "k2": [1, 1, 2, 3, 3, 4, 4]})
data.duplicated()
data.drop_duplicates()
#给data增加一列v1
data["v1"] = range(7)
print(data)
#根据k1列值去重,重复的则保留第一次观察到的
a = data.drop_duplicates(subset=["k1"])
print(a)
#根据k1和k2列值去重,用keep参数指定如果出现重复值则保留最后一次观察到的值
b = data.drop_duplicates(["k1", "k2"], keep="last")
print(b)
上面代码先给data增加了一个v1列,输出:
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(subset=["k1"]) 以k1列值去重, 输出:
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
data.drop_duplicates(["k1", "k2"], keep="last") 以k1、k2列的值去重,输出:
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 |
二、使用函数或映射(map)转换数据
在分析处理数据集的过程中,经常要根据 DataFrame 的列、Series 或数组的值执行一些转换。例如:我们定义一个包含肉类食品和重量的DataFrame对象,然后将这些肉类对应到动物身上,肉类食品和动物的映射关系用一个字典存储。
import numpy as np
import pandas as pd
#数据转换
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]})
print(data)
#肉类食品雨动物的对应字典
meat_to_animal = {
"bacon": "pig",
"pulled pork": "pig",
"pastrami": "cow",
"corned beef": "cow",
"honey ham": "pig",
"nova lox": "salmon"
}
#将肉类食品对应到动物字典上,并在data上增加animal列。
data["animal"] = data["food"].map(meat_to_animal)
print(data)
未对应前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 |
使用map()方法映射转换后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 |
在上面的map()方法中,我们传递的是一个字典,它也可以接收一个函数作为参数来进行数据转换映射。例如:
import numpy as np
import pandas as pd
#数据转换
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]})
print(data)
#肉类食品雨动物的对应字典
meat_to_animal = {
"bacon": "pig",
"pulled pork": "pig",
"pastrami": "cow",
"corned beef": "cow",
"honey ham": "pig",
"nova lox": "salmon"
}
def get_animal(x):
return meat_to_animal[x]
#map()接收一个函数作为参数。
a = data["food"].map(get_animal)
print(a)
data["food"].map(get_animal) 输出:
0 pig
1 pig
2 pig
3 cow
4 cow
5 pig
6 cow
7 pig
8 salmon
Name: food, dtype: object
使用 map() 是执行元素转换和其他数据清理相关操作还是非常方便的。
三、替换值
使用 fillna() 方法填充缺失数据是更通用。使用map() 方法转换映射数据更方便,但替换数据使用 replace()将会是一种更简单、更灵活的方法。让我们开始学习(注意看代码注释):
import numpy as np
import pandas as pd
# Series中的-999可能是由于缺失数据而使用的占位值
data = pd.Series([1., -999., 2., -999., -1000., 3.])
print(data)
# 通过replace方法将-999替换为NaN
data.replace(-999, np.nan)
# 如果要一次替换多个值,改为传递一个列表
data.replace([-999, -1000], np.nan)
# 对于替换多个不同的值,如果要替换为不同的值,那么替换值也可以传递一个列表
# 将-999替换为NaN,-1000替换为0
data.replace([-999, -1000], [np.nan, 0])
# 替换多个值为不同的值也可以通过传递字典参数来实现
a= data.replace({-999: np.nan, -1000: 0})
print(a)
未替换前的data输出:
0 1.0
1 -999.0
2 2.0
3 -999.0
4 -1000.0
5 3.0
dtype: float64
替换后返回一个新对象,输出结果:
0 1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 3.0
dtype: float64
data.replace 方法不同于 data.str.replace,后者执行元素级字符串替换。
四、重命名轴索引
与 Series 中的值一样,轴标签可以通过某种形式的函数或映射进行类似转换,以生成新的、不同标记的对象。还可以就地修改轴,而无需创建新的数据结构。下面是一个简单的示例(使用map方法,轴索引有个map方法):
import numpy as np
import pandas as pd
data = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=["Ohio", "Colorado", "New York"],
columns=["one", "two", "three", "four"])
print(data)
# 将字符转为大写
def transform(x):
return x[:4].upper()
# 将行索引值转为大写
data.index.map(transform)
# 就地修改 DataFrame的index
data.index = data.index.map(transform)
print(data)
原data输出:
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colorado | 4 | 5 | 6 | 7 |
New York | 8 | 9 | 10 | 11 |
data.index = data.index.map(transform) 就地转化后输出:
one | two | three | four | |
---|---|---|---|---|
OHIO | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
如果要在不修改原始数据集的情况下创建数据集的转换版本,则可以使用rename()方法:
import numpy as np
import pandas as pd
data = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=["Ohio", "Colorado", "New York"],
columns=["one", "two", "three", "four"])
print(data)
a = data.rename(index=str.title, columns=str.upper)
print(a)
data.rename(index=str.title, columns=str.upper)不会改变原data内容,会产生一个新对象,这里用变量a接收了,a输出结果:
ONE | TWO | THREE | FOUR | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colorado | 4 | 5 | 6 | 7 |
New York | 8 | 9 | 10 | 11 |
值得注意的是,rename 可以与类似字典的对象结合使用,为轴标签的子集提供新值:
import numpy as np
import pandas as pd
data = pd.DataFrame(np.arange(12).reshape((3, 4)),
index=["Ohio", "Colorado", "New York"],
columns=["one", "two", "three", "four"])
def transform(x):
return x[:4].upper()
data.index.map(transform)
print(data)
a = data.rename(index=str.title, columns=str.upper)
print(a)
# rename 与类似字典的对象结合使用,改变某个轴或索引值
b= data.rename(index={"OHIO": "INDIANA"},
columns={"three": "peekaboo"})
print(b)
输出:
one | two | peekaboo | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
五、离散化和分箱
连续数据通常被离散化或以其他方式分离到“箱”中进行分析。假如我们有一组人的年龄数据,打算将他们分组到不同年龄段的存储箱中进行分析。
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
我们使用pandas的cut方法,将这些年龄分为 18 到 25、26 到 35、36 到 60 以及最后 61 岁及以上的箱中。
bins = [18, 25, 35, 60, 100]
用法也很简单,将ages列表和区间bins传给cut方法:
import numpy as np
import pandas as pd
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
age_categories = pd.cut(ages, bins)
print(age_categories)
输出结果:
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
Length: 12
Categories (4, interval[int64, right]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
从输出结果可以看出ages列表中的每个年龄值都分在了一个区间,最后分成了4个区间。pandas 返回的对象是一个特殊的 Categorical 对象。从输出描述了 pandas.cut 计算的 bin。每个 bin 由一个特殊的 (对于 pandas 唯一) 间隔值类型标识,其中包含每个 bin 的下限和上限。
Categorical对象有一些属性可以查看区间信息,例如:
import numpy as np
import pandas as pd
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
age_categories = pd.cut(ages, bins)
age_codes = age_categories.codes
print(age_codes)
age_cat = age_categories.categories
print(age_cat)
cat0 = age_categories.categories[0]
print(cat0)
couts = age_categories.value_counts()
print(couts)
age_categories.codes输出:[0 0 0 1 0 0 2 1 3 2 2 1]
age_categories.categories输出:IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]], dtype='interval[int64, right]')
age_categories.categories[0] 输出:(18, 25]
age_categories.value_counts() 对每个区间有多少个值计数,输出:
(18, 25] 5
(25, 35] 3
(35, 60] 3
(60, 100] 1
Name: count, dtype: int64
在区间的字符串表示形式中,括号表示该侧是开放的(不包括),而方括号表示它是闭合的(包括)。可以通过传递 right=False 来更改关闭哪一侧。例如(18, 25]表示不包括18但包括25。
对于上面的ages列表和bins列表使用 pd.cut(ages, bins, right=False),输出结果如下:
[[18, 25), [18, 25), [25, 35), [25, 35), [18, 25), ..., [25, 35), [60, 100), [35, 60), [35, 60), [25, 35)] Length: 12 Categories (4, interval[int64, left]): [[18, 25) < [25, 35) < [35, 60) < [60, 100)]
从上面的输出可以看出区间做为标签还是挺不方便的,我们可以使用labels参数给他们重新赋值,例如将(18, 25] 替换为 标签 "Youth":
import numpy as np
import pandas as pd
ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
bins = [18, 25, 35, 60, 100]
# 用于给区间标签重新赋值
group_names = ["Youth", "YoungAdult", "MiddleAged", "Senior"]
age_categories = pd.cut(ages, bins, labels=group_names)
print(age_categories)
输出:
['Youth', 'Youth', 'Youth', 'YoungAdult', 'Youth', ..., 'YoungAdult', 'Senior', 'MiddleAged', 'MiddleAged', 'YoungAdult']
Length: 12
Categories (4, object): ['Youth' < 'YoungAdult' < 'MiddleAged' < 'Senior']
如果只把整数个 bin 传递给 pandas.cut 而不是显式 bin 边缘,将根据数据中的最小值和最大值计算等长的 bin区间。例如:
import numpy as np
import pandas as pd
data = np.random.uniform(size=20)
# 4表示分成4个bin,这里会根据data中的最小值和最大值平均分成4个区间,precision=2表示小数位精确的2位
cats = pd.cut(data, 4, precision=2)
print(cats)
输出:
[(0.0095, 0.25], (0.49, 0.74], (0.49, 0.74], (0.49, 0.74], (0.49, 0.74], ..., (0.25, 0.49], (0.49, 0.74], (0.25, 0.49], (0.74, 0.98], (0.74, 0.98]]
Length: 20
Categories (4, interval[float64, right]): [(0.0095, 0.25] < (0.25, 0.49] < (0.49, 0.74] <
(0.74, 0.98]]
与 pandas.cut 相近的函数 pandas.qcut 根据样本分位数对数据进行分箱。根据数据的分布,使用 pandas.cut 通常不会导致每个 bin 具有相同数量的数据点,但是由于 pandas.qcut 使用样本分位数,因此您将获得大小大致相等的 bin,例如:
import numpy as np
import pandas as pd
data = np.random.standard_normal(1000)
quartiles = pd.qcut(data, 4, precision=2)
print(quartiles, quartiles.value_counts())
输出结果:
[(0.7, 2.81], (0.7, 2.81], (-3.44, -0.66], (0.7, 2.81], (0.7, 2.81], ..., (-0.66, 0.057], (-0.66, 0.057], (0.057, 0.7], (0.7, 2.81], (0.7, 2.81]]
Length: 1000
Categories (4, interval[float64, right]): [(-3.44, -0.66] < (-0.66, 0.057] < (0.057, 0.7] <
(0.7, 2.81]]
(-3.44, -0.66] 250
(-0.66, 0.057] 250
(0.057, 0.7] 250
(0.7, 2.81] 250
Name: count, dtype: int64
从以上输出可以看出4个bin区间,每个区间都包含250个数据。
与 pandas.cut 类似,也可以传递自己的分位数(介于 0 和 1 之间的数字,包括 0 和 1):
import numpy as np
import pandas as pd
data = np.random.standard_normal(1000)
cates_counts = pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.]).value_counts()
print(cates_counts)
输出结果:
(-3.311, -1.269] 100
(-1.269, -0.0234] 400
(-0.0234, 1.199] 400
(1.199, 2.752] 100
Name: count, dtype: int64
六、检测和筛选离群值
筛选或转换离群值在很大程度上是应用数组操作的问题。我们用一个包含正态分布数据的 DataFrame来学习使用。
import numpy as np
import pandas as pd
#设置一个种子,为了每次运行np.random.standard_normal((1000, 4))能获取到同样的随机值。
np.random.seed(12345)
data = pd.DataFrame(np.random.standard_normal((1000, 4)))
des = data.describe()
print(data)
print(des)
data输出结果是:
0 1 2 3
0 -0.204708 0.478943 -0.519439 -0.555730
1 1.965781 1.393406 0.092908 0.281746
2 0.769023 1.246435 1.007189 -1.296221
3 0.274992 0.228913 1.352917 0.886429
4 -2.001637 -0.371843 1.669025 -0.438570
.. ... ... ... ...
995 1.089085 0.251232 -1.451985 1.653126
996 -0.478509 -0.010663 -1.060881 -1.502870
997 -1.946267 1.013592 0.037333 0.133304
998 -1.293122 -0.322542 -0.782960 -0.303340
999 0.089987 0.292291 1.177706 0.882755
[1000 rows x 4 columns]
data.describe()输出这个data的统计描述信息如下:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.067684 0.067924 0.025598 -0.002298
std 0.998035 0.992106 1.006835 0.996794
min -3.428254 -3.548824 -3.184377 -3.745356
25% -0.774890 -0.591841 -0.641675 -0.644144
50% -0.116401 0.101143 0.002073 -0.013611
75% 0.616366 0.780282 0.680391 0.654328
max 3.366626 2.653656 3.260383 3.927528
假设我们想在某列上查找绝对值超过 3 的值,可以如下操作:
import numpy as np
import pandas as pd
#设置一个种子,为了每次运行np.random.standard_normal((1000, 4))能获取到同样的随机值。
np.random.seed(12345)
data = pd.DataFrame(np.random.standard_normal((1000, 4)))
print(data)
data.describe()
#在第2列中找绝对值大于3的值
col = data[2]
print(col[col.abs() > 3])
输出:
5 3.248944
102 3.176873
324 3.260383
499 -3.056990
586 -3.184377
Name: 2, dtype: float64
如果要选择值某个值超过 3 或 –3 的所有行,可以在返回的 DataFrame 布尔数组上使用 any 方法,例如:
import numpy as np
import pandas as pd
#设置一个种子,为了每次运行np.random.standard_normal((1000, 4))能获取到同样的随机值。
np.random.seed(12345)
data = pd.DataFrame(np.random.standard_normal((1000, 4)))
print(data)
data.describe()
#在第2列中找绝对值大于3的值
print(data[(data.abs() > 3).any(axis="columns")])
输出:
0 1 2 3
5 -0.539741 0.476985 3.248944 -1.021228
97 -0.774363 0.552936 0.106061 3.927528
102 -0.655054 -0.565230 3.176873 0.959533
305 -2.315555 0.457246 -0.025907 -3.399312
324 0.050188 1.951312 3.260383 0.963301
400 0.146326 0.508391 -0.196713 -3.745356
499 -0.293333 -0.242459 -3.056990 1.918403
523 -3.428254 -0.296336 -0.439938 -0.867165
586 0.275144 1.179227 -3.184377 1.369891
808 -0.362528 -3.548824 1.553205 -2.186301
900 3.366626 -2.372214 0.851010 1.332846
以下是限制区间 –3 到 3 之外的值的代码,语句 np.sign(data) 根据 data 中的值是正值还是负值生成 1 和 –1 值:
import numpy as np
import pandas as pd
#设置一个种子,为了每次运行np.random.standard_normal((1000, 4))能获取到同样的随机值。
np.random.seed(12345)
data = pd.DataFrame(np.random.standard_normal((1000, 4)))
data[data.abs() > 3] = np.sign(data) * 3
print(data.describe())
print(np.sign(data).head())
输出统计描述信息如下:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.067623 0.068473 0.025153 -0.002081
std 0.995485 0.990253 1.003977 0.989736
min -3.000000 -3.000000 -3.000000 -3.000000
25% -0.774890 -0.591841 -0.641675 -0.644144
50% -0.116401 0.101143 0.002073 -0.013611
75% 0.616366 0.780282 0.680391 0.654328
max 3.000000 2.653656 3.000000 3.000000
np.sign(data).head()这行代码输出(将正数替换为1,负数替换为-1):
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
七、排列和随机采样
可以使用 numpy.random.permutation 函数对 Series 或 DataFrame 中的行进行排列(随机重新排序)。使用要置换的轴的长度调用 permutation 会生成一个整数数组,指示新的排序。然后可以使用take()函数或iloc[]进行重新排序,例如:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.arange(5 * 7).reshape((5, 7)))
sampler = np.random.permutation(5)
print(sampler)
a = df.take(sampler)
b = df.iloc[sampler]
print(a, '\n', b)
输出结果:
[4 3 2 1 0]
0 1 2 3 4 5 6
4 28 29 30 31 32 33 34
3 21 22 23 24 25 26 27
2 14 15 16 17 18 19 20
1 7 8 9 10 11 12 13
0 0 1 2 3 4 5 6
0 1 2 3 4 5 6
4 28 29 30 31 32 33 34
3 21 22 23 24 25 26 27
2 14 15 16 17 18 19 20
1 7 8 9 10 11 12 13
0 0 1 2 3 4 5 6
通过调用使用了参数 axis=“columns” 的 take,我们还可以选择列的排列:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.arange(5 * 7).reshape((5, 7)))
column_sampler = np.random.permutation(7)
# 使用参数axis="columns",对列进行排序
df.take(column_sampler, axis="columns")
要选择不重复的子集样本(同一行不出现两次),可以在 Series 和 DataFrame 上使用 sample 方法;如果要重复选择样本,将 replace=True 传递给sample方法。例如:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.arange(5 * 7).reshape((5, 7)))
column_sampler = np.random.permutation(7)
# 使用参数axis="columns",对列进行排序
df.take(column_sampler, axis="columns")
print(df.sample(n=3))
choices = pd.Series([5, 7, -1, 6, 4])
print(choices.sample(n=10, replace=True))
输出:
0 1 2 3 4 5 6
2 14 15 16 17 18 19 20
3 21 22 23 24 25 26 27
1 7 8 9 10 11 12 13
0 5
0 5
4 4
3 6
2 -1
3 6
0 5
4 4
0 5
4 4
dtype: int64
八、计算指标/虚拟变量
统计建模或机器学习应用程序的另一种转换类型是将分类变量转换为虚拟矩阵或指标矩阵。如果 DataFrame 中的列有 k 个不同的值,则可以派生一个矩阵或 DataFrame,其中 k 列包含所有 1 和 0。pandas 有一个 pandas.get_dummies 函数来执行此操作,但我们也可以自己设计一个。让我们来看个示例:
import numpy as np
import pandas as pd
df = pd.DataFrame({"key": ["b", "b", "a", "c", "a", "b"],
"data1": range(6)})
a = pd.get_dummies(df["key"], dtype=float)
print(df)
print(a)
df输出:
key | data1 | |
---|---|---|
0 | b | 0 |
1 | b | 1 |
2 | a | 2 |
3 | c | 3 |
4 | a | 4 |
5 | b | 5 |
pd.get_dummies(df["key"], dtype=float) 输出:
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 |
在某些情况下,可能希望为指标 DataFrame 中的列添加前缀,然后可以将其与其他数据合并。pandas.get_dummies() 有一个 prefix 参数来执行此操作:
import numpy as np
import pandas as pd
df = pd.DataFrame({"key": ["b", "b", "a", "c", "a", "b"],
"data1": range(6)})
dummies = pd.get_dummies(df["key"], prefix="key", dtype=float)
df_with_dummy = df[["data1"]].join(dummies)
print(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.join 方法将在后面的学习中还会详细解释。
如果 DataFrame 中的一行属于多个类别,我们必须使用不同的方法来创建虚拟变量。让我们看看 MovieLens 数据集,数据量在1M左右。其目录和数据内容如下图所示:
import numpy as np
import pandas as pd
mnames = ["movie_id", "title", "genres"]
movies = pd.read_table("datasets/movielens/movies.dat", sep="::",
header=None, names=mnames, engine="python")
#输出前10行
print(movies[:10])
输出前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 |
下面用pandas的str.get_dummies 方法处理genres列:
import numpy as np
import pandas as pd
mnames = ["movie_id", "title", "genres"]
movies = pd.read_table("datasets/movielens/movies.dat", sep="::",
header=None, names=mnames, engine="python")
#输出前10行
print(movies[:10])
#用str的get_dummies方法生成movies列的指标矩阵
dummies = movies["genres"].str.get_dummies("|")
print(dummies.iloc[:10, :6])
输出:
Action | Adventure | Animation | Children's | Comedy | Crime | |
---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 1 | 0 |
1 | 0 | 1 | 0 | 1 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 1 | 0 |
3 | 0 | 0 | 0 | 0 | 1 | 0 |
4 | 0 | 0 | 0 | 0 | 1 | 0 |
5 | 1 | 0 | 0 | 0 | 0 | 1 |
6 | 0 | 0 | 0 | 0 | 1 | 0 |
7 | 0 | 1 | 0 | 1 | 0 | 0 |
8 | 1 | 0 | 0 | 0 | 0 | 0 |
9 | 1 | 1 | 0 | 0 | 0 | 0 |
然后,可以像之前一样,将其与电影结合使用,同时使用 add_prefix 方法将 “Genre_” 添加到 DataFrame 中的列名:
import numpy as np
import pandas as pd
mnames = ["movie_id", "title", "genres"]
movies = pd.read_table("datasets/movielens/movies.dat", sep="::",
header=None, names=mnames, engine="python")
#用str的get_dummies方法生成movies列的指标矩阵
dummies = movies["genres"].str.get_dummies("|")
movies_windic = movies.join(dummies.add_prefix("Genre_"))
print(movies_windic.iloc[0])
输出:
movie_id 1
title Toy Story (1995)
genres Animation|Children's|Comedy
Genre_Action 0
Genre_Adventure 0
Genre_Animation 1
Genre_Children's 1
Genre_Documentary 0
Genre_Drama 0
Genre_Fantasy 0
Genre_Film-Noir 0
Genre_Horror 0
Genre_Musical 0
Genre_Mystery 0
Genre_Romance 0
Genre_Sci-Fi 0
Genre_Thriller 0
Genre_War 0
Genre_Western 0
Name: 0, dtype: object
注意:对于更大的数据,这种构造具有多个成员的指示变量的方法并不是特别快。最好编写一个直接写入 NumPy 数组的较低级别函数,然后将结果包装在 DataFrame 中。
统计应用的一个常用方法是将 pandas.get_dummies 与离散化函数(如 pandas.cut)结合使用。例如:
import numpy as np
import pandas as pd
np.random.seed(12345)
values = np.random.uniform(size=10)
print(values)
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
cates_dic = pd.get_dummies(pd.cut(values, bins))
print(cates_dic)
输出结果:
[0.92961609 0.31637555 0.18391881 0.20456028 0.56772503 0.5955447
0.96451452 0.6531771 0.74890664 0.65356987]
(0.0, 0.2] (0.2, 0.4] (0.4, 0.6] (0.6, 0.8] (0.8, 1.0]
0 False False False False True
1 False True False False False
2 True False False False False
3 False True False False False
4 False False True False False
5 False False True False False
6 False False False False True
7 False False False True False
8 False False False True False
9 False False False True False