为什么在分组的data.table中使用lm上的更新会丢失模型数据?

好吧,这是一个奇怪的。 我怀疑这是data.table内部的一个bug,但是如果有人能解释为什么会发生这种情况,那么这将是有用的 – update在做什么?

我在data.table里面使用list(list())技巧来存储拟合的模型。 当您为不同的分组创build一个lm对象序列,然后update这些模型时,所有模型的模型数据将变为最后一个分组的模型数据。 这似乎是一个参考挂在哪里应该有一个副本,但我无法find哪里,我不能在lm之外重现此update

具体例子:

从虹膜数据开始,先将这三种物种的样本大小进行比较,然后对每个物种进行lm模型拟合,更新这些模型:

 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] 

原始数据表的每个种类都有不同的数字

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

第一个契合证实了这一点:

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

但更新的第二个适合所有型号显示42

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

我们也可以看看包含用于拟合的数据的模型属性,并且看到所有的模型确实正在使用最终的组数据。 问题是这是怎么发生的?

 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 

这不是一个答案,但太长的评论

术语组件的.Environment对于每个生成的模型都是相同的

 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 

看起来, data.table使用相同的内存位 (我的非技术术语)每个组的j评估(这是有效的)。 但是,当update被调用时,它正在使用这个来重新设置模型。 这将包含最后一组的值。

所以,如果你这样做,它会工作

 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 

不是一个长期的解决scheme,但至less是一个解决方法。