Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with split and data.table

I have a data.table that I want to split into a list and then modify. I'm discovering some weird behavior when I try to delete a column on one of the data.tables in the list after calling split. Here's a MWE (that throws an error and causes my R session to crash):

library(data.table)
d = data.table(level = c(1, 1, 2, 2), value = 1:4)
list = split(d, f = d$level)
list[[1]][, level := NULL]
list

I get:

Error in .shallow(x, cols = cols, retain.key = TRUE) : Internal error: length(names)>0 but <length(dt)
like image 417
random_forest_fanatic Avatar asked Aug 23 '26 00:08

random_forest_fanatic


1 Answers

I recommend to use l name for a variable instead of list.
This seems to be a bug caused by split.data.frame method utilized in the process.
I've quite recently proposed a new split.data.table method defined below. It seems to address your problem.

Update 2016-03-30:

split.data.table has been implemented in data.table 1.9.7. Now use can simply use:

library(data.table)
d = data.table(level = c(1, 1, 2, 2), value = 1:4)
l = split(d, by = "level")
l[[1L]][, level := NULL]
l
#$`1`
#   value
#1:     1
#2:     2
#
#$`2`
#   level value
#1:     2     3
#2:     2     4

The old answer below, it may be useful if you stuck with 1.9.6 or below. Be aware that it won't handle factor levels the same way as split.data.frame, this isn't the case for method developed in data.table 1.9.7 which is consistent to data.frame method.

library(data.table)
split.data.table = function(x, f, drop = FALSE, by, flatten = FALSE, ...){
    if(missing(by) && !missing(f)) by = f
    stopifnot(!missing(by), is.character(by), is.logical(drop), is.logical(flatten), !".ll" %in% names(x), by %in% names(x))
    if(!flatten){
        .by = by[1L]
        tmp = x[, list(.ll=list(.SD)), by = .by, .SDcols = if(drop) setdiff(names(x), .by) else names(x)]
        setattr(ll <- tmp$.ll, "names", tmp[[.by]])
        if(length(by) > 1L) return(lapply(ll, split.data.table, drop = drop, by = by[-1L])) else return(ll)
    } else {
        tmp = x[, list(.ll=list(.SD)), by=by, .SDcols = if(drop) setdiff(names(x), by) else names(x)]
        setattr(ll <- tmp$.ll, 'names', tmp[, .(nm = paste(.SD, collapse = ".")), by = by, .SDcols = by]$nm)
        return(ll)
    }
}
d = data.table(level = c(1, 1, 2, 2), value = 1:4)
l = split.data.table(d, by = "level")
# below setattr to be addressed in split.data.table
invisible(lapply(l, setattr, ".data.table.locked", NULL))
l[[1]][, level := NULL]
l
#$`1`
#   value
#1:     1
#2:     2
#
#$`2`
#   level value
#1:     2     3
#2:     2     4

I've also filled a bug report describing your case, you can find it in data.table#1481.

like image 178
jangorecki Avatar answered Aug 25 '26 18:08

jangorecki