Как в R действительно быстро перебирать строки кадра данных?

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

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

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

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

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

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


person Winston C. Yang    schedule 26.07.2010    source источник
comment
Чем это отличается от df [, column1]? Также см.? Применить с маржей = 1.   -  person Greg    schedule 27.07.2010
comment
Этот пример был не тем, чем я действительно хотел заниматься. Я хотел записать некоторые значения во фрейме данных как данные в файле javascript. +1 за информацию о применяемой марже.   -  person Winston C. Yang    schedule 30.07.2010
comment
Мне нужно было перебирать строки, чтобы перемещать значения в столбцах в определенных ситуациях. Мне напомнили, что лучший способ сделать это в R: stackoverflow.com/questions/7746567/   -  person thadk    schedule 10.08.2016


Ответы (3)


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

Я также хотел бы добавить, что некоторые члены семейства приложений, не исследованные в других примерах, обладают интересными характеристиками производительности. Сначала я покажу репликации относительных результатов 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 таким образом, и его полезность намного превосходит любую подобную проблему скорости в большинстве случаев.

person John    schedule 26.07.2010
comment
+1 за ссылку на nullglob. В его сообщении есть ссылка на статью Уве Лиггеса и Джона Фокса «Как я могу избежать этого цикла или сделать его быстрее?» В R News, май 2008 г. Спасибо, что написали о функциях apply. - person Winston C. Yang; 30.07.2010

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

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

person Shane    schedule 26.07.2010
comment
Возможно, нет способа избежать зацикливания того, что я хотел сделать - см. Мой ответ на комментарий Грега выше. - person Winston C. Yang; 30.07.2010
comment
примерно с такой же скоростью? Вы прочитали все ответы? В своем ответе я показываю, что использование vapply в 3 раза быстрее (для этого примера), чем цикл for ... - person Tommy; 23.06.2011
comment
Что касается алгоритмической эффективности, они очень похожи по скорости: алгоритмическая эффективность - person Toby; 27.08.2013
comment
векторизованная версия действительно более эффективна, чем версия для цикла, спасибо @Shane - person bourneli; 02.08.2016

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

do.call(paste, data.frame(x=c(1,2), z=c("a","b"), z=c(5,6)))
person Mark B.    schedule 30.11.2015
comment
Но это не зацикливание. - person Adam Ryczkowski; 18.01.2017
comment
Отличный ответ, ОП четко спросил: «Как вы перебираете строки». Это, безусловно, наиболее эффективный способ сделать это, и он совсем не требует for. У меня был цикл for, и теперь я применил этот подход, который повысил скорость примерно в 40 раз! - person MS Berends; 26.04.2021