¿Por qué el uso de update en una lm dentro de un data.table agrupado pierde sus datos de modelo?

Ok, esta es una extraña. Sospecho que esto es un error dentro de data.table , pero sería útil si alguien puede explicar por qué sucede esto, ¿qué está haciendo exactamente la update ?

Estoy usando la list(list()) truco dentro de data.table para almacenar modelos ajustados. Cuando crea una secuencia de objetos lm cada uno para diferentes agrupaciones, y luego update esos modelos, los datos del modelo para todos los modelos pasan a ser los de la última agrupación. Parece que una referencia está dando vueltas en algún lugar donde debería haberse hecho una copia, pero no puedo encontrar dónde y no puedo reproducir esto fuera de lm y update .

Ejemplo concreto:

Comenzando con los datos del iris, primero haga que las tres especies tengan diferentes tamaños de muestra, luego ajuste un modelo de lm a cada especie, actualice esos modelos:

 set.seed(3) DT = data.table(iris) DT = DT[rnorm(150) < 0.9] fit = DT[, list(list(lm(Sepal.Length ~ Sepal.Width + Petal.Length))), by = Species] fit2 = fit[, list(list(update(V1[[1]], ~.-Sepal.Length))), by = Species] 

La tabla de datos original tiene diferentes números de cada especie

 DT[,.N, by = Species] # Species N # 1: setosa 41 # 2: versicolor 39 # 3: virginica 42 

Y el primer ajuste confirma esto:

 fit[, nobs(V1[[1]]), by = Species] # Species V1 # 1: setosa 41 # 2: versicolor 39 # 3: virginica 42 

Pero el segundo ajuste actualizado muestra 42 para todos los modelos

 fit2[, nobs(V1[[1]]), by = Species] # Species V1 # 1: setosa 42 # 2: versicolor 42 # 3: virginica 42 

También podemos ver el atributo del modelo que contiene los datos utilizados para la adaptación, y ver que todo el modelo efectivamente utiliza los datos finales de los grupos. La pregunta es ¿cómo ha sucedido esto?

 head(fit$V1[[1]]$model) # Sepal.Length Sepal.Width Petal.Length # 1 5.1 3.5 1.4 # 2 4.9 3.0 1.4 # 3 4.7 3.2 1.3 # 4 4.6 3.1 1.5 # 5 5.0 3.6 1.4 # 6 5.4 3.9 1.7 head(fit$V1[[3]]$model) # Sepal.Length Sepal.Width Petal.Length # 1 6.3 3.3 6.0 # 2 5.8 2.7 5.1 # 3 6.3 2.9 5.6 # 4 7.6 3.0 6.6 # 5 4.9 2.5 4.5 # 6 7.3 2.9 6.3 head(fit2$V1[[1]]$model) # Sepal.Length Sepal.Width Petal.Length # 1 6.3 3.3 6.0 # 2 5.8 2.7 5.1 # 3 6.3 2.9 5.6 # 4 7.6 3.0 6.6 # 5 4.9 2.5 4.5 # 6 7.3 2.9 6.3 head(fit2$V1[[3]]$model) # Sepal.Length Sepal.Width Petal.Length # 1 6.3 3.3 6.0 # 2 5.8 2.7 5.1 # 3 6.3 2.9 5.6 # 4 7.6 3.0 6.6 # 5 4.9 2.5 4.5 # 6 7.3 2.9 6.3 

Esta no es una respuesta, pero es demasiado larga para un comentario

El .Environment for the terms component es idéntico para cada modelo resultante

 e1 < - attr(fit[['V1']][[1]]$terms, '.Environment') e2 <- attr(fit[['V1']][[2]]$terms, '.Environment') e3 <- attr(fit[['V1']][[3]]$terms, '.Environment') identical(e1,e2) ## TRUE identical(e2, e3) ## TRUE 

Parece que data.table utiliza el mismo bit de memoria (mi término no técnico) para cada evaluación de j por grupo (que es eficiente). Sin embargo, cuando se llama a la update , está usando esto para reajustar el modelo. Esto contendrá los valores del último grupo.

Entonces, si manipulas esto, funcionará

 fit = DT[, { xx < -list2env(copy(.SD)) mymodel <-lm(Sepal.Length ~ Sepal.Width + Petal.Length) attr(mymodel$terms, '.Environment') <- xx list(list(mymodel))}, by= 'Species'] lfit2 <- fit[, list(list(update(V1[[1]], ~.-Sepal.Width))), by = Species] lfit2[,lapply(V1,nobs)] V1 V2 V3 1: 41 39 42 # using your exact diagnostic coding. lfit2[,nobs(V1[[1]]),by = Species] Species V1 1: setosa 41 2: versicolor 39 3: virginica 42 

no es una solución a largo plazo, pero al menos una solución.