R語言進(jìn)階之4:數(shù)據(jù)整形(reshape)
一、通過重新構(gòu)建數(shù)據(jù)進(jìn)行整形
數(shù)據(jù)整形最直接的思路就把數(shù)據(jù)全部向量化,然后按要求用向量構(gòu)建其他類型的數(shù)據(jù)。這樣是不是會(huì)產(chǎn)生大量的中間變量、占用大量內(nèi)存?沒錯(cuò)。R語言的任何函數(shù)(包括賦值)操作都會(huì)有同樣的問題,因?yàn)镽函數(shù)的參數(shù)傳遞方式是傳值不傳址,變量不可能原地址修改后再放回原地址。
矩陣和多維數(shù)組的向量化有直接的類型轉(zhuǎn)換函數(shù): as.vector,向量化后的結(jié)果順序是先列后行再其他:
- > (x <- matrix(1:4, ncol=2)) #為節(jié)省空間,下面的結(jié)果省略了一些空行
- [,1] [,2]
- [1,] 1 3
- [2,] 2 4
- > as.vector(x)
- [1] 1 2 3 4
- > (x <- array(1:8, dim=c(2,2,2)))
- , , 1
- [,1] [,2]
- [1,] 1 3
- [2,] 2 4
- , , 2
- [,1] [,2]
- [1,] 5 7
- [2,] 6 8
- > as.vector(x)
- [1] 1 2 3 4 5 6 7 8
列表向量化可以用unlist,數(shù)據(jù)框本質(zhì)是元素長度相同的列表,所以也用unlist:
- > (x <- list(x=1:3, y=5:10))
- $x
- [1] 1 2 3
- $y
- [1] 5 6 7 8 9 10
- > unlist(x)
- x1 x2 x3 y1 y2 y3 y4 y5 y6
- 1 2 3 5 6 7 8 9 10
- > x <- data.frame(x=1:3, y=5:7)
- > unlist(x)
- x1 x2 x3 y1 y2 y3
- 1 2 3 5 6 7
其他類型的數(shù)據(jù)一般都可以通過數(shù)組、矩陣或列表轉(zhuǎn)成向量。一些軟件包有自定義的數(shù)據(jù)類型,如果考慮周到的話應(yīng)該會(huì)有合適的類型轉(zhuǎn)換函數(shù)。
二、transform 和 within函數(shù)
transform 函數(shù)對數(shù)據(jù)框進(jìn)行操作,作用是為原數(shù)據(jù)框增加新的列變量。但應(yīng)該注意的是“原數(shù)據(jù)框”根本不是原來的那個(gè)數(shù)據(jù)框,而是一個(gè)它的拷貝。下面代碼為airquality數(shù)據(jù)框增加了一列l(wèi)og.ozone,但因?yàn)闆]有把結(jié)果賦值給原變量名,所以原數(shù)據(jù)是不變的:
- > head(airquality,2)
- Ozone Solar.R Wind Temp Month Day
- 1 41 190 7.4 67 5 1
- 2 36 118 8.0 72 5 2
- > aq <- transform(airquality, loglog.ozone=log(Ozone))
- > head(airquality,2)
- Ozone Solar.R Wind Temp Month Day
- 1 41 190 7.4 67 5 1
- 2 36 118 8.0 72 5 2
- > head(aq,2)
- Ozone Solar.R Wind Temp Month Day log.ozone
- 1 41 190 7.4 67 5 1 3.713572
- 2 36 118 8.0 72 5 2 3.583519
transform可以增加新列變量,可以改變列變量的值,也可以通過NULL賦值的方式刪除列變量:
- > aq <- transform(airquality, loglog.ozone=log(Ozone), Ozone=NULL, WindWind=Wind^2)
- > head(aq,2)
- Solar.R Wind Temp Month Day log.ozone
- 1 190 54.76 67 5 1 3.713572
- 2 118 64.00 72 5 2 3.583519
- > aq <- transform(airquality, loglog.ozone=log(Ozone), Ozone=NULL, Month=NULL, WindWind=Wind^2)
- > head(aq,2)
- Solar.R Wind Temp Day log.ozone
- 1 190 54.76 67 1 3.713572
- 2 118 64.00 72 2 3.583519
within 比 transform 靈活些,除數(shù)據(jù)框外還可以使用其他類型數(shù)據(jù),但用法不大一樣,而且函數(shù)似乎也不夠完善:
- > aq <- within(airquality, {
- + log.ozone <- log(Ozone)
- + squared.wind <- Wind^2
- + rm(Ozone, Wind)
- + } )
- > head(aq,2)
- Solar.R Temp Month Day squared.wind log.ozone
- 1 190 67 5 1 54.76 3.713572
- 2 118 72 5 2 64.00 3.583519
- > (x <- list(a=1:3, b=letters[3:10], c=LETTERS[9:14]))
- $a
- [1] 1 2 3
- $b
- [1] "c" "d" "e" "f" "g" "h" "i" "j"
- $c
- [1] "I" "J" "K" "L" "M" "N"
- > within(x, {log.a <- log(a); d <- paste(b, c, sep=':'); rm(b)})
- $a
- [1] 1 2 3
- $c
- [1] "I" "J" "K" "L" "M" "N"
- $d
- [1] "c:I" "d:J" "e:K" "f:L" "g:M" "h:N" "i:I" "j:J"
- $log.a
- [1] 0.0000000 0.6931472 1.0986123
- > within(x, {log.a <- log(a); d <- paste(b, c, sep=':'); rm(b,c)})
- $a
- [1] 1 2 3
- $b #為什么刪除兩個(gè)列表元素會(huì)得到這樣的結(jié)果?
- NULL
- $c
- NULL
- $d
- [1] "c:I" "d:J" "e:K" "f:L" "g:M" "h:N" "i:I" "j:J"
- $log.a
- [1] 0.0000000 0.6931472 1.0986123
三、reshape、stack和unstack 函數(shù)
reshape是R base/stats的函數(shù),主要用于數(shù)據(jù)框長格式和寬格式之間的轉(zhuǎn)換。reshape函數(shù)的參數(shù)很多,不容易記,牛人Hadley Wickham搞出reshape和reshape2包以后這個(gè)函數(shù)幾乎被人遺忘:
- reshape(data, varying = NULL, v.names = NULL, timevar = "time",
- idvar = "id", ids = 1:NROW(data),
- times = seq_along(varying[[1]]),
- drop = NULL, direction, new.row.names = NULL,
- sep = ".",
- split = if (sep == "") {
- list(regexp = "[A-Za-z][0-9]", include = TRUE)
- } else {
- list(regexp = sep, include = FALSE, fixed = TRUE)}
- )
既然可以被遺忘,那就等你走投無路的時(shí)候(估計(jì)不會(huì)有這樣的情況發(fā)生)再去了解它吧。
stack 和 unstack 的作用和reshape類似,用于數(shù)據(jù)框/列表的長、寬格式之間轉(zhuǎn)換。數(shù)據(jù)框?qū)捀袷绞俏覀冇涗浽紨?shù)據(jù)常用的格式,類似這樣:
- > x <- data.frame(CK=c(1.1, 1.2, 1.1, 1.5), T1=c(2.1, 2.2, 2.3, 2.1), T2=c(2.5, 2.2, 2.3, 2.1))
- > x
- CK T1 T2
- 1 1.1 2.1 2.5
- 2 1.2 2.2 2.2
- 3 1.1 2.3 2.3
- 4 1.5 2.1 2.1
一般統(tǒng)計(jì)和作圖用的是長格式,stack可以做這個(gè):
- > (xx <- stack(x))
- values ind
- 1 1.1 CK
- 2 1.2 CK
- 3 1.1 CK
- 4 1.5 CK
- 5 2.1 T1
- 6 2.2 T1
- 7 2.3 T1
- 8 2.1 T1
- 9 2.5 T2
- 10 2.2 T2
- 11 2.3 T2
- 12 2.1 T2
而unstack的作用正好和stack相反,但是要注意它的第二個(gè)參數(shù)是公式類型:公式左邊的變量是值,右邊的變量會(huì)被當(dāng)成因子類型,它的每個(gè)水平都會(huì)形成一列:
- > unstack(xx, values~ind)
- CK T1 T2
- 1 1.1 2.1 2.5
- 2 1.2 2.2 2.2
- 3 1.1 2.3 2.3
- 4 1.5 2.1 2.1
四、reshape/reshape2 包
Hadley Wickham,牛人,很牛X的一個(gè)人,寫了很多R語言包,著名的有g(shù)gplot2, plyr, reshape/reshape2等。reshape2包是reshape包的重寫版,用reshape2就行,都在CRAN源中,用install.packages函數(shù)就可以安裝。reshape/reshape2的函數(shù)很少,一般用戶直接使用的是melt, acast 和 dcast 函數(shù)。
melt是溶解/分解的意思,即拆分?jǐn)?shù)據(jù)。reshape/reshape2的melt函數(shù)是個(gè)S3通用函數(shù),它會(huì)根據(jù)數(shù)據(jù)類型(數(shù)據(jù)框,數(shù)組或列表)選擇melt.data.frame, melt.array 或 melt.list函數(shù)進(jìn)行實(shí)際操作。
如果是數(shù)組(array)類型,melt的用法就很簡單,它依次對各維度的名稱進(jìn)行組合將數(shù)據(jù)進(jìn)行線性/向量化。如果數(shù)組有n維,那么得到的結(jié)果共有n+1列,前n列記錄數(shù)組的位置信息,最后一列才是觀測值:
- > datax <- array(1:8, dim=c(2,2,2))
- > melt(datax)
- Var1 Var2 Var3 value
- 1 1 1 1 1
- 2 2 1 1 2
- 3 1 2 1 3
- 4 2 2 1 4
- 5 1 1 2 5
- 6 2 1 2 6
- 7 1 2 2 7
- 8 2 2 2 8
- > melt(datax, varnames=LETTERS[24:26],value.name="Val")
- X Y Z Val
- 1 1 1 1 1
- 2 2 1 1 2
- 3 1 2 1 3
- 4 2 2 1 4
- 5 1 1 2 5
- 6 2 1 2 6
- 7 1 2 2 7
- 8 2 2 2 8
如果是列表數(shù)據(jù),melt 函數(shù)將列表中的數(shù)據(jù)拉成兩列,一列記錄列表元素的值,另一列記錄列表元素的名稱;如果列表中的元素是列表,則增加列變量存儲(chǔ)元素名稱。元素值排列在前,名稱在后,越是頂級(jí)的列表元素名稱越靠后:
- > datax <- list(agi="AT1G10000", GO=c("GO:1010","GO:2020"), KEGG=c("0100", "0200", "0300"))
- > melt(datax)
- value L1
- 1 AT1G10000 agi
- 2 GO:1010 GO
- 3 GO:2020 GO
- 4 0100 KEGG
- 5 0200 KEGG
- 6 0300 KEGG
- > melt(list(at_0100=datax))
- value L2 L1
- 1 AT1G10000 agi at_0100
- 2 GO:1010 GO at_0100
- 3 GO:2020 GO at_0100
- 4 0100 KEGG at_0100
- 5 0200 KEGG at_0100
- 6 0300 KEGG at_0100
如果數(shù)據(jù)是數(shù)據(jù)框類型,melt的參數(shù)就稍微復(fù)雜些:
- melt(data, id.vars, measure.vars,
- variable.name = "variable", ..., na.rm = FALSE,
- value.name = "value")
其中 id.vars 是被當(dāng)做維度的列變量,每個(gè)變量在結(jié)果中占一列;measure.vars 是被當(dāng)成觀測值的列變量,它們的列變量名稱和值分別組成 variable 和 value兩列,列變量名稱用variable.name 和 value.name來指定。我們用airquality數(shù)據(jù)來看看:
- > str(airquality)
- 'data.frame': 153 obs. of 6 variables:
- $ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ...
- $ Solar.R: int 190 118 149 313 NA NA 299 99 19 194 ...
- $ Wind : num 7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
- $ Temp : int 67 72 74 62 56 66 65 59 61 69 ...
- $ Month : int 5 5 5 5 5 5 5 5 5 5 ...
- $ Day : int 1 2 3 4 5 6 7 8 9 10 ...
如果打算按月份分析臭氧和太陽輻射、風(fēng)速、溫度三者(列2:4)的關(guān)系,我們把它轉(zhuǎn)成長格式數(shù)據(jù)框:
- > aq <- melt(airquality, var.ids=c("Ozone", "Month", "Day"),
- + measure.vars=c(2:4), variable.name="V.type", value.name="value")
- > str(aq)
- 'data.frame': 459 obs. of 5 variables:
- $ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ...
- $ Month : int 5 5 5 5 5 5 5 5 5 5 ...
- $ Day : int 1 2 3 4 5 6 7 8 9 10 ...
- $ V.type: Factor w/ 3 levels "Solar.R","Wind",..: 1 1 1 1 1 1 1 1 1 1 ...
- $ value : num 190 118 149 313 NA NA 299 99 19 194 ...
var.ids 可以寫成id,measure.vars可以寫成measure。id(即var.ids)和 觀測值(即measure.vars)這兩個(gè)參數(shù)可以只指定其中一個(gè),剩余的列被當(dāng)成另外一個(gè)參數(shù)的值;如果兩個(gè)都省略,數(shù)值型的列被看成觀測值,其他的被當(dāng)成id。如果想省略參數(shù)或者去掉部分?jǐn)?shù)據(jù),參數(shù)名最好用 id/measure,否則得到的結(jié)果很可能不是你要的:
- > str(melt(airquality, var.ids=c(1,5,6), measure.vars=c(2:4)))
- 'data.frame': 459 obs. of 5 variables:
- $ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ...
- $ Month : int 5 5 5 5 5 5 5 5 5 5 ...
- $ Day : int 1 2 3 4 5 6 7 8 9 10 ...
- $ variable: Factor w/ 3 levels "Solar.R","Wind",..: 1 1 1 1 1 1 1 1 1 1 ...
- $ value : num 190 118 149 313 NA NA 299 99 19 194 ...
- > str(melt(airquality, var.ids=1, measure.vars=c(2:4))) #看這里,雖然id只引用了一列,但結(jié)果卻不是這樣
- 'data.frame': 459 obs. of 5 variables:
- $ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ...
- $ Month : int 5 5 5 5 5 5 5 5 5 5 ...
- $ Day : int 1 2 3 4 5 6 7 8 9 10 ...
- $ variable: Factor w/ 3 levels "Solar.R","Wind",..: 1 1 1 1 1 1 1 1 1 1 ...
- $ value : num 190 118 149 313 NA NA 299 99 19 194 ...
- > str(melt(airquality, var.ids=1)) #這樣用更慘,結(jié)果不是我們要的吧?
- Using as id variables
- 'data.frame': 918 obs. of 2 variables:
- $ variable: Factor w/ 6 levels "Ozone","Solar.R",..: 1 1 1 1 1 1 1 1 1 1 ...
- $ value : num 41 36 12 18 NA 28 23 19 8 NA ...
- > str(melt(airquality, id=1)) #這樣才行
- 'data.frame': 765 obs. of 3 variables:
- $ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ...
- $ variable: Factor w/ 5 levels "Solar.R","Wind",..: 1 1 1 1 1 1 1 1 1 1 ...
- $ value : num 190 118 149 313 NA NA 299 99 19 194 ...
數(shù)據(jù)整容有什么用?當(dāng)然有。別忘了reshape2和ggplot2都是Hadley Wickham的作品,melt 以后的數(shù)據(jù)(稱為molten數(shù)據(jù))用ggplot2做統(tǒng)計(jì)圖就很方便了,可以快速做出我們需要的圖形:
- library(ggplot2)
- aq$Month <- factor(aq$Month)
- p <- ggplot(data=aq, aes(x=Ozone, y=value, color=Month)) + theme_bw()
- p + geom_point(shape=20, size=4) + geom_smooth(aes(group=1), fill="gray80") + facet_wrap(~V.type, scales="free_y")
melt獲得的數(shù)據(jù)(molten data)可以用 acast 或 dcast 還原。acast獲得數(shù)組,dcast獲得數(shù)據(jù)框。和unstack函數(shù)一樣,cast函數(shù)使用公式參數(shù)。公式的左邊每個(gè)變量都會(huì)作為結(jié)果中的一列,而右邊的變量被當(dāng)成因子類型,每個(gè)水平都會(huì)在結(jié)果中產(chǎn)生一列。
- > head(dcast(aq, Ozone+Month+Day~V.type))
- Ozone Month Day Solar.R Wind Temp
- 1 1 5 21 8 9.7 59
- 2 4 5 23 25 9.7 61
- 3 6 5 18 78 18.4 57
- 4 7 5 11 NA 6.9 74
- 5 7 7 15 48 14.3 80
- 6 7 9 24 49 10.3 69
cast函數(shù)的作用不只是還原數(shù)據(jù),還可以使用函數(shù)對數(shù)據(jù)進(jìn)行匯總(aggregate)。事實(shí)上,melt函數(shù)是為cast服務(wù)的,目的是使用cast函數(shù)對數(shù)據(jù)進(jìn)行aggregate:
- > dcast(aq, Month~V.type, fun.aggregate=mean, na.rm=TRUE)
- Month Solar.R Wind Temp
- 1 5 181.2963 11.622581 65.54839
- 2 6 190.1667 10.266667 79.10000
- 3 7 216.4839 8.941935 83.90323
- 4 8 171.8571 8.793548 83.96774
- 5 9 167.4333 10.180000 76.90000
五、plyr 包
plyr 的功能已經(jīng)遠(yuǎn)遠(yuǎn)超出數(shù)據(jù)整容的范圍,Hadley在plyr中應(yīng)用了split-apply-combine的數(shù)據(jù)處理哲學(xué),即:先將數(shù)據(jù)分離,然后應(yīng)用某些處理函數(shù),最后將結(jié)果重新組合成所需的形式返回。某些人士喜歡用“揉”來表述這樣的數(shù)據(jù)處理;“揉”,把數(shù)據(jù)當(dāng)面團(tuán)搗來搗去,很哲,磚家們的磚頭落下來,拍死人絕不償命
。先別哲了,來點(diǎn)實(shí)際的:plyr的函數(shù)命名方式比較規(guī)律,很容易記憶和使用。比如 a開頭的函數(shù)aaply, adply 和 alply 將數(shù)組(array)分別轉(zhuǎn)成數(shù)組、數(shù)據(jù)框和列表;daply, ddply 和 dlply 將數(shù)據(jù)框分別轉(zhuǎn)成數(shù)組、數(shù)據(jù)框和列表;而laply, ldaply, llply將列表(list)分別轉(zhuǎn)成數(shù)組、數(shù)據(jù)框和列表。
下面我們看看如何使用ldply函數(shù)將ath1121501.db包中的KEGG列表數(shù)據(jù)轉(zhuǎn)成數(shù)據(jù)框:
- > library(ath1121501.db)
- > keggs <- as.list(ath1121501PATH[mappedkeys(ath1121501PATH)])
- > head(ldply(keggs, paste, collapse='; '))
- .id V1
- 1 261579_at 00190
- 2 261569_at 04712
- 3 261583_at 00010; 00020; 00290; 00620; 00650; 01100; 01110
- 4 261574_at 00903; 00945; 01100; 01110
- 5 261043_at 00051; 00520; 01100
- 6 261044_at 04122
plyr包的函數(shù)較多,不再一一介紹,更多用法請參考它的在線幫助,Hadley 也寫了很詳細(xì)的tutorial:http://plyr.had.co.nz/09-user/
原文鏈接:http://helloxxxxxx.blog.163.com/blog/static/21601509520133343821837/?latestBlog
【編輯推薦】
1.R語言學(xué)習(xí)筆記(2):數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)