R是進行運算、清洗、匯總及生成概率統計等數據處理的一個絕佳選擇。此外,由于它獨立于平臺、短期內不會消失,所以生成的程序可以在任何地方運行。并且,它具備非常棒的輔助資源。
本文摘錄自James D.Miller撰寫的《數據科學統計學》(Statistics for Data Science)一書,該書由Packt Publishing出版。
R是一種易上手的語言和環境,它本身很靈活且專注于統計計算,因此成為運算、清洗、匯總及生成概率統計等數據處理的一個絕佳選擇。
此外,以下是用R進行數據清洗的其他原因:
由于大量數據科學家都在使用R,所以它短時間內不會消失。
R獨立于平臺,因此可以在任意地方運行程序。
R有絕佳的輔助資源---Google一下,你就可以看到。
注:盡管作者將示例數據命名為“賭博數據”(Gamming Data),它只是用來演示代碼的賭博數據。
離群點
對離群點最簡單的解釋是:離群點是和其余數據不匹配的數據點。按照慣例,任何過高、過低或者異常(基于項目背景)的數據都是離群點。作為數據清洗的一部分,數據科學家通常要識別出離群點并用通用的方法解決它:
刪除離群點的值,甚至是離群點對應的實際變量。
轉換變量值或變量本身。
讓我們來看一下實際案例中如何用R識別并解決數據離群點。
老虎機在賭博界十分流行(老虎機的操作方法是把硬幣投入到機器中,并拉動把手來決定回報)。如今大部分老虎機都電子化了,編程使它們的所有活動都能被持續追蹤。在本文的案例中,賭場的投資者希望利用這些數據(以及各種補充數據)來調整盈利策略。換句話說,什么能讓老虎機賺更多錢?是機器的主題還是類型?新機器比舊機器或老式機器更有利可圖嗎?機器的位置會產生怎樣的影響?低面額的機器會賺更多錢嗎?我們嘗試用離群點來找到答案。
給定一個集合或賭博數據庫(格式為逗號分隔或CSV文本文件),其中包括的數據如老虎機的位置、錢的面額、月份、日、年、機器類型、機器的年齡、促銷、優惠券、天氣和投幣量(投幣量是放入機器的錢幣總額減去支付的數額)。
作為一個數據科學家,第一步要對數據進行綜評(有時稱為概述),此時我們要確定是否存在異常值,第二步是解決這些離群點。
步驟一 數據概述
R使這一步驟變得非常簡單。盡管可以通過很多方式編程求解,但我們要嘗試用最少的程序代碼或腳本來解決問題。將CSV文件定義為R的變量(命名為MyFile)并將文件讀入為數據框(命名為Mydata):
MyFile《-“C:/GammingData/SlotsResults.csv” MyData《- read.csv(file=MyFile, header=TRUE, sep=“,”)
在統計學上,箱型圖是一種簡單的方式以得到統計數據集的分布、變異性和中心(或中位數)相關信息,所以我們將用箱型圖來研究我們能否識別出中位數Coin-in以及能否找到離群點。為了達成這些,我們可以讓R畫出文件中每個老虎機的Coin-in值,繪制箱型圖的函數如下:
boxplot(MyData[11],main=‘GammingData Review’, ylab = “Coin-in”)
注:Coin-in是文件中的第11列,所以直接將它作為boxplot函數的參數。此外還添加了一個可選擇的參數(再次強調,本文已盡量保持代碼的簡潔度),以便在可視化圖中添加標題。
執行前文的代碼可以得到下圖效果,包括中位數(中位數在箱型圖中是中間橫穿的線)以及四個離群點:
步驟2-處理離群點
現在我們發現數據中確實存在離群點,我們要解決這些點以保證它們不會對本研究產生負面影響。首先,我們知道Coin-in有負值是不合理的,因為機器輸出的錢幣一定不會比投入到機器中的硬幣多。基于這個原則,我們可以從文件中刪除Coin-in為負值的記錄。此外,R可以幫助我們用subset生成一個新的數據框,新數據集中只有Coin-in中的非負值。
我們要將subset數據框命名為noNegs:
noNegs《- subset(MyData, MyData[11]》0)
接下來,我們要再一次畫圖以確定已經刪除負值離群點:
boxplot(noNegs[11],main=‘GammingData Review’, ylab = “Coin-in”)
這就產生了新的箱型圖,如下圖中所示:
我們可以用同樣的方法去除Coin-in中極端的正值(大于1500美元)得到另一個數據子集并再次畫圖:
noOutliers《-subset(noNegs, noNegs[11]《1500) boxplot(noOutliers[11],main=‘GammingData Review’, ylab = “Coin-in”)
當你對數據進行不同的迭代后,建議你保存大部分版本的數據(如果不是最重要的)。你可以用write.csv這個R函數:
write.csv(noOutliers,file=“C:/GammingData/MyData_lessOutliers.csv”)
注:大部分數據科學家在整個項目中采取通用的命名規律。文件的名字應該盡可能清晰以便今后幫助你節省時間。此外,特別是在處理大量數據時,你需要注意內存空間的問題。
以上代碼的輸出結果如下:
領域知識
接下來,另一個數據清洗的技術是基于領域知識清理數據。這并不復雜,這種技術的關鍵是使用數據中無法察覺的信息。例如,當我們知道Coin-in不可能有負值時,我們排除了Coin-in負值的情況。另一個案例是颶風Sandy襲擊美國東北部的時間。在這段時間內,機器的Coin-in值都很低(非零)。數據科學家應該基于信息判斷是否要移除某段特定時期內的數據。
有效性檢查
交叉驗證是一種幫助數據科學家在數據庫中使用規則的技術。
注:有效性檢查是統計數據清洗中最普遍的形式,并且是數據開發者和數據科學家都非常熟悉的流程。
數據清洗時可以設定任意數量的有效性原則,這些原則要遵循數據科學家的意圖或目標。例如有如下原則:數據類型(例如,某個字段一定要是數值型),范圍限制(數據或日期要在一個特定范圍內),要求(某個字段不能為空或沒有值),唯一性(一個字段,或字段的結合,一定是數據庫中唯一的),組成員(這個值一定是列表中的值),外鍵(案例中一定要被定義的明確的值或滿足特殊規則),正則表達式模式(簡單地說就是這個值的格式滿足預設的格式),交叉字段驗證(案例中的字段組合要滿足特定標準)。
按照前文提到的內容,我們來看一些案例,從數據類型開始(也稱為強制原則)。R提供的六個強制函數如下:
as.numeric
as.integer
as.character
as.logical
as.factor
as.ordered
as.Date
這些函數,結合一些R的知識,使得在數據庫中轉換數據變得簡單。例如,以前文的賭博數據為例,我們可以生成新的賭博結果文件,其中年齡值被存為字符型(或文本值)。為清理它,我們需要將其轉化為數據型。我們可以運用以下R代碼完成快速轉化:
noOutliers[“Age”]《-as.numeric(noOutliers[“Age”])
一個需要注意的地方:用這種簡單方法時,如果有數據不能轉化,需要將其設定為NA值。在類型轉換中,最大的工作是理解需要輸入什么數據以及哪些數據類型是合法的;R有很廣泛的數據類型,包括標量、向量(數值型,字符型,邏輯型),矩陣,數據框及列表。
數據清洗中我們要關注的另一個領域是正則表達式。在實踐中,特別是當處理的數據來源于很多渠道時,數據科學家確實面對如下問題:字段不是理想的格式(對于當下目標而言)或者字段值的格式不一致(可能會引發錯誤的結果)。例如日期、社會安全號碼(SSN)以及手機號碼。基于數據的來源,你不得不重新輸入(如前文描述),但是通常情況下,你需要基于目標將數據重新定義為可以使用的模式。
注:重新輸入數據是很重要的,這樣R就知道將值作為目前的數據并且你可以正確使用各種R數據函數。
一個常見的案例是當數據包括形式為YYYY/MM/DD的日期數據時,你想按每周匯總的形式呈現出時間序列分析,或者其他需要日期值的操作但是可能需要重新定義日期格式,或者你需要將其變為R日期類型。所以,假定一個新的賭博文件——只有兩列數據:日期和投幣量,這個文件是一個老虎機每天的投幣量。
新的文件記錄如下截圖所示:
數據科學家可以用各種數據清洗的案例。從驗證每個數據點的數據類型入手,我們可以用R函數class來驗證文檔的數據類型。首先(如我們在前文案例中所作),讀入CSV文件存為數據框:
MyFile《-“C:/GammingData/SlotsByMachine.csv” MyData《- read.csv(file=MyFile, header=TRUE, sep=“,”)
隨后,我們可以使用class函數,如下圖截圖所示:
從上圖中可以看到用class來顯示數據類型。
MyData是用來保存賭博數據的數據框,日期Date是向量類型,投幣量Coinin是一個整數。所以,數據框和整數是有意義的,但是要注意R將日期設置為向量(factor)類型。向量是分類變量,在匯總統計、繪圖和回歸中非常有用,但它不是非常適用日期型。為了解決這個問題,我們可以使用R函數substr和paste,如下所示:
MyData$Date《-paste(substr(MyData$Date,6,7),substr(MyData$Date,9,10), substr(MyData$Date,1,4),sep=“/”)
以上代碼重新定義了日期字段的格式。它將數據字段值分成三部分(月、日和年)然后按照理想的順序(/分隔符(sep))粘貼在一起,如下截圖所示:
我們發現這一行腳本將日期字段轉換為字符類型,最后我們可以用as.Date函數將值重設為日期(Date)類型:
稍微嘗試一下,就可以重新格式化來得到理想的字符串或字符數據點。
改善數據
通過改善進行數據清理是另一種常見的技術,添加相關信息、事實或數據使得數據變得完整(可能更有價值)。這些附加數據的來源可以是用數據中現有信息或從其他來源添加信息進行計算。數據科學家花費時間完善數據的原因有很多。
基于當前的目的或目標,數據科學家補充的信息可能用于參考、比較、對比或發現趨勢。
典型的用例包括:
衍生事實計算
對比日歷與財政年度的使用
轉換時區
貨幣轉換
添加當前和前期指標
計算價值,如每天總出貨量
保持緩慢變化的維度
注:作為數據科學家,你要經常用腳本來改善數據,這個方法要比直接編輯數據文檔好得多,因為這樣出錯的可能性更低并且可以維持原始文件的完整性。此外,建立腳本可讓你將改善的過程重復應用于多個文件或收到的新版文件中,不需要重做同樣的工作。
回到我們的賭博數據中,假定我們在接收老虎機的投幣量文檔,同時公司在美國大陸外的地方設立賭場。這些新地點正在向我們發送文件,并且數據將納入到我們的統計分析中。我們發現這些國際文件是以當地貨幣計算的投幣量。為了正確地對數據建模,我們要將數據轉化為美元。
場景如下:
文件來源:英國
使用貨幣:英鎊
將英鎊轉化為美元的公式十分簡單,只要用數額乘以匯率即可。所以,在R中:
MyData$Coinin《-MyData$Coinin* 1.4
以上代碼可以完成我們想要的轉換;然而,數據科學家要決定那種貨幣將被轉化(英鎊)以及匯率應當是多少。這并不是什么大問題,但是我們可以嘗試創建一個用戶定義的函數來確定要使用的匯率,如下所示:
getRate《- function(arg){ if(arg==“GPB”) { myRate 《- 1.4 } if(arg==“CAD”) { myRate 《-1.34 } return(myRate) }
盡管之前的代碼更簡單,但以上代碼說明了創建邏輯的要點,以便我們今后可以重復使用:
最終,為了使整個過程更完美,我們要將函數儲存(在R文檔中)以便將來使用:
source(“C:/GammingData/CurerncyLogic.R”)
隨后:
MyFile《-“C:/GammingData/SlotsByMachine.csv” MyData《- read.csv(file=MyFile, header=TRUE, sep=“,”) MyData$Coin《- MyData$Coinin * getRate(“CAD”)
注:當然,在最理想的情況下,我們可改進函數以便在表或文件中根據國家代碼查找匯率,這樣匯率能夠隨即時價值而改變并且可以從程序中解耦數據。
數據調和
基于研究分析的整體目標,數據科學家可以通過數據調和來轉換、翻譯、或將數據值映射到其他理想值。最普遍的案例是性別或國家代碼。例如,如果你的文檔中將性別編碼為0和1或M和F,你想將數據轉化為一致的MALE或FEMALE。
關于國家代碼,數據科學家想要繪制地區的匯總:北美、南美和歐洲,而不是分開的美國、加拿大、墨西哥、巴西、智利、英國、法國和德國。在這種情況下,將產生合計值如下:
北美=美國+加拿大+墨西哥
南美=巴西+智利
歐洲=英國+法國+德國
需要強調的是,數據科學家可能會將所有包括性別的調查文檔合并在一起,稱為gender.txt,但是文檔中的性別編碼不同(1,0,M,F,Male和Female)。如果我們嘗試用R函數表,我們會看到如下可理解的結果:
如果在最理想的狀態下進行可視化分析:
lbs= c(“Male”, “Female”) pie(table(MyData),main=“Gambling by Gender”)
我們看到如下截圖:
為了解決性別數據編碼不一致的問題,我借用了前文案例中的概念并生成簡單的函數來幫助我們重新編碼:
setGender《- function(arg){ if(substr(arg,1,1)==“0”| toupper(substr(arg,1,1))==“M”) { Gender 《- “MALE” } if(substr(arg,1,1)==“1”| toupper(substr(arg,1,1))==“F”) { Gender 《- “FEMALE” } return(Gender) }
此次,我加入了toupper函數,因此我們不必擔憂大小寫,并且有substr來控制長度大于一個字符的值。
注:假定參數的值是0,1,m,M,f,F,Male或Female,否則將會引發報錯。
由于R將性別作為向量類型,我發現很難應用簡單的函數,所以我決定生成新的R數據框來容納調和后的數據。并且用一個循環來讀入文檔中的記錄并將其轉化為Male 或Female:
MyFile《-“C:/GammingData/Gender.txt” MyData《- read.csv(file=MyFile, header=TRUE, sep=“,”) GenderData《-data.frame(nrow(MyData)) for(iin 2:nrow(MyData)) { x《-as.character(MyData[i,1]) GenderData[i,1] 《-setGender(x) }
現在我們將通過以下語句得到更適合的可視化結果:
lbls= c(“Male”, “Female”) pie(table(GenderData),labels=lbls, main=“Gambling by Gender”)
以上代碼的輸出結果如下所示:
標準化
大多數主流數據科學家都已經注意到在開始統計研究或分析項目之前,將數據標準化作為數據清理過程一部分的重要性。這是很重要的,如果沒有標準化,量綱不同的數據點對分析的貢獻會不均等。
如果你認為在0到100之間的數據點比0到1范圍內的變量影響更大,你可以理解數據標準化的重要性。使用這些未經過標準化的變量,事實上在分析中賦予較大范圍的變量更多的權重。為了解決這一問題并均衡這些變量,數據科學家試圖將數據轉化為可比的量綱。
數據點的中心化是數據標準化中最常見的例子(盡管還有很多)。為了使數據點中心化,數據科學家把文件中的每個數據點減去所有數據的平均值。
R不是做運算,它提供了scale函數,其默認方法可以通過一行代碼將文件中的數值中心化或縮減。讓我們來看一個簡單的例子。
回到老虎機的案例中!在我們的賭博文件中,你可能還記得有一個字段叫投幣量(Coinin),它是一個表示投入到機器中美元總額的值,這被看作衡量機器盈利能力的指標。這似乎是我們盈利能力分析中使用的一個重要的數據點。然而這些金額可能是誤導性的,因為不同的機器有不同面額(換句話說,一些機器接受美分,而其他機器接受一角硬幣或美元)。也許機器面值的差別造成了不同的量綱,我們可以使用scale函數來解決這種情況。首先,我們在下面的截圖中看到,Coin.in的值:
我們可以通過以下語句對數據點Coin.in進行中心化處理:
scale(MyData[11],center = TRUE, scale = TRUE)
center的值決定了如何行中心化。center為TRUE是需要對應的行減去Coin.in均值(省略NA)。scale的值決定了如何行縮放(在中心化之后)。如果scale的值是TRUE且center值是TRUE,那么縮放是通過除以(中心化后的)Coin.in的標準差來進行的。如果center值是False,將得到均方根值。
在下圖截屏中看到了差別:
評論
查看更多