Почему запуск «уникального» быстрее на кадре данных, чем матрица в R?
-
26-10-2019 - |
Вопрос
Я начал верить, что рамки данных не имеют никаких преимуществ по сравнению с матрицами, за исключением удобства для нотации. Однако я заметил эту странность при беге 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
Результаты времени расходятся еще более существенно по мере увеличения количества строк. Итак, в этом вопросе есть две части.
Почему это медленнее для матрицы? Кажется быстрее конвертировать в кадр данных, запустить
unique
, а затем преобразовать обратно.Есть ли причина не просто обернуть
unique
вmyUnique
, что делает преобразования в части № 1?
Примечание 1. Учитывая, что матрица является атомной, кажется, что unique
должно быть быстрее для матрицы, а не медленнее. Возможность итерации по сравнению с фиксированным размером, смежные блоки памяти, как правило, должны быть быстрее, чем запуск отдельных блоков связанных списков (я предполагаю, как реализованы кадры данных ...).
Примечание 2. Как показано на производительности data.table
, Бег unique
На кадре данных или матрице - сравнительно плохая идея - см. Ответ Мэтью Даула и комментарии к относительным временам. Я перенес много объектов в таблицы данных, и эта производительность является еще одной причиной для этого. Поэтому, хотя пользователи должны быть хорошо выполнены для применения таблиц данных, по причинам педагогических / сообществ, я оставлю этот вопрос открытым в отношении Почему Это занимает больше времени на объектах матрицы. Приведенный ниже адресной адрес куда время уходит и как еще Можем ли мы получить лучшую производительность (т.е. таблицы данных). Ответ на Почему близко под рукой - код можно найти через unique.data.frame
а также unique.matrix
. Анкет :) Английское объяснение того, что он делает и почему все, чего не хватает.
Решение
В этой реализации,
unique.matrix
такой же какunique.array
> identical(unique.array, unique.matrix)
[1] TRUE
unique.array
должен обрабатывать многомерные массивы, которые требуют дополнительной обработки, чтобы «свернуть» дополнительные измерения (эти дополнительные вызовы кpaste()
), которые не нужны в двухмерном случае. Ключевым разделом кода является:collapse <- (ndim > 1L) && (prod(dx[-MARGIN]) > 1L)
temp <- if (collapse) apply(x, MARGIN, function(x) paste(x, collapse = "\r"))
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
Что ты хочешь?
Другие советы
Не уверен, но я думаю, что потому что
matrix
является одним из смежных векторов, сначала копирует его в векторы столбцов (какdata.frame
) потому чтоpaste
нужен список векторов. Обратите внимание, что оба медленные, потому что оба используютpaste
.Возможно, потому что
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
, предположительно агрегирует результаты.
Хотя это объясняет куда время происходит, это не объясняет Почему Они имеют разные реализации, а также эффекты простого изменения от одного типа объекта на другой.