Немного исследовали, и это выглядит как ошибка data.table
.
> DT = data.table(a=1:1e6,b=1:1e6,c=1:1e6,d=1:1e6)
> Rprofmem()
> sapply(DT,class)
a b c d
"integer" "integer" "integer" "integer"
> Rprofmem(NULL)
> noquote(readLines("Rprofmem.out"))
[1] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply"
[2] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply"
[3] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply"
[4] 4000040 :"as.list.data.table" "as.list" "lapply" "sapply"
> tracemem(DT)
> sapply(DT,class)
tracemem[000000000431A290 -> 00000000065D70D8]: as.list.data.table as.list lapply sapply
a b c d
"integer" "integer" "integer" "integer"
Так, глядя на as.list.data.table
:
> data.table:::as.list.data.table
function (x, ...)
{
ans <- unclass(x)
setattr(ans, "row.names", NULL)
setattr(ans, "sorted", NULL)
setattr(ans, ".internal.selfref", NULL)
ans
}
<environment: namespace:data.table>
>
Обратите внимание на досадные unclass
на первой линии. ?unclass
подтверждает, что он принимает глубокую копию своего аргумента. Из этого быстрого взгляда не похоже, что sapply
или lapply
делают копирование (я не думал, что они делали, так как R хорош при копировании на запись, и это не написано), а as.list
в lapply
(который отправляет as.list.data.table
).
Итак, если мы избежим unclass
, он должен ускориться. Давайте попробуем:
> DT = data.table(a=1:1e7,b=1:1e7,c=1:1e7,d=1:1e7)
> system.time(sapply(DT,class))
user system elapsed
0.28 0.06 0.35
> system.time(sapply(DT,class)) # repeat timing a few times and take minimum
user system elapsed
0.17 0.00 0.17
> system.time(sapply(DT,class))
user system elapsed
0.13 0.04 0.18
> system.time(sapply(DT,class))
user system elapsed
0.14 0.03 0.17
> assignInNamespace("as.list.data.table",function(x)x,"data.table")
> data.table:::as.list.data.table
function(x)x
> system.time(sapply(DT,class))
user system elapsed
0 0 0
> system.time(sapply(DT,class))
user system elapsed
0.01 0.00 0.02
> system.time(sapply(DT,class))
user system elapsed
0 0 0
> sapply(DT,class)
a b c d
"integer" "integer" "integer" "integer"
>
Так что, да, бесконечно лучше.
Я поднял bug report #2000 удалить метод as.list.data.table
, так как data.table
is()
уже является list
тоже. Это может ускорить довольно много идиом на самом деле, например lapply(.SD,...)
. [EDIT: Это было исправлено в v1.8.1].
Спасибо, что задали этот вопрос !!
Не уверен, что я следую. Почему не просто 'sapply (DT, class)'? –
тайминги добавлены в текст выше –
@MatthewDowle: Я думаю, что OP означает, что sapply создает временные переменные с подмножествами data.table, чтобы перейти к FUN для каждого столбца. Поскольку его data.table действительно большая и имеет много столбцов, неэффективна. По этой причине его решение заключается в том, чтобы сначала уменьшить данные. Таблица в одну строку, а затем вызвать sapply ... – digEmAll