R語言中數據清洗、整理的方法

1 人贊了文章

R語言中數據清洗、整理的方法

數據的清理

如同列夫託爾斯泰所說的那樣:「幸福的家庭都是相似的,不幸的家庭各有各的不幸」,糟糕的噁心的數據各有各的糟糕之處,好的數據集都是相似的。一份好的,乾淨而整潔的數據至少包括以下幾個要素:

1、每一個觀測變數構成一列

2、每一個觀測對象構成一行

3、每一個類型的觀測單元構成一個表就像我們最常接觸的鳶尾花數據: ## Sepal.Length Sepal.Width Petal.Length Petal.Width Species ## 1 5.1 3.5 1.4 0.2 setosa ## 2 4.9 3.0 1.4 0.2 setosa ## 3 4.7 3.2 1.3 0.2 setosa ## 4 4.6 3.1 1.5 0.2 setosa ## 5 5.0 3.6 1.4 0.2 setosa每一列就是觀測的指標:花瓣長度,花瓣寬度,萼片長度,萼片寬度,種類;每一行就是一株鳶尾花的觀測值,構成整張表的元素就是四個數值變數,一個分類分類變數。

然而出於排版的考慮我們抓下來的數據往往不是那麼的友好,比如說我們可以看到的數據通常是這樣的:

## religion <10k 10k-50k 50k-100k ## 1 Agnostic 12 31 23 ## 2 Buddhist 58 43 43 ## 3 Catholic 79 56 23而不是: ## religion income freq ## 1 Agnostic <10k 12 ## 2 Agnostic 10k-50k 58 ## 3 Agnostic 50k-100k 79

## 4 Buddhist <10k 31

當然,除了這種把列表每一列代表一些數值這種情況外,還有多個變數儲存為一列(比如列表不僅以"<10k","10k-50k","50k-100k"做表頭,甚至還加上性別信息"m<10k","m10k-50k","m50k-100k","f<10k","f10k-50k","f50k-100k",其中m代表男性,f代表女性),還有更過分的將列表的變數不僅儲存在列中,行中也有統計變數。面對這些不好的table,我們首先要做的就是數據管理,將數據整理為一個乾淨的數據集。

數據管理

按照en:DAMA的定義:「數據資源管理,致力於發展處理企業數據生命週期的適當的建構、策略、實踐和程序」。這是一個高層而包含廣泛的定義,而並不一定直接涉及數據管理的具體操作(如關係資料庫的技術層次上的管理)。我們這裡主要講述對於數據的變數命名與數據的合併,旨在方便數據共享。數據管理首先要做的就是大致上瞭解你的數據,比如有什麼樣的變數,每一行大致長成什麼樣,最常用的就是head(),tail().我們要做的基本上就是這麼幾項工作: 給每一個變數命名,而不是V1,V2,如果有必要可以給出code book。 每個變數名最好具有可讀性,除非過長,否則不要用縮寫,例如AgeAtDiagnosis這個命名遠好於AgeDx。 通常來說,最好將數據放在一張表裡面,如果因為數據過多,項目過雜,分成了幾張表。那麼一定需要有一列使得這些表之間能夠連接起來,但盡量避免這樣做。

我們以UCI的Human Activity Recognition Using Smartphones Data Set 為例來看看數據是如何變成一個基本符合要求的數據。這個數據我們已經下載下來了,其中關於數據的詳細信息可以參閱read me文檔,由於UCI的數據通常都是一個基本合乎規範的數據集(主要是指它的數據集的變數名都是以V1,V2來命名的)加上一個code

book。那麼我們看看各個數據的名稱(在feature文件裏)> setwd("C:/R/UCI HAR Dataset")> name<-read.table("./features.txt",stringsAsFactors = F)> head(name) V1 V21 1 tBodyAcc-mean()-X2 2 tBodyAcc-mean()-Y3 3 tBodyAcc-mean()-Z4 4 tBodyAcc-std()-X5 5 tBodyAcc-std()-Y6 6 tBodyAcc-std()-Z我們可以看到各個特徵的名稱直接標在數據上是非常不友善的,我們為了讓他具有可讀性,我們以展示在我們眼前的6個數據為例:variablename <- head(name)# 將標籤中的大寫字母轉為小寫,我們這裡沒有所以不再賦值,如果需要全變為大寫,可以使用touppertolower(variablename$V2) ## [1] "tbodyacc-mean()-x" "tbodyacc-mean()-y" "tbodyacc-mean()-z" ## [4] "tbodyacc-std()-x" "tbodyacc-std()-y" "tbodyacc-std()-z"# 將變數名分離成3部分splitNames <- strsplit(variablename$V2, "-")splitNames[[1]]## [1] "tBodyAcc" "mean()" "X"# 將變數名合成有意的名稱named <- function(x) { rr <- paste(x[2], x[1], "-", x[3], sep = "")

chartr("()", "of", rr)

}sapply(splitNames, named) ## [1] "meanoftBodyAcc-X" "meanoftBodyAcc-Y" "meanoftBodyAcc-Z" ## [4] "stdoftBodyAcc-X" "stdoftBodyAcc-Y" "stdoftBodyAcc-Z"用這樣的名字給數據集命名就感覺舒服多了,我們將一些R中對字元串常用的操作函數總結如下,方便我們對數據名稱的修改: sub:替換字元串中的第一個模式為設定模式(pattern). gsub:全局替換字元串中的相應模式 grep,grepl:這兩個函數返迴向量水平的匹配結果,grep僅返回匹配項的下標,而grepl返回所有的查詢結果,並用邏輯向量表示有沒有找到匹配。 nchar:統計字元串單字數目

substr:取子串

paste:將字元串鏈接起來,sep參數可以設置連接符 str_trim:去掉字元串空格變數的名稱建議滿足如下要求: 英文變數名儘可能用小寫 儘可能的描述清楚變數特徵 (Diagnosis versus Dx) 不要太複雜 不要有下劃線、點、空格字元型變數應該滿足: 是因子類型的應該轉化為factor

因子儘可能具有一定的描述性 (例如:如果0/1表示真假,那麼用TRUE/FALSE代替0/1;在表示性別時用Male/Female代替M/F)

接下來我們討論數據集的合併,主要使用函數merge。我們以下面兩個數據集的合併為例:df1 <- data.frame(id = sample(1:10), reviewer_id = sample(5:14), time_left = sample(1321:1330), x = rnorm(10))df2 <- data.frame(id = sample(1:10), answer = rep("B", 10), time_left = sample(321:330), y = rnorm(10))head(df1, n = 3) ## id reviewer_id time_left x ## 1 3 9 1326 -0.9232 ## 2 10 5 1322 2.5069

## 3 1 14 1330 2.2478

head(df2, n = 3) ## id answer time_left y ## 1 1 B 329 0.8180 ## 2 10 B 327 1.4639 ## 3 9 B 323 0.8141merge函數調用格式為: merge(x, y, by = intersect(names(x), names(y)), by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,

sort = TRUE, suffixes = c(".x",".y"),

incomparables = NULL, ...)參數說明: x,y:兩個數據框 by, by.x, by.y:指定用於合併的列的名稱。 all,all.x,all.y:默認的all = FALSE相當於自然連接, 或者說是內部鏈接. all.x = TRUE是一個左連接, all.y = TRUE是一個又連接, all = TRUE 相當於一個外部鏈接.仔細觀察下面3個例子你就會發現其中的奧祕:mergedData <- merge(df1,df2,by.x="reviewer_id",by.y="id",all=TRUE)head(mergedData) ## reviewer_id id time_left.x x answer time_left.y y ## 1 1 NA NA NA B 329 0.8180 ## 2 2 NA NA NA B 330 -0.7706 ## 3 3 NA NA NA B 325 -0.4851mergedData <- merge(df1,df2,by.x="id",by.y="id",all=TRUE)head(mergedData) ## id reviewer_id time_left.x x answer time_left.y y ## 1 1 14 1330 2.24783 B 329 0.8180 ## 2 2 12 1324 1.03181 B 330 -0.7706 ## 3 3 9 1326 -0.92317 B 325 -0.4851 ## 4 4 7 1321 -0.07841 B 322 0.1801mergedData2 <- merge(df1,df2,all=TRUE)head(mergedData2) ## id time_left reviewer_id x answer y ## 1 1 329 NA NA B 0.8180 ## 2 1 1330 14 2.2478 <NA> NA ## 3 2 330 NA NA B -0.7706在plyr包中還提供了join,join_all,arrange等函數來實現表的連接,但我想merge這個函數已經足夠用了,所以我們不在多說。當然,在極少數特別好的情況下(比如列的變數是一致的,或者行的觀測個體是一致的時候)rbind,cbind也是有用的。有些時候我們會遇到一些特殊的字元串:日期。R中提供了各式各樣的函數來處理時間:Sys.setlocale("LC_TIME", "C")## [1] "C"x <- c("1jan1960", "2jan1960", "31mar1960", "30jul1960")z <- as.Date(x, "%d%b%Y")format(z, "%a %b %d")## [1] "Fri Jan 01" "Sat Jan 02" "Thu Mar 31" "Sat Jul 30"weekdays(z)## [1] "Friday" "Saturday" "Thursday" "Saturday"julian(z) ## [1] -3653 -3652 -3563 -3442 ## attr(,"origin") ## [1] "1970-01-01"transform(z, weekend = as.POSIXlt(z, format = "%Y/%m/%d")$wday %in% c(0, 6)) ## X_data weekend ## 1 1960-01-01 FALSE ## 2 1960-01-02 TRUE ## 3 1960-03-31 FALSE ## 4 1960-07-30 TRUE數據操作與整合說到數據操作,這也是一個十分寬泛的話題,在這裡我們就以下4個方面進行介紹: 數據的篩選,過濾:根據一些特定條件選出或者刪除一些觀測 數據的變換:增加或者修改變數 數據的匯總:分組計算數據的和或者均值 數據的排序:改變觀測的排列順序然而在進行這一切之前首先要做的就是了解你的數據,我們以世界銀行的數據Millennium Development Goals為例,來一步步演示如何進行數據操作:if (!file.exists("C:/Users/yujun/Documents/MDG_Data.csv")) { download.file("databank.worldbank.org/","F:/MDG.zip") unzip("F:/MDG.zip")}MDstats<-read.csv("C:/Users/yujun/Documents/MDG_Data.csv")首先先來看一部分數據:head(MDstats) ## Country.Name Country.Code ## 1 Afghanistan AFG ## 2 Afghanistan AFG ## 3 Afghanistan AFGtail(MDstats) ## Country.Name Country.Code ## 33093 Zimbabwe ZWE ## 33094 Zimbabwe ZWE ## 33095 Zimbabwe ZWE ## 33096 Zimbabwe ZWE我們顯然發現了這不是一個tidy data,那麼我們先將其變換為我們喜歡的tidy data,之後再看看數據摘要及數據集各單元的屬性: ## countryname countrycode ## 1 Afghanistan AFG ## 2 Afghanistan AFG ## 3 Afghanistan AFG ## 4 Afghanistan AFG ## 5 Afghanistan AFG ## 6 Afghanistan AFG ## indicatorname ## 1 Adolescent fertility rate (births per 1,000 women ages 15-19) ## 2 Agricultural support estimate (% of GDP)我們可以看看各個數值數據的分位數:quantile(MDstatsMelt$value,na.rm=TRUE) ## 0% 25% 50% 75% 100% ## -9.431e+08 1.054e+01 5.060e+01 9.843e+01 7.526e+13看看各個國家的統計數據有多少:table(MDstatsMelt$countrycode) ## ## ABW ADO AFG AGO ALB ARB ARE ARG ARM ASM ATG AUS AUT AZE BDI ## 3216 3216 3216 3216 3216 3216 3216 3216 3216 3216 3216 3216 3216 3216 3216 ## BEL BEN BFA BGD BGR BHR BHS BIH BLR BLZ BMU BOL BRA BRB BRN 看看缺失值:sum(is.na(MDstatsMelt$value)) #總的缺失值## [1] 495519colSums(is.na(MDstatsMelt)) #每一列的缺失值 ## countryname countrycode indicatorname indicatorcode year ## 0 0 0 0 0 ## value ## 495519# 如果我們用回tidy前的數據集,那麼這個函數會顯得比較有用colSums(is.na(MDstats)) ## Country.Name Country.Code Indicator.Name Indicator.Code X1990 ## 0 0 0 0 23059 ## X1991 X1992 X1993 X1994 X1995 ## 22293 21672 21753 21491 20970 ## X1996 X1997 X1998 X1999 X2000 ## 20680 20448 20419 19933 18822 # 等價的處理方式stat <- function(x) { sum(is.na(x))}tapply(MDstatsMelt$value, MDstatsMelt$year, stat) ## X1990 X1991 X1992 X1993 X1994 X1995 X1996 X1997 X1998 X1999 X2000 X2001 ## 23059 22293 21672 21753 21491 20970 20680 20448 20419 19933 18822 19598 ## X2002 X2003 X2004 X2005 X2006 X2007 X2008 X2009 X2010 X2011 X2012 X2013 ## 19119 19478 19269 18704 19044 18641 19256 19162 18756 20360 21967 30625統計某個國家的統計數據佔總統計數目的多少table(MDstatsMelt$countryname %in% c("China")) ## ## FALSE TRUE ## 791136 3216prop <- table(MDstatsMelt$countryname %in% c("China"))[2]/sum(table(MDstatsMelt$countryname %in% c("China")))prop ## TRUE ## 0.004049看看數據集的大小:object.size(MDstatsMelt)## 22301832 bytesprint(object.size(MDstatsMelt),units="Mb")## 21.3 Mb至此,我們可以說我們對數據有了一定的瞭解。另外值得一提的是,對於某些特定的數據,也許xtabs,ftable是有用的。

數據的篩選要提取相應內容的數據,最為常用的就是提取相應元素,比如提取某個元素,提取某一行,某一列。我們通過下面下面的例子來學習:data<-data.frame(a=sample(1:10),b=rep(c("a","b"),each=5),cdf=rnorm(10))data ## a b cdf ## 1 1 a 0.5755 ## 2 10 a 0.8087 ## 3 2 a 0.9810 ## 4 7 a -0.4635 ## 5 4 a 0.5094#提取相應元素data[2,1]## [1] 10data[[1]][[2]]## [1] 10data[[c(1,2)]]## [1] 10data$a[2]## [1] 10#提取某一列data[[3]] ## [1] 0.5755 0.8087 0.9810 -0.4635 0.5094 1.0514 -1.5338 1.0047 ## [9] 1.0004 -1.3566data$cdf ## [1] 0.5755 0.8087 0.9810 -0.4635 0.5094 1.0514 -1.5338 1.0047 ## [9] 1.0004 -1.3566data$c ## [1] 0.5755 0.8087 0.9810 -0.4635 0.5094 1.0514 -1.5338 1.0047 ## [9] 1.0004 -1.3566data[["c"]]## NULLdata[["c", exact = FALSE]] ## [1] 0.5755 0.8087 0.9810 -0.4635 0.5094 1.0514 -1.5338 1.0047 ## [9] 1.0004 -1.3566數據的篩選還有一個最為常用的的就是移除缺失值:data<-data.frame(a=c(sample(1:5),NA,NA,sample(6:10)),b=c(rep(c("a","b"),each=5),NA,NA),cdf=rnorm(12))data ## a b cdf ## 1 5 a -0.276400 ## 2 1 a -1.861240good <- complete.cases(data)data[good, ] ## a b cdf ## 1 5 a -0.2764 ## 2 1 a -1.8612 ## 3 3 a -2.0280bad <- as.data.frame(is.na(data))data[!(bad$a|bad$b|bad$c),] ## a b cdf ## 1 5 a -0.2764 ## 2 1 a -1.8612數據篩選有時是為了獲得符合條件的數據:X <- data.frame("var1"=sample(1:5),"var2"=sample(6:10),"var3"=sample(11:15))X <- X[sample(1:5),]; X$var2[c(1,3)] = NAX ## var1 var2 var3 ## 2 5 NA 13 ## 5 3 6 15 ## 1 2 NA 12 ## 3 1 8 11 ## 4 4 9 14X[(X$var1 <= 3 & X$var3 > 11),] ## var1 var2 var3 ## 5 3 6 15 ## 1 2 NA 12subset(X,(X$var1 <= 3 & X$var3 > 11)) ## var1 var2 var3 ## 5 3 6 15 ## 1 2 NA 12X[(X$var1 <= 3 | X$var3 > 15),] ## var1 var2 var3 ## 5 3 6 15 ## 1 2 NA 12 ## 3 1 8 11X[which(X$var1 <= 3 | X$var3 > 15),] ## var1 var2 var3 ## 5 3 6 15 ## 1 2 NA 12 ## 3 1 8 11對於取子集的函數subset,在幫助文檔中有一段warning是值得我們注意的:「This is a convenience function intended for use interactively. For programming it is better to use the standard subsetting functions like [, and in particular the non-standard evaluation of argument subset can have unanticipated consequences."

數據的變換

常見的數據變換函數有: abs(x) 絕對值 sqrt(x) 開根號 ceiling(x) 求上線,例:ceiling(3.475) = 4 floor(x) 求下線,例:floor(3.475) = 3 round(x,digits=n) 四捨五入,例:round(3.475,digits=2) = 3.48 signif(x,digits=n) 四捨五入,例:signif(3.475,digits=2) = 3.5 cos(x), sin(x) etc.三角變換 log(x) 對數變換 log2(x), log10(x) 以2、10為底的對數變換 exp(x) 指數變換除此以外,我們還經常對數據加標籤,以期在回歸中測量其效應。我們以MASS包的shuttle數據集為例,想知道不同類型的風(wind)是否需要使用不同的裝載機(use),這裡我們希望將head wind標記為1,auto use也記為1,我們可以按照如下辦法設置虛擬變數:library(MASS)data(shuttle)head(shuttle) ## stability error sign wind magn vis use ## 1 xstab LX pp head Light no auto ## 2 xstab LX pp head Medium no auto ## 3 xstab LX pp head Strong no auto ## 4 xstab LX pp tail Light no auto ## 5 xstab LX pp tail Medium no auto ## 6 xstab LX pp tail Strong no auto## Make our own variables just for illustrationshuttle$auto <- 1 * (shuttle$use == "auto")shuttle$headwind <- 1 * (shuttle$wind == "head")head(shuttle) ## stability error sign wind magn vis use auto headwind ## 1 xstab LX pp head Light no auto 1 1 ## 2 xstab LX pp head Medium no auto 1 1當然對於因子類型變數,relevel函數在線性模型的分析中也是能取得等價效果的。有些時候,我們還常常將連續數據離散化,這時我們需要用到函數cut:data <- rnorm(1000)table(cut(data, breaks = quantile(data))) ## ## (-3.28,-0.637] (-0.637,0.0321] (0.0321,0.672] (0.672,3.37] ## 249 250 250 250library(Hmisc)table(cut2(data, g = 4)) ## ## [-3.2847,-0.6372) [-0.6372, 0.0334) [ 0.0334, 0.6829) [ 0.6829, 3.3704] ## 250 250 250 250detach("package:Hmisc", unload = TRUE)獲得分組區間後,我們只需要將區間的因子重命名就成功的實現了數據的離散化。數據的匯總對數據進行匯總,分類匯總是我們也比較常用的,比如對行或列求和,求均值,求分位數:data <- matrix(1:16, 4, 4)data ## [,1] [,2] [,3] [,4] ## [1,] 1 5 9 13 ## [2,] 2 6 10 14 ## [3,] 3 7 11 15 ## [4,] 4 8 12 16apply(data, 2, mean)## [1] 2.5 6.5 10.5 14.5apply(data, 1, sum)## [1] 28 32 36 40apply(data, 1, quantile, probs = c(0.25, 0.75)) ## [,1] [,2] [,3] [,4] ## 25% 4 5 6 7 ## 75% 10 11 12 13apply(data, 2, quantile, probs = c(0.25, 0.75)) ## [,1] [,2] [,3] [,4] ## 25% 1.75 5.75 9.75 13.75 ## 75% 3.25 7.25 11.25 15.25有時候,為了更快些,我們會用一些函數替代apply: rowSums = apply(x, 1, sum) rowMeans = apply(x, 1, mean) colSums = apply(x, 2, sum) colMeans = apply(x, 2, mean我們有時也會處理一些列表,對列表的分類匯總我們會用到sapply,lapply,不同的是前者返回一個向量或矩陣,後者返回一個列表,例:x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))lapply(x, mean) ## $a ## [1] 5.5 ## ## $beta ## [1] 4.535 ## ## $logic ## [1] 0.5sapply(x, mean) ## a beta logic ## 5.500 4.535 0.500# median and quartiles for each list elementlapply(x, quantile, probs = 1:3/4) ## $a ## 25% 50% 75% ## 3.25 5.50 7.75 ## ## $beta ## 25% 50% 75% ## 0.2516 1.0000 5.0537 ## ## $logic ## 25% 50% 75% ## 0.0 0.5 1.0sapply(x, quantile) ## a beta logic ## 0% 1.00 0.04979 0.0 ## 25% 3.25 0.25161 0.0 ## 50% 5.50 1.00000 0.5 ## 75% 7.75 5.05367 1.0 ## 100% 10.00 20.08554 1.0有時候我們還會進行分類匯總,如統計男女工資均值,這時你可以用tapply:group <- (rbinom(32, n = 20, prob = 0.4))groups <- factor(rep(1:2,10))tapply(group, groups, length) ## 1 2 ## 10 10tapply(group, groups, sum) ## 1 2 ## 135 122tapply(group, groups, mean) ## 1 2 ## 13.5 12.2數據的排序數據的排序需要用到的函數常見的有sort和order,其中sort返回排序的結果,order返回對應數據的排名。例:X <- data.frame("var1"=sample(1:5),"var2"=sample(6:10),"var3"=sample(11:15))X <- X[sample(1:5),]X$var2[c(1,3)] <- NAsort(X$var2,decreasing=TRUE)## [1] 9 8 6sort(X$var2,decreasing=TRUE,na.last=TRUE)## [1] 9 8 6 NA NAorder(X$var2,decreasing=TRUE)## [1] 2 5 4 1 3order(X$var2,decreasing=TRUE,na.last=TRUE)## [1] 2 5 4 1 3X[order(X$var2),] ## var1 var2 var3 ## 2 1 6 13 ## 5 5 8 15 ## 4 4 9 11 ## 1 2 NA 14 ## 3 3 NA 12#deal with the linkX$var2[c(1)] <- sample(na.omit(X$var2),1)X[order(X$var2,X$var3),] ## var1 var2 var3 ## 2 1 6 13 ## 5 5 8 15 ## 4 4 9 11 ## 1 2 9 14 ## 3 3 NA 12有些時候,更為強大的aggregate函數是我們需要的,我們以R的內置數據集state.x77為例:aggregate(state.x77, list(Region = state.region, Cold = state.x77[,"Frost"] > 130), mean) ## Region Cold Population Income Illiteracy Life Exp Murder HS Grad ## 1 Northeast FALSE 8802.8 4780 1.1800 71.13 5.580 52.06 ## 2 South FALSE 4208.1 4012 1.7375 69.71 10.581 44.34 ## 3 North Central FALSE 7233.8 4633 0.7833 70.96 8.283 53.37 ## 4 West FALSE 4582.6 4550 1.2571 71.70 6.829 60.11 ## 5 Northeast TRUE 1360.5 4308 0.7750 71.44 3.650 56.35 ## 6 North Central TRUE 2372.2 4589 0.6167 72.58 2.267 55.67當然,這裡還有一個更為基本與靈活的函數,split,可以幫助你將數據分為若干張滿足分類條件的表,你可以一張一張的處理它們:library(datasets)head(airquality) ## Ozone Solar.R Wind Temp Month Day ## 1 41 190 7.4 67 5 1 ## 2 36 118 8.0 72 5 2 ## 3 12 149 12.6 74 5 3 ## 4 18 313 11.5 62 5 4 ## 5 NA NA 14.3 56 5 5

圖片未顯示,點擊後可以關注微信公眾號「四川CDA數據分析師」。


推薦閱讀:
相關文章