2017-02-20 36 views
1

在这里有很多关于在R中重复行预定次数的问题,但是我找不到解决具体问题的一个问题I在问。根据数据帧中的另一个值在数据帧中重复行的块

我有一个调查答复的数据框,其中每个答复者回答5至10个问题。作为玩具的例子:

df <- data.frame(ID = rep(1:2, each = 5), 
      Response = sample(LETTERS[1:4], 10, replace = TRUE), 
      Weight = rep(c(2,3), each = 5)) 

> df 
    ID Response Weight 
1 1  D  2 
2 1  C  2 
3 1  D  2 
4 1  D  2 
5 1  B  2 
6 2  D  3 
7 2  C  3 
8 2  B  3 
9 2  D  3 
10 2  B  3 

我想重复受访者1的答案的两倍,作为一个块,然后答辩2的答案3倍,作为一个块,和我想的反应各块有一个唯一的ID。换句话说,我想最终的结果看起来是这样:

 ID Response Weight 
1 11  D  2 
2 11  C  2 
3 11  D  2 
4 11  D  2 
5 11  B  2 
6 12  D  2 
7 12  C  2 
8 12  D  2 
9 12  D  2 
10 12  B  2 
11 21  D  3 
12 21  C  3 
13 21  B  3 
14 21  D  3 
15 21  B  3 
16 22  D  3 
17 22  C  3 
18 22  B  3 
19 22  D  3 
20 22  B  3 
21 23  D  3 
22 23  C  3 
23 23  B  3 
24 23  D  3 
25 23  B  3 

我做的方式,这是目前真的笨重,而且,因为我有我的数据集> 3000名受访者中,是无法忍受慢。

这里是我的代码:

df.expanded <- NULL 
for(i in unique(df$ID)) { 
    x <- df[df$ID == i,] 
    y <- x[rep(seq_len(nrow(x)), x$Weight),1:3] 
    y$order <- rep(1:max(x$Weight), nrow(x)) 
    y <- y[with(y, order(order)),] 
    y$IDNew <- rep(max(y$ID)*100 + 1:max(x$Weight), each = nrow(x)) 
    df.expanded <- rbind(df.expanded, y) 
} 

有一个更快的方法来做到这一点?

+1

请问为什么要执行这样的任务? – DJJ

+0

当然。我正在研究响应的潜在类条件logit分析(在实际数据集中,它是1/0,而不是上面的字母)。在我实际做分析的Stata中,'lclogit'不接受权重,所以我支持我有的反向概率权重。 – TheChainsOfMarkov

+1

重复'ID 1'两次:'df [df $ ID == 1,] [rep(seq_len(nrow(df [df $ ID == 1,])),2),]' –

回答

1

有一个更简单的解决方案。我想你想根据你的代码中显示的Weight复制行。

df2 <- df[rep(seq_along(df$Weight), df$Weight), ] 
df2$ID <- paste(df2$ID, unlist(lapply(df$Weight, seq_len)), sep = '') 

# sort the rows 
df2 <- df2[order(df2$ID), ] 

此方法更快吗?让我们来看一下:

library(microbenchmark) 

microbenchmark(
    m1 = { 
     df.expanded <- NULL 
     for(i in unique(df$ID)) { 
      x <- df[df$ID == i,] 
      y <- x[rep(seq_len(nrow(x)), x$Weight),1:3] 
      y$order <- rep(1:max(x$Weight), nrow(x)) 
      y <- y[with(y, order(order)),] 
      y$IDNew <- rep(max(y$ID)*100 + 1:max(x$Weight), each = nrow(x)) 
      df.expanded <- rbind(df.expanded, y) 
     } 
    }, 
    m2 = { 
     df2 <- df[rep(seq_along(df$Weight), df$Weight), ] 
     df2$ID <- paste(df2$ID, unlist(lapply(df$Weight, seq_len)), sep = '') 

     # sort the rows 
     df2 <- df2[order(df2$ID), ] 
    } 
) 

# Unit: microseconds 
# expr  min  lq  mean median  uq  max neval 
# m1 806.295 862.460 1101.6672 921.0690 1283.387 2588.730 100 
# m2 171.731 194.199 245.7246 214.3725 283.145 506.184 100 

可能有其他更有效的方法。

+0

哇。这太快了。谢谢! – TheChainsOfMarkov

1

另一种方法是使用data.table

假设你已经从“DT”为你data.table,尝试:

library(data.table) 
DT[, list(.id = rep(seq(Weight[1]), each = .N), Weight, Response), .(ID)] 

我没有粘贴标识列在一起,而是创造了一个次级柱。这对我来说似乎更加灵活。


测试数据。更改n以创建要播放的较大数据集。

set.seed(1) 
n <- 5 
weights <- sample(3:15, n, TRUE) 
df <- data.frame(ID = rep(seq_along(weights), weights), 
       Response = sample(LETTERS[1:5], sum(weights), TRUE), 
       Weight = rep(weights, weights)) 
DT <- as.data.table(df) 
相关问题