【问题标题】:Combining the data of randomly selected participants with dplyr将随机选择的参与者的数据与 dplyr 相结合
【发布时间】:2017-12-14 16:19:09
【问题描述】:

我有以下数据框'df'。 每个参与者(这里是 10 个参与者)看到了几个刺激物(这里是 100 个),并做出了 关于它的判断(这里是一个随机数)。对于每一个刺激,我都知道真实的 答案(这里是一个随机数;每个刺激都有一个不同的数字,但总是 所有参与者的答案相同)

participant <- rep(1:10, each=100)
stimuli <- rep(1:100, 10)
judgment <- rnorm(1000)
df1 <- data.frame(participant, stimuli, judgment)
df2 <- data.frame(stimuli=1:100, criterion=rnorm(100))
df <- merge(df1, df2, by='stimuli') %>% arrange(participant, stimuli)

这是我想要做的:

1) 随机选择 n 个参与者(这里 n 介于 1 和 10 之间)。

2) 计算他们对每个刺激的判断的平均值

3) 计算该均值与真实答案之间的相关性

我想对所有 n 执行步骤 1-3(即我想随机选择 1 名参与者并执行步骤 1-3,然后我想随机选择 2 名参与者并执行步骤 1-3 .. . 10 名随机选择的参与者并执行步骤 1-3。 结果应该是一个包含 10 行和 2 个变量的数据框:N 和相关性。我只想使用 dplyr。

我的解决方案是基于 lapply。这里是:

participants_id = unique (df$participant)      

MyFun = function(Data) {

HelpFun = function(x, Data) { 
# x is the index for the number of participants.
# It Will be used in the lapply call bellow
participants_x = sample(participants_id, x)
filter(Data, participant %in% participants_x) %>% 
  group_by(stimuli) %>% 
  summarise( mean_x = mean(judgment),
             criterion = unique(criterion) ) %>%
  summarise(cor = cor(.$mean_x, .$criterion))
  }
 N <- length(unique(Data$participant))

lapply(1:N, HelpFun, Data) %>% bind_rows()
}  

MyFun(df) 

问题是这段代码很慢。由于每次选择都是随机的,我 执行所有这些 10,000 次。而且这个慢。在我的机器(Windows 10、16 GB)上,1000 次模拟需要 2 分钟。 10,000 次模拟需要 20 分钟。 (我也尝试过使用循环,但它没有帮助,尽管由于某些原因它有点快)。它必须是一个更快的解决方案。毕竟,计算并没有那么复杂。 下面我写了100个模拟只是为了不干扰你的电脑。 system.time(replicate(100, MyFun(df), simple = FALSE) %>% bind_rows())

有没有让这一切变得更快的想法?

【问题讨论】:

  • 首先,在外循环中移动participants_id = unique (Data$participant) 怎么样
  • 谢谢。我添加了你的建议。然而,它仍然很慢......
  • @Meir 有多慢?在我的机器上 100 次复制需要 11.5 秒。我问是因为我很好奇你为什么需要它更快;您最终会将其扩展到更多复制或更高的 N 吗?您是希望其他人在某个时候在他们自己的计算机上运行的东西,还是您只是担心自己的计算时间?
  • 我添加了一些精度。理想情况下,我想要 10,000 次模拟。 1000 个模拟需要 2 分钟,10,000 个大约需要 20 分钟。我认为我的代码这么慢一定有问题。

标签: r performance random dplyr combinations


【解决方案1】:

使用data.table 和 for 循环,我们可以获得快 10 倍的解决方案。 我的功能:

minem <- function(n) { # n - simulation count
  require(data.table)
  participants_id <- unique(df$participant)    
  N <- length(unique(df$participant))
  dt <- as.data.table(df)
  setkey(dt, stimuli)
  L <- list()
  for (j in 1:n) {
    corss <- rep(0, N)
    for (i in 1:N) {
      participants_x <- sample(participants_id, i)
      xx <- dt[participant %in% participants_x,
               .(mean_x = mean(judgment),
                 criterion = first(criterion)),
               by = stimuli]
      corss[i] <- cor(xx$mean_x, xx$criterion)
    }
    L[[j]] <- corss
  }
  unlist(L)
}

head(minem(10))
# [1]  0.13642499 -0.02078109 -0.14418400  0.04966805 -0.09108837 -0.15403185

你的功能:

Meir <- function(n) {
  replicate(n, MyFun(df), simplify = FALSE) %>% bind_rows()
}

基准测试:

microbenchmark::microbenchmark(
  Meir(10),
  minem(10),
  times = 10)
# Unit: milliseconds
#      expr       min        lq      mean    median        uq       max neval cld
#  Meir(10) 1897.6909 1956.3427 1986.5768 1973.5594 2043.4337 2048.5809    10   b
# minem(10)  193.5403  196.0426  201.4132  202.1085  204.9108  215.9961    10  a 

大约快 10 倍

system.time(minem(1000)) # ~19 sek

更新

如果您的数据大小和内存限制允许,那么您可以使用这种方法更快地完成:

minem2 <- function(n) {
  require(data.table)
  participants_id <- unique(df$participant)    
  N <- length(unique(df$participant))
  dt <- as.data.table(df)
  setkey(dt, participant)
  L <- lapply(1:n, function(x) 
    sapply(1:N, function(i)
      sample(participants_id, i)))
  L <- unlist(L, recursive = F)
  names(L)  <- 1:length(L)
  g <- sapply(seq_along(L), function(x) rep(names(L[x]), length(L[[x]])))
  L <- data.table(participant = unlist(L), .id = as.integer(unlist(g)),
                  key = "participant")
  L <- dt[L, allow.cartesian = TRUE]
  xx <- L[, .(mean_x = mean(judgment), criterion = first(criterion)),
          keyby = .(.id, stimuli)]
  xx <- xx[, cor(mean_x, criterion), keyby = .id][[2]]
  xx
}

microbenchmark::microbenchmark(
  Meir(100),
  minem(100),
  minem2(100),
  times = 2, unit = "relative")
# Unit: relative
#        expr       min        lq      mean    median        uq       max neval cld
#   Meir(100) 316.34965 316.34965 257.30832 257.30832 216.85190 216.85190     2   c
#  minem(100)  31.49818  31.49818  26.48945  26.48945  23.05735  23.05735     2  b 
# minem2(100)   1.00000   1.00000   1.00000   1.00000   1.00000   1.00000     2 a  

但是你需要自己测试一下。

【讨论】:

  • 谢谢。是否因为使用 data.table 而不是 dplyr 而更快?还是因为您使用 for 循环而不是 apply?
  • @Meir 因为data.table。使用lapply 或多或少与for 循环相同,因此通常不会影响性能。
  • 难以置信。我希望用 dplyr 找到解决方案。令人惊讶的是,data.table 比 dplyr 快。
  • @Rtist 最后一种方法因为data.table 没有那么快,而是因为我们在 lapply 中没有做任何计算,只是样本索引选择。所以这回答了你的问题?如果是这样,那么您可能应该接受这个答案...
猜你喜欢
  • 1970-01-01
  • 2016-11-21
  • 2013-01-19
  • 2011-04-18
  • 2014-05-06
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多