2012-05-14 5 views
8

Я хочу определить классы столбцов большой таблицы данных.R: цикл над столбцами в data.table

colClasses <- sapply(DT, FUN=function(x)class(x)[1]) 

работает, но, по-видимому локальные копии сохраняются в памяти:

> memory.size() 
[1] 687.59 
> colClasses <- sapply(DT, class) 
> memory.size() 
[1] 1346.21 

Цикл кажется не представляется возможным, так как data.table "с = FALSE" всегда приводит к data.table.

Быстро и очень грязный метод:

DT1 <- DT[1, ] 
colClasses <- sapply(DT1, FUN=function(x)class(x)[1]) 

Что самое Elegent и эффективный способ сделать это?

+1

Не уверен, что я следую. Почему не просто 'sapply (DT, class)'? –

+0

тайминги добавлены в текст выше –

+3

@MatthewDowle: Я думаю, что OP означает, что sapply создает временные переменные с подмножествами data.table, чтобы перейти к FUN для каждого столбца. Поскольку его data.table действительно большая и имеет много столбцов, неэффективна. По этой причине его решение заключается в том, чтобы сначала уменьшить данные. Таблица в одну строку, а затем вызвать sapply ... – digEmAll

ответ

10

Немного исследовали, и это выглядит как ошибка 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.tableis() уже является list тоже. Это может ускорить довольно много идиом на самом деле, например lapply(.SD,...). [EDIT: Это было исправлено в v1.8.1].

Спасибо, что задали этот вопрос !!

+0

Очень информативный пост. Спасибо за то, что вы указали шаги, которые вы использовали для отладки. –

+0

Спасибо, Мэтью! –

2

Я не вижу ничего плохого в подходе, как этот

colClasses <- sapply(head(DT1,1), FUN=class) 

это в основном ваше решение quick'n'dirty, но, возможно, немного понятнее (даже если это не так много) ...

+0

Это действительно хорошее решение, но не так элегантно, как я надеялся. –

+0

@ user1393348: да, это все еще обходное решение после всего :) – digEmAll