日々のつれづれ

不惑をむかえ戸惑いを隠せない男性の独り言

data.frameのrbindにはみんな苦労しているようだ

id:gepuroさんの日記が目にとまった。
R言語でデータを縦に結合させる
どうも、このかたも同じことで苦労されていたようだ。

私はSASを使ったことが無い。だからset関数を知らない。
でも、Rのrbindの仕様はSAS使いの人を混乱させるようだ。

で、それぞれのブログにあるset関数とset2関数はこんな関数だった。

  • set関数なら
> x <- data.frame(A=1:5,B=5:1,row.names=1:5)
> y <- data.frame(B=1:5,C=5:9,row.names=1:5)
> z <- data.frame(C=3:8,A=8:13,row.names=2:7)
> 
> set <- function(x,y)
+ {
+ coln <- unique(c(colnames(x),colnames(y)))
+ x[coln[!coln %in% colnames(x)]] <- NA
+ y[coln[!coln %in% colnames(y)]] <- NA
+ rbind(x,y)
+ }
> set(z,set(x,y))
    C  A  B
2   3  8 NA
3   4  9 NA
4   5 10 NA
5   6 11 NA
6   7 12 NA
7   8 13 NA
71 NA  1  5
8  NA  2  4
9  NA  3  3
10 NA  4  2
11 NA  5  1
12  5 NA  1
13  6 NA  2
14  7 NA  3
15  8 NA  4
16  9 NA  5
  • set2関数なら
> set2 <- function(x,...){
+ df <- x
+ for(i in list(...)){
+ df <- set(df,i)
+ }
+ df
+ }
> set2(z,x,y)
    C  A  B
2   3  8 NA
3   4  9 NA
4   5 10 NA
5   6 11 NA
6   7 12 NA
7   8 13 NA
71 NA  1  5
8  NA  2  4
9  NA  3  3
10 NA  4  2
11 NA  5  1
12  5 NA  1
13  6 NA  2
14  7 NA  3
15  8 NA  4
16  9 NA  5

set2関数の方が拡張性があるのかなぁ?
でも、明示的にオブジェクトを指定するのでsetを打ち込む手間が省けるだけのような気もする…
オブジェクトが増えたならforでなんとかできるし…

> dat <- list(x,y,z)
> for(i in seq(dat)[-1]) dat[[1]] <- set(dat[[1]], dat[[i]])
> dat[[1]]
    A  B  C
1   1  5 NA
2   2  4 NA
3   3  3 NA
4   4  2 NA
5   5  1 NA
6  NA  1  5
7  NA  2  6
8  NA  3  7
9  NA  4  8
10 NA  5  9
21  8 NA  3
31  9 NA  4
41 10 NA  5
51 11 NA  6
61 12 NA  7
71 13 NA  8

個人的にはrbind関数の副作用は同じrownameがあるとき、rownamseが連番になってくれないことと思っている。
で、私も昔はこんな関数を書いていたことを思い出した。

> rbind2 <- function(list, mn=TRUE){
+ ncol <- unique(unlist(lapply(list,colnames)))
+ nrow <- unlist(lapply(list,rownames))
+ 
+ list <- lapply(list, function(x,nc=ncol){
+ mat <- matrix(NA, ncol=sum(!nc%in%colnames(x)), nrow=nrow(x),
+ dimnames=list(rownames(x),nc[!nc%in%colnames(x)]))
+ cbind(x,mat)[,nc]
+ })
+ 
+ if(mn){
+ nrow <- unique(nrow)
+ dat <- matrix(NA,ncol=length(ncol),nrow=length(nrow),dimnames=list(seq(nrow),ncol)) 
+ for(i in nrow) for(j in ncol)
+ dat[i,j] <- mean(unlist(sapply(list, function(mat,xy=c(i,j)) mat[xy[1],xy[2]]))
+ , na.rm=TRUE)
+ }else{
+ dat <- do.call("rbind",list)
+ rownames(dat) <- seq(nrow(dat))
+ }
+ dat[is.na(dat)] <- NA
+ return(as.data.frame(dat))
+ }
> list <- list(x,y,z)
> rbind2(list,mn=TRUE)
   A  B   C
1  1  3 5.0
2  5  3 4.5
3  6  3 5.5
4  7  3 6.5
5  8  3 7.5
6 12 NA 7.0
7 13 NA 8.0
> rbind2(list,mn=FALSE)
    A  B  C
1   1  5 NA
2   2  4 NA
3   3  3 NA
4   4  2 NA
5   5  1 NA
6  NA  1  5
7  NA  2  6
8  NA  3  7
9  NA  4  8
10 NA  5  9
11  8 NA  3
12  9 NA  4
13 10 NA  5
14 11 NA  6
15 12 NA  7
16 13 NA  8

もう、何に使ったのかすら思い出せないけど。