導讀:資料工作者經常會遇到各種狀況,比如你收集到的資料並不像你期待的那樣完整、乾凈。此前我們講解了用OpenRefine搞定資料清洗,本文進一步探討用pandas和NumPy插補缺失資料並將資料規範化、標準化。
作者:託馬茲·卓巴斯(Tomasz Drabas)
如需轉載請聯絡大資料(ID:hzdashuju)
本文將使用一個資料集,包含985項真實的房產交易。這些交易是連續5天內在Sacramento發生的。資料下載自:
http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv
資料已轉成多種格式,放在GitHub程式碼庫的Data/Chapter01檔案夾中。
https://github.com/drabastomek/practicalDataAnalysisCookbook.git
01 插補缺失值
資料的收集工作很棘手。收集工具壞了,調查問捲上某些問題人們不想回答,或者檔案被損壞了;這些還只是資料集可能不全的一小部分原因。如果想使用這個資料集,我們有兩個選擇:忽略缺失的資料,或者用一些值替代。
1. 準備
要實踐本技巧,你要先裝好pandas模組。
2. 怎麼做
csv_read DataFrame可供使用。要插補缺失值,你只需要使用下麵的程式碼(data_imput.py檔案):
# 估算平均數以替代空值
csv_read[‘price_mean’] = csv_read[‘price’] \
.fillna(
csv_read.groupby(‘zip’)[‘price’].transform(‘mean’)
)
3. 原理
pandas的.fillna(…)方法幫我們處理了所有重活。這是DataFrame物件的一個方法,將要估算的值作為唯一必須傳入的引數。
查閱pandas檔案中.fillna(…)的部分,瞭解可傳入的其他引數。檔案位於:
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.fillna.html
在我們的處理過程中,我們假設每個郵編可能會有不同的均價。這就是我們用.groupby(…)方法對資料分組的原因。房產的價格重度依賴於房間的數目,這個推論也是成立的;如果我們的資料集更大,我們還能考慮beds這個變數。
.groupby(…)方法傳回一個GroupBy物件。其.transform(…)方法高效地對郵編分組,在我們的例子中,分組的依據是各郵編價格資料的平均數。
現在,.fillna(…)方法簡單地用這個平均數替代缺失的觀測資料即可。
4. 更多
插補資料不是填補缺失值的唯一方法。資料對稱分佈且沒有異常值時,才會傳回一個合理的值;如果分佈比較偏,平均值是有偏差的。衡量集中趨勢更好的維度是中位數。我們前面的例子只需要改一個小地方:
# 估算中位數以替代空值
csv_read[‘price_median’] = csv_read[‘price’] \
.fillna(
csv_read.groupby(‘zip’)[‘price’].transform(‘median’)
)
02 將特徵規範化、標準化
為了提高計算效率,我們將特徵規範化(或標準化),這樣不會超出計算機的限制。探索模型中變數之間的相互作用時也建議這麼處理。
計算機是有限制的:整型值是有上限的(儘管目前在64位機器上這不是個問題),浮點型的精確度也有上限。
資料規範化是讓所有的值落在0到1的範圍內(閉區間)。資料標準化是移動其分佈,使得資料的平均數是0、標準差是1。
1. 準備
要實踐本技巧,你要先裝好pandas模組。
其他沒有什麼要準備的了。
2. 怎麼做
要實現規範化與標準化,我們定義了兩個輔助函式(data_standardize.py檔案):
def normalize(col):
”’
規範化
”’
return (col – col.min()) / (col.max() – col.min())
def standardize(col):
”’
標準化
”’
return (col – col.mean()) / col.std()
3. 原理
要規範化資料,即讓每個值都落在0和1之間,我們減去資料的最小值,並除以樣本的範圍。統計學上的範圍指的是最大值與最小值的差。normalize(…)方法就是做的前面描述的工作:對資料的集合,減去最小值,除以範圍。
標準化的過程類似:減去平均數,除以樣本的標準差。這樣,處理後的資料,平均數為0而標準差為1。standardize(…)方法做了這些處理:
csv_read[‘n_price_mean’] = normalize(csv_read[‘price_mean’])
csv_read[‘s_price_mean’] = standardize(csv_read[‘price_mean’])
03 分級資料
當我們想檢視資料分佈的形狀,或將資料轉換為有序的形式時,資料分級就派上用場了。
1. 準備
要實踐本技巧,你要先裝好pandas和NumPy模組。
2. 怎麼做
可以用下麵的程式碼(data_binning.py檔案)對資料分級(比如處理成直方圖):
# 根據線性劃分的價格的範圍,建立價格的容器
bins = np.linspace(
csv_read[‘price_mean’].min(),
csv_read[‘price_mean’].max(),
6
)
# 將容器應用到資料上
csv_read[‘b_price’] = np.digitize(
csv_read[‘price_mean’],
bins
)
3. 原理
第一步是建立容器。對於價格資料(缺失值用估算的平均數填補),我們建立了六個容器,在最小值和最大值之間均勻分配。.linspace(…)方法做了這點工作:建立長度為6的NumPy陣列,其中每個元素比前一個大固定的差值。比如,.linspace(0, 6, 6)生成陣列[0., 1.2, 2.4, 3.6, 4.8, 6.]。
NumPy對線性代數來說是個強大的數字處理庫。可輕鬆處理大型陣列和矩陣,還提供了極其豐富的函式運算元據。想瞭解更多,可訪問:
http://www.numpy.org
.digitize(…)方法對指定列中的每個值,都傳回所屬的容器索引。第一個引數是要分級的列,第二個引數是容器的陣列。
使用DataFrame的.value_counts()得到每個容器中的記錄計數,counts_b = csv_read[‘b_price’].value_counts()。
4. 更多
有時候我們不會用均勻間隔的值,我們會讓每個桶中擁有相同的數目。要達成這個標的,我們可以使用分位數。
分位數與百分位數有緊密的聯絡。區別在於百分位數傳回的是給定百分數的值,而分位數傳回的是給定分位點的值。想瞭解更多,可訪問:
https://www.stat.auckland.ac.nz/~ihaka/787/lectures-quantiles-handouts.pdf
我們想把列拆成十分位數,即10個(差不多)相等的容器。要做到這點,我們可以使用下麵的程式碼(你可以一眼看出其和之前方法的相似之處):
# 根據十分位數建立容器
decile = csv_read[‘price_mean’].quantile(np.linspace(0, 1, 11))
# 將容器應用到資料上
csv_read[‘p_price’] = np.digitize(
csv_read[‘price_mean’],
decile
)
.quantile(…)方法可以傳一個(0到1之間的)數字,來表明要傳回的分位數(例如,0.5是中位數,0.25和0.75是上下四分位數)。它也可以傳入一個分位的串列,傳回相應的值的陣列。.linspace(0, 1, 11)方法會生成這個陣列:
[ 0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.]
所以,.quantile(…)方法會以price_mean列的最小值開始,直到最大值,傳回十分位數的串列。
04 編碼分類變數
為資料的探索階段準備的最後一步就是分類變數了。有些軟體包在背後做了這個工作,但最好還是理解這步處理的時機與做法。
統計模型只能接受有序的資料。分類變數(有時根據背景關係可表示為數字)不能直接在模型中使用。要使用它們,我們要先進行編碼,也就是給它們一個唯一的數字編號。這解釋了什麼時候做。至於如何做—應用下述技巧即可。
1. 準備
要實踐本技巧,你要先裝好pandas模組。
其他沒有什麼要準備的了。
2. 怎麼做
pandas又提供了一個方法,幫我們做完所有事(data_dummy_code.py檔案):
# 根據房產型別處理的簡單程式碼
csv_read = pd.get_dummies(
csv_read,
prefix=‘d’,
columns=[‘type’]
)
3. 原理
.get_dummies(…)方法將分類變數轉換為簡單的變數。比如,考慮一個變數,以三種水平中的某一種作為值:
1 One
2 Two
3 Three
需要用三列進行編碼:
1 One 1 0 0
2 Two 0 1 0
3 Three 0 0 1
有時可用兩列。如果有一個水平等效於null的話,我們可以這樣做:
1 One 1 0
2 Two 0 1
3 Three 0 0
.get_dummies(…)方法的第一個引數是DataFrame物件。columns引數指定了程式碼要處理的DataFrame的列(或某些列,因為可以傳入串列)。透過指定字首,我們告訴方法生成的列名以d打頭;本例中生成的列會叫d_Condo。下劃線是預設的,可以透過指定prefix_sep引數更改。
.get_dummies(…)方法的完整引數串列,參見:
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.get_dummies.html
關於作者:託馬茲·卓巴斯(Tomasz Drabas),微軟資料科學家,致力於解決高維特徵空間的問題。他有超過13年的資料分析和資料科學經驗:在歐洲、澳大利亞和北美洲三大洲期間,工作領域遍及高新技術、航空、電信、金融和諮詢。
本文摘編自《資料分析實戰》,經出版方授權釋出。
延伸閱讀《資料分析實戰》
點選上圖瞭解及購買
轉載請聯絡微信:togo-maruko
推薦語:透過大量的現實案例,詳細講解資料分析相關的各種方法。