共计 3971 个字符,预计需要花费 10 分钟才能阅读完成。
缺失数据的处理
1. 缺失数据的表现
对于一些数据,可能某些值是空的,是缺失的。pandas 中可以有多种处理缺失数据的方式。在 pandas 中,缺失数据的表现是 NaN(Not a Number)
,可以使用isnull()
函数检测出是否有数据缺失。
In [4]: data = pd.Series(['aaa','bbb',np.nan,'ddd'])
In [5]: data
Out[5]:
0 aaa
1 bbb
2 NaN
3 ddd
dtype: object
In [6]: data.isnull()
Out[6]:
0 False
1 False
2 True
3 False
dtype: bool
isnull 返回一个布尔型的对象,这些布尔值表示哪些值是缺失值 NaN,该对象返回的类型和原类型一样。
处理缺失数据的方式有多种,常见为删除缺失数据与填充缺失数据。
2. 丢弃缺失数据
pandas 的 dropna 方法可以有效的删除掉缺失数据,对于 Series 来说,dropna 方法返回一个仅含非空数据和索引值的 Series:
In [7]: data
Out[7]:
0 aaa
1 bbb
2 NaN
3 ddd
dtype: object
In [8]: data.dropna()
Out[8]:
0 aaa
1 bbb
3 ddd
dtype: object
对于 DataFrame,dropna 可以删除所有含有空值的行或列,也可以根据个人删除指定的行或列,dropna 默认会删除所有含有 NaN 的行:
In [17]: df
Out[17]:
name age area
0 aa 18 北京
1 NaN 18 广州
2 cc 20 NaN
In [18]: df.dropna()
Out[18]:
name age area
0 aa 18 北京
传入 how=’all’,那么 dropna 只会删除一行全是缺失值的行:
In [19]: df.loc[3]=[np.nan,np.nan,np.nan]
In [20]: df
Out[20]:
name age area
0 aa 18.0 北京
1 NaN 18.0 广州
2 cc 20.0 NaN
3 NaN NaN NaN
In [21]: df.dropna(how='all')
Out[21]:
name age area
0 aa 18.0 北京
1 NaN 18.0 广州
2 cc 20.0 NaN
如果要删除有缺失值的一列,只需要传入 axis=1:
In [25]: df['avg'] = [20,30,40,50]
In [26]: df
Out[26]:
name age area avg
0 aa 18.0 北京 20
1 NaN 18.0 广州 30
2 cc 20.0 NaN 40
3 NaN NaN NaN 50
In [27]: df.dropna(axis=1)
Out[27]:
avg
0 20
1 30
2 40
3 50
同样,dropna(axis=1,how='all')
只会删除整列都是缺失值的那一列。
如果想留下部分含有缺失值的数据作为观察,或者说只想删除部分数据,那么就需要使用 thresh 参数了,保留至少有 n 个非 NaN 数据的行 / 列:
In [52]: df
Out[52]:
name age area avg
0 aa 18.0 北京 20 # 这一行 4 个值都不是 NaN 值,非 NaN 值为 4
1 NaN 18.0 广州 30 # 有 1 个 NaN 值, 非 NaN 值为 3
2 cc 20.0 NaN 40 # 有 1 个 NaN 值,非 NaN 值为 3
3 NaN NaN NaN 50 # 有 3 个 NaN 值,非 NaN 值为 1
In [53]: df.dropna(thresh=4) # 选取非空值至少有 4 个的行
Out[53]:
name age area avg
0 aa 18.0 北京 20
In [54]: df.dropna(thresh=2)
Out[54]:
name age area avg
0 aa 18.0 北京 20
1 NaN 18.0 广州 30
2 cc 20.0 NaN 40
In [55]: df.dropna(thresh=4,axis=1) # 选取非空值至少有 4 个的列
Out[55]:
avg
0 20
1 30
2 40
3 50
3. 填充缺失数据
对于缺失数据不想丢弃而想填充新的数据,fillna()
方法也许是首要选择。通过一个常数调用 fillna 就会将缺失值替换为那个常数值:
In [62]: df
Out[62]:
name age area avg
0 aa 18.0 北京 20
1 NaN 18.0 广州 30
2 cc 20.0 NaN 40
3 NaN NaN NaN 50
In [64]: df.fillna(0)
Out[64]:
name age area avg
0 aa 18.0 北京 20
1 0 18.0 广州 30
2 cc 20.0 0 40
3 0 0.0 0 50
若是通过一个字典调用 fillna,就可以实现对不同的列填充不同的值:
In [61]: df.fillna({'name':'dd','age':22,'area':'深圳'})
Out[61]:
name age area avg
0 aa 18.0 北京 20
1 dd 18.0 广州 30
2 cc 20.0 深圳 40
3 dd 22.0 深圳 50
fillna 默认会返回新对象,但也可以对现有对象进行就地修改:
In [65]: df.fillna(0,inplace=True)
In [66]: df
Out[66]:
name age area avg
0 aa 18.0 北京 20
1 0 18.0 广州 30
2 cc 20.0 0 40
3 0 0.0 0 50
数据去重
数据中也有可能出现多行数据重复的情况,DataFrame 的 duplicated 方法返回一个布尔型的对象,表示各行是否是重复的行,它每次都会对比前面出现的行,如果出现与前面出现的行一样的行,那么这一行就是重复行:
In [68]: data
Out[68]:
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
6 two 4 # 这一行与前一行重复,由于这一行在第 5 行后,所以会被判断为重复行
In [69]: data.duplicated()
Out[69]:
0 False
1 False
2 False
3 False
4 False
5 False
6 True
dtype: bool
而drop_duplicates 方法
,它会返回一个 DataFrame,重复的数组会标为 False 并且删除:
In [70]: data.drop_duplicates()
Out[70]:
k1 k2
0 one 1
1 two 1
2 one 2
3 two 3
4 one 3
5 two 4
上面两个方法默认会判断全部列,如果只想根据一列进行重复值判断,可以传入包含列索引的列表:
In [75]: data
Out[75]:
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
In [76]: data.drop_duplicates(['k1'])
Out[76]:
k1 k2 v1
0 one 1 0
1 two 1 1
duplicated 和 drop_duplicates 默认保留的是第一个出现的值组合。传入 keep=’last’ 则保留最后一个:
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
数据替换
有的时候需求是将数据框的某个值替换掉,pandas 提供了 replace 方法,注意它与字符串的 replace 方法不一样。
pandas 的 replace 方法操作的对象是 DataFrame 的值。
In [83]: data
Out[83]:
age name
0 -20 小明
1 20 老王
2 23 苹果
In [84]: data.replace(-20,0) # 将 -20 替换为 0
Out[84]:
age name
0 0 小明
1 20 老王
2 23 苹果
如果要让替换值有不同的值,要传入一个列表
In [88]: data.replace(['小明','老王'],['猕猴桃','西瓜'])
Out[88]:
age name
0 -20 猕猴桃
1 20 西瓜
2 23 苹果
替换多个值:
In [90]: data.replace(['小明','老王'],'苹果')
Out[90]:
age name
0 -20 苹果
1 20 苹果
2 23 苹果
数据文本函数
假如有如下数据:
In [94]: data
Out[94]:
position avg brief
0 Java 开发 10 ['开发','工程师','高新']
1 数据分析师 13 ['分析师','大数据']
2 算法工程师 15 ['算法','数学']
需求是在 avg 的列的值加上字母 k,去掉 brief 列的值的方括号和单引号。pandas 中有很多种方法可以实现,下面介绍一种简单的方法。
+ k 需求:
In [98]: data.avg
Out[98]:
0 10
1 13
2 15
Name: avg, dtype: int64
In [99]: data.avg.astype('str')+'k'
Out[99]:
0 10k
1 13k
2 15k
Name: avg, dtype: object
In [100]: data['avg'] = data.avg.astype('str')+'k'
In [101]: data
Out[101]:
position avg brief
0 Java 开发 10k ['开发','工程师','高新']
1 数据分析师 13k ['分析师','大数据']
2 算法工程师 15k ['算法','数学']
去除方括号和去除单引号需求:
In [104]: data.brief
Out[104]:
0 ['开发','工程师','高新']
1 ['分析师','大数据']
2 ['算法','数学']
Name: brief, dtype: object
In [105]: data.brief.str[1:-1]
Out[105]:
0 '开发','工程师','高新'
1 '分析师','大数据'
2 '算法','数学'
Name: brief, dtype: object
In [106]: data.brief.str[1:-1].str.replace("'","")
Out[106]:
0 开发, 工程师, 高新
1 分析师, 大数据
2 算法, 数学
Name: brief, dtype: object