Python之数据清洗实操大全
以下语句均在pycharm中演示,可直接复制到pycharm中查看运行结果
# 数据清洗实操
#1.1数据规整化:清理,转换,合并,重塑
#1.2数据聚合与分组运算
import numpy as np
import pandas as pd
# 2数据规整化:清理,转换,合并,重塑
#合并数据集
#2.1 pandas.nmerge:可根据一个或多个键将不同的Dataframe的行链接起来
#2.2 pandas.concat:可沿着一条轴将多个对象堆叠到一起
#2.3 combine_first:可将重复数据编接在一起,用一个对象中的值填充另一个对象中的缺失值
# 3.数据库风格的DataFrame合并
#3.1数据集的合并或者连接运算:通过一个或多个键将行链接起来
df1=pd.DataFrame({"key":['b',"b","a","c","a","a","b"],"data1":range(7)})
print(df1)
df2=pd.DataFrame({"key":["a","b","d"],"data2":range(3)})
print(df2)
#3.2多对一的合并
print(pd.merge(df1,df2))
#若没有指定用哪一个列进行连接,merge 会将重叠列名当作键,指定如下
print(pd.merge(df1,df2,on="key"))
#若两个对象的列名不同,可分别进行指定:
df3=pd.DataFrame({"1key":['b',"b","a","c","a","a","b"],"data1":range(7)})
df4=pd.DataFrame({"rkey":["a","b","d"],"data2":range(3)})
a=pd.merge(df3,df4,left_on='1key',right_on="rkey")
print(a)
#默认情况下,merge做inner链接,结果中的键是交集,。外连接求取的是键的并集
b=pd.merge(df1,df2,how='outer')
print(b)
#多对多的合并操作
c=pd.merge(df1,df2,on="key",how="left")
print(c)
d=pd.merge(df1,df2,on="key",how="right")
print(d)
#链接方式只影响出现在结果中的键,根据多个键进行合并,传入一个由列名组成的列表
left=pd.DataFrame({"key1":["foo","foo","bar"],
"key2":["one","two","one"],
"lva1":[1,2,3]})
print(left)
right=pd.DataFrame({"key1":["foo","foo","bar","bar"],
"key2":["one","one","one","two"],
"rva1":[4,5,6,7]})
print(right)
a1=pd.merge(left,right,on=["key1","key2"],how="outer")
print(a1)
#在进行列——列转换时,Dataframe对象中的索引会被丢弃
#suffixes选项:指定附加到左右两个Dataframe对象的重叠列名上的字符串
print(pd.merge(left,right,on="key1"))
print(pd.merge(left,right,on="key1",suffixes=("_left","_right")))
# 索引上的合并
#当Dataframe 中的连接键位于其索引中时,传入left_index=True,right_index=TrUE,以说明索引应该被用作连接键
left1=pd.DataFrame({"key":["a","b","a","a","b","c"],
"vakye":range(6)})
print(left1)
right1=pd.DataFrame({"group_val":[3.5,7]},index=["a","b"])
print(right1)
f=pd.merge(left1,right1,left_on="key",right_index=True)
print(f)
#对于层次化索引的数据:
lefth=pd.DataFrame({"key1":["ohio","ohio","ohio","Nevda","Nevda"],
"key2":[2000,2001,2002,2001,2002],
"data":np.arange(5.)})
print(lefth)
righth=pd.DataFrame(np.arange(12).reshape((6,2)),
index=[["Nevada","Nevada","ohio","ohio","ohio","ohio"],
[2001,2000,2000,2000,2001,2002]],
columns=["event1","event2"])
print(righth)
g=pd.merge(lefth,righth,left_on=["key1","key2"],right_index=True)
print(g)
left2=pd.DataFrame({"ohio":[1.,3.,5.],"Nevada":[2.,4.,6.]},
index=list("ace"))
print(left2)
right2=pd.DataFrame({"Missouri":[7.,9.,11.,13.],"Alabama":[8.,10.,12.,14.]}
,index=list("bcde"))
print(right2)
#使用合并双方的索引:
print(pd.merge(left2,right2,how="outer",left_index=True,right_index=True))
#Dataframe的join 实例方法:
print(left2.join(right2,how="outer"))
#更方便的实现按索引合并,不管有没有重叠的列,在连接键上做左连接,支持参数Dataframe 的某个列之间的连接:
print(left1.join(right1,on="key"))
#对于简单的索引合并,可以向join传入一组Dataframe(concat函数也是这个功能)
another=pd.DataFrame([[7.,8.],[9.,10.],[11.,12.],[16.,17.]],
index=list("acef"),columns=["new york","oregon"])
print(another)
print(left2.join([right2,another]))
# 轴连接
#数据合并运算
# 连接,绑定,堆叠
#Numpy 有一个用户合并原始Numpy数组的concatenation函数
arr=np.arange(12).reshape((3,4))
print(arr)
print(np.concatenate([arr,arr],axis=1))
#pandas 的concat函数:
s1=pd.Series([0,1],index=["a","b"])
s2=pd.Series([2,3,4],index=list("cde"))
s3=pd.Series([5,6],index=list("fg"))
print(s1)
print(s2)
print(s3)
print(pd.concat([s1,s2,s3]))
#默认情况下,coacat在axis=0上工作。传入axis=1,产生一个DataFrame
print(pd.concat([s1,s2,s3],axis=1))
#这种情况下,另外一条轴上没有重叠,传入join ="inner"得到他们的交集:
print(pd.concat([s1,s3],axis=1,join='inner'))
#使用key参数,在连接轴上创建一个层次化索引:
result=pd.concat([s1,s1,s3],keys=["one","two","three"])
print(result)
print(result.unstack())
#沿着axis=1对Series进行合并,keys就会完成为Dataframe的列头:
print(pd.concat([s1,s2,s3],axis=1,keys=["one","two","three"]))
#对Dataframe对象也是如此:
df1=pd.DataFrame(np.arange(6.).reshape((3,2)),index=list("abc"),
columns=["one","two"])
print(df1)
df2=pd.DataFrame(5+np.arange(4).reshape((2,2)),index=list("ac"),columns=["three","four"])
print(df2)
print(pd.concat([df1,df2],axis=1,keys=["level1","level2"]))
#传入一个字典,则字典的键会被当做keys选项的值:
print(pd.concat({"level1":df1,"level2":df2},axis=1))
#跟当前分析无关的dataframe行索引:
df1=pd.DataFrame(np.random.randn(3,4),columns=list("abcd"))
df2=pd.DataFrame(np.random.randn(2,3),columns=list("bda"))
print(df1)
print(df2)
#传入ignore_index=True
print(pd.concat([df1,df2],ignore_index=True))
print(pd.concat([df1,df2]))
#合并重叠数据
#关于索引全部或者部分重叠的两个数据集
#Numpy 的where函数,用于表达一种矢量化的if -else
a=pd.Series([np.nan,2.5,np.nan,3.5,4.5,np.nan],
index=list("fedcba"))
b=pd.Series(np.arange(len(a),dtype=np.float64),
index=list("fedcba"))
print(a)
print(b)
print(np.where(pd.isnull(a),b,a))
#Series的combine_first方法,实现与上面一样的功能,并会进行数据对齐
print(b[:-2].combine_first(a[2:]))
#对于Dataframe一样
df1=pd.DataFrame({"a":[1.,np.nan,5.,np.nan],
"b":[np.nan,2.,np.nan,6.],
"c":range(2,18,4)})
print(df1)
df2=pd.DataFrame({"a":[5.,4,np.nan,3.,7.],
"b":[np.nan,3.,4.,6.,8.]})
print(df2)
print(df1.combine_first(df2))
#可以看作参数对象中的数据为调用者对象的缺失数据"打补丁"
#重塑和轴旋转
#用于重新排列表格型数据的基础运算:重塑(reshape)和轴向旋转(pivot)
#重塑层次化索引
#stark:将数据的列旋转为行
#unstack:将数据的行旋转为列
data=pd.DataFrame(np.arange(6).reshape(2,3),index=pd.Index(["ohio","colorado"],name="state",
columns=pd.Index(["one","two","three"],name="number")))
print(data)
#用stack方法将行转为列,得到一个Series:
result=data.stack()
print(result)
print("--------------")
#对于层次化索引的Series,可以用unstack将其重新排为一个Dataframe:
print(result.unstack())
#默认情况下,unstack操作最内层
#传入分层级别的编号或名称可对其他级别进行unstack操作
print(result.unstack(0))
print(result.unstack("state"))
#如果不是所有级别值都能在各组找到的话,unstack操作可能会引入缺失值数据:
s1=pd.Series([0,1,2,3],index=list("abcd"))
s2=pd.Series([4,5,6],index=list("cde"))
data2=pd.concat([s1,s2],keys=["one","two"])
print(data2.unstack())
#stack默认会过滤掉缺失数据,因此该运算是可逆的:
print(data2.unstack().stack())
print(data2.unstack().stack(dropna=False))
#对Dataframe进行unstack操作时,作为旋转轴的级别将会成为结果中的最低级别
df=pd.DataFrame({"left":result,"right":result+5},
columns=pd.Index(["left","right"],name="side"))
print(df)
print(df.unstack("state"))
print(df.unstack("state").stack("side"))
#将"长格式"旋转为"款格式"
#时间序列数据通常以"长格式(long)或者"堆叠格式(stacked)"存储在数据库以及CSV中
ldata=pd.DataFrame({"data":["1959-3-31","1959-3-31","1959-3-31","1959-6-30","1959-6-30","1959-6-30"],
"item":list("riuriu"),
"value":[2710,0,5,2778,2,5]})
print(ldata)
#转成DataFrame,用pivot方法:
pivoted=pd.pivot_table(ldata,index=["data","item"])
print(pivoted.head())
#得到的Datafrme带有层次化的列:
pivoted=ldata.pivot("data","item")
print(pivoted)
pivoted["value"]
#假设有两个需要参与重塑的数据列:
ldata["value2"]=np.random.randn(len(ldata))
print(ldata)
#pivot 其实只是一个快捷方式:用set_index 创建层次化索引,再用unstack重塑
unstacked=ldata.set_index(["data","item"]).unstack("item")
print(unstacked)
#以上时数据的重排,下面是过滤,清理以及其他转换工作
#数据转化
# 移除重复数据,DataFrame中出现的重复行:
data=pd.DataFrame({"k1":["one"]*3+["two"]*4,"K2":[1,1,2,3,3,4,4]})
print(data)
#Dataframe的duplicated方法返回一个布尔型Series,表示各行是否是重复行,drop_duplicates方法返回一个移除了重复行的Dataframe
print(data.duplicated())
print(data.drop_duplicates())
#指定部分列进行重复项判断,如只是希望根据K1列过滤重复项
data["v1"]=range(7)
print(data)
print(data.drop_duplicates("k1"))
#duplicated和drop_duplicates 默认保留重复数值里第一次出现的组合,传入keep=last则保留最后一个:
print(data.drop_duplicates(["k1"],keep="last"))
#利用函数或映射进行数据转换
#根据数组,series或者Dataframe列中的值来实现转换
data=pd.DataFrame({"food":["bacon","pulled pork","bacon","Pastrami","corned beef","Bacon","pastrami","honey ham","nova lox"],
"counces":[4,3,12,6,7,8,3,5,6]})
print(data)
#编写一个肉类到动物的映射:
meat_to_animals={"bacon":"pig","pulled pork":"pig","pastrami":"cow","corned beef":"cow","honey ham":"pig","nova lox":"salmon"}
print(meat_to_animals)
#Series 的map 方法:可以接受一个函数或含有映射关系的字典型对象,用于修改对象的数据子集
data["animals"]=data["food"].map(str.lower).map(meat_to_animals)
print(data)
#也可以传入一个能够完成全部这些工作的函数:
print(data["food"].map(lambda x:meat_to_animals[x.lower()]))
#替换值
#replace 方法:替换
#利用fillna方法填充缺失数据可以看作替换的一个特殊情况,替换一个值和一次性替换多个值
data=pd.Series([1.,-999.,2.,-999,-1000.,3.])
print(data)
print(data.replace(-999,np.nan))
print(data.replace([-999,-1000],np.nan))
#对不同的值进行不同的替换
print(data.replace([-999,-1000],[np.nan,0]))
#传入的参数也可以是字典
print(data.replace({-999:np.nan,-1000:0}))
#重命名轴索引 轴标签有一个map方法:
data=pd.DataFrame(np.arange(12).reshape((3,4)),index=["OHio","Colordao","new york"],
columns=["one","two","three","four"])
print(data)
print(data.index.map(str.upper))
#对函数或映射进行转换,从而得到一个新对象,将其值赋给index ,就可以对Dataframe进行就地修改:
data.index=data.index.map(str.upper)
print(data)
#要创建数据集的转换版,而不是修改原始数据,用rename
print(data.rename(index=str.title,columns=str.upper))
#rename结合字典型对象可以实现对部分轴标签的更新
print(data.rename(index={"OHIO":"INDIANA"},columns={"three":"peekaboo"}))
#rename实现了复制Dataframe并对其索引和列标签进行赋值,就地修改某个数据集,传入inplace=true
print(data.rename(index={"OHIO":"INDIANA"},inplace=True))
#离散化和面元划分
#为了便于分析,连续数据常常被离散化或者拆分为"面元(bin)",用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)
print(cats)
#pandas 返回的是一个特殊的categorical对象,它含有一个表示不同分类名称的数组和一个为年龄数据进行标号的属性
print(cats.categories)
print(cats.codes)
#哪边是闭端可以通过right=Fslse进行修改:
print(pd.cut(ages,[18,26,36,61,100],right=False))
#设置自己的面元名称:
group_names=["youth","youngAdult","MiddleAged","senior"]
print(pd.cut(ages,bins,labels=group_names))
#将labels选项设置为一个列表或者数组即可,如果向cut传入的是面元的数量而不是确切的面元边界,则它会根据数据的最小值和最大值计算等长面元
data=np.random.randn(20)
print(pd.cut(data,4,precision=2))
#将一些均匀分布的数据分成了四组,
# qcut函数:根据样本分位数对数据进行面元划分,由于qcut使用的是样本分位数,可以得到大小基本相等的面元(而cut根据数据的分布情况,可能无法使各个面元中含有相同数量的数据点)
data=np.random.randn(1000)
cats=pd.qcut(data,4)
print(cats)
print(pd.value_counts(cats))
#设置自定义的分位数:
print(pd.qcut(data,[0,0.1,0.5,0.9,1]))
#在聚合和分组运算时会再次用到cut和qcut这两个离散化函数
#检测和过滤异常值 判断是否存在异常值(outlier)
print(np.random.seed(12345))
data=pd.DataFrame(np.random.randn(1000,4))
print(data.describe())
#找出某列中绝对值大小超过3的值:
col=data[3]
print(col[np.abs(col>3)])
#选出全部含有"超过3或-3的值"行:
print(data[(np.abs(data)>3).any(1)])
#将值限制在区间-3到3以内:
data[np.abs(data)>3]=np.sign(data)*3
print(data.describe())
#np.sign将这个ufunc返回的是一个由1和-1组成的数列,表示原始值的符号
#排列和随机采样
#numpy.random.permutation函数:对Series和Dataframe的列排列
df=pd.DataFrame(np.arange(5*4).reshape(5,4))
print(df)
sampler=np.random.permutation(5)
print(sampler)
#Permutation(5):需要排列的轴的长度,然后就可以在基于ix的索引操作或者take函数中使用该数组
print(df)
print(df.take(sampler))
#选取随机限子集(非替换)
print(df.take(np.random.permutation(len(df))[:3]))
#用替换的方式产生样本:
bag=np.array([5,7,-1,6,4])
print(bag)
sampler=np.random.randn(0,len(bag))
print(sampler)
#计算指标/哑变量
#将分类变量(categorical)转换为"哑变量矩阵(dummY matrix)"或"指标矩阵(Indicator matrix)
df=pd.DataFrame({"key":["b","b","a","c","a","b"],"datal":range(6)})
print(df)
print(pd.get_dummies(df["key"]))
#给Dataframe的列加上一个前缀,以便能够跟其他数据进行合并
dummies=pd.get_dummies(df["key"],prefix="key")
print(dummies)
df_with_dummy=df[["datal"]].join(dummies)
print(df_with_dummy)
#字符串操作字符串对象的内置方法
val="a,b,guido"
print(val.split(","))
pieces=[x.strip() for x in val.split(",")]
print(pieces)
first,second,third=pieces
print(first+"::"+second+"::"+third)
print("::".join(pieces))
#find找不到返回-1,index找不到引发一个异常
print("guido" in val)
print(val.index(","))
print(val.find(":"))
#传入空字符串常常用于删除模式
print(val.count(","))
print(val.replace(",","::"))
print(val.replace(",",""))
print(val)
#正则表达式(regex)
#提供了一种灵活的在文本中搜索或者匹配字符串模式的方法,python内置的re模块负责对字符串应用正则表达式
#re模块的函数分为三个大类:模式匹配,替换,拆分
import re
text="foo bar baz tqux"
print(text)
print(re.split("s+",text))
regex=re.compile("s+")
print(regex.split(text))
#描述一个或者多个空白符的regex是s+,调用re.split("s+",text)时,正则表达式会先被编辑,然后再在text上调用其他split方法
#可以用re.compile自己编译一个regex,以得到一个可重用的regex对象,如上所述,如果打算对许多字符串应用同一条正则表达式,强烈建议通过这种方法,可以节省大量的cpu时间,得到匹配的regex的所有模式
print(regex.findall(text))
# finddall:返回字符串中所有的匹配项
#search:只返回第一个匹配项
#match:只匹配字符串的首部
#pandas中矢量化的字符串函数
#通过data.map,所有字符串和正则表达式方法都能被应用于各个值,但是如果存在NA就会报错,为了解决这个问题,Series有一些能够跳过NA值的字符串操作方法,通过Series的str属性即可访问这些方法
data={"Dave":"dave@google.com","Steve":"steve@gmail.com","Rob":"rob@gmail.com","Wes":np.nan}
print(pd.Series(data))
print(data.isnull())
print(data.str.contains("gmail"))
#对字符串进行子串截取
print(data.str[:5])
#数据聚合与分组运算
#对数据集进行分组并对各组应用一个函数,再将数据集准备好之后,通常的任务就是计算分组统计或者生成透视表,pandas提供了一个灵活高效的groupby功能:利用任何可以接受pandas对象或者Numpy数组的函数
#groupby技术:
#分组运算:split(拆分)——>apply(应用)——>combine(合并)
#分组键的形式:
#列表或者数组,其长度与待分组的轴一样
#表示Dataframe某个列名的值
#字典或者Series,给出待分组轴上的值与分组名之间的对应关系
#函数,用于处理轴索引或者索引中的各个标签
df=pd.DataFrame({"key1":["a","b","b","b","a"],"key2":["one","two","one","two","one"],"data1":np.random.randn(5),"data2":np.random.randn(5)})
print(data)
grouped=df["datal"].groupby(df["key1"])
print(grouped)
#访问data1,并根据key1调用groupby
#变量grouped是一个GroupBy对象,它实际上还没有进行任何计算,只是含有一些有关分组键df["key1"]的中间数据
#例如,调用GroupBy的mean方法来计算分组平均值
print(grouped.mean())
means=df["datal"].groupby([df["key1"],df["df["key2"]]).mean()
print(means)
#Series根据分组键进行了聚合,产生了一个新的Series,其索引为key1列中的唯一值,通过两个键对数据进行了分组后,得到的Series具有一个层次化索引
print(means.unstack())
#分组键可以时任何长度适当的数组:
states=np.array(["ohio","california","california","ohio","ohio"])
years=np.array([2005,2005,2006,2005,2006])
df["datal"].groupby([states,years]).mean()
#将列名用作分组键:
print(df.groupby("key1").mean())
print(df.groupby(["key1","key2"]).mean())
#Groupby 的Series方法返回一个含有分组大小的Series
df.groupby(["key1","key2"]).size()
#对分组进行迭代 :Groupby对象支持迭代,可以产生一个一组二元元组(由分组名和数据块组成)
for name,group in df.groupby("key1"):
print(name)
print(group)
for(k1,k2),group in df.groupby(["key1","key2"]):
print(k1,k2)
print(group)
#对于多重键,元组的第一个元素将会是由键值组成的元组,对于数据片段进行操作,如将这些数据片段做成一个字典
pieces=dict(list(df.groupby("key1")))
#groupby默认在axis=0进行分组,通过设置可以在其它任何轴上进行分组,如可以根据dtype对列进行分组
print(df.dtypes)
grouped=df.groupby(df.dtype,axis=1)
dict(list(grouped))
#选取一个或一组列
#对于 由Dataframe 产生的Groupby对象,用一个或一组(单个字符串或者字符串数组)列名对其进行索引,就能实现选取部分列进行聚合的目的
print(df.groupby("key1")["datal"])
print(df.groupby("key1")["data2"])
print(df["data1"].groupby(df["key1"]))
print(df[["data2"]].groupby(df["key1"]))
#例如,对部分列进行聚合:计算data2列的平均值并以Dataframe形式得到结果
print(df.groupby(["key1","key2"])[["data2"]].mean())
#返回一个已分组的Dataframe(传入的是列表或者数据)或者Series(传入的是标量形式的单个列名)
s_grouped=df.groupby(["key1","key2"])["data2"]
print(s_grouped.mean())
#通过字典或者Series进行分组,除数组外分组信息还可以其他形式存在
people=pd.DataFrame(np.random.randn(5,5),columns=list("abcde"),index=["Joe","Steve","Wes","Jim","Travis"])
people.loc[2:3,["b","c"]]=np.nan
print(people)
#根据分组计算列的sum:
mapping={"a":"red","b":"red","c":"blue","d":"blue","e":"red","f":"orange"}
by_ccolumns=people.groupby(mapping,axis=1)
print(by_ccolumns.sum())
#将mapping这个字典传给groupby即可,用Series作为分组键
map_series=pd.Series(mapping)
print(map_series)
people.groupby(map_series,axis=1).sum()
#这里Series可以被看作一个固定大小的映射,pandas 会检查Series 以确保其索引根分组轴时对齐的
#通过函数进行分组
#任何被当作分组键的函数都会在各个索引值上被调用一次,其返回值就会被用作分组名称
print(people.groupby(len).sum())
#将函数跟数组,列表,字典,Series混合使用(任何东西最终都会被转换为数组):
key_list=["one","one","one","two","two"]
print(people.groupby([len,key_list]).min())
![50091ec250874ae629923ef6e0bd0ed2.png](https://i-blog.csdnimg.cn/blog_migrate/72a53f31bd74229cebe612fe7ee9bccf.jpeg)