处理缺失数据
对于数值数据,pandas使用浮点值NaN(Not a Number)表示缺失数据
初始化数据
1 2 3 4 5 6 7 8 9 In [10]: string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado']) In [11]: string_data Out[11]: 0 aardvark 1 artichoke 2 NaN 3 avocado dtype: object
isnull()
判断当前数据是否是缺失值,是就返回true,否就返回false
1 2 3 4 5 6 7 In [12]: string_data.isnull() Out[12]: 0 False 1 False 2 True 3 False dtype: bool
利用索引直接置为None
1 2 3 4 5 6 7 8 9 In [13]: string_data[0] = None In [14]: string_data.isnull() Out[14]: 0 True 1 False 2 True 3 False dtype: bool
滤除缺失数据
dropna
对于Series
等价于data[data.notnull()]
1 2 3 4 5 6 In [18]: data[data.notnull()] Out[18]: 0 1.0 2 3.5 4 7.0 dtype: float64
对于DataFrame
默认丢弃任何含有缺失值的行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 In [19]: data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA], ....: [NA, NA, NA], [NA, 6.5, 3.]]) In [20]: cleaned = data.dropna() In [21]: data Out[21]: 0 1 2 0 1.0 6.5 3.0 1 1.0 NaN NaN 2 NaN NaN NaN 3 NaN 6.5 3.0 In [22]: cleaned Out[22]: 0 1 2 0 1.0 6.5 3.0
传入how='all'将只丢弃全为NA的那些行:
1 2 3 4 5 6 In [23]: data.dropna(how='all') Out[23]: 0 1 2 0 1.0 6.5 3.0 1 1.0 NaN NaN 3 NaN 6.5 3.0
丢弃全为NA的列,传入axis=1即可
先添加第四列为全NA的列:data[4] = NA
1 2 3 4 5 6 7 In [26]: data.dropna(axis=1, how='all') Out[26]: 0 1 2 0 1.0 6.5 3.0 1 1.0 NaN NaN 2 NaN NaN NaN 3 NaN 6.5 3.0
利用thresh参数删除指定确实个数的行
用切片将不用观察的行和列之间赋值NA,然后再用默认dropna丢弃
切片赋值NA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 n [27]: df = pd.DataFrame(np.random.randn(7, 3)) In [28]: df.iloc[:4, 1] = NA In [29]: df.iloc[:2, 2] = NA In [30]: df Out[30]: 0 1 2 0 -0.204708 NaN NaN 1 -0.555730 NaN NaN 2 0.092908 NaN 0.769023 3 1.246435 NaN -1.296221 4 0.274992 0.228913 1.352917 5 0.886429 -2.001637 -0.371843 6 1.669025 -0.438570 -0.539741
1 2 3 4 5 6 In [31]: df.dropna() Out[31]: 0 1 2 4 0.274992 0.228913 1.352917 5 0.886429 -2.001637 -0.371843 6 1.669025 -0.438570 -0.539741
填充缺失数据
主要使用fillna这个方法
将缺失值替换为指定的常数值
1 2 3 4 5 6 7 8 9 10 In [33]: df.fillna(0) Out[33]: 0 1 2 0 -0.204708 0.000000 0.000000 1 -0.555730 0.000000 0.000000 2 0.092908 0.000000 0.769023 3 1.246435 0.000000 -1.296221 4 0.274992 0.228913 1.352917 5 0.886429 -2.001637 -0.371843 6 1.669025 -0.438570 -0.539741
通过字典对不同的列填充不同的值
1 2 3 4 5 6 7 8 9 10 In [34]: df.fillna({1: 0.5, 2: 0}) Out[34]: 0 1 2 0 -0.204708 0.500000 0.000000 1 -0.555730 0.500000 0.000000 2 0.092908 0.500000 0.769023 3 1.246435 0.500000 -1.296221 4 0.274992 0.228913 1.352917 5 0.886429 -2.001637 -0.371843 6 1.669025 -0.438570 -0.539741
默认会返回新对象,但也可以对现有对象进行就地修改
1 2 3 4 5 6 7 8 9 10 11 12 In [35]: _ = df.fillna(0, inplace=True) In [36]: df Out[36]: 0 1 2 0 -0.204708 0.000000 0.000000 1 -0.555730 0.000000 0.000000 2 0.092908 0.000000 0.769023 3 1.246435 0.000000 -1.296221 4 0.274992 0.228913 1.352917 5 0.886429 -2.001637 -0.371843 6 1.669025 -0.438570 -0.539741
对reindexing有效的那些插值方法也可用于fillna:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 In [37]: df = pd.DataFrame(np.random.randn(6, 3)) In [38]: df.iloc[2:, 1] = NA In [39]: df.iloc[4:, 2] = NA In [40]: df Out[40]: 0 1 2 0 0.476985 3.248944 -1.021228 1 -0.577087 0.124121 0.302614 2 0.523772 NaN 1.343810 3 -0.713544 NaN -2.370232 4 -1.860761 NaN NaN 5 -1.265934 NaN NaN In [41]: df.fillna(method='ffill') Out[41]: 0 1 2 0 0.476985 3.248944 -1.021228 1 -0.577087 0.124121 0.302614 2 0.523772 0.124121 1.343810 3 -0.713544 0.124121 -2.370232 4 -1.860761 0.124121 -2.370232 5 -1.265934 0.124121 -2.370232 In [42]: df.fillna(method='ffill', limit=2) Out[42]: 0 1 2 0 0.476985 3.248944 -1.021228 1 -0.577087 0.124121 0.302614 2 0.523772 0.124121 1.343810 3 -0.713544 0.124121 -2.370232 4 -1.860761 NaN -2.370232 5 -1.265934 NaN -2.370232
传入Series的平均值或中位数:
1 2 3 4 5 6 7 8 9 10 In [43]: data = pd.Series([1., NA, 3.5, NA, 7]) In [44]: data.fillna(data.mean()) Out[44]: 0 1.000000 1 3.833333 2 3.500000 3 3.833333 4 7.000000 dtype: float64
数据转换
移除重复数据
1 2 3 4 5 6 7 8 9 10 11 12 13 In [45]: data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'], ....: 'k2': [1, 1, 2, 3, 3, 4, 4]}) In [46]: data Out[46]: k1 k2 0 one 1 1 two 1 2 one 2 3 two 3 4 one 3 5 two 4 6 two 4
duplicated方法
表示各行是否是重复行(前面出现过的行):
1 2 3 4 5 6 7 8 9 10 In [47]: data.duplicated() Out[47]: 0 False 1 False 2 False 3 False 4 False 5 False 6 True dtype: bool
drop_duplicates方法
指定部分列进行重复项判断。假设我们还有一列值,且只希望根据k1列过滤重复项:
duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入keep='last'则保留最后一个:
1 2 3 4 5 6 7 8 9 In [51]: data.drop_duplicates(['k1', 'k2'], keep='last') Out[51]: 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 In [52]: 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]}) In [53]: data Out[53]: 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
添加一列表示该肉类食物来源的动物类型
编写一个不同肉类到动物的映射
1 2 3 4 5 6 7 8 meat_to_animal = { 'bacon': 'pig', 'pulled pork': 'pig', 'pastrami': 'cow', 'corned beef': 'cow', 'honey ham': 'pig', 'nova lox': 'salmon' }
调用map前,有些肉类肉类首字母大写,比如前面0 bacon和5
Bacon,所以需要先转换成小写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 In [55]: lowercased = data['food'].str.lower() In [56]: lowercased Out[56]: 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基础上再加1列,用map实现肉类与肉类动物来源一一对应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 In [57]: data['animal'] = lowercased.map(meat_to_animal) In [58]: data Out[58]: 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
一个能够完成全部这些工作的函数:
1 2 3 4 5 6 7 8 9 10 11 12 In [59]: data['food'].map(lambda x: meat_to_animal[x.lower()]) Out[59]: 0 pig 1 pig 2 pig 3 cow 4 cow 5 pig 6 cow 7 pig 8 salmon Name: food, dtype: object
替换值(主要使用replace)
map可用于修改对象的数据子集,而replace则提供了一种实现该功能的更简单、更灵活的方式
1 2 3 4 5 6 7 8 9 10 In [60]: data = pd.Series([1., -999., 2., -999., -1000., 3.]) In [61]: data Out[61]: 0 1.0 1 -999.0 2 2.0 3 -999.0 4 -1000.0 5 3.0
-999这个值可能是一个表示缺失数据的标记值。要将其替换为pandas能够理解的NA值,我们可以利用replace来产生一个新的Series(除非传入inplace=True):
1 2 3 4 5 6 7 8 9 In [62]: data.replace(-999, np.nan) Out[62]: 0 1.0 1 NaN 2 2.0 3 NaN 4 -1000.0 5 3.0 dtype: float64
一次性替换多个值
可以传入一个由待替换值组成的列表以及一个替换值
1 2 3 4 5 6 7 8 9 In [63]: data.replace([-999, -1000], np.nan) Out[63]: 0 1.0 1 NaN 2 2.0 3 NaN 4 NaN 5 3.0 dtype: float64
每个值有不同的替换值
可以传递一个替换列表:
1 2 3 4 5 6 7 8 9 In [64]: data.replace([-999, -1000], [np.nan, 0]) Out[64]: 0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64
传入的参数也可以是字典:
1 2 3 4 5 6 7 8 9 In [65]: data.replace({-999: np.nan, -1000: 0}) Out[65]: 0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64
注:data.replace方法与data.str.replace不同,后者做的是字符串的元素级替换。我们会在后面学习Series的字符串方法。
重命名轴索引
跟Series中的值一样,轴标签也可以通过函数或映射进行转换,从而得到一个新的不同标签的对象。轴还可以被就地修改,而无需新建一个数据结构
data.index中map
1 2 3 In [66]: data = pd.DataFrame(np.arange(12).reshape((3, 4)), ....: index=['Ohio', 'Colorado', 'New York'], ....: columns=['one', 'two', 'three', 'four'])
使用匿名函数实现修改所有列
Ohio
0
1
2
3
Colorado
4
5
6
7
New York
8
9
10
11
1 2 3 4 In [67]: transform = lambda x: x[:4].upper() In [68]: data.index.map(transform) Out[68]: Index(['OHIO', 'COLO', 'NEW '], dtype='object')
就地修改
1 2 3 4 5 6 7 8 In [69]: data.index = data.index.map(transform) In [70]: data Out[70]: one two three four OHIO 0 1 2 3 COLO 4 5 6 7 NEW 8 9 10 11
rename
创建转换版不修改原式数据
创建数据集的转换版(而不是修改原始数据),比较实用的方法是rename:
1 2 3 4 5 6 In [71]: data.rename(index=str.title, columns=str.upper) Out[71]: ONE TWO THREE FOUR Ohio 0 1 2 3 Colo 4 5 6 7 New 8 9 10 11
部分轴标签的更新:
用字典指明将原来换成对应的什么
1 2 3 4 5 6 7 In [72]: data.rename(index={'OHIO': 'INDIANA'}, ....: columns={'three': 'peekaboo'}) Out[72]: one two peekaboo four INDIANA 0 1 2 3 COLO 4 5 6 7 NEW 8 9 10 11
就地修改
inplace=True
1 2 3 4 5 6 7 8 In [73]: data.rename(index={'OHIO': 'INDIANA'}, inplace=True) In [74]: data Out[74]: one two three four INDIANA 0 1 2 3 COLO 4 5 6 7 NEW 8 9 10 11
检测和过滤异常值
一个含有正态分布数据的DataFrame
1 2 3 4 5 6 7 8 9 10 11 12 13 In [92]: data = pd.DataFrame(np.random.randn(1000, 4)) In [93]: data.describe() Out[93]: 0 1 2 3 count 1000.000000 1000.000000 1000.000000 1000.000000 mean 0.049091 0.026112 -0.002544 -0.051827 std 0.996947 1.007458 0.995232 0.998311 min -3.645860 -3.184377 -3.745356 -3.428254 25% -0.599807 -0.612162 -0.687373 -0.747478 50% 0.047101 -0.013609 -0.022158 -0.088274 75% 0.756646 0.695298 0.699046 0.623331 max 2.653656 3.525865 2.735527 3.366626
在列中找指定范围的值
找出某列中绝对值大小超过3的值
1 2 3 4 5 6 7 In [94]: col = data[2] In [95]: col[np.abs(col) > 3] Out[95]: 41 -3.399312 136 -3.745356 Name: 2, dtype: float64
挑选指定行,any
要选出全部含有“超过3或-3的值”的行,你可以在布尔型DataFrame中使用any方法
1 2 3 4 5 6 7 8 9 10 11 12 13 In [96]: data[(np.abs(data) > 3).any(1)] Out[96]: 0 1 2 3 41 0.457246 -0.025907 -3.399312 -0.974657 60 1.951312 3.260383 0.963301 1.201206 136 0.508391 -0.196713 -3.745356 -1.520113 235 -0.242459 -3.056990 1.918403 -0.578828 258 0.682841 0.326045 0.425384 -3.428254 322 1.179227 -3.184377 1.369891 -1.074833 544 -3.548824 1.553205 -2.186301 1.277104 635 -0.578093 0.193299 1.397822 3.366626 782 -0.207434 3.525865 0.283070 0.544635 803 -3.645860 0.255475 -0.549574 -1.907459
any(1)表示每一行,any(0)表示每一列
限制值的范围
1 2 3 4 5 6 7 8 9 10 11 12 13 In [97]: data[np.abs(data) > 3] = np.sign(data) * 3 In [98]: data.describe() Out[98]: 0 1 2 3 count 1000.000000 1000.000000 1000.000000 1000.000000 mean 0.050286 0.025567 -0.001399 -0.051765 std 0.992920 1.004214 0.991414 0.995761 min -3.000000 -3.000000 -3.000000 -3.000000 25% -0.599807 -0.612162 -0.687373 -0.747478 50% 0.047101 -0.013609 -0.022158 -0.088274 75% 0.756646 0.695298 0.699046 0.623331 max 2.653656 3.000000 2.735527 3.000000
根据数据的值是正还是负,np.sign(data)可以生成1和-1:
1 2 3 4 5 6 7 8 In [99]: np.sign(data).head() Out[99]: 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
计算指标/哑变量
get_dummies
实现独热编码
独热编码就是用来表示当前这个数据点属于什么类,如果是这个类对应位置就是1,不是这个类对应位置就是0.
下面这个例子是对key这一列进行独热编码。以第0行为例。第0行是b,所以在独热矩阵中第0行,b对应的列是1,a和c对应的列是0,表示第一行表示的数是b
加前缀
1 2 3 4 5 6 7 8 9 10 11 In [112]: df_with_dummy = df[['data1']].join(dummies) In [113]: df_with_dummy Out[113]: data1 key_a key_b key_c 0 0 0 1 0 1 1 0 1 0 2 2 1 0 0 3 3 0 0 1 4 4 1 0 0 5 5 0 1 0
MovieLens 1M数据集:
读取
利用read_table,从路径下读取文件,其中以"::"作为分隔符,列名不用原来的列名,而用之前规定好的列名
从数据集中抽取出不同的genre值
1 2 3 4 5 6 In [117 ]: all_genres = [] In [118 ]: for x in movies.genres: .....: all_genres.extend(x.split('|' )) In [119 ]: genres = pd.unique(all_genres)
1 2 3 4 5 6 In [120]: genres Out[120]: array(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy', 'Romance', 'Drama', 'Action', 'Crime', 'Thriller','Horror', 'Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir', 'Western'], dtype=object)
构建指标DataFrame
首先从全零开始
全零矩阵的行是之前读取的电影部数,列是之前抽取的genre值
1 2 3 zero_matrix = np.zeros((len(movies), len(genres))) dummies = pd.DataFrame(zero_matrix, columns=genres) dummies