В R, как вы действительно быстро зацикливаетесь на строках фрейма данных? - PullRequest
29 голосов
/ 26 июля 2010

Предположим, что у вас есть фрейм данных с множеством строк и столбцов.

Столбцы имеют имена.Вы хотите получить доступ к строкам по номеру и столбцам по имени.

Например, один (возможно, медленный) способ циклического перемещения по строкам - это

for (i in 1:nrow(df)) {
  print(df[i, "column1"])
  # do more things with the data frame...
}

Другой способ - создать "спискиmsgstr "для отдельных столбцов (например, column1_list = df[["column1"]) и доступа к спискам за один цикл.Этот подход может быть быстрым, но также неудобным, если вы хотите получить доступ ко многим столбцам.

Существует ли быстрый способ циклического перемещения по строкам фрейма данных?Какая другая структура данных лучше для быстрого зацикливания?

Ответы [ 3 ]

14 голосов
/ 27 июля 2010

Я думаю, мне нужно сделать это полным ответом, потому что мне труднее отслеживать комментарии, и я уже потерял один комментарий по этому поводу. Есть пример nullglob , который демонстрирует различия между для, и применять семейные функции гораздо лучше, чем другие примеры. Когда кто-то делает функцию такой, чтобы она была очень медленной, то здесь расходуется вся скорость, и вы не найдете различий между вариациями зацикливания. Но когда вы сделаете функцию тривиальной, вы сможете увидеть, как зацикливание влияет на вещи.

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

n <- 1e6
system.time(for(i in 1:n) sinI[i] <- sin(i))
  user  system elapsed 
 5.721   0.028   5.712 

lapply runs much faster for the same result
system.time(sinI <- lapply(1:n,sin))
   user  system elapsed 
  1.353   0.012   1.361 

Он также нашел Саппли намного медленнее. Вот некоторые другие, которые не были проверены.

Обычные старые применяются к матричной версии данных ...

mat <- matrix(1:n,ncol =1),1,sin)
system.time(sinI <- apply(mat,1,sin))
   user  system elapsed 
  8.478   0.116   8.531 

Итак, сама команда apply () существенно медленнее, чем цикл for. (цикл for не сильно замедляется, если я использую sin (mat [i, 1]).

Еще один, который, похоже, не тестировался в других постах, это tapply.

system.time(sinI <- tapply(1:n, 1:n, sin))
   user  system elapsed 
 12.908   0.266  13.589 

Конечно, никто бы никогда не использовал tapply таким образом, и его утилита в большинстве случаев выходит далеко за рамки такой проблемы со скоростью.

12 голосов
/ 26 июля 2010

Самый быстрый способ - это не зацикливаться (т.е. векторизованные операции).Один из тех случаев, когда вам нужно выполнить цикл, это когда есть зависимости (то есть одна итерация зависит от другой).В противном случае попытайтесь выполнить как можно больше векторизованных вычислений вне цикла.

Если вам нужно сделать цикл, то использование цикла for по сути так же быстро, как и все остальное (lapply может быть немного быстрее, но прочее apply функции, как правило, имеют примерно ту же скорость, что и for).

0 голосов
/ 30 ноября 2015

Используя тот факт, что data.frames по сути являются списками векторов столбцов, можно использовать do.call, чтобы применить функцию с числом столбцов в каждом столбце data.frame (аналогично «архивированию»). поверх списка на других языках).

do.call(paste, data.frame(x=c(1,2), z=c("a","b"), z=c(5,6)))
...