R数据阐明傍边的化整为零(Split-Apply-Combine)计策
当前位置:以往代写 > 其他教程 >R数据阐明傍边的化整为零(Split-Apply-Combine)计策
2019-06-14

R数据阐明傍边的化整为零(Split-Apply-Combine)计策

R数据阐明傍边的化整为零(Split-Apply-Combine)计策

本文心得自:The Split-Apply-Combine Strategy for Data Analysis, Hadley Wickham, Journal of Statistical Software, April 2011, V.40.

引子:
我们经常会碰着这样的问题,数据量很大,并不需要依顺序来依次处理惩罚。公道分块处理惩罚,并最终整合起来是一个不错的选择。这也就是所谓的Split-Apply-Combine Strategy计策。这在速度上会有比做一个loop有优势,因为它可以并行处理惩罚数据。

什么时候我们需要利用到化整为零的计策呢?有以下三种环境:

  1. 数据需要分组处理惩罚
  2. 数据需要凭据每行可能每列来处理惩罚
  3. 数据需要分级处理惩罚,和分组很雷同,可是分级时需要思量分级之间的干系。

化整为零计策有点雷同于由Google推广的map-reduce计策。虽然map-reduce计策的基本是网格,而这里的Split-Apply-Combine的基本完全可以是单机,甚至不支持并行处理惩罚的单机都可以。

然而,化整为零并不是一个很直观的编程进程。最直观的进程是利用Loop轮回。这里利用一个例子来讲授一下如何实现化整为零计策。在plyr包中有数据ozone,它是一个三维矩阵(24X24X72),个中最后一维72是指的6年12个月每个月的功效。也就是ozone是一个包罗了持续72个月24X24的三维矩阵数据。三维别离是lat,long,time。我们需要由对时间robust linear model之后的残基residuals。

> library(plyr) # need for dataset ozone
> library(MASS) # need for function rlm
> month <- ordered(rep(1:12, length=72)) #set time sequence
> #try one set
> one <- ozone[1,1,]
> model <- rlm(one ~ month - 1); model
Call:
rlm(formula = one ~ month - 1)
Converged in 9 iterations
 
Coefficients:
  month1   month2   month3   month4   month5   month6   month7   month8   month9  month10  month11  month12 
264.3964 259.2036 255.0000 252.0052 258.5089 265.3387 274.0000 276.6724 277.0000 285.0000 283.6036 273.1964 
 
Degrees of freedom: 72 total; 60 residual
Scale estimate: 4.45 
> deseas <- resid(model)

此刻我们对每一组数据都做沟通的处理惩罚。首先利用for loop,这样较量直观

> deseasf <- function(value) rlm(value ~ month -1) #the function
> models <- as.list(rep(NA, 24*24)) #prepare the variable
> dim(models) <- c(24, 24)
> deseas <- array(NA, c(24,24,72)) #prepare the variable
> dimnames(deseas) <- dimnames(ozone)
> for (i in seq_len(24)) { #for loop for first dimension
+ 	for(j in seq_len(24)) { #for loop for second dimension
+ 		mod <- deseasf(ozone[i, j, ]) #apply function
+ 		models[[i, j]] <- mod #save data
+ 		deseas[i, j, ] <- resid(mod) #get residure
+ 	}
+ }

接下来我们利用化整为零的计策。因为数据可以分成24X24块来处理惩罚,每一块都是单独运算,可以并行处理惩罚。而利用for loop,只能一块接一块的处理惩罚,在速度上大概没有并行处理惩罚来得快。而在R傍边,有一系列相关的函数,apply, lapply, sapply, tapply, mapply, sweep。我们先得相识这些函数,然后再来应用它们。最简朴的是apply。

其形式是apply(array, margin, function, …)。首先,apply的工具是矩阵array可能matrix。它的第二个参数是指的维度,假如你的array是一个二维矩阵,需要按横排的方法计较每一排的平均值,那么你的第二个参数就应该是1。假如需要按纵列的方法计较每一列的平均值,那么第二个参数就应该是2。虽然还可以利用c(1,2)这样的方法来配置第二个参数,就是并行计较每个值。第三个参数是需要应用的函数。之后的…是需要传入函数的其它参数。而apply的返回值就是由function来确定的,它大概是vector, array or list。下面举个例子较量容易领略。

> x<-cbind(x1=3,x2=c(4:1,2:5))
> dimnames(x)[[1]]<-letters[1:8]
> x
  x1 x2
a  3  4
b  3  3
c  3  2
d  3  1
e  3  2
f  3  3
g  3  4
h  3  5
> apply(x,2,mean,trim =.2) #在这里,trim =.2就是mean(x, trim = 0, na.rm = FALSE, ...)函数傍边的一个参数。
x1 x2 
 3  3 
> apply(x,1,mean,trim =.2)
  a   b   c   d   e   f   g   h 
3.5 3.0 2.5 2.0 2.5 3.0 3.5 4.0 
> col.sums <- apply(x, 2, sum)
> row.sums <- apply(x, 1, sum)
> rbind(cbind(x, Rtot = row.sums), Ctot = c(col.sums, sum(col.sums)))
     x1 x2 Rtot
a     3  4    7
b     3  3    6
c     3  2    5
d     3  1    4
e     3  2    5
f     3  3    6
g     3  4    7
h     3  5    8
Ctot 24 24   48
> sum.plus.y <- function(x,y){
+ 	sum(x) + y
+ }
> apply(x, 1, sum.plus.y, 3) #利用自界说函数
 a  b  c  d  e  f  g  h 
10  9  8  7  8  9 10 11

#p#分页标题#e#

领略了apply,就可较量容易地领略lapply, sapply, vapply了。这三者针对的工具是list可能Vector。其形式为

lapply(X, FUN, ...)
sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)

我们看到,它没有了apply傍边所需要的第二个参数margin,其原因就是输入工具不是array可能matrix,而是list可能Vector。先举个最简朴的应用例子。

> x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))
> x
$a
 [1]  1  2  3  4  5  6  7  8  9 10
 
$beta
[1]  0.04978707  0.13533528  0.36787944  1.00000000  2.71828183  7.38905610 20.08553692
 
$logic
[1]  TRUE FALSE FALSE  TRUE
 
> lapply(x,mean)
$a
[1] 5.5
 
$beta
[1] 4.535125
 
$logic
[1] 0.5

也就是说,其x的维度应该是一维的,虽然,其下面的元素可以是多维的。那么假如x是一个矩阵呢?我们先来看例子。

> x<-cbind(x1=3,x2=c(4:1,2:5))
> dimnames(x)[[1]]<-letters[1:8]
> x
  x1 x2
a  3  4
b  3  3
c  3  2
d  3  1
e  3  2
f  3  3
g  3  4
h  3  5
> x<-as.data.frame(x)
> as.list(x)
$x1
[1] 3 3 3 3 3 3 3 3
 
$x2
[1] 4 3 2 1 2 3 4 5
 
> lapply(x,function(.ele) mean(.ele))
$x1
[1] 3
 
$x2
[1] 3
 
> sapply(x,mean)
x1 x2 
 3  3 
> vapply(x,mean,1)
x1 x2 
 3  3

从它们的说明文件我们知道,无论你传入的x是什么,它首先做的一步说是利用as.list来将其转换成一个一维的list。所以,一个data.frame传入lapply之后,它的colnames将会转换成list的names,而rownames大概会丢失。较量可知,lapply和sapply的不同在于,lapply的返回值是一个list,而sapply的返回值是一个矩阵。sapply的返回值其实就是在lapply的基本上再利用了simplify2array(x, higher=TRUE)函数,利用其功效酿成一个array。为了更清楚地相识sapply和vapply,我们看下面的例子

> i39 <- sapply(3:9, seq)
> i39
[[1]]
[1] 1 2 3
 
[[2]]
[1] 1 2 3 4
 
[[3]]
[1] 1 2 3 4 5
 
[[4]]
[1] 1 2 3 4 5 6
 
[[5]]
[1] 1 2 3 4 5 6 7
 
[[6]]
[1] 1 2 3 4 5 6 7 8
 
[[7]]
[1] 1 2 3 4 5 6 7 8 9
 
> sapply(i39, fivenum)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,]  1.0  1.0    1  1.0  1.0  1.0    1
[2,]  1.5  1.5    2  2.0  2.5  2.5    3
[3,]  2.0  2.5    3  3.5  4.0  4.5    5
[4,]  2.5  3.5    4  5.0  5.5  6.5    7
[5,]  3.0  4.0    5  6.0  7.0  8.0    9
> vapply(i39, fivenum,
+        c(Min. = 0, "1st Qu." = 0, Median = 0, "3rd Qu." = 0, Max. = 0))
        [,1] [,2] [,3] [,4] [,5] [,6] [,7]
Min.     1.0  1.0    1  1.0  1.0  1.0    1
1st Qu.  1.5  1.5    2  2.0  2.5  2.5    3
Median   2.0  2.5    3  3.5  4.0  4.5    5
3rd Qu.  2.5  3.5    4  5.0  5.5  6.5    7
Max.     3.0  4.0    5  6.0  7.0  8.0    9

个中,fivenum函数会返回一组数的最小值,四分位低值(lower-hinge),中值,四分位高值(upper-hinge),以及较大值。从上面的较量中,我们很清楚的看到,sapply返回值的分列形式,以list的names为colnames。可以想象,它利用的是按列填充matrix的方法输出的。而vapply是在sapply的基本上,为rownames做出了界说。

除了上面先容的,尚有tapply,mapply,sweep等。它们的界说如下。假如需要相识和把握它们,需要熟悉上面先容的apply, lapply, sapply以及vapply。还需要相识split。所以这里就不多加表明白(因为篇幅会很长)。

tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE,
       USE.NAMES = TRUE)
sweep(x, MARGIN, STATS, FUN="-", check.margin=TRUE, ...)

有了上面的相识,我们可以看一下最前面的for loop可以奈何改写为apply形式了。

> models <- apply(ozone, 1:2, deseasf) #这里相当于for loop傍边的
for(i in seq_len(24)){for(j in seq_len(24)){mod<-deseasf(ozone[i,j,]); models[[i,j]]<-mod;}}, 
可是运算却是并行处理惩罚的。
> resids_list <- lapply(models, resid)
> resids <- unlist(resids_list)
> dim(resids) <- c(72, 24, 24)
> deseas <- aperm(resids, c(2, 3, 1))
> dimnames(deseas) <- dimnames(ozone)

#p#分页标题#e#

假如,我们知道的并不是ozone这样一个24X24X72的已知确切维度的三维数组,如果我们只有一个名为ozonedf的matrix,它的5列别离为lat, long, time, month,和value。我们假如需要做上述的阐明应该怎么办呢?在思路上,我们的想法大概会是先从ozonedf出产生成一个雷同ozone这样子的数据,然后再利用apply,lapply这样的函数来完成绩可以。第一步生成ozone这样子的数据,就是化整为零计策(Split-Apply-Combine)的第一步了。

R> deseasf_df <- function(df) {
+ rlm(value ~ month - 1, data = df)
+ }
R> pieces <- split(ozonedf, list(ozonedf$lat, ozonedf$long))
R> models <- lapply(pieces, deseasf_df)
R> results <- mapply(function(model, df) {
+ cbind(df[rep(1, 72), c("lat", "long")], resid(model))
+ }, models, pieces)
R> deseasdf <- do.call("rbind", results)


12下一页

    关键字:

在线提交作业