python数据清洗与准备

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计算出的箱。你可以把它当做一个表示箱名的字符串数组;它在内部包含一个categories(类别)数组,他制定了不同的类别名称以及codes属性中的categories类别:

cats.codes
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
cats.categories
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]],
              closed='right',
              dtype='interval[int64]')
#value_counts是对箱数量的计数
pd.value_counts(cats)
(18, 25]     5
(35, 60]     3
(25, 35]     3
(60, 100]    1
dtype: int64

与数学区间的符号一样,区间默认左开右闭。你可以通过传递right = False来改变那一边是封闭的:pd.cut(ages,[18,26,36,61,100],right = False)
你也可以通过向 labels 选项传递一个列表或数组来传入自定义的箱名:
如果你根据cut 整数个的箱来代替显式的箱边,pandas将根据数组中的最大值和最小值计算出等长的箱。pd.cut(data,4,precision=2)# 将十进制精度限制在两位。
qcut 函数,基于样本分位数进行分箱。你可以使用 qcut 获得等长(箱内数据量相等)的箱。

data = np.random.randn(1000)#正态分布
cats = 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类似,你可以传入自定义的分位数(0合1之间的数据,包括边)pd.qcut(data,[0,0.1,0.5,0.9,1.])

检测和过滤异常值

假设你想找出一列中绝对值大于三的值 col = data[2] col[np.abs(col)>3]
要选出所有大于3小于-3的行,你可以对布尔值DataFrame使用any方法

data[(np.abs(data) > 3).any(1) ]

这里eny()用于判断给定的iterable可迭代参数是否全部为False,只要有一个为True,则返回True。(这样使布尔序列的索引,与原索引对象的索引匹配。)
将绝对值大于三的值根据其正负转换成3或-3 data[np.abs(data)>3] = np.sign(data)*3

置换和随机抽样

使用numpy.random.permutation 对DataFrame中的Series或行进行置换(随机重排序)是非常方便的。
在调用permutation时根据你想要的轴长度可以产生一个表示新顺序的整数数组:

整数数组可以用在基于iloc的索引和等价的take函数中:

df = 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 = 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

要选出一个不含有替代值的随机子集,可以使用Series和DataFrame中的sample方法

df.sample(n=3)
    0   1   2   3
1   4   5   6   7
0   0   1   2   3
3   12  13  14  15

要生成一个带有替代值的样本(允许有重复选择),将replace = True传入sample方法:
draws = choices.sample(n=10,replace = True)

choices = pd.Series([5,7,-1,6,4])
draws = choices.sample(n=10,replace = 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个不同的值,则可以衍生一个k列的值为1和0的矩阵或DataFrame。
pandas有一个get_dummies 函数用于实现该功能,pd.get_dummies(df['key'])

df = pd.DataFrame({'key':['b','b','a','c','a','b'],
                  'data':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['key'])
    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['data'])

    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的列上后加入前缀,然后与其他数据合并。在get_dummies方法中有一个前缀参数prefix用于实现该功能:
pd.get_dummies(df['key'],prefix = 'key')
df_with_dummy = df[['data']].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函数前面的索引项是一个列表,这样导出的是一个DataFrame,否则生成Series,与join函数大小不一致,会报错。
(!!!重点:书中205页案例)

字符串对象方法

一个逗号分隔的字符串可以使用split方法拆分成多块 val ='a,b, guide' val.split(',')
split常和strip一起使用,用于清除空格(包括换行) pieces = [x.strip() for x in val.split(',')]
这些字符串可以使用加法与两个冒号分隔符连接在一起:first,second,third = pieces
first + '::' + second+ '::' + third ---- 'a::b::guide'
在字符串‘::’的join方法中传入一个列表或元组是一种更快的pythonic(python风格化)的方法 '::'.join(pieces)
使用和关键字in 是检验子字符串的最佳方法,index和find也能实现同样的功能
请注意find和index 的区别:index在字符串没有找到时抛出一个异常,而find返回的是-1 'guide' in val val.index(',') val.find(':')
count返回的是某个特定的子字符串在字符串中出现的次数 val.count(',')
replace 将用另一种模式替代一种模式。也可用于传入空字符来删除某个模式val.replace(',','::') val.replace(',','')

正则表达式

一种在文本中灵活查找或匹配字符串模式的方法。Python内建的re 模块适用于将正则表达式应用到字符串上的库。
re模块有三的主题:模式匹配、替代、拆分
描述一个或多个空白字符的正则表达式是 \s+
你可以使用re.compile自行编译,形成一个可复用的正则表达式对象:re.compile('\s+')
re.IGNORECASE使正则表达式不区分大小写 re.compile(pattern,flags = re.IGNORECASE)
如果想获得 一个所有匹配正则表达式的列表,使用findall方法 regex.findall(text)
search匹配对象只能返回 模式在字符串中起始和结束的位置
match如果在起始位置没有匹配到,则返回None
sub会返回 一个新的字符串,原字符串中的模式会被新的字符串替代:

pandas中的向量化字符串函数

部分向量字符串方法列表

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,841评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,415评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,904评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,051评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,055评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,255评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,729评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,377评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,517评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,420评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,467评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,144评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,735评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,812评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,029评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,528评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,126评论 2 341

推荐阅读更多精彩内容