Python數據處理 II:數據的清洗(預處理)
簡介:上一篇介紹了如何用Python讀取和存儲數據。在數據收集的時候,原始數據總是或多或少存在一些問題(缺失、異常、格式不統一等等)。今天,我們就來說一說如何用Python進行數據的預處理(也可以叫做「清洗」)。
標籤:計算機技術,Python
人生苦短,我用Python。 —— 魯迅
引 子:
在半導體製造公司里,每天都會產生數以億計的生產和測試數據。採用自動化的腳本語言對生產測試數據進行批量,將大大提高工程師的工作效率。雖然Perl語言在半導體製造公司內具有更高的普及率,但是隨著大數據和人工智慧技術的不斷發展,Python受到了越來越高的重視,因此我選擇了Python作為數據處理的腳本工具。
平台:Windows系統,Spyder 3.1.4 軟體,Python 3.6 語言。
本文主要內容:
1. 數據的讀取與存儲
2. 數據的清洗(預處理)3. 數據的處理方法簡介
我們拿到的數據或多或少都存在一些瑕疵和不足。多個數據可能存儲在一個字元串中,也有可能分散在多個數據文件內,需要對數據進行拆分和組合。此外,數據還可能存在缺失、亂碼、格式不統一等問題,這都需要對數據進行預處理。
本部分主要包括如下內容:
1. 字元串的拆分
2. 格式化字元串3. 用正則表達式re庫處理字元串數據4. 在DataFrame中對數據進行預處理5. 將各種數據結構轉化為DataFrame
1. 字元串的拆分
用python內建的split( ) 函數可以對字元串進行拆分,並返回一個列表。如:
str = abc.edfntemp = str.split(.) # 在出現「.」的位置對字元串進行拆分nprint(temp)n
輸出結果如下:
在DataFrame中也可以實現字元串的拆分:
import pandas as pdnndf = pd.DataFrame({"a" : ["1","2","3","4"],n "b" : ["5-9","6-10","7-11","8-12"]})nprint(df)nprint(----------)n# 將b按「-」拆分成兩個字元串並分別存儲在b和c中:ndf[b], df[c] = df[b].str.split(-, 1).str nprint (df)n
輸出結果如圖:
2. 格式化字元串
用內建的format函數可以實現字元串的格式化,其功能類似於C語言的print函數,只不過輸出的結果保存在了一個字元串變數里。例如:
a = 12nb = 1nc = testnstr = format(%02d-%02d-%s %(a, b, c))n
上述代碼的格式化輸出結果為「12-01-test」:
用這個方法可以將不同格式的數據合併並統一起來。運用諸如 %02d 這樣的語句,還可以在數據前自動補齊0,這可以解決很多數據合併時可能遇到的問題。
%之後可以跟隨的內容主要包括:
類型代碼:
%s 字元串(str()的顯示)
%r 字元串採用repr()顯示 %c 單個字元 %b 二進位整數 bin %i 十進位整數 int %o 八進位整數 oct %x 十六進位整數 hex %f 浮點數 %e 指數 %% 字元% (前提是裡面要有格式符的話需要這麼寫)關於對齊,可提供的值有:
+ 右對齊,整數前加正號,負數前加負號 - 左對齊,正數錢無符號,負數前加負號; 空格 右對齊;正數前加空格,負數前加負號 0 右對齊,正數前無符號,負數前加負號;用0填充
詳細內容可參考:
%格式化和format格式化--python - CSDN博客3. 用正則表達式re庫處理字元串數據
正則表達式,又稱規則表達式。(英語:Regular Expression,在python庫中被稱為re)。正則表達式通常被用來檢索、替換那些符合某個模式(或者叫規則)的文本。
運用正則表達式,可以命令計算機執行諸如「分離出所有的郵編」、「分離出所有叫Ann的學生」等等命令,例如:
import re # 調用re庫nstr = abc1de23figh456kk0nnum = re.findall(d+, str) # 尋找str中所有的數字及其1~n擴展nprint(num)n
輸出的結果為:
常用的正則表達式如下:
. 任何單字元
[ ] 字符集[^ ] 非字符集* 前一字元0~n擴展+ 前一字元1~n擴展? 前一字元0~1擴展L|R 左右表達式的任意一個a{m} 擴展a字元m次a{m,n} 擴展a字元m~n次^ 匹配字元串開頭部分$ 匹配字元串結尾部分
( ) 分組標記,括弧內只可使用"|"操作符d 數字0~9w 單字元A~Z加a~z加0~9
舉例如下:
^[A-Z a-z]+$ # 26字母組成的字元串n^[A-Z a-z 0-9]+$ # 26字母加數字組成的字元串n^-?d+$ # 整數字元串n^[0-9]*[1-9][0-9]*$ # 正整數字元串n[1-9]d{5} # 中國大陸郵編n[u4e00-u9fa5] # 匹配中文字元nd{3}-d{8}|d{4}-d{7} # 國內電話號碼n
在re庫中,常用的函數有:
import re # 載入re庫npattern = d+ # 正則式nstring = |5(5)|4(5) # 原始字元串nn# search:在一個字元串中搜索匹配正則表達式的第一個位置,返回match對象nre.search(pattern, string, flags = 0) nn# match:從開始位置搜索nre.match(pattern, string, flags = 0) nn# findall:以列表形式返回全部匹配結果nre.findall(pattern, string, flags = 0) nn# split:按結果分割並返回一個列表,maxsplit指最大分割數(匹配數))nre.split(pattern, string, maxsplit, flags = 0) nn# finditer:循環用,每一個結果單獨使用nre.finditer(pattern, string, flags = 0) nn# sub:替換字元串nrepl = FLAG # 替換字元串nre.sub(pattern, repl, string, count = 0, flags = 0) # count指替換次數n
要注意的是,re庫在進行字元串匹配時會默認採用「貪婪匹配」,也就是說會返回符合條件的最長字元串。通過在操作符後增加一個「?」可以將正則式變成最小匹配:
match1 = re.search(rPY.*N, PYANBNCNDN) # 貪婪匹配nmatch2 = re.search(rPY.*?N, PYANBNCNDN) # 最小匹配n# 輸出結果為:n# match1 = PYANBNCNDNn# match2 = PYANn
在半導體行業數據處理中,經常需要從一個字元串中讀出數字信息,這時候採用正則表達式進行處理就會非常簡單快捷。正則表達式在Perl、Office高級查詢等文本處理工具中也有很廣泛的應用,建議大家深入了解一下。這裡推薦一個百度文庫的鏈接:
python_re模塊_百度文庫4. 在DataFrame中對數據進行預處理
DataFrame是一種存儲結構化數據非常有力的方法,其內建的函數可以幫助我們快速而方便地對數據進行預處理。
4.1. DataFrame的基本操作
在數據預處理過程中涉及的DataFrame基本操作主要包括:
a. 行和列的添加與刪除
b. 對DataFrame中指定位置數據的處理c. 對DataFrame進行限定條件的篩選(類Database操作)。
a. 行和列的添加與刪除
添加一列數據到DataFrame的方法很簡單,直接以DataFrame索引的形式讀取data數據即可。若DataFrame中不存在這個索引,會自動創建該索引並導入這一列數據。要注意的是,導入的數據長度和原數據長度應相同,否則會報錯。如果讀取的是一個數據(如一個數或者一個字元串),DataFrame會自動將這一列數據全部設為被讀取的數據。
# 添加一列數據到DataFrame數據中:nnum = [0, 1, 2, 3, 4]ndf[data] = num # 將num導入到df的data列,注意df和num的列長度應相同ninfo = RnD Datandf[info] = info # 每一行的info都會是「RnD Data」n
通過append功能可以將兩個相同結構的DataFrame拼接在一起:
df1 = df1.append(df2, ignore_index=True)n# 也可以寫成:ndf1.append(df2, ignore_index = True, inplace = True)n
還可以以任意方式連接DataFrame的多個列,生成新的數據:
df[City] + df[State] # 連接兩個series 類似於"123"+"abc" = "123abc"ndf[Location] = df[City] + ", " + df[State] # 自定義連接並賦值給新列n
通過drop或者del功能可以刪除指定的數據:
# 刪除第1行和第2行的數據:ndf.drop([1,2], axis = 0, inplace = True) n# axis = 1, drop列; axis = 0, drop行:ndf.drop("string", axis = 1, inplace = true) n# drop掉title為City和State的列:ndf.drop([City, State], axis = 1, inplace = True) nndel df[data] # 刪除df中標籤為data的列n
pop方法可以將選中的數據提取出來,並且原DataFrame中也不再保留該列:
temp = df.pop[data] # 刪除df中的data列,並將data列的數據傳遞給tempn
b. 對DataFrame中指定位置數據的處理
對指定位置數據的處理,重點在於如何指定位置。對DataFrame進行位置指定的方法主要有:
- loc,基於列label和行的index選擇數據;
- iloc,基於行/列的position;
- at,根據指定行index及列label,快速定位DataFrame的元素(只可選擇一個數據);
- iat,與at類似,不同的是根據position來定位的;
- ix,為loc與iloc的混合體,既支持label也支持position。但是python3.6里已經不建議使用ix,最好還是採用loc或者iloc的方法。
print df.loc[1:3, [total_bill, tip]]nprint df.loc[1:3, tip: total_bill]nprint df.iloc[1:3, [1,2]]nprint df.iloc[1:3, 1:3]nprint df.at[3, tip]nprint df.iat[3,1]nprint df.ix[1:3, [1,2]]nprint df.ix[1:3, [total_bill, tip]]n
此外,有更為簡潔的行/列選取方式:
print df[1: 3]nprint df[[total_bill, tip]] n
c. 對DataFrame進行限定條件的篩選(類Database操作)
按DataFrame的某些列進行排序:
Series的處理法:ndf.title.sort_values( )ndf[title].sort_values( ) # 對Series進行sort處理,默認是正序ndf[title].sort_values( ascending = False ) # 逆序sortndf[title].sort_values( ascending = True ) # 正序sortnnDataFrame的處理法:ndf.sort_values(title) # 對DataFrame按title列進行sortn多行排序:ndf.sort_values([title,name])n
按條件篩選數據:
# Method 1:ndata = df[df.duration >= 200]ndata = df[df.duration >= 200][genre] # 選擇genre這一列,限定條件為duration>=200nn# Method 2:ndata = df.loc[df.duration >= 200, genre]n
多條件篩選:
ufo[(ufo.duration >= 200) and (ufo.genre == Drama)]n
還可以用numpy庫的函數對DataFrame進行限定等:
import pandas as pdnimport numpy as npndf = pd.DataFrame({A:[1,2,3,4], B:[5,6,7,8], C:[1,1,1,1]})ndf[D] = np.where(df.A<3, 1, 0) # 如果A<3,D為1;如果A≥3,D為0nprint(df)n
輸出結果為:
關於DataFrame的操作,我是在B站上看教學視頻學習的。雖然是個英文的教學視頻,但是語速比較低,難度不大。而且我個人認為講的比許多中文教程要好很多,推薦大家看一看:
【全30集】使用 pandas 進行數據分析:Data analysis in Python with pandas
4.2. 數據缺失(NAN)的處理
缺失的數據在DataFrame中會被顯示為「NAN」(Not a number)。針對不同的情況,可以有不同的解決方法。我在數據處理過程中主要遇到的情況為如下兩種:
a. 數據缺失,該行數據需要被刪除:
# 用dropna來刪除數據n# inplace = True表示將填充後的結果覆蓋原始的DataFramendf.dropna(inplace = True)n
b. 需要根據前序數據補齊內容:
# 對df中的空數據進行自動填充,填充方法:填充上一行數據n# inplace = True表示將填充後的結果覆蓋原始的DataFramendf.fillna( method = pad, inplace = True) n
此外,fillna( ) 函數還有不同的method可選,包括:
df.fillna(method = bfill, inplace = True) # 用後一個值替代NANndf.fillna(0) # 用0(也可以是任意一個數字)替代NANndf.fillna(missing) # 用一個字元串替代NANn
在fillna( ) 中還可以嵌套調用 df 的一些屬性。例如,可以用平均值代替NAN值:
df.fillna(df.mean()) # 用平均值代替NANn
還可以選擇特定的列進行NAN值處理:
df.fillna(df.mean()[DUT:CHIP])n
5. 將各種數據結構轉化為DataFrame
DataFrame是一種非常好用的數據類型,推薦大家都在DataFrame中處理數據。這也將涉及到如何將各種數據轉換為DataFrame,或者如何添加數據到DataFrame中。
5.1 字典轉化為DataFrame
字典類型可以直接轉換為DataFrame:
import pandas as pdndic = {one:[A,A,B,C,C,A,B,B,A,A],n two:[B,B,C,C,A,A,C,B,C,A],n three:[C,B,A,A,B,B,B,A,C,D]}ndf = pd.DataFrame(dic)nprint(df)n
輸出結果如圖:
5.2 列錶轉化為DataFrame
DataFrame結構的數據具有列標籤(label)、行標籤(index)和數據內容。在轉換成DataFrame的時候,系統會自動添加行標籤,而列標籤需要指定。可以採用如下代碼添加標籤:
import pandas as pdnx = [0, 1, 2, 3, 4]ny = [34, 54, 56, 76, 44]ndf = pd.DataFrame({x:x, y:y}) # 列錶轉換為DataFrame並添加標籤 nprint(df)n
輸出結果如下:
多維列表可以直接轉換為DataFrame,此時指定的label為默認值(0,1,2,……)。然後用 rename 功能來對label重命名:
import pandas as pdnx = [ [1,2,3,4], [5,6,7,8] ] # 包含兩個不同的子列表[1,2,3,4]和[5,6,7,8]ndf = pd.DataFrame(x) # 這時候是以行為標準寫入的nprint(df)nprint(----------)ndf.rename(columns={0:a,1:b},inplace=True) # 注意label的0和1都不是字元串nprint(df)n
5.3 np.array 轉化為DataFrame
np.array 型數據和列表數據類似,都是沒有標籤的數據類型,需要在轉換的時候聲明標籤名稱:
import pandas as pdnimport numpy as npnns1 = np.array([1,2,3,4])ns2 = np.array([5,6,7,8])ndf = pd.DataFrame({"a":s1,"b":s2})nprint(df)n
輸出結果如下:
5.4 向已有DataFrame中添加數據:
添加新列的方法很簡單,示例代碼如下:
import pandas as pdnimport numpy as npnndf = pd.DataFrame({"A":[1,2,3,4],"B":[5,6,7,8],"C":[1,1,1,1]})nprint(df)nprint(---------------------)nlis = [high, low, middle, high]narr = np.array([78,67,56,45])ndf[D] = lis # 添加一個列表到df,標籤名為Dndf[E] = arr # 添加一個np.array到df,標籤名為Enprint(df)n
輸出結果為:
附:常用的格式轉換函數
總 結
原始的數據有時候是一串文字(過度聚合),有時候是分散在多個位置的分離文件(過於分散)。數據的預處理就是要把原始的數據拆分、組合、標準化,並將異常的數據剔除掉。在這個過程中,字元串的拆分與合併非常重要。python內建函數可以實現簡單字元串的拆分與合併(split與format函數)。而re庫、pandas庫又提供了更加強大的數據預處理手段,能幫助我們運用簡單的代碼實現複雜的功能。在這裡,筆者也建議大家在進行數據處理的時候,將數據轉換為DataFrame格式,運用pandas庫強大的功能庫進行處理。
推薦閱讀:
※如何回答同學知道我在學 Python 時問我「會盜 QQ 號嗎」?
※Python 抓取網頁亂碼原因分析
※用爬蟲抓取崑崙決所有選手信息並保存為PDF檔案!(淺談搏擊運動數據分析)
※Python數據分析模塊 | pandas做數據分析(一):基本數據對象