Недопустимый .internal.selfref в data.table
Мне нужно было назначить "второй" id для группировки некоторых значений внутри моего исходного id
. это мои данные образца:
dt<-structure(list(id = c("aaaa", "aaaa", "aaas", "aaas", "bbbb", "bbbb"),
period = c("start", "end", "start", "end", "start", "end"),
date = structure(c(15401L, 15401L, 15581L, 15762L, 15430L, 15747L), class = c("IDate", "Date"))),
class = c("data.table", "data.frame"),
.Names = c("id", "period", "date"),
sorted = "id")
> dt
id period date
1: aaaa start 2012-03-02
2: aaaa end 2012-03-05
3: aaas start 2012-08-21
4: aaas end 2013-02-25
5: bbbb start 2012-03-31
6: bbbb end 2013-02-11
column id
необходимо сгруппировать (используя то же значение в say id2
) в соответствии с этим списком:
> groups
[[1]]
[1] "aaaa" "aaas"
[[2]]
[1] "bbbb"
Я использовал следующий код, который, кажется, работает, дает следующее warning
:
> dt[, id2 := which(vapply(groups, function(x,y) any(x==y), .BY[[1]], FUN.VALUE=T)), by=id]
Warning message:
In `[.data.table`(dt, , `:=`(id2, which(vapply(groups, function(x, :
Invalid .internal.selfref detected and fixed by taking a copy of the whole table,
so that := can add this new column by reference. At an earlier point, this data.table has
been copied by R (or been created manually using structure() or similar). Avoid key<-,
names<- and attr<- which in R currently (and oddly) may copy the whole data.table. Use
set* syntax instead to avoid copying: setkey(), setnames() and setattr(). Also,
list (DT1,DT2) will copy the entire DT1 and DT2 (R list() copies named objects),
use reflist() instead if needed (to be implemented). If this message doesn't help,
please report to datatable-help so the root cause can be fixed.
> dt
id period date id2
1: aaaa start 2012-03-02 1
2: aaaa end 2012-03-02 1
3: aaas start 2012-08-29 1
4: aaas end 2013-02-26 1
5: bbbb start 2012-03-31 2
6: bbbb end 2013-02-11 2
может кто-нибудь кратко объяснить природу этого предупреждения и любое возможное последствие окончательных результатов (если таковые имеются)? спасибо
ИЗМЕНИТЬ:
фактический код фактически показывает, когда создается dt
и как передается функция, которая дает предупреждение:
f.main <- function(){
f2 <- function(x){
groups <- list(c("aaaa", "aaas"), "bbbb") # actually generated depending on the similarity between values of x$id
x <- x[, id2 := which(vapply(groups, function(x,y) any(x==y), .BY[[1]], FUN.VALUE=T)), by=id]
return(x)
}
x <- f1()
if(!is.null(x[["res"]])){
x <- f2(x[["res"]])
return(x)
} else {
# something else
}
}
f1 <- function(){
dt<-data.table(id = c("aaaa", "aaaa", "aaas", "aaas", "bbbb", "bbbb"),
period = c("start", "end", "start", "end", "start", "end"),
date = structure(c(15401L, 15401L, 15581L, 15762L, 15430L, 15747L), class = c("IDate", "Date")))
return(list(res=dt, other_results=""))
}
> f.main()
id period date id2
1: aaaa start 2012-03-02 1
2: aaaa end 2012-03-02 1
3: aaas start 2012-08-29 1
4: aaas end 2013-02-26 1
5: bbbb start 2012-03-31 2
6: bbbb end 2013-02-11 2
Warning message:
In `[.data.table`(x, , `:=`(id2, which(vapply(groups, function(x, :
Invalid .internal.selfref detected and fixed by taking a copy of the whole table,
so that := can add this new column by reference. At an earlier point, this data.table
has been copied by R (or been created manually using structure() or similar).
Avoid key<-, names<- and attr<- which in R currently (and oddly) may copy the whole
data.table. Use set* syntax instead to avoid copying: setkey(), setnames() and setattr().
Also, list(DT1,DT2) will copy the entire DT1 and DT2 (R list() copies named objects),
use reflist() instead if needed (to be implemented). If this message doesn't help,
please report to datatable-help so the root cause can be fixed.
Ответы
Ответ 1
Да, проблема в списке. Вот простой пример:
DT <- data.table(1:5)
mylist1 <- list(DT,"a")
mylist1[[1]][,id:=.I]
#warning
mylist2 <- list(data.table(1:5),"a")
mylist2[[1]][,id:=.I]
#no warning
Вам следует избегать копирования таблицы data.table в список (и чтобы быть в безопасности, я бы вообще не использовал DT в списке). Попробуйте следующее:
f1 <- function(){
mylist <- list(res=data.table(id = c("aaaa", "aaaa", "aaas", "aaas", "bbbb", "bbbb"),
period = c("start", "end", "start", "end", "start", "end"),
date = structure(c(15401L, 15401L, 15581L, 15762L, 15430L, 15747L), class = c("IDate", "Date"))))
other_results <- ""
mylist$other_results <- other_results
mylist
}
Ответ 2
Вы можете "мелкой копии" при создании списка, так что 1) вы не делаете полную копию памяти (скорость не изменяется), а 2) вы не получаете внутреннюю ошибку ref (благодаря @mnel для этот трюк).
Создание данных:
set.seed(45)
ss <- function() {
tt <- sample(1:10, 1e6, replace=TRUE)
}
tt <- replicate(100, ss(), simplify=FALSE)
tt <- as.data.table(tt)
Как вам следует создавать список (мелкая копия):
system.time( {
ll <- list(d1 = { # shallow copy here...
data.table:::settruelength(tt, 0)
invisible(alloc.col(tt))
}, "a")
})
user system elapsed
0 0 0
> system.time(tt[, bla := 2])
user system elapsed
0.012 0.000 0.013
> system.time(ll[[1]][, bla :=2 ])
user system elapsed
0.008 0.000 0.010
Таким образом, вы не станете жертвовать скоростью, и вы не получите предупреждение, за которым следует полная копия.
Надеюсь, это поможет.
Ответ 3
"Недопустимый .internal.selfref обнаружен и исправлен, взяв копию..."
Нет необходимости делать копию при назначении id2 внутри f2(), вы можете добавить столбец напрямую, изменив:
# From:
x <- x[, id2 := which(vapply(groups, function(x,y) any(x==y), .BY[[1]], FUN.VALUE=T)), by=id]
# To something along the lines of:
x$id2 <- findInterval( match( x$id, unlist(groups)), cumsum(c(0,sapply(groups, length)))+1)
Затем вы можете продолжать использовать таблицу данных "x", как обычно, без предупреждения.
Кроме того, чтобы просто подавить предупреждение, вы можете использовать suppressWarnings() вокруг вызова f2(x[["res"]])
.
Даже на небольших таблицах может быть существенная разница в производительности:
Performance Comparison:
Unit: milliseconds
expr min lq median uq max neval
f.main() 2.896716 2.982045 3.034334 3.137628 7.542367 100
suppressWarnings(f.main()) 3.005142 3.081811 3.133137 3.210126 5.363575 100
f.main.direct() 1.279303 1.384521 1.413713 1.486853 5.684363 100