1. 程式人生 > >用 Python 做資料處理必看:12 個使效率倍增的 Pandas 技巧(上下)

用 Python 做資料處理必看:12 個使效率倍增的 Pandas 技巧(上下)

http://datartisan.com/article/detail/81.html

導語

Python正迅速成為資料科學家偏愛的語言,這合情合理。它擁有作為一種程式語言廣闊的生態環境以及眾多優秀的科學計算庫。如果你剛開始學習Python,可以先了解一下Python的學習路線。
在眾多的科學計算庫中,我認為Pandas對資料科學運算最有用。Pandas,加上Scikit-learn幾乎能構成了資料科學家所需的全部工具。 本文旨在提供Python資料處理的12種方法。文中也分享了一些會讓你的工作更加便捷的小技巧。
在繼續推進之前,我推薦讀者閱覽一些關於資料探索 (data exploration)的程式碼。
為了幫助理解,本文用一個具體的資料集進行運算和操作。本文使用了貸款預測(loan prediction) 問題資料集,下載資料集請到

http://datahack.analyticsvidhya.com/contest/practice-problem-loan-prediction。

開始工作

首先我要匯入要用的模組,並把資料集載入Python環境。


import pandas as pd

import numpy as np

data = pd.read_csv("train.csv", index_col="Loan_ID")

1.布林索引(Boolean Indexing)

如何你想用基於某些列的條件篩選另一列的值,你會怎麼做?例如,我們想要一個全部無大學學歷但有貸款的女性列表。這裡可以使用布林索引。程式碼如下:


data.loc[(data["Gender"]=="Female") & (data["Education"]=="Not Graduate") & (data["Loan_Status"]=="Y"), ["Gender","Education","Loan_Status"]]


想了解更多請閱讀 Pandas Selecting and Indexing

2.Apply函式

Apply是擺弄資料和創造新變數時常用的一個函式。Apply把函式應用於資料框的特定行/列之後返回一些值。這裡的函式既可以是系統自帶的也可以是使用者定義的。例如,此處可以用它來尋找每行每列的缺失值個數:


#建立一個新函式:

def num_missing(x):

  return sum(x.isnull())

#Apply到每一列:

print "Missing values per column:"

print data.apply(num_missing, axis=0) #axis=0代表函式應用於每一列

#Apply到每一行:

print "\nMissing values per row:"

print data.apply(num_missing, axis=1).head() #axis=1代表函式應用於每一行

輸出結果:

由此我們得到了想要的結果。
注意:第二個輸出使用了head()函式,因為資料包含太多行。
想了解更多請閱讀 Pandas Reference (apply)

3.替換缺失值

‘fillna()’ 可以一次解決這個問題。它被用來把缺失值替換為所在列的平均值/眾數/中位數。


#首先匯入一個尋找眾數的函式:

from scipy.stats import mode

mode(data['Gender'])

輸出: ModeResult(mode=array([‘Male’], dtype=object), count=array([489]))
返回了眾數及其出現次數。記住,眾數可以是個陣列,因為高頻的值可能不只一個。我們通常預設使用第一個:


mode(data['Gender']).mode[0]


現在可以填補缺失值,並用上一步的技巧來檢驗。


#值替換:

data['Gender'].fillna(mode(data['Gender']).mode[0], inplace=True)

data['Married'].fillna(mode(data['Married']).mode[0], inplace=True)

data['Self_Employed'].fillna(mode(data['Self_Employed']).mode[0], inplace=True)

#再次檢查缺失值以確認:

print data.apply(num_missing, axis=0)


由此可見,缺失值確定被替換了。請注意這是最基本的替換方式,其他更復雜的技術,如為缺失值建模、用分組平均數(平均值/眾數/中位數)填充,會在今後的文章提到。
想了解更多請閱讀 Pandas Reference (fillna)

4.透視表

Pandas可以用來建立 Excel式的透視表。例如,“LoanAmount”這個重要的列有缺失值。我們可以用根據 ‘Gender’、‘Married’、‘Self_Employed’分組後的各組的均值來替換缺失值。每個組的 ‘LoanAmount’可以用如下方法確定:


#Determine pivot table

impute_grps = data.pivot_table(values=["LoanAmount"], index=["Gender","Married","Self_Employed"], aggfunc=np.mean)

print impute_grps


想了解更多請閱讀 Pandas Reference (Pivot Table)

5.多重索引

你可能注意到上一步驟的輸出有個奇怪的性質。每個索引都是由三個值組合而成。這叫做多重索引。它可以幫助運算快速進行。
延續上面的例子,現在我們有了每個分組的值,但還沒有替換。這個任務可以用現在學過的多個技巧共同完成。


#只在帶有缺失值的行中迭代:

for i,row in data.loc[data['LoanAmount'].isnull(),:].iterrows():

  ind = tuple([row['Gender'],row['Married'],row['Self_Employed']])

  data.loc[i,'LoanAmount'] = impute_grps.loc[ind].values[0]

#再次檢查缺失值以確認:

print data.apply(num_missing, axis=0)


注:

多重索引需要在loc中用到定義分組group的元組(tuple)。這個元組會在函式中使用。
需要使用.values[0]字尾。因為預設情況下元素返回的順序與原資料庫不匹配。在這種情況下,直接指派會返回錯誤。

6. 二維表

這個功能可被用來獲取關於資料的初始“印象”(觀察)。這裡我們可以驗證一些基本假設。例如,本例中“Credit_History” 被認為對欠款狀態有顯著影響。可以用下面這個二維表進行驗證:


pd.crosstab(data["Credit_History"],data["Loan_Status"],margins=True)


這些數字是絕對數值。不過,百分比數字更有助於快速瞭解資料。我們可以用apply函式達到目的:


def percConvert(ser):

  return ser/float(ser[-1])

  pd.crosstab(data["Credit_History"],data["Loan_Status"],margins=True).apply(percConvert, axis=1)


現在可以很明顯地看出,有信用記錄的人獲得貸款的可能性更高:有信用記錄的人有80% 獲得了貸款,沒有信用記錄的人只有 9% 獲得了貸款。
但不僅僅是這樣,其中還包含著更多資訊。由於我現在知道了有信用記錄與否非常重要,如果用信用記錄來預測是否會獲得貸款會怎樣?令人驚訝的是,在614次試驗中我們能預測正確460次,足足有75%!
如果此刻你在納悶,我們要統計模型有什麼用,我不會怪你。但相信我,在此基礎上提高0.001%的準確率都是充滿挑戰性的。你是否願意接受這個挑戰?
注:對訓練集而言是75% 。在測試集上有些不同,但結果相近。同時,我希望這個例子能讓人明白,為什麼提高0.05% 的正確率就能在Kaggle排行榜上跳升500個名次。
想了解更多請閱讀Pandas Reference (crosstab)


7 – 資料框合併

當我們有收集自不同來源的資料時,合併資料框就變得至關重要。假設對於不同的房產型別,我們有不同的房屋均價資料。讓我們定義這樣一個數據框:


prop_rates = pd.DataFrame([1000, 5000, 12000], index=['Rural','Semiurban','Urban'],columns=['rates'])

prop_rates


現在可以把它與原始資料框合併:


data_merged = data.merge(right=prop_rates, how='inner',left_on='Property_Area',right_index=True, sort=False)

data_merged.pivot_table(values='Credit_History',index=['Property_Area','rates'], aggfunc=len)


這張透視表驗證了合併成功。注意這裡的 ‘values’無關緊要,因為我們只是單純計數。
想了解更多請閱讀Pandas Reference (merge)

8 – 給資料框排序

Pandas可以輕鬆基於多列排序。方法如下:


data_sorted = data.sort_values(['ApplicantIncome','CoapplicantIncome'], ascending=False)

data_sorted[['ApplicantIncome','CoapplicantIncome']].head(10)


注:Pandas 的“sort”函式現在已經不推薦使用,我們用 “sort_values”函式代替。
想了解更多請閱讀Pandas Reference (sort_values)

9 – 繪圖(箱型圖&直方圖)

許多人可能沒意識到Pandas可以直接繪製箱型圖和直方圖,不必單獨呼叫matplotlib。只需要一行程式碼。舉例來說,如果我們想根據貸款狀態Loan_Status來比較申請者收入ApplicantIncome:


data.boxplot(column="ApplicantIncome",by="Loan_Status")


data.hist(column="ApplicantIncome",by="Loan_Status",bins=30)


可以看出獲得/未獲得貸款的人沒有明顯的收入差異,即收入不是決定性因素。
想了解更多請閱讀Pandas Reference (hist) | Pandas Reference (boxplot)

10 – 用Cut函式分箱

有時把數值聚集在一起更有意義。例如,如果我們要為交通狀況(路上的汽車數量)根據時間(分鐘資料)建模。具體的分鐘可能不重要,而時段如“上午”“下午”“傍晚”“夜間”“深夜”更有利於預測。如此建模更直觀,也能避免過度擬合。
這裡我們定義一個簡單的、可複用的函式,輕鬆為任意變數分箱。


#分箱:

def binning(col, cut_points, labels=None):

  #Define min and max values:

  minval = col.min()

  maxval = col.max()

  #利用最大值和最小值建立分箱點的列表

  break_points = [minval] + cut_points + [maxval]

  #如果沒有標籤,則使用預設標籤0 ... (n-1)

  if not labels:

    labels = range(len(cut_points)+1)

  #使用pandas的cut功能分箱

  colBin = pd.cut(col,bins=break_points,labels=labels,include_lowest=True)

  return colBin



#為年齡分箱:

cut_points = [90,140,190]

labels = ["low","medium","high","very high"]

data["LoanAmount_Bin"] = binning(data["LoanAmount"], cut_points, labels)

print pd.value_counts(data["LoanAmount_Bin"], sort=False)


想了解更多請閱讀 Pandas Reference (cut)

11 – 為分類變數編碼

有時,我們會面對要改動分類變數的情況。原因可能是:

有些演算法(如羅吉斯迴歸)要求所有輸入專案是數字形式。所以分類變數常被編碼為0, 1….(n-1)
有時同一個分類變數可能會有兩種表現方式。如,溫度可能被標記為“High”, “Medium”, “Low”,“H”, “low”。這裡 “High” 和 “H”都代表同一類別。同理, “Low” 和“low”也是同一類別。但Python會把它們當作不同的類別。
一些類別的頻數非常低,把它們歸為一類是個好主意。

這裡我們定義了一個函式,以字典的方式輸入數值,用‘replace’函式進行編碼。


#使用Pandas replace函式定義新函式:

def coding(col, codeDict):

  colCoded = pd.Series(col, copy=True)

  for key, value in codeDict.items():

    colCoded.replace(key, value, inplace=True)

  return colCoded

 ​

#把貸款狀態LoanStatus編碼為Y=1, N=0:

print 'Before Coding:'

print pd.value_counts(data["Loan_Status"])

data["Loan_Status_Coded"] = coding(data["Loan_Status"], {'N':0,'Y':1})

print '\nAfter Coding:'

print pd.value_counts(data["Loan_Status_Coded"])


編碼前後計數不變,證明編碼成功。
想了解更多請閱讀 Pandas Reference (replace)

12 – 在一個數據框的各行迴圈迭代

這不是一個常見的操作。但你總不想卡在這裡吧?有時你會需要用一個for迴圈來處理每行。例如,一個常見的問題是變數處置不當。通常見於以下情況:

帶數字的分類變數被當做數值。
(由於出錯)帶文字的數值變數被當做分類變數。

所以通常來說手動定義變數型別是個好主意。如我們檢查各列的資料型別:


#檢查當前資料型別:

data.dtypes


這裡可以看到分類變數Credit_History被當作浮點數。對付這個問題的一個好辦法是建立一個包含變數名和型別的csv檔案。通過這種方法,我們可以定義一個函式來讀取檔案,併為每列指派資料型別。舉例來說,我們建立了csv檔案datatypes.csv


#載入檔案:

colTypes = pd.read_csv('datatypes.csv')

print colTypes


載入這個檔案之後,我們能對每行迭代,把用‘type’列把資料型別指派到‘feature’ 列對應的專案。


#迭代每行,指派變數型別。

#注,astype用來指定變數型別。

for i, row in colTypes.iterrows(): #i: dataframe索引; row: 連續的每行  

  if row['feature']=="categorical":

    data[row['feature']]=data[row['feature']].astype(np.object)

  elif row['feature']=="continuous":

    data[row['feature']]=data[row['feature']].astype(np.float)

  print data.dtypes

現在信用記錄這一列的型別已經成了‘object’ ,這在Pandas中代表分類變數。
想了解更多請閱讀Pandas Reference (iterrows)

結語

本文中我們介紹了多個可以幫助我們減輕資料探索、特徵工程工作負擔的函式。此外,我們也定義了一些函式,這些函式可以在不同的資料集上覆用以獲得相同效果。
原作者:AARSHAY JAIN 
翻譯:王鵬宇
原文地址:
http://www.analyticsvidhya.com/blog/2016/01/12-pandas-techniques-python-data-manipulation/


​ ​