7.1处理缺失值
对于数值型数据,pandas使用浮点数NaN(not a number 来表示缺失值)。我们称NaN为容易检测到的缺失值:
import numpy as np
import pandas as pd
from pandas import Series,DataFrame
string_data = pd.Series(['aardvark','artichoke',np.nan,'avocado'])
string_data.isnull()
0 False
1 False
2 True
3 False
dtype: bool
在pandas中我们采用了R语言中的编程惯例,将缺失值成为NA,意思是not available(不可用)。
Python内建的None值在对象数组中也被当作NA处理:
string_data[0] = None
string_data.isnull()
0 True
1 False
2 True
3 False
dtype: bool
过滤缺失值
虽然你可以使用pandas.isnull和布尔值索引手动地过滤缺失值,但dropna在过滤缺失值时是非常有用的。
在Series上使用dropna,它会返回Series中所有的非空数据及其索引值。
from numpy import nan as NA
data = pd.Series([1,NA,3.5,NA,7])
data.dropna()
0 1.0
2 3.5
4 7.0
dtype: float64
这等价于
data[data.notnull()]
当处理DataFrame对象时,你可能想要删除全部为NA或包含有NA的行或列
dropna默认情况下会删除包含缺失值的行。
传入how = 'all'时,将删除所有值均为NA的行。(此时默认axis = 0)
传入参数axis = 1和how= 'all',将删除所有值均为NA的列。
如果想保留包含一定数量观察值的行,用thresh参数:df.dropna(thresh = 2)向上保留两行
补全缺失值
大多数情况下主要使用fillna方法来补全缺失值,调用它时可以使用一个是 常数 来替代缺失值。
在调用fillna时使用字典,你可以为不同 列 设定不同的 填充值。
fillna返回的是一个新的对象,但你也可以修改已经存在的对象,inplace 修改被调用的对象,而不是生成一个备份 df.fillna(0,inplace = True)
用于重建索引的相同的插值方法也可以用于fillna,method:插值方法,如果没有其他参数,默认是‘ffill’,limit:用于前向或后向填充时最大的填充范围df.fillna(method = 'ffill',limit = 2)
你也可以将Series的平均值或中位数用于填充缺失值:Series.fillna(Series.mean())
还有一种比较常用数学上的朗格朗日插值法以及牛顿插值法,见博客。
7.2数据转换
由于各种原因,DataFrame中会出现重复行。
DataFrame的duplicated方法返回的是一个布尔值Series,这个Series反映的是每行是否存在 重复 情况。
drop_duplicates返回的是DataFrame,内容是duplicated返回数组中为False的部分(相当于去除重复行后的部分)。
data = pd.DataFrame({'k1':['one','two']*3 + ['two'],'k2':[1,1,2,3,3,4,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
这些方法默认都是对 列 进行操作。你可以指定数据的任何子集来检测是否有重复。假设我们有一个额外的列,并想基于‘k1’列去除重复值:data.drop_duplicates(['k1'])
duplicated 和 drop_duplicates默认都是保留第一个观测到的值,传入参数keep = ‘last’将会返回最后一个 :data.drop_duplicates(['k1','k2'],keep = 'last')
使用 函数 或 映射 进行数据转换
对于许多数据集,你可能希望基于DataFrame中的数组、列或列中的数值进行一些转换。
data = pd.DataFrame({'food':['bacon','pulled pork','bacon', 'Pastrami','corned beef','Bacon',
'pastrami','honey ham','nova lox'], 'oneces':[4,3,12,6,7.5,8,3,5,6]})
meat_to_animal = {'bacon':'pig', 'pulled pork':'pig', 'pastrami':'cow', 'corned beef':'pig',
'honey ham':'pig', 'nova lox':'salmon'}
lowercased = data['food'].str.lower()
data['animal'] = lowercased.map(meat_to_animal)
data
food oneces 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 pig
5 Bacon 8.0 pig
6 pastrami 3.0 cow
7 honey ham 5.0 pig
8 nova lox 6.0 salmon
Series 的map方法接收一个函数或包含映射关系的字典型对象,可以传入一个能够完成所有工作的函数:
data['food'].map(lambda x: meat_to_animal[x.lower()])
0 pig
1 pig
2 pig
3 cow
4 pig
5 pig
6 cow
7 pig
8 salmon
Name: food, dtype: object
替代值
使用fillna填充缺失值是通用值替换的特殊案例。
map 可以用来修改一个对象中的子集的值,但是replace提供了更为简单灵活的实现
-999可能是缺失值的标识。如果要使用NA来替代,可以使用replace方法生成新的Series(除非你传入了inplace= True
data = pd.Series([1.,-999,2.,-999,-1000.,3.])
data.replace(-999,np.nan)
0 1.0
1 NaN
2 2.0
3 NaN
4 -1000.0
5 3.0
dtype: float64
如果你想一次替代多个值,传入一个列表和一个替代值:data.replace([-999,-1000],np.nan)
要将不同的值替换成对应不同的值,传入替代值的列data.replace([-999,-1000],[np.nan,0])
参数也可以通过字典传递:data.replace({-999:np.nan,-1000:0})
data.replace 方法与data.str.replace 方法不同的,data.str.replace是对 字符串 按元素替代
重命名轴索引
和Series一样,你可以通过函数或某种形式的映射对 轴标签 进行类似的转换,生成新的且带有不同标签的对象。你也可以在不生成新的的数据结构的情况下修改轴。
与Series类似,轴索引也有一个map方法,transform = lambda x: x[:4].upper(),然后赋值给index,修改DataFrame data.index = data.index.map(transform)
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
transform = lambda x: x[:4].upper()
data.index.map(transform)
Index(['OHIO', 'COLO', 'NEW '], dtype='object')
#也可以赋值给index,修改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)
rename 可以结合字典型对象使用,为轴标签的子集提供新的值(修改轴标签)
data.rename(index = {'OHIO':'INDIADA'},columns = {'three':'peekaboo'})
如果你想修改原有的数据集,传入inplace = True
data.rename(index = {'OHIO':'INDIADA'},inplace = True)
离散化和分箱
假设你有一组人群的数据,你想将他们分组,放入离散的年龄框中cats = pd.cut(ages,bins)
#将这些年龄分为18-25,26-35,36-60,以及61以上等若干组。你可以使用pandas中的cut:
#(区间左开右闭)
ages = [20,22,25,27,21,23,37,31,61,45,41,32]
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, interval[int64]): [(18, 25] <(25, 35] <(35, 60] <(60, 100]]
你看到的输出描述了由pandas.cut计算出的箱。你可以把它当做一个表示箱名的字符串数组&#xff1b;它在内部包含一个categories&#xff08;类别&#xff09;数组&#xff0c;他制定了不同的类别名称以及codes属性中的categories类别&#xff1a;
cats.codes
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype&#61;int8)
cats.categories
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]],
closed&#61;&#39;right&#39;,
dtype&#61;&#39;interval[int64]&#39;)
#value_counts是对箱数量的计数
pd.value_counts(cats)
(18, 25] 5
(35, 60] 3
(25, 35] 3
(60, 100] 1
dtype: int64
与数学区间的符号一样&#xff0c;区间默认左开右闭。你可以通过传递right &#61; False来改变那一边是封闭的&#xff1a;pd.cut(ages,[18,26,36,61,100],right &#61; False)
你也可以通过向 labels 选项传递一个列表或数组来传入自定义的箱名&#xff1a;
如果你根据cut 整数个的箱来代替显式的箱边&#xff0c;pandas将根据数组中的最大值和最小值计算出等长的箱。pd.cut(data,4,precision&#61;2)# 将十进制精度限制在两位。
qcut 函数&#xff0c;基于样本分位数进行分箱。你可以使用 qcut 获得等长&#xff08;箱内数据量相等&#xff09;的箱。
data &#61; np.random.randn(1000)#正态分布
cats &#61; pd.qcut(data,4)#切成4份
cats
[(0.018, 0.679], (0.018, 0.679], (0.679, 2.951], (-0.684, 0.018], (-2.96, -0.684], ..., (-0.684, 0.018], (-0.684, 0.018], (-2.96, -0.684], (0.018, 0.679], (-2.96, -0.684]]
Length: 1000
Categories (4, interval[float64]): [(-2.96, -0.684] <(-0.684, 0.018] <(0.018, 0.679] <(0.679, 2.951]]
pd.value_counts(cats)
(0.679, 2.951] 250
(0.018, 0.679] 250
(-0.684, 0.018] 250
(-2.96, -0.684] 250
dtype: int64
与cut类似&#xff0c;你可以传入自定义的分位数&#xff08;0合1之间的数据&#xff0c;包括边&#xff09;pd.qcut(data,[0,0.1,0.5,0.9,1.])
检测和过滤异常值
假设你想找出一列中绝对值大于三的值 col &#61; data[2] col[np.abs(col)>3]
要选出所有大于3小于-3的行&#xff0c;你可以对布尔值DataFrame使用any方法
data[(np.abs(data) > 3).any(1) ]
这里eny()用于判断给定的iterable可迭代参数是否全部为False&#xff0c;只要有一个为True&#xff0c;则返回True。&#xff08;这样使布尔序列的索引&#xff0c;与原索引对象的索引匹配。&#xff09;
将绝对值大于三的值根据其正负转换成3或-3 data[np.abs(data)>3] &#61; np.sign(data)*3
置换和随机抽样
使用numpy.random.permutation 对DataFrame中的Series或行进行置换&#xff08;随机重排序&#xff09;是非常方便的。
在调用permutation时根据你想要的轴长度可以产生一个表示新顺序的整数数组&#xff1a;
整数数组可以用在基于iloc的索引和等价的take函数中&#xff1a;
df &#61; pd.DataFrame(np.arange(5*4).reshape((5,4)))
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 &#61; np.random.permutation(5)#array([0, 1, 3, 4, 2])
df.take(sampler)
0 1 2 3
0 0 1 2 3
1 4 5 6 7
3 12 13 14 15
4 16 17 18 19
2 8 9 10 11
df.iloc[sampler]
0 1 2 3
0 0 1 2 3
1 4 5 6 7
3 12 13 14 15
4 16 17 18 19
2 8 9 10 11
要选出一个不含有替代值的随机子集&#xff0c;可以使用Series和DataFrame中的sample方法
df.sample(n&#61;3)
0 1 2 3
1 4 5 6 7
0 0 1 2 3
3 12 13 14 15
要生成一个带有替代值的样本&#xff08;允许有重复选择&#xff09;&#xff0c;将replace &#61; True传入sample方法&#xff1a;
draws &#61; choices.sample(n&#61;10,replace &#61; True)
choices &#61; pd.Series([5,7,-1,6,4])
draws &#61; choices.sample(n&#61;10,replace &#61; True)
4 4
0 5
1 7
3 6
1 7
4 4
1 7
4 4
0 5
4 4
dtype: int64
计算指标/虚拟变量
如果DataFrame中的一列有k个不同的值&#xff0c;则可以衍生一个k列的值为1和0的矩阵或DataFrame。
pandas有一个get_dummies 函数用于实现该功能&#xff0c;pd.get_dummies(df[&#39;key&#39;])
df &#61; pd.DataFrame({&#39;key&#39;:[&#39;b&#39;,&#39;b&#39;,&#39;a&#39;,&#39;c&#39;,&#39;a&#39;,&#39;b&#39;],
&#39;data&#39;:range(6)}
key data
0 b 0
1 b 1
2 a 2
3 c 3
4 a 4
5 b 5
pd.get_dummies(df[&#39;key&#39;])
a b c
0 0 1 0
1 0 1 0
2 1 0 0
3 0 0 1
4 1 0 0
5 0 1 0
pd.get_dummies(df[&#39;data&#39;])
0 1 2 3 4 5
0 1 0 0 0 0 0
1 0 1 0 0 0 0
2 0 0 1 0 0 0
3 0 0 0 1 0 0
4 0 0 0 0 1 0
5 0 0 0 0 0 1
#你可能想在指标DataFrame的列上后加入前缀&#xff0c;然后与其他数据合并。在get_dummies方法中有一个前缀参数prefix用于实现该功能&#xff1a;
pd.get_dummies(df[&#39;key&#39;],prefix &#61; &#39;key&#39;)
df_with_dummy &#61; df[[&#39;data&#39;]].join(dummies)
data 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
这里的join函数前面的索引项是一个列表&#xff0c;这样导出的是一个DataFrame&#xff0c;否则生成Series&#xff0c;与join函数大小不一致&#xff0c;会报错。
&#xff08;!!!重点:书中205页案例&#xff09;
字符串对象方法
一个逗号分隔的字符串可以使用split方法拆分成多块 val &#61;&#39;a,b, guide&#39; val.split(&#39;,&#39;)
split常和strip一起使用&#xff0c;用于清除空格&#xff08;包括换行&#xff09; pieces &#61; [x.strip() for x in val.split(&#39;,&#39;)]
这些字符串可以使用加法与两个冒号分隔符连接在一起&#xff1a;first,second,third &#61; pieces
first &#43; &#39;::&#39; &#43; second&#43; &#39;::&#39; &#43; third ---- &#39;a::b::guide&#39;
在字符串‘&#xff1a;&#xff1a;’的join方法中传入一个列表或元组是一种更快的pythonic&#xff08;python风格化&#xff09;的方法 &#39;::&#39;.join(pieces)
使用和关键字in 是检验子字符串的最佳方法&#xff0c;index和find也能实现同样的功能
请注意find和index 的区别&#xff1a;index在字符串没有找到时抛出一个异常&#xff0c;而find返回的是-1 &#39;guide&#39; in val val.index(&#39;,&#39;) val.find(&#39;:&#39;)
count返回的是某个特定的子字符串在字符串中出现的次数 val.count(&#39;,&#39;)
replace 将用另一种模式替代一种模式。也可用于传入空字符来删除某个模式val.replace(&#39;,&#39;,&#39;::&#39;) val.replace(&#39;,&#39;,&#39;&#39;)
正则表达式
一种在文本中灵活查找或匹配字符串模式的方法。Python内建的re 模块适用于将正则表达式应用到字符串上的库。
re模块有三的主题&#xff1a;模式匹配、替代、拆分
描述一个或多个空白字符的正则表达式是 \s&#43;
你可以使用re.compile自行编译&#xff0c;形成一个可复用的正则表达式对象&#xff1a;re.compile(&#39;\s&#43;&#39;)
re.IGNORECASE使正则表达式不区分大小写 re.compile(pattern,flags &#61; re.IGNORECASE)
如果想获得 一个所有匹配正则表达式的列表&#xff0c;使用findall方法 regex.findall(text)
search匹配对象只能返回 模式在字符串中起始和结束的位置
match如果在起始位置没有匹配到&#xff0c;则返回None
sub会返回 一个新的字符串&#xff0c;原字符串中的模式会被新的字符串替代&#xff1a;
pandas中的向量化字符串函数
部分向量字符串方法列表