用R语言做数据清理
數(shù)據(jù)的清理
如同列夫托爾斯泰所說的那樣:“幸福的家庭都是相似的,不幸的家庭各有各的不幸”,糟糕的惡心的數(shù)據(jù)各有各的糟糕之處,好的數(shù)據(jù)集都是相似的。一份好的,干凈而整潔的數(shù)據(jù)至少包括以下幾個要素:
1、每一個觀測變量構(gòu)成一列
2、每一個觀測對象構(gòu)成一行
3、每一個類型的觀測單元構(gòu)成一個表
就像我們最常接觸的鳶尾花數(shù)據(jù):
每一列就是觀測的指標(biāo):花瓣長度,花瓣寬度,萼片長度,萼片寬度,種類;每一行就是一株鳶尾花的觀測值,構(gòu)成整張表的元素就是四個數(shù)值變量,一個分類分類變量。
然而出于排版的考慮我們抓下來的數(shù)據(jù)往往不是那么的友好,比如說我們可以看到的數(shù)據(jù)通常是這樣的:
而不是:
當(dāng)然,除了這種把列表每一列代表一些數(shù)值這種情況外,還有多個變量儲存為一列(比如列表不僅以"<10k","10k-50k","50k-100k"做表頭,甚至還加上性別信息"m<10k","m10k-50k","m50k-100k","f<10k","f10k-50k","f50k-100k",其中m代表男性,f代表女性),還有更過分的將列表的變量不僅儲存在列中,行中也有統(tǒng)計變量。
面對這些不好的table,我們首先要做的就是數(shù)據(jù)管理,將數(shù)據(jù)整理為一個干凈的數(shù)據(jù)集。
數(shù)據(jù)管理
按照en:DAMA的定義:“數(shù)據(jù)資源管理,致力于發(fā)展處理企業(yè)數(shù)據(jù)生命周期的適當(dāng)?shù)慕?gòu)、策略、實(shí)踐和程序”。這是一個高層而包含廣泛的定義,而并不一定直接涉及數(shù)據(jù)管理的具體操作(如關(guān)系數(shù)據(jù)庫的技術(shù)層次上的管理)。我們這里主要講述對于數(shù)據(jù)的變量命名與數(shù)據(jù)的合并,旨在方便數(shù)據(jù)共享。
數(shù)據(jù)管理首先要做的就是大致上了解你的數(shù)據(jù),比如有什么樣的變量,每一行大致長成什么樣,最常用的就是head(),tail().
我們要做的基本上就是這么幾項工作:
-
給每一個變量命名,而不是V1,V2,如果有必要可以給出code book。
-
每個變量名最好具有可讀性,除非過長,否則不要用縮寫,例如AgeAtDiagnosis這個命名遠(yuǎn)好于AgeDx。
-
通常來說,最好將數(shù)據(jù)放在一張表里面,如果因為數(shù)據(jù)過多,項目過雜,分成了幾張表。那么一定需要有一列使得這些表之間能夠連接起來,但盡量避免這樣做。
-
我們以UCI的Human Activity Recognition Using Smartphones Data Set 為例來看看數(shù)據(jù)是如何變成一個基本符合要求的數(shù)據(jù)。這個數(shù)據(jù)我們已經(jīng)下載下來了,其中關(guān)于數(shù)據(jù)的詳細(xì)信息可以參閱read me文檔,由于UCI的數(shù)據(jù)通常都是一個基本合乎規(guī)范的數(shù)據(jù)集(主要是指它的數(shù)據(jù)集的變量名都是以V1,V2來命名的)加上一個code book。那么我們看看各個數(shù)據(jù)的名稱(在feature文件里)
我們可以看到各個特征的名稱直接標(biāo)在數(shù)據(jù)上是非常不友善的,我們?yōu)榱俗屗哂锌勺x性,我們以展示在我們眼前的6個數(shù)據(jù)為例:
variablename <- head(name)# 將標(biāo)簽中的大寫字母轉(zhuǎn)為小寫,我們這里沒有所以不再賦值,如果需要全變?yōu)榇髮?#xff0c;可以使用touppertolower(variablename$V2)用這樣的名字給數(shù)據(jù)集命名就感覺舒服多了,我們將一些R中對字符串常用的操作函數(shù)總結(jié)如下,方便我們對數(shù)據(jù)名稱的修改:
-
sub:替換字符串中的第一個模式為設(shè)定模式(pattern).
-
gsub:全局替換字符串中的相應(yīng)模式
-
grep,grepl:這兩個函數(shù)返回向量水平的匹配結(jié)果,grep僅返回匹配項的下標(biāo),而grepl返回所有的查詢結(jié)果,并用邏輯向量表示有沒有找到匹配。
-
nchar:統(tǒng)計字符串單字?jǐn)?shù)目
-
substr:取子串
-
paste:將字符串鏈接起來,sep參數(shù)可以設(shè)置連接符
-
str_trim:去掉字符串空格
變量的名稱建議滿足如下要求:
-
英文變量名盡可能用小寫
-
盡可能的描述清楚變量特征 (Diagnosis versus Dx)
-
不要太復(fù)雜
-
不要有下劃線、點(diǎn)、空格
字符型變量應(yīng)該滿足:
-
是因子類型的應(yīng)該轉(zhuǎn)化為factor
-
因子盡可能具有一定的描述性 (例如:如果0/1表示真假,那么用TRUE/FALSE代替0/1;在表示性別時用Male/Female代替M/F)
接下來我們討論數(shù)據(jù)集的合并,主要使用函數(shù)merge。
我們以下面兩個數(shù)據(jù)集的合并為例:
merge函數(shù)調(diào)用格式為:
參數(shù)說明:
-
x,y:兩個數(shù)據(jù)框
-
by, by.x, by.y:指定用于合并的列的名稱。
-
all,all.x,all.y:默認(rèn)的all = FALSE相當(dāng)于自然連接, 或者說是內(nèi)部鏈接. all.x = TRUE是一個左連接, all.y = TRUE是一個又連接, all = TRUE 相當(dāng)于一個外部鏈接.
仔細(xì)觀察下面3個例子你就會發(fā)現(xiàn)其中的奧秘:
mergedData <- merge(df1,df2,by.x="reviewer_id",by.y="id",all=TRUE) head(mergedData)在plyr包中還提供了join,join_all,arrange等函數(shù)來實(shí)現(xiàn)表的連接,但我想merge這個函數(shù)已經(jīng)足夠用了,所以我們不在多說。當(dāng)然,在極少數(shù)特別好的情況下(比如列的變量是一致的,或者行的觀測個體是一致的時候)rbind,cbind也是有用的。
有些時候我們會遇到一些特殊的字符串:日期。R中提供了各式各樣的函數(shù)來處理時間:
Sys.setlocale("LC_TIME", "C") x <- c("1jan1960", "2jan1960", "31mar1960", "30jul1960")z <- as.Date(x, "%d%b%Y") format(z, "%a %b %d") weekdays(z) julian(z)數(shù)據(jù)操作與整合
說到數(shù)據(jù)操作,這也是一個十分寬泛的話題,在這里我們就以下4個方面進(jìn)行介紹:
-
數(shù)據(jù)的篩選,過濾:根據(jù)一些特定條件選出或者刪除一些觀測
-
數(shù)據(jù)的變換:增加或者修改變量
-
數(shù)據(jù)的匯總:分組計算數(shù)據(jù)的和或者均值
-
數(shù)據(jù)的排序:改變觀測的排列順序
然而在進(jìn)行這一切之前首先要做的就是了解你的數(shù)據(jù),我們以世界銀行的數(shù)據(jù)Millennium Development Goals為例,來一步步演示如何進(jìn)行數(shù)據(jù)操作:
if (!file.exists("C:/Users/yujun/Documents/MDG_Data.csv")) {download.file("http://databank.worldbank.org/data/download/MDG_csv.zip","F:/MDG.zip") unzip("F:/MDG.zip") }MDstats<-read.csv("C:/Users/yujun/Documents/MDG_Data.csv")首先先來看一部分?jǐn)?shù)據(jù):
head(MDstats)我們顯然發(fā)現(xiàn)了這不是一個tidy data,那么我們先將其變換為我們喜歡的tidy data,之后再看看數(shù)據(jù)摘要及數(shù)據(jù)集各單元的屬性:
我們可以看看各個數(shù)值數(shù)據(jù)的分位數(shù):
quantile(MDstatsMelt$value,na.rm=TRUE)看看各個國家的統(tǒng)計數(shù)據(jù)有多少:
table(MDstatsMelt$countrycode)看看缺失值:
sum(is.na(MDstatsMelt$value)) #總的缺失值 ## [1] 495519 colSums(is.na(MDstatsMelt)) #每一列的缺失值統(tǒng)計某個國家的統(tǒng)計數(shù)據(jù)占總統(tǒng)計數(shù)目的多少
table(MDstatsMelt$countryname %in% c("China"))看看數(shù)據(jù)集的大小:
object.size(MDstatsMelt) ## 22301832 bytes print(object.size(MDstatsMelt),units="Mb") ## 21.3 Mb至此,我們可以說我們對數(shù)據(jù)有了一定的了解。另外值得一提的是,對于某些特定的數(shù)據(jù),也許xtabs,ftable是有用的。
數(shù)據(jù)的篩選
要提取相應(yīng)內(nèi)容的數(shù)據(jù),最為常用的就是提取相應(yīng)元素,比如提取某個元素,提取某一行,某一列。我們通過下面下面的例子來學(xué)習(xí):
data<-data.frame(a=sample(1:10),b=rep(c("a","b"),each=5),cdf=rnorm(10))data數(shù)據(jù)的篩選還有一個最為常用的的就是移除缺失值:
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數(shù)據(jù)篩選有時是為了獲得符合條件的數(shù)據(jù):
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對于取子集的函數(shù)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."
數(shù)據(jù)的變換
常見的數(shù)據(jù)變換函數(shù)有:
-
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) 對數(shù)變換
-
log2(x), log10(x) 以2、10為底的對數(shù)變換
-
exp(x) 指數(shù)變換
除此以外,我們還經(jīng)常對數(shù)據(jù)加標(biāo)簽,以期在回歸中測量其效應(yīng)。我們以MASS包的shuttle數(shù)據(jù)集為例,想知道不同類型的風(fēng)(wind)是否需要使用不同的裝載機(jī)(use),這里我們希望將head wind標(biāo)記為1,auto use也記為1,我們可以按照如下辦法設(shè)置虛擬變量:
library(MASS) data(shuttle) head(shuttle)當(dāng)然對于因子類型變量,relevel函數(shù)在線性模型的分析中也是能取得等價效果的。
有些時候,我們還常常將連續(xù)數(shù)據(jù)離散化,這時我們需要用到函數(shù)cut:
data <- rnorm(1000) table(cut(data, breaks = quantile(data)))獲得分組區(qū)間后,我們只需要將區(qū)間的因子重命名就成功的實(shí)現(xiàn)了數(shù)據(jù)的離散化。
數(shù)據(jù)的匯總
對數(shù)據(jù)進(jìn)行匯總,分類匯總是我們也比較常用的,比如對行或列求和,求均值,求分位數(shù):
data <- matrix(1:16, 4, 4)data有時候,為了更快些,我們會用一些函數(shù)替代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)有時候我們還會進(jìn)行分類匯總,如統(tǒng)計男女工資均值,這時你可以用tapply:
group <- (rbinom(32, n = 20, prob = 0.4))groups <- factor(rep(1:2,10)) tapply(group, groups, length)數(shù)據(jù)的排序
數(shù)據(jù)的排序需要用到的函數(shù)常見的有sort和order,其中sort返回排序的結(jié)果,order返回對應(yīng)數(shù)據(jù)的排名。例:
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 6 sort(X$var2,decreasing=TRUE,na.last=TRUE) ## [1] 9 8 6 NA NA order(X$var2,decreasing=TRUE) ## [1] 2 5 4 1 3 order(X$var2,decreasing=TRUE,na.last=TRUE) ## [1] 2 5 4 1 3 X[order(X$var2),]有些時候,更為強(qiáng)大的aggregate函數(shù)是我們需要的,我們以R的內(nèi)置數(shù)據(jù)集state.x77為例:
aggregate(state.x77, list(Region = state.region, Cold = state.x77[,"Frost"] > 130), mean)當(dāng)然,這里還有一個更為基本與靈活的函數(shù),split,可以幫助你將數(shù)據(jù)分為若干張滿足分類條件的表,你可以一張一張的處理它們:
library(datasets) head(airquality)總結(jié)
- 上一篇: MySQL创建数据库时指定编码和用户授权
- 下一篇: 分布式消息通信ActiveMQ原理-持久