天天看點

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

本節書摘來自華章計算機《資料科學r語言實踐:面向計算推理與問題求解的案例研究法》一書中的第2章,第2.3節,作者:[美] 德博拉·諾蘭(deborah nolan)  鄧肯·坦普·朗(duncan temple lang)  更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

本節我們考慮如何将特征矩陣清單menresmat轉換為合适的格式以便于資料分析。目前,這些資料值都是字元型,這對于諸如找到參賽者年齡的中位數這樣的資料分析是無益的。但是,我們可以利用as.numeric()函數很容易地将年齡轉換為數值型。我們需要将整個矩陣都轉換為數值型矩陣嗎?事實并非如此,比如将參賽者的名字轉換為數值型就毫無意義。為此,我們需要建立一個可以允許擁有不同類型變量的資料框。現在我們有6個變量:參賽者姓名、居住地、年齡以及3種類型的時間。正如剛才所說,我們将年齡轉換為數值型,而名字依然保留為字元型。那麼其他的變量呢?我們或許也要将居住地保留為字元型。

時間被存儲為一個字元串,其格式為hh:mm:ss。為了更容易地生成概要和進行模組化,我們需要将時間轉換為數值型。一種可行的方式是将時間轉換為分鐘數,即hh*60+mm+ ss/60。要執行這樣的計算,我們必須将時間字段分割為成組片段,然後把每組片段轉換為數值型。strsplit()函數能夠幫助我們在諸如冒号的地方對字元串進行分割,另外,我們需要讓3種不同類型的記錄時間(比賽時間、淨時間和平常的終場時間)保持一緻。淨時間被認為要比比賽時間更加準确,是以當可以獲得淨時間時,我們就使用淨時間,否則,使用比賽時間或終場時間中被記錄的任何一種。當然我們也可以儲存全部3種不同類型的時間,由分析師來判斷它們之間的關系并決定使用哪種時間,但是現在為了處理簡便,我們僅考慮為每個參賽者記錄一種時間。

在将字元串轉換為數值型值之前,我們還要考慮是否應該建立一些新的變量。如果要将所有14年的記錄資料整合到一個資料框中,那麼我們就應該記錄這些資料的年份。同樣,如果我們将男女參賽選手的資料整合到一起,那麼就需要一個變量來表示選手的性别。使用rep()函數可以很簡單地實作這些操作。

我們首先使用as.numeric()函數來建立數值型變量—年齡,以2012年的男選手資料為例:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

我們收到了一條警告資訊,提示在将年齡從字元型轉化到數值型的過程中出現了na值,這意味着一些值并不對應相應的數字。要想進一步探究出現這些資訊的具體原因,首先我們要檢查age。

我們根據參賽選手每年的年齡分布,建立并排式箱線圖以快速地檢查年齡值的合理性。

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

圖2-4揭示了其中2個年份資料中的問題。2003年中所有參賽選手的年齡均小于10,而2006年超過1/4的參賽選手的年齡小于10,顯然這是有問題的。

圖2-4 曆年年齡資料的箱線圖。這些并排的年齡箱線圖表明2003年和2006年的資料存在一些問題。這幾年的參賽選手異乎尋常地年輕

下面讓我們來檢查2003年和2006年的原始文本。

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

我們注意到在2003年中,年齡值列相較于它對應的‘=’字元向右偏移了一位,這就意味着我們隻取了年齡值中十位上的數字。而在2006年,某些行(并不是所有行)的年齡值向外溢出了一個字元。

我們可以簡單地通過将列之間的空白字元包含到列值中來解決這兩個問題,在執行資料抽取時,通過改變每個變量結束的索引就可以實作這個操作。也就是說,修改selectcols()函數中定位每一列結束位置的那行代碼以包含空白字元,即

當我們在selectcols()函數中使用這個修改計算後,每個字段後的空白字元将會被包含進來。而這并不影響我們将文本資料轉換為數值型,而且如果不想讓字元型變量以空白符結尾,我們也可以通過正規表達式很容易地移除這些空白字元。

在确認年齡從字元型轉換為數值型的過程中,發現了資料抽取中存在的問題。我們需要修改2.2節中的輔助函數selectcols()來處理這個問題。由于我們要不斷去檢查得到的資料是否合理,是以該處理是個疊代的過程。當發現無意義的結果時,需要進一步研究它們,這可能使得我們需要折回到前面的步驟以清理髒資料。

在修改了selectcols()函數中的這一行代碼後,我們将更新後的函數版本再次應用于比賽結果表中,使用箱線圖重新對資料進行彙總統計,此時我們會發現存在過多年輕選手的問題已經清除(見圖2-5)。

現在我們轉向上面處理字元型年齡轉換為數值型時顯示的警告消息,有幾條消息為“na introduced by coercion”。統計每年資料中na值的個數:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

圖2-5 曆年年齡資料的箱線圖。這些并排的年齡箱線圖顯示了一個合理的年齡分布。例如,所有年度年齡範圍的下四分位數都在29到32之間。之前發現的2003年和2006年的資料問題已被解決

2001年中有61個值為na的年齡,我們需要探讨此問題。為了更便于工作,我們使用如下方式,将2001年的年齡資料指派到名為age2001的向量中:

下面我們來檢查與向量age2001中某個na值對應的原始檔案中的行。回想一下,我們在抽取變量值之前舍棄了原始檔案的頭部,是以,為了能夠從原始資料表中讀取到正确的行,我們需要給age2001中na所在行的位置加上一個偏移量。采用如下方法确定偏移量:

然後我們采用下列方法,找到在原始檔案中年齡值為壞資料的行:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

除了最後一行外,其餘的行全部為空串。最後一行對應的是腳注,它定義了“#”辨別符的含義。那麼這些空白行在表格中又處于什麼位置呢?

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

可見這些空白行分散在檔案中的各個位置。我們可以在資料抽取過程中使用正規表達式檢測這些空白行并把它們移除。

上述表達式将定位所有隻包含空格的行。grep的第一個參數采用幾個元字元指定我們将要比對的字元串模式。其中,“^”錨定字元串的開始位置,“$”錨定字元串的結尾,“[[:blank:]]”指代的是空格符或tab符的等價類,“”表示空格符可以出現0次或多次。整個表達式“^[[:blank:]]$”表示能夠比對從開頭到結尾含有任意個空格符的字元串。也就是隻含有空格字元的行。

采用一個簡單的表達式可以定位注解行,即以“#”或“*”開始的行。通過修改extractvariables()函數移除那些我們不想要的行,在此我們将該任務留作練習。通過添加上述代碼對資料表進行額外的清洗後,2001年中的61個na都被消除了,同時其他年份中的很多(但不是所有的)na也被消除了。

繼續考察圖2-5所暴露的另一個問題:2001、2002、2003年中年齡的最小值都很小,接近于0,這顯然是不可能的。下面讓我們找出這些年齡值小于5的參賽選手,并從原始資料表中找出他們的參賽資訊。以2001年資料為例:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

顯然有許多參賽選手的年齡輸入為0!鑒于這些都是表中的實際值,我們在後面分析資料時,可以根據需要來決定如何處理這些選手的資料。至此,我們已經成功地建立了年齡變量。然而,由于一個變量基于位置上的錯誤往往會導緻在其他變量上産生錯誤,一般我們需要對所有變量同時進行清洗。這樣,在清洗其他變量時,我們可能也需要再次檢查年齡資料,以確定年齡值仍然有效。

接下來我們進行時間變量的建立。如本節開頭所述,時間格式顯示為hh:mm:ss,我們希望将它轉換成分鐘數。然而,為了執行轉換,我們必須将時間字段分割為分組片段的形式。此外,一些參賽選手的比賽用時不到一個小時,這樣他們的時間顯示稍有不同,即為mm:ss,是以,該過程中我們需要能夠處理上述兩種格式。為了簡便起見,我們同樣從轉換某一年的時間變量開始,如以2012年的資料為例。編寫如下代碼來建立向量:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

處理過程中字元串中的“:”将被舍棄,從strsplit()函數傳回的是一個字元向量清單。每個輸入的時間字元串對應一個向量,向量中的元素為時間字元串中被每個“:”分開的片段。我們通過檢查第一個和最後一個時間來确認分割是否正确,即

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

顯然我們的時間轉換是正确的。在前面我們看到2012年最快的參賽選手完成比賽用時為45分15秒,也就是45.25分鐘;最慢的參賽者用時為2小時30分鐘59秒,也就是将近151分鐘。在此我們留一練習:将該轉換過程封裝到名為converttime()的函數中。

下面我們将這些轉換過程打包到一個函數中,并将此函數應用于menresmat中的字元矩陣,然後傳回一個包含所有變量的資料框以用于分析,我們将這一函數命名為createdf()。除了将字元串轉換為數值外,我們還另外建立兩個新的變量,year和sex。為了做到這一點,必須從輸入參量得知我們正在清洗的是哪一年份的資料,以及結果是關于男選手還是女選手的。最後,我們還要以淨時間優先的方式,從3種可用的時間變量中選擇将哪一種時間包含到資料框中。函數定義如下:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

将這個新函數createdf(),應用于所有男選手的結果資料,如下:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

對收到的警告消息進行檢查:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

以上警告中提示的轉換問題很可能來自于将時間由字元串轉換成分鐘數的轉換過程中,因為前面我們已經處理了年齡的轉換問題。下面檢查runtime中出現的na值的個數:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

可以看到在2007年、2009年和2010年出現了大量的na,并且顯示在2006年資料中所有跑步時間的值都是na。

下面讓我們先檢查幾個在2007、2009 和2010年中跑步時間為na值的記錄。我們發現這是由于有的選手隻完成了一半的比賽而沒有最後的終場時間,另外就是一些選手的時間後面帶有腳注符号,例如:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

我們可以簡單通過修改createdf()函數來消除時間資訊中帶有的腳注符(“#”和“*”),并去掉那些沒有完成比賽的選手記錄。函數修正如下:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

當我們将修改後的函數應用于menresmat以建立資料框後,除了2006年的資料之外,其餘年份中所有在時間上的缺失值都消失了。

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

對2006年檔案的頭部進行仔細觀察,我們便可發現問題所在,但是為了内容簡潔,我們還是将此問題留作練習。

最後,對于作為輸入的資料框清單,用do.call()函數去調用rbind(),把所有年份的比賽結果和男選手的資料整合到一個資料框中。方法如下:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

do.call()函數讓我們很友善地将清單中的元素作為單個參量傳入一個函數中。例如,rbind()函數的第一個參量是“…”,即

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

“…”參量表示允許調用者向此函數傳入任意數量的參量,就rbind()函數而言,這些傳入的參量被合并成為一個對象。我們也可以使用以下方法調用rbind()函數:

《資料科學R語言實踐:面向計算推理與問題求解的案例研究法》一一2.3 資料清洗和變量格式化

但這樣的話就有些繁瑣,并且需要提前知道mendf包含14個資料框。使用do.call()函數,可以将這些輸入作為一個清單提供給rbind()函數作為參量,然後do.call()為我們一起調用rbind()函數。

檢查合并後的資料框次元:

另外,我們對cbmen中變量的概要進行檢查,檢視是否還有問題出現,如在将各個資料框強制綁定到一起時可能産生的問題。

在這14年的比賽中,有70 070名男選手完成了櫻花公路賽。另外,也有70 000多名女選手完成了該項賽事。這裡我們将對女選手比賽結果的處理留作練習。在下一節,我們将進一步檢視比賽結果。

繼續閱讀