R: применить против do.call - PullRequest
0 голосов
/ 06 июня 2018

Я только что прочитал профиль @David Arenburg и нашел кучу полезных советов о том, как развить хорошие навыки / привычки R-программирования, и один из них особенно поразил меня.Я всегда думал, что функции apply в R были краеугольным камнем работы с фреймами данных, но он пишет:

Если вы работаете с data.frames, забудьте, что есть функция с именем apply - что бы вы ниделать - не использовать это.Особенно с полем 1 (единственный хороший вариант использования этой функции - работать со столбцами матрицы - поле 2).

Несколько хороших альтернатив:? Do.call,? Pmax / pmin,? Max.col,? rowSums / rowMeans / etc, потрясающие пакеты matrixStats (для матриц),? rowum и многие другие

Может кто-нибудь объяснить мне это?Почему применяются функции, не одобряемые?

Ответы [ 3 ]

0 голосов
/ 06 июня 2018
  • apply(DF, 1, f) преобразует каждую строку DF в вектор, а затем передает этот вектор в f.Если DF представляет собой смесь строк и чисел, то строка будет преобразована в символьный вектор, прежде чем передать его в f, так что, например, apply(iris, 1, function(x) sum(x[-5])) не будет работать, даже если строка iris[i, -5] содержит все числовые значения.элементы.Строка преобразуется в строку символов, и вы не можете суммировать строки символов.С другой стороны, apply(iris[-5], 1, sum) будет работать так же, как и rowSums(iris[-5]).

  • , если f создает вектор, результатом является матрица, а не другой фрейм данных;Кроме того, результатом является транспонирование того, что вы можете ожидать.Это

    apply(BOD, 1, identity)
    

    дает следующее, а не возвращает BOD:

           [,1] [,2] [,3] [,4] [,5] [,6]
    Time    1.0  2.0    3    4  5.0  7.0
    demand  8.3 10.3   19   16 15.6 19.8
    

    Много лет назад Хэдли Уикхем сделал post iapply, который является идемпотентом вощущение, что iapply(mat, 1, identity) возвращает mat, а не t(mat), где mat - матрица.Совсем недавно с его пакетом plyr можно написать:

    library(plyr)
    ddplyr(BOD, 1, identity)
    

    и получить BOD обратно как фрейм данных.

С другой стороны apply(BOD, 1, sum) будетвыдайте тот же результат, что rowSums(BOD) и apply(BOD, 1, f) могут быть полезны для функций f, для которых f создает скаляр, и нет аналога, такого как в случае sum / rowSums.Также, если f выдает вектор, и вы не возражаете против матричного результата, вы можете транспонировать вывод apply самостоятельно, и, хотя уродливо, он будет работать.

0 голосов
/ 06 июня 2018

Это связано с тем, как R хранит матрицы и кадры данных *.Как вы, возможно, знаете, data.frame - это list векторов, то есть каждый столбец в data.frame является вектором.Будучи векторизованным языком, предпочтительно работать с векторами, и по этой причине apply с полем 2 не одобряется: тем самым вы не будете работать с векторами, скорее вы будете охватывать разные векторы на каждомитерация.

Насколько я знаю, использование apply с полем 1 мало чем отличается от использования do.call.Хотя последний может обеспечить большую гибкость использования.

* Эта информация должна быть где-то в руководствах .

0 голосов
/ 06 июня 2018

Я думаю, что автор имеет в виду, что вы должны использовать предварительно созданные / векторизованные функции (потому что это проще), если вы можете и не применять (потому что в принципе это цикл for и занимает больше времени):

library(microbenchmark)

d <- data.frame(a = rnorm(10, 10, 1),
                b = rnorm(10, 200, 1))

# bad - loop
microbenchmark(apply(d, 1, function(x) if (x[1] < x[2]) x[1] else x[2]))

# good - vectorized but same result
microbenchmark(pmin(d[[1]], d[[2]])) # use double brackets!

# edited:
# -------
# bad: lapply
microbenchmark(data.frame(lapply(d, round, 1)))

# good: do.call faster than lapply
microbenchmark(do.call("round", list(d, digits = 1)))

# --------------
# Unit: microseconds
#                                  expr     min    lq     mean  median      uq     max neval
# do.call("round", list(d, digits = 1)) 104.422 107.1 148.3419 134.767 184.524 332.009   100
#                            expr     min       lq     mean  median      uq      max neval
# data.frame(lapply(d, round, 1)) 235.619 243.2055 298.5042 252.353 276.004 1550.265   100
#
#                                  expr    min      lq    mean median       uq     max neval
# do.call("round", list(d, digits = 1)) 96.389 97.5055 113.075 98.175 105.5375 730.954   100
#                            expr     min       lq     mean  median      uq      max neval
# data.frame(lapply(d, round, 1)) 235.619 243.2055 298.5042 252.353 276.004 1550.265   100
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...