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

Почему функция diag так медленно? [в R 3.2.0 или ранее]

Я смотрел тесты в этом ответе и хотел сравнить их с diag (используется в другом ответе). К сожалению, кажется, что diag занимает возраст:

nc  <- 1e4
set.seed(1)
m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc)

microbenchmark(
  diag = diag(m),
  cond = m[row(m)==col(m)],
  vec  = m[(1:nc-1L)*nc+1:nc],
  mat  = m[cbind(1:nc,1:nc)],
times=10)

Комментарии: Я тестировал их с помощью identical. Я взял "cond" из одного из ответов на этот вопрос о домашнем задании. Результаты аналогичны матрице целых чисел 1:26 вместо letters.

Результаты:

Unit: microseconds
 expr         min          lq         mean       median          uq         max neval
 diag  604343.469  629819.260  710371.3320  706842.3890  793144.019  837115.504    10
 cond 3862039.512 3985784.025 4175724.0390 4186317.5260 4312493.742 4617117.706    10
  vec     317.088     329.017     432.9099     350.1005     629.460     651.376    10
  mat     272.147     292.953     441.7045     345.9400     637.506     706.860    10

Это просто операция подмножества матриц, поэтому я не знаю, почему так много накладных расходов. Заглянув внутрь функции, я вижу несколько проверок, а затем c(m)[v], где v - это тот же вектор, который используется в тесте "vec". Сроки этих двух...

v <- (1:nc-1L)*nc+1:nc
microbenchmark(diaglike=c(m)[v],vec=m[v])
# Unit: microseconds
#      expr        min          lq        mean     median          uq        max neval
#  diaglike 579224.436 664853.7450 720372.8105 712649.706 767281.5070 931976.707   100
#       vec    334.843    339.8365    568.7808    646.799    663.5825   1445.067   100

... кажется, я нашел своего преступника. Итак, новая вариация моего вопроса такова: Почему существует, по-видимому, ненужное и очень трудоемкое c в diag?

4b9b3361

Ответ 1

Резюме

От R версии 3.2.1 (всемирно известный астронавт) diag() получил обновление. Обсуждение переместилось в r-devel, где было отмечено, что c() разделяет атрибуты, отличные от имени, и, возможно, именно поэтому он был помещен туда. В то время как некоторые люди опасались, что удаление c() приведет к неизвестным проблемам на матричных объектах, Питер Дальгаард обнаружил, что "Единственный случай, когда эффект c() внутри diag() имеет значение, - это где M[i,j] != M[(i-1)*m+j] AND c(M) stringize M в порядке столбца, так что M[i,j] == c(M)[(i-1)*m+j]."

Люк Тирни проверил удаление @Frank c(), обнаружив, что это ничего не повлияло на CRAN или BIOC и поэтому было реализовано, чтобы заменить c (x) [...] на x [...] на строка 27. Это приводит к относительно большим ускорениям в diag(). Ниже приведен тест скорости, показывающий улучшение с версией R 3.2.1 diag().

library(microbenchmark)
nc  <- 1e4
set.seed(1)
m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc)

    microbenchmark(diagOld(m),diag(m))
    Unit: microseconds
           expr        min          lq        mean      median         uq        max neval
     diagOld(m) 451189.242 526622.2775 545116.5668 531905.5635 540008.704 682223.733   100
        diag(m)    222.563    646.8675    644.7444    714.4575    740.701   1015.459   100