数据的过滤、清理和其他转换工作也是数据规整化的一大类操作。
一、移除重复数据
data=DataFrame({'k1':['one']*3+['two']*4,'k2':[1,1,2,3,3,4,4]})
#duplicated方法返回的是一个布尔型Series,表示各行是否是重复行,且默认第一个出现值为True
d1=data.duplicated()
#drop_duplicates方法移除重复行,默认保留第一个出现的值
d2=data.drop_duplicates()
#保留最后一个出现的值
d3=data.drop_duplicates(keep='last')
#指定部分列进行重复项判断
d4=data.drop_duplicates(['k1'])
"""
data: d1: d2: d3: d4:
k1 k2 k1 k2 k1 k2 k1 k2
0 one 1 0 False 0 one 1 1 one 1 0 one 1
1 one 1 1 True 2 one 2 2 one 2 3 two 3
2 one 2 2 False 3 two 3 4 two 3
3 two 3 3 False 5 two 4 6 two 4
4 two 3 4 True
5 two 4 5 False
6 two 4 6 True
"""
二、利用函数或映射进行数据转换
在对数据集进行转换时,利用map函数可以对数组、Series或DataFrame列中的值来实现元素级转换以及其他数据的清理工作。
#map函数
data=DataFrame({'names':['Bob','Jane','Tom','Ann'],'ages':[22,25,26,20]})
#假设这四个人都是18岁入职的,现计算他们的工作年限years
data['years']=data['ages'].map(lambda x:x-18)
"""
data: map映射后:
ages names ages names years
0 22 Bob 0 22 Bob 4
1 25 Jane 1 25 Jane 7
2 26 Tom 2 26 Tom 8
3 20 Ann 3 20 Ann 2
"""
三、替换值
利用fillna方法填充缺失数据可以看做值替换的一种特殊情况。map函数可以用于修改对象的数据子集,而replace则提供了一种实现该功能的更简单、更灵活的方式。
#replace函数
data=Series([1,-999,2,-1000,3,-999])
#第一个参数是to_place(被换的值),第二个参数是value(换后的值)
d1=data.replace(-999,np.nan)
#一次性换多个值,传入一个由待替换组成的列表
d2=data.replace([-999,-1000],np.nan)
#对不同的值进行不同的替换,可以传入列表
d3=data.replace([-999,-1000],[np.nan,0])
#对不同的值进行不同的替换,还可以传入字典
d4=data.replace({-999:np.nan,-1000:0})
"""
data: d1: d2: d3/d4:
0 1 0 1.0 0 1.0 0 1.0
1 -999 1 NaN 1 NaN 1 NaN
2 2 2 2.0 2 2.0 2 2.0
3 -1000 3 -1000.0 3 NaN 3 0.0
4 3 4 3.0 4 3.0 4 3.0
5 -999 5 NaN 5 NaN 5 NaN
dtype: int64 dtype: float64 dtype: float64 dtype: float64
"""
四、离散化和面元划分
为了便于分析,连续数据常常被离散化或拆成”面元“(bin)。例如,有一组人员数据,而你希望将他们划分为不同的年龄组:你可以使用pandas的cut函数。
#cut函数
ages=[24,19,33,45,56,48,46,55,20,24,23,43,62]
#你想将该数据划分为18-25,26-35,36-60,60以上的几个面元(左不包括,右包括,即(])
bins=[18,25,35,60,100]
#cut函数默认左不包括,右包括,即左开右闭,可以设置right=True,使得左包括
cats=pd.cut(ages,bins)
"""返回的是category 对象
cats=[(18, 25], (18, 25], (25, 35], (35, 60], (35, 60], ..., (18, 25], (18, 25], (18, 25], (35, 60], (60, 100]]
Length: 13
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
"""
#为年龄数组进行标号的codes属性
cats.codes #array([0, 0, 1, ..., 0, 2, 3], dtype=int8)
#显示不同分类名称
cats.categories #IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]])
#
pd.value_counts(cats)
"""
(35, 60] 6
(18, 25] 5
(60, 100] 1
(25, 35] 1
dtype: int64
"""
五、检测和过滤异常值
异常值(又称孤立值或离群值)的过滤或变换运算在很大程度上其实就是数组运算。
#异常值过滤和检测
data=DataFrame(np.random.randn(1000,4))
data.describe()
"""data.describe():
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean 0.021462 0.028700 -0.048713 0.021413
std 0.967959 1.027700 0.993056 0.991414
min -2.750967 -3.856974 -2.777925 -3.652881
25% -0.625540 -0.694761 -0.771328 -0.645742
50% 0.017576 0.007304 -0.048511 -0.010639
75% 0.690868 0.727350 0.616060 0.726299
max 3.065890 3.288369 2.852040 2.706435
"""
#若找出第三列绝对值大于3的值
data[3][np.abs(data[3])>3] #result:158 -3.652881
#找出含有绝对值大于3的值的所有行,利用DataFrame和any
data[(np.abs(data)>3).any(1)]
"""
0 1 2 3
38 3.065890 -0.739700 -1.112838 -0.329292
158 -0.412867 0.670742 0.808559 -3.652881
340 -0.056876 3.096786 -1.502054 0.763512
366 -1.018817 -3.856974 -1.069169 -0.055118
434 -2.116043 3.288369 -0.781302 -1.276858
628 0.317649 -3.416358 0.912362 0.525939
648 -0.440840 -3.572644 -0.591399 -1.177463
"""
#将值限制在[-3,3]之间
#np.sign函数返回的是1和-1的数组,代表原始值的符号
data[(np.abs(data)>3)]=np.sign(data)*3
data.describe()
"""
result:
0 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean 0.021396 0.030161 -0.048713 0.022066
std 0.967754 1.020466 0.993056 0.989204
min -2.750967 -3.000000 -2.777925 -3.000000
25% -0.625540 -0.694761 -0.771328 -0.645742
50% 0.017576 0.007304 -0.048511 -0.010639
75% 0.690868 0.727350 0.616060 0.726299
max 3.000000 3.000000 2.852040 2.706435
"""
六、排序和随机采样
利用numpy.random.permutation函数可以提轻松实现对Series或DataFrame的列的排序工作。通过需要排列的轴的长度调用permutation,可产生一个新的顺序的整数数组:
#np.random.permutation(len(df)) #result:array([0, 1, 4, 2, 3])
#np.random.permutation(len(df))[:3] #result:array([0, 1, 4])
bags=np.array([5,7,-1,6,4])
sampler=np.random.randint(0,len(bags),size=10)
#sampler=array([2, 2, 1, 1, 3, 1, 3, 0, 2, 1])
#bags和sampler之间的关系:bags=[5,7,-1,6,4]的下标是[0,1,2,3,4];samplers中数代表的是bags的下标
draws=bags.take(sampler)
#draws=array([-1, -1, 7, 7, 6, 7, 6, 5, -1, 7])
七、计算指标/哑变量
另一种常用于统计建模或机器学习的转换方式是:将分类变量(categorical variable)转换为”哑变量矩阵“(dummpy matrix)或”指标矩阵“(indicator matrix)。如果DataFrame的某一列中含有k个不同的值,则可以派生出一个k列矩阵或DataFrame(其值全为1和0)。pandas有一个get_dummies函数可以实现该功能。
#计算指标/哑变量
df1=DataFrame({'key':['b','b','a','c','a','b'],'data':range(6)})
#pd.get_dummies(data,prifix=None,...,columns=None)函数还可加前缀和列名等
df2=pd.get_dummies(df['key'])
"""
df1: df2:
data key a b c
0 0 b 0 0 1 0
1 1 b 1 0 1 0
2 2 a 2 1 0 0
3 3 c 3 0 0 1
4 4 a 4 1 0 0
5 5 b 5 0 1 0
"""
八、字符串操作
python能够成为流行的数据处理语言,部分原因是因其简单易用的字符串和文本处理功能。大部分文本运算都直接做成了字符串对象的内置方法。对于更为复杂的模式匹配和文本操作,则可能要用到正则表达式。pandas对此进行了加强,它使得你能够对整组数据应用字符串表达式和正则表达式,而且能处理烦人的缺失数据。
8.1字符串对象方法
相关代码:
#字符串操作:
val='a,b, guido'
#split方法通过分隔符拆分字符串
#val分隔符是‘,’,还可以是空格或其他
val.split(',') #result:['a', 'b', ' guido']
#strip方法:用于修剪空白符(包括换行符)
piece=[x.strip() for x in val.split(',')] #piece= ['a', 'b', 'guido']
#以分隔符(自定义字符串皆可)形式将子字符串连接起来
'+'.join(piece) #result:'a+b+guido'
#子字符串定位
'a' in val #return:True
val.index('g') #return:5
val.index(':') #找不到会报错
val.find('g') #return:5
val.find(':') #找不到,返回-1
#计算字符子串出现次数
val.count(',') #return:2
#子字符串的替换,当替换对象为空字符串时,其作用为删除
val.replace(',','::') # return:'a::b:: guido'
#删除空格
val.replace(' ','') #return: 'a,b,guido'
8.2 正则表达式
正则表达式(通常称作regex)提供了一种灵活的文本中搜索或匹配字符串模式的方法。正则表达式是根据正则表达式语言编写的字符串。python内置的re模块负责对字符串应用正则表达式。
re模块的函数可以分为三大类:模式匹配、替换以及拆分。当然它们之间是相辅相成的。一个regex描述了需要在文本中定位的一个模式,它可以用于许多目的。
相关代码:
#正则表达式
import re
text="lizihua come\t \ton\t !"
#'\s+'用来描述一个或多个空白符
re.split('\s+',text) #result:['lizihua', 'come', 'on', '!']
#等效于
regex=re.compile('\s+')
regex.split(text) #result:['lizihua', 'come', 'on', '!']
#得到匹配regex的所有模式,可以使用findall
regex.findall(text) #return: [' ', '\t \t', '\t ']
regex.match(text) #return:None
m=regex.search(text)
#search返回模式在原字符串中起始地址和结束地址,span以及匹配的结果
#return:<_sre.SRE_Match object; span=(7, 9), match=' '>
#但若要只显示结果,则需:
text[m.start():m.end()] #return:' '
#将匹配到的模式替换为指定字符串,并返回所得到的新字符串
regex.sub('1',text) #return:'lizihua1come1on1!'
8.3pandas中矢量化的字符串函数
清理待分析的散乱数据时,常常需要做一些字符串规整化工作。更为复杂的情况是,含有字符串的列有时还含有缺失数据,因此,清理这类散乱数据常用的矢量化字符串方法有: