R-每次在一个列中的一个单元格上运行在另一列中的每个单元格上

时间:2018-12-10 07:45:03

标签: r for-loop vectorization

我有一个函数,她的输入应该在一个单元格中的每一个单元格中的每一个单元格上的另一列中的每一次运行。

我可以循环执行此操作,但是,我希望对过程进行矢量化或使其更快。就目前而言,我将需要几天时间才能完成该过程。 理想情况下,它将使用tidyverse,但任何帮助将不胜感激。

我的循环如下:

results <- data.frame(
  pathSubject1 = as.character(), 
  pathSubject2 = as.character())

i <- 1 #Counter first loop
j <- 1 #Counter second loop
#Loop over subject 1
for (i in 1:dim(df)[1]) {#Start of first loop
  #Loop over subject 2  
  for (j in 1:dim(df)[1]) {#Start of second loop
    #calc my function for the subjects
    tempPercentSync <- myFunc(df$subject1[i], df$subject2[j])

    results <- rbind(
      results, 
      data.frame(
        pathSubject1 = df$value[i], 
        pathSubject2 = df$value[j], 
        syncData = nest(tempPercentSync)))
  } #End second loop
} #End first loop

我的示例函数:

myFunc <- function(x, y) { 
  temp <- dplyr::inner_join(
    as.data.frame(x),
    as.data.frame(y),
    by = "Time")
  out <- as.data.frame(summary(temp))
}

使用dput的数据集示例:

structure(list(value = c("data/ExportECG/101_1_1_0/F010.feather", 
"data/ExportECG/101_1_1_0/F020.feather"), ID = c(101, 101), run = c(1, 
1), timeComing = c(1, 1), part = c(0, 0), paradigm = c("F010", 
"F020"), group = c(1, 1), subject1 = list(structure(list(Time = c(0, 
0.5, 1, 1.5, 2, 2.5), subject1 = c(9.73940345482368, 9.08451907157601, 
8.42963468832833, 7.77475030508065, 7.11986592183298, 7.24395122629289
)), .Names = c("Time", "subject1"), row.names = c(NA, 6L), class = "data.frame"), 
    structure(list(Time = c(0, 0.5, 1, 1.5, 2, 2.5), subject1 = c(58.3471156751544, 
    75.9103303197856, 83.014068283342, 89.7923167579699, 88.6748903116088, 
    84.7651306939912)), .Names = c("Time", "subject1"), row.names = c(NA, 
    6L), class = "data.frame")), subject2 = list(structure(list(
    Time = c(0, 0.5, 1, 1.5, 2, 2.5), subject2 = c(77.7776200371528, 
    77.4139420609906, 74.9760822165258, 75.3915183650012, 77.5672070195079, 
    80.7418145918357)), .Names = c("Time", "subject2"), row.names = c(NA, 
6L), class = "data.frame"), structure(list(Time = c(0, 0.5, 1, 
1.5, 2, 2.5), subject2 = c(101.133666720578, 105.010792226714, 
107.01541987713, 104.471173834529, 97.5910271952943, 92.9840354003295
)), .Names = c("Time", "subject2"), row.names = c(NA, 6L), class = "data.frame"))), .Names = c("value", 
"ID", "run", "timeComing", "part", "paradigm", "group", "subject1", 
"subject2"), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-2L))

输出看起来应该像:

                           pathSubject1
1 data/ExportECG/101_1_1_0/F010.feather
2 data/ExportECG/101_1_1_0/F010.feather
3 data/ExportECG/101_1_1_0/F020.feather
4 data/ExportECG/101_1_1_0/F020.feather
                           pathSubject2
1 data/ExportECG/101_1_1_0/F010.feather
2 data/ExportECG/101_1_1_0/F020.feather
3 data/ExportECG/101_1_1_0/F010.feather
4 data/ExportECG/101_1_1_0/F020.feather
                                                                                                                                                                           data
1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 20, 5, 17, 14, 8, 11, 21, 6, 19, 16, 10, 13, 22, 7, 18, 15, 9, 12
2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 21, 6, 17, 14, 8, 12, 22, 7, 19, 16, 10, 13, 20, 5, 18, 15, 9, 11
3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 20, 5, 17, 14, 8, 11, 21, 7, 19, 16, 10, 13, 22, 6, 18, 15, 9, 12
4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 21, 6, 17, 14, 8, 12, 22, 7, 19, 16, 10, 13, 20, 5, 18, 15, 9, 11

谢谢!

1 个答案:

答案 0 :(得分:1)

我认为您正在寻找lapply(或相关功能)。

花费最多的时间是rbind,因为在循环的每一步中,整个对象results都会变得稍大,这意味着它会被完全复制。使用lapply,首先计算所有结果,然后才将它们与 dplyr::rbind_list dplyr::bind_rows
组合 你得到的是这样的:

results <- dplyr::bind_rows(lapply(1:dim(df)[1], function(i) {
  dplyr::bind_rows(lapply(1:dim(df)[1], function(j) {
    data.frame(pathSubject1 = df$value[i],
               pathSubject2 = df$value[j],
               syncData = tidyr::nest(myFunc(df$subject1[[i]], df$subject2[[j]])))
  }))
}))

能解决您的问题吗?

编辑:加快速度

我已编辑使用bind_rows而不是rbind_list,它应该更快。
此外,如果在对myFunc的调用中使用[[i]]而不是[i],则可以将as.data.frame(x)放到那里(有些用于j / y)。
最后,您可以通过不分配任何中间结果来优化myFunc:

myFunc <- function(x, y) { 
  as.data.frame(summary(dplyr::inner_join(x, y, by = "Time")))
}

但是我的直觉告诉我们这些将是很小的差异。为了获得更高的速度,我们需要减少实际计算量,然后实际数据是什么以及结果需要什么就变得很重要。 根据您的示例的一些观察结果:

  • 我们需要单独的data.frames吗?我们将df$subject1中的所有值与df$subject2中的所有值进行比较。在此示例中,首先为subject1创建一个大data.frame,然后为subject2创建另一个大data.frame(如果需要,带有一个额外的标签)将加快连接速度。
  • 为什么要加入?现在,联接的摘要仅提供了没有联接也可以得到的信息。
  • 我们加入Time,但是在示例中,主题1和2的时间戳相同。检查它们是否相同,然后进行简单复制会更快
  • 作为最终结果,我们有一个data.frame,其中一列包含data.frames,其中包含联接的摘要。这就是您需要的方式吗?我认为,如果仅计算实际需要的值,您的代码可能会更快。而且我对包含data.frames的data.frames并没有做很多工作,但是很可能bind_rows不能有效地处理它。一个简单的列表(作为data.frame的列)可能会更好,因为开销更少。

最后,可能是您无法透露有关真实数据的更多信息,或者它太复杂了。
在那种情况下,我认为您可能会期望使用各种分析工具,这些工具可以帮助您了解花费最多时间的地方。就个人而言,我喜欢profvis-tool
print(profvis::profvis({ mycode }, interval= seconds ))放在一段代码中,执行完后,您会看到哪行花费最多时间,以及哪些功能在后台被调用。 在示例代码中,几乎所有时间都花在行绑定和制作data.frames上。但是在实际数据中,我预计其他功能可能会很耗时。