R - запускать каждый раз над одной ячейкой из одного столбца над каждой ячейкой в ​​другом столбце - PullRequest
0 голосов
/ 10 декабря 2018

У меня есть функция, которую ее вход должен запускать каждый раз над одной ячейкой из одного столбца над каждой ячейкой в ​​другом столбце.

Я могу сделать это с помощью цикла, однако я стремлюсь векторизовать процесс или сделать его быстрее.Что касается сейчас, мне потребовались бы дни, чтобы закончить процесс.В идеале, это будет использовать 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 голосов
/ 10 декабря 2018

Я думаю, что вы ищете 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, это должно быть быстрее.
Кроме того, если вы используете [[i]] вместо [i] при вызове myFunc, вы можете сбросить туда 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.В этом примере сначала создание одного большого data.frame для subject1, а затем другого для subject2, при необходимости с дополнительной меткой, ускорит объединение.
  • Почему объединение?Прямо сейчас сводка объединения дает только ту информацию, которую мы могли бы получить и без объединения.
  • Мы присоединяемся к Time, но в этом примере метки времени для subject1 и 2 идентичны.Проверка, являются ли они одинаковыми, с последующим простым копированием будет быстрее
  • В качестве конечного результата у нас есть data.frame с одним столбцом, содержащим data.frames, содержащий сводную информацию о объединении.Вам так нужно?Я думаю, что ваш код может быть намного быстрее, если вы вычисляете только те значения, которые вам действительно нужны.И я мало работал с data.frames, содержащими data.frames, но вполне может быть, что bind_rows не справляется с этим эффективно.Возможно, простой список (как столбец вашего data.frame) будет работать лучше, так как будет меньше накладных расходов.

Наконец, возможно, вы не сможете больше узнать о своих реальных данных, илиэто слишком сложно.
В этом случае, я думаю, вы могли бы найти множество различных инструментов профилирования, которые могут помочь вам показать, на что тратится большинство времени.Лично мне нравится инструмент profvis
Put print(profvis::profvis({ mycode }, interval= секунд )) вокруг блока кода, и после его завершения вы видите, какие строкизаняло больше всего времени, и какие функции называются под капотом.В примере кода почти все время тратится на привязку строк и создание data.frames.Но в реальных данных я ожидаю, что другие функции могут занять много времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...