Почему запуск «уникального» быстрее на кадре данных, чем матрица в R?

StackOverflow https://stackoverflow.com/questions/7809570

Вопрос

Я начал верить, что рамки данных не имеют никаких преимуществ по сравнению с матрицами, за исключением удобства для нотации. Однако я заметил эту странность при беге unique На матрицах и кадрах данных: он, похоже, работает быстрее на кадре данных.

a   = matrix(sample(2,10^6,replace = TRUE), ncol = 10)
b   = as.data.frame(a)

system.time({
    u1 = unique(a)
})
 user  system elapsed
1.840   0.000   1.846


system.time({
    u2 = unique(b)
})
 user  system elapsed
0.380   0.000   0.379

Результаты времени расходятся еще более существенно по мере увеличения количества строк. Итак, в этом вопросе есть две части.

  1. Почему это медленнее для матрицы? Кажется быстрее конвертировать в кадр данных, запустить unique, а затем преобразовать обратно.

  2. Есть ли причина не просто обернуть unique в myUnique, что делает преобразования в части № 1?


Примечание 1. Учитывая, что матрица является атомной, кажется, что unique должно быть быстрее для матрицы, а не медленнее. Возможность итерации по сравнению с фиксированным размером, смежные блоки памяти, как правило, должны быть быстрее, чем запуск отдельных блоков связанных списков (я предполагаю, как реализованы кадры данных ...).

Примечание 2. Как показано на производительности data.table, Бег unique На кадре данных или матрице - сравнительно плохая идея - см. Ответ Мэтью Даула и комментарии к относительным временам. Я перенес много объектов в таблицы данных, и эта производительность является еще одной причиной для этого. Поэтому, хотя пользователи должны быть хорошо выполнены для применения таблиц данных, по причинам педагогических / сообществ, я оставлю этот вопрос открытым в отношении Почему Это занимает больше времени на объектах матрицы. Приведенный ниже адресной адрес куда время уходит и как еще Можем ли мы получить лучшую производительность (т.е. таблицы данных). Ответ на Почему близко под рукой - код можно найти через unique.data.frame а также unique.matrix. Анкет :) Английское объяснение того, что он делает и почему все, чего не хватает.

Это было полезно?

Решение

  1. В этой реализации, unique.matrix такой же как unique.array

    > identical(unique.array, unique.matrix)

    [1] TRUE

  2. unique.array должен обрабатывать многомерные массивы, которые требуют дополнительной обработки, чтобы «свернуть» дополнительные измерения (эти дополнительные вызовы к paste()), которые не нужны в двухмерном случае. Ключевым разделом кода является:

    collapse <- (ndim > 1L) && (prod(dx[-MARGIN]) > 1L)

    temp <- if (collapse) apply(x, MARGIN, function(x) paste(x, collapse = "\r"))

  3. unique.data.frame оптимизирован для 2D случая, unique.matrix не является. Это может быть, как вы предлагаете, это просто не в текущей реализации.

Обратите внимание, что во всех случаях (уникальный. {Массив, матрица, data.table}), где существует более одного измерения, это строковое представление, которое сравнивается для уникальности. Для номеров с плавающей запятой это означает 15 десятичных цифр, поэтому

NROW(unique(a <- matrix(rep(c(1, 1+4e-15), 2), nrow = 2)))

является 1 пока

NROW(unique(a <- matrix(rep(c(1, 1+5e-15), 2), nrow = 2)))

а также

NROW(unique(a <- matrix(rep(c(1, 1+4e-15), 1), nrow = 2)))

оба 2. Анкет Ты Конечно unique Что ты хочешь?

Другие советы

  1. Не уверен, но я думаю, что потому что matrix является одним из смежных векторов, сначала копирует его в векторы столбцов (как data.frame) потому что paste нужен список векторов. Обратите внимание, что оба медленные, потому что оба используют paste.

  2. Возможно, потому что unique.data.table уже много раз быстрее. Пожалуйста, перейдите на v1.6.7, загрузив его из репозитория R-Forge, потому что это имеет исправление к unique Вы поднялись в этот вопрос. data.table не использует paste сделать unique.

a = matrix(sample(2,10^6,replace = TRUE), ncol = 10)
b = as.data.frame(a)
system.time(u1<-unique(a))
   user  system elapsed 
   2.98    0.00    2.99 
system.time(u2<-unique(b))
   user  system elapsed 
   0.99    0.00    0.99 
c = as.data.table(b)
system.time(u3<-unique(c))
   user  system elapsed 
   0.03    0.02    0.05  # 60 times faster than u1, 20 times faster than u2
identical(as.data.table(u2),u3)
[1] TRUE

Пытаясь ответить на мой собственный вопрос, особенно часть 1, мы можем увидеть, где время потрачено, глядя на результаты Rprof. Анкет Я запустил это снова, с 5 -метровыми элементами.

Вот результаты для первой уникальной операции (для матрицы):

> summaryRprof("u1.txt")
$by.self
                     self.time self.pct total.time total.pct
"paste"                   5.70    52.58       5.96     54.98
"apply"                   2.70    24.91      10.68     98.52
"FUN"                     0.86     7.93       6.82     62.92
"lapply"                  0.82     7.56       1.00      9.23
"list"                    0.30     2.77       0.30      2.77
"!"                       0.14     1.29       0.14      1.29
"c"                       0.10     0.92       0.10      0.92
"unlist"                  0.08     0.74       1.08      9.96
"aperm.default"           0.06     0.55       0.06      0.55
"is.null"                 0.06     0.55       0.06      0.55
"duplicated.default"      0.02     0.18       0.02      0.18

$by.total
                     total.time total.pct self.time self.pct
"unique"                  10.84    100.00      0.00     0.00
"unique.matrix"           10.84    100.00      0.00     0.00
"apply"                   10.68     98.52      2.70    24.91
"FUN"                      6.82     62.92      0.86     7.93
"paste"                    5.96     54.98      5.70    52.58
"unlist"                   1.08      9.96      0.08     0.74
"lapply"                   1.00      9.23      0.82     7.56
"list"                     0.30      2.77      0.30     2.77
"!"                        0.14      1.29      0.14     1.29
"do.call"                  0.14      1.29      0.00     0.00
"c"                        0.10      0.92      0.10     0.92
"aperm.default"            0.06      0.55      0.06     0.55
"is.null"                  0.06      0.55      0.06     0.55
"aperm"                    0.06      0.55      0.00     0.00
"duplicated.default"       0.02      0.18      0.02     0.18

$sample.interval
[1] 0.02

$sampling.time
[1] 10.84

И для кадры данных:

> summaryRprof("u2.txt")
$by.self
                     self.time self.pct total.time total.pct
"paste"                   1.72    94.51       1.72     94.51
"[.data.frame"            0.06     3.30       1.82    100.00
"duplicated.default"      0.04     2.20       0.04      2.20

$by.total
                        total.time total.pct self.time self.pct
"[.data.frame"                1.82    100.00      0.06     3.30
"["                           1.82    100.00      0.00     0.00
"unique"                      1.82    100.00      0.00     0.00
"unique.data.frame"           1.82    100.00      0.00     0.00
"duplicated"                  1.76     96.70      0.00     0.00
"duplicated.data.frame"       1.76     96.70      0.00     0.00
"paste"                       1.72     94.51      1.72    94.51
"do.call"                     1.72     94.51      0.00     0.00
"duplicated.default"          0.04      2.20      0.04     2.20

$sample.interval
[1] 0.02

$sampling.time
[1] 1.82

Мы замечаем, что версия матрицы тратит много времени на apply, paste, а также lapply. Анкет Напротив, версия кадры данных простые запуска duplicated.data.frame и большую часть времени тратится в paste, предположительно агрегирует результаты.

Хотя это объясняет куда время происходит, это не объясняет Почему Они имеют разные реализации, а также эффекты простого изменения от одного типа объекта на другой.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top