Подтвердить что ты не робот

В R, как вы быстро перебираете строки данных?

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

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

Например, один (возможно, медленный) способ петли над строками

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

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

Есть ли быстрый способ перебора строк в кадре данных? Является ли какая-то другая структура данных более быстрой для быстрого цикла?

4b9b3361

Ответ 1

Я думаю, мне нужно сделать это полным ответом, потому что я нахожу комментарии сложнее, чтобы отслеживать, и я уже потерял один комментарий по этому поводу... Есть пример 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. (для цикла не замедление заметно, если я использую sin (mat [i, 1]).

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

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

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

Ответ 2

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

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

Ответ 3

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

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