Почему использование обновления на lm внутри сгруппированных data.table потеряет данные модели?
Хорошо, это странно. Я подозреваю, что это ошибка внутри data.table
, но было бы полезно, если кто-нибудь сможет объяснить, почему это происходит - что делает update
точно?
Я использую трюк list(list())
внутри data.table
для хранения установленных моделей. Когда вы создаете последовательность объектов lm
, каждая для разных группировок, а затем update
этих моделей, данные модели для всех моделей становятся данными модели последней группы. Это похоже на то, что ссылка где-то где-то, где должна была быть сделана копия, но я не могу найти, где и не могу воспроизвести это вне 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
И первая подгонка подтверждает thsi:
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
Ответы
Ответ 1
Это не ответ, но слишком длинный для комментария
Компонент .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
не долгосрочное решение, но, по крайней мере, обходное решение.