R: loop over columns in data.table
data.table, r, sapply
Solution
Have briefly investigated, and it looks like a `data.table` bug.
> 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"
So, looking at `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>
>
Note the pesky `unclass` on the first line. `?unclass` confirms that it takes a deep copy of its argument. From this quick look it doesn't seem like `sapply` or `lapply` are doing the copying (I didn't think they did since R is good at copy-on-write, and those aren't writing), but rather the `as.list` in `lapply` (which dispatches to `as.list.data.table`).
So, if we avoid the `unclass`, it should speed up. Let's try:
> 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"
>
So, yes, infinitely better.
I've raised bug report #2000 to remove the `as.list.data.table` method, since a `data.table` `is()` already a `list`, too. This might speed up quite a few idioms actually, such as `lapply(.SD,...)`. [EDIT: This was fixed in v1.8.1].
Thanks for asking this question!!
Problem
I want to determine the column classes of a large data.table. ``` colClasses <- sapply(DT, FUN=function(x)class(x)[1]) ``` works, but apparently local copies are stored into memory: ``` > memory.size() [1] 687.59 > colClasses <- sapply(DT, class) > memory.size() [1] 1346.21 ``` A loop seems not possible, because a data.table "with=FALSE" always results in a data.table. A quick and very dirty method is: ``` DT1 <- DT[1, ] colClasses <- sapply(DT1, FUN=function(x)class(x)[1]) ``` What is the most elegent and efficient way to do this?