R: цикл по столбцам в данных.таблица

Я хочу определить классы столбцов больших данных.таблица.

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

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

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

цикл кажется невозможным, потому что данные.таблица "with=FALSE" всегда приводит к данным.таблица.

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

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

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

2 ответов


кратко исследовали, и это похоже на 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" 
> 

да, бесконечно лучше.

я поднял сообщение об ошибке #2000 удалить as.list.data.table метод, поскольку data.table is() уже list тоже. Это может ускорить довольно много идиом на самом деле, таких как lapply(.SD,...). [EDIT: это было исправлено в v1.8.1].

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


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

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

это в основном ваше быстрое решение, но, возможно, немного яснее (даже если не так много)...