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\\t 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":"[email protected]","Steve":"[email protected]","Rob":"[email protected]","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())
閱讀更多 商業數據分析師 的文章