Предположим, что у Вас есть кадр данных со многими строками и многими столбцами.
Столбцы имеют имена. Вы хотите получить доступ к строкам числом и столбцам по имени.
Например, один (возможно медленный) способ циклично выполниться по строкам
for (i in 1:nrow(df)) {
print(df[i, "column1"])
# do more things with the data frame...
}
Иначе должен создать "списки" для отдельных столбцов (как column1_list = df[["column1"]
), и доступ списки в одном цикле. Этот подход мог бы быть быстрым, но также и неудобным, если Вы хотите получить доступ ко многим столбцам.
Существует ли быстрый способ цикличного выполнения по строкам кадра данных? Некоторая другая структура данных лучше для цикличного выполнения быстро?
Думаю, мне нужно дать полный ответ, потому что мне труднее отслеживать комментарии, и я уже потерял один комментарий по этому поводу ... Есть пример от nullglob это демонстрирует различия между for и применяет семейные функции намного лучше, чем другие примеры. Если сделать функцию очень медленной, тогда будет потрачена вся скорость, и вы не найдете различий между вариациями цикла. Но когда вы сделаете функцию тривиальной, вы увидите, насколько сильно на вещи влияет цикл.
Я также хотел бы добавить, что некоторые члены семейства apply, не исследованные в других примерах, обладают интересными характеристиками производительности. Сначала я покажу репликации относительных результатов 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
Он также обнаружил, что sapply намного медленнее. Вот некоторые другие, которые не тестировались.
Обычное старое применение к матричной версии данных ...
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, и его полезность намного превосходит любую подобную проблему скорости в большинстве случаев.
Самый быстрый способ - не зацикливать (т. Е. Векторизованные операции). Один из единственных случаев, когда вам нужно выполнить цикл, - это когда есть зависимости (т.е. одна итерация зависит от другой). В противном случае постарайтесь выполнить как можно больше векторизованных вычислений вне цикла.
Если вам нужен цикл, то использование цикла for
по сути так же быстро, как и все остальное ( lapply
может быть немного быстрее, но другие применяют
функции, как правило, имеют примерно такую же скорость, как для
).