2012-05-14 18 views
8

Quiero determinar las clases de columna de una gran data.table.R: loop sobre columnas en data.table

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

obras, pero al parecer las copias locales se almacenan en la memoria:

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

Un bucle parece que no es posible, debido a que un data.table "con = FALSO" siempre resulta en un data.table.

Un método rápido y muy sucio es:

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

¿Cuál es la forma más elegent y eficiente de hacer esto?

+1

No estoy seguro de que lo siga. ¿Por qué no simplemente 'sapply (DT, clase)'? –

+0

sincronizaciones agregadas en el texto anterior –

+3

@MatthewDowle: Creo que el OP significa que crea samente las variables temporales con los subconjuntos de data.table para pasar a FUN para cada columna. Dado que data.table es realmente grande y tiene muchas columnas, no es eficiente. Por esta razón, su solución consiste en reducir primero la tabla de datos a una fila, luego llamar sapply ... – digEmAll

Respuesta

10

Ha investigado brevemente, y parece un error 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í, mirando a 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> 
> 

Nota del unclass molestos en la primera línea. ?unclass confirma que se necesita una copia profunda de su argumento. De este vistazo rápido, no parece que sapply o lapply están haciendo la copia (no pensé que lo hicieran, ya que R es bueno para copiar y escribir, y no están escribiendo), sino más bien el as.list en lapply (que se distribuye al as.list.data.table).

Por lo tanto, si evitamos el unclass, debería acelerarse. Vamos a tratar:

> 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" 
> 

Así que, sí, infinitamente mejor.

He criado bug report #2000 para eliminar el método as.list.data.table, ya que un data.tableis() ya un list, también. Esto podría acelerar un buen número de modismos, como lapply(.SD,...). [EDITAR: Esto fue arreglado en v1.8.1].

Gracias por preguntar esta pregunta !!

+0

Publicación muy informativa. Gracias por mostrar los pasos que usaste para depurar esto. –

+0

Gracias Matthew! –

2

No veo nada malo en un enfoque como este

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

es básicamente su solución quick'n'dirty pero quizás un poco más clara (aunque no tanto) ...

+0

De hecho, es una buena solución, pero no tan elegante como esperaba. –

+0

@ user1393348: sí, sigue siendo una solución después de todo :) – digEmAll

Cuestiones relacionadas