重塑数据(更快的方法)

5

今天我遇到了一个需要将频率计数表扩展为原始值数据框的问题。我已经解决了这个问题,但是想知道是否有更快的方法,可以使用reshape包或data.table来实现?

原始表格如下:

   i1 i2 i3 i4  m  f
1   0  0  0  0 22 29
2   1  0  0  0 30 50
3   0  1  0  0 13 15
4   0  0  1  0  1  6
5   1  1  0  0 24 67
6   1  0  1  0  5 12
7   0  1  1  0  1  2
8   1  1  1  0 10 22
9   0  0  0  1 10  7
10  1  0  0  1 27 30
11  0  1  0  1 14  4
12  0  0  1  1  1  0
13  1  1  0  1 54 63
14  1  0  1  1  8 10
15  0  1  1  1  8  6
16  1  1  1  1 57 51

使用dput可以轻松获取数据:

dat <- structure(list(i1 = c(0L, 1L, 0L, 0L, 1L, 1L, 0L, 1L, 0L, 1L, 
0L, 0L, 1L, 1L, 0L, 1L), i2 = c(0L, 0L, 1L, 0L, 1L, 0L, 1L, 1L, 
0L, 0L, 1L, 0L, 1L, 0L, 1L, 1L), i3 = c(0L, 0L, 0L, 1L, 0L, 1L, 
1L, 1L, 0L, 0L, 0L, 1L, 0L, 1L, 1L, 1L), i4 = c(0L, 0L, 0L, 0L, 
0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), m = c(22L, 30L, 
13L, 1L, 24L, 5L, 1L, 10L, 10L, 27L, 14L, 1L, 54L, 8L, 8L, 57L
), f = c(29L, 50L, 15L, 6L, 67L, 12L, 2L, 22L, 7L, 30L, 4L, 0L, 
63L, 10L, 6L, 51L)), .Names = c("i1", "i2", "i3", "i4", "m", 
"f"), class = "data.frame", row.names = c(NA, -16L))

我重塑数据的方法(是否有更快的方法?):

#step 1: method 1 (in this case binding and stacking uses less code than reshape)
dat2 <- data.frame(rbind(dat[,1:4], dat[, 1:4]), 
    sex = rep(c('m', 'f'), each=16),
    n = c(dat$m, dat$f))
dat2

#step 1: method 2    
dat3 <- reshape(dat, direction = "long", idvar = 1:4,
    varying = list(c("m", "f")),
    v.names = c("n"),
    timevar = "sex",
    times = c("m", "f"))
    rownames(dat3) <- 1:nrow(dat3)
    dat3 <- data.frame(dat3)
    dat3$sex <- as.factor(dat3$sex)

all.equal(dat3, dat2) #just to show both method 1 and 2 give the same data frame

#step 2
dat4 <- dat2[rep(seq_len(nrow(dat2)), dat2$n), 1:5]
rownames(dat4) <- 1:nrow(dat4)
dat4

我认为这是一个常见的问题,因为当你想从文章中取出一个表格并复制它时,你需要进行一些解包操作。我发现自己越来越频繁地这样做,希望确保我的效率。

4个回答

7

这是一个简短的命令。

dat2 <- ddply(dat, 1:4, summarize, sex = c(rep('m', m), rep('f', f)))

5

这里是一个基于R语言的一行代码。

dat2 <- cbind(dat[c(rep(1:nrow(dat), dat$m), rep(1:nrow(dat), dat$f)),1:4],
              sex=c(rep("m",sum(dat$m)), rep("f", sum(dat$f))))

或者,稍微通俗一点说:
d1 <- dat[,1:4]
d2 <- as.matrix(dat[,5:6])
dat2 <- cbind(d1[rep(rep(1:nrow(dat), ncol(d2)), d2),], 
              sex=rep(colnames(d2), colSums(d2)))

3
考虑到目前没有人发布了data.table的解决方案(如原始问题中所建议的)。
library(data.table)
DT <- as.data.table(dat)   
DT[,list(sex = rep(c('m','f'),c(m,f))), by=  list(i1,i2,i3,i4)]

或者更简洁地说,
DT[,list(sex = rep(c('m','f'),c(m,f))), by=  'i1,i2,i3,i4']

c(m,f)(以及list(i1,i2,i3,i4))能否修改为引用包含列名的变量?例如,如果我有100个列(比如Var0到Var99),而不想输入每个列的名称,那该怎么办? - dnlbrky

2

我会在第一步使用melt,在第二步使用ddply

library(reshape2)
library(plyr)
d <- ddply( 
  melt(dat, id.vars=c("i1","i2","i3","i4"), variable.name="sex"), 
  c("i1","i2","i3","i4","sex"), 
  summarize, 
  id=rep(1,value) 
)
d$id <- cumsum(d$id)

我远比我的方法更喜欢它。如果没有人提出更有效率的方案(即不需要编写更多代码),我将把这个标记为正确答案。+1 - Tyler Rinker
我将其标记为正确。我认为没有人能够超过这么多的代码量。 - Tyler Rinker
我改正了。令人印象深刻的结果。比我一直使用的代码要少得多。 - Tyler Rinker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接