我想将lm
对象保存到文件中并将其重新加载到另一个程序中。我知道我可以通过saveRDS
/ readRDS
写/读二进制文件来做到这一点,但我想要一个ASCII文件而不是二进制文件。在更一般的层面上,我想知道为什么我在dput
输出中阅读的习语一般不符合我的期望。
下面是简单拟合的示例,以及模型的成功和不成功的重新创建:
dat_train <- data.frame(x=1:4, z=c(1, 2.1, 2.9, 4))
fit <- lm(z ~ x, dat_train)
rm(dat_train) # Just to make sure fit is not dependent upon `dat_train existence`
dat_score <- data.frame(x=c(1.5, 3.5))
## This works (of course)
predict(fit, dat_score)
# 1 2
# 1.52 3.48
保存到二进制文件:
## http://stackoverflow.com/questions/5118074/reusing-a-model-built-in-r
saveRDS(fit, "model.RDS")
fit2 <- readRDS("model.RDS")
predict(fit2, dat_score)
# 1 2
# 1.52 3.48
这样做(dput
在R会话中不是文件):
fit2 <- eval(dput(fit))
predict(fit2, dat_score)
# 1 2
# 1.52 3.48
但如果我将文件保存到磁盘,我无法弄清楚如何恢复正常形状:
dput(fit, file = "model.R")
fit3 <- source("model.R")$value
# Error in is.data.frame(data): object 'dat_train' not found
predict(fit3, dat_score)
# Error in predict(fit3, dat_score): object 'fit3' not found
尝试使用eval
明确无效:
## http://stackoverflow.com/questions/9068397/import-text-file-as-single-character-string
dput(fit, file="model.R")
fit4 <- eval(parse(text=paste(readLines("model.R"), collapse=" ")))
# Error in is.data.frame(data): object 'dat_train' not found
predict(fit4, dat_score)
# Error in predict(fit4, dat_score): object 'fit4' not found
在上述两种情况下,我都希望fit3
和fit4
都可以正常工作,但是他们不会重新编译成lm
对象,我可以使用predict()
}。
任何人都可以告诉我如何将模型持久化到具有structure(...)
类似ASCII的结构的文件,然后将其重新读回作为lm
对象,我可以在{ {1}}?为什么我目前的方法不起作用?
答案 0 :(得分:13)
第1步:
您需要控制解析选项:
dput(fit, control = c("quoteExpressions", "showAttributes"), file = "model.R")
您可以在?.deparseOpts
中了解有关所有可能选项的更多信息。
&#34; quoteExpressions&#34;使用quote
包装所有调用/表达式/语言,以便在以后重新解析它们时不会对它们进行评估。注意:
source
正在进行解析; call
字段对象是一个电话:
fit$call
# lm(formula = z ~ x, data = dat_train)
因此,如果没有&#34; quoteExpressions&#34;,R将尝试在解析期间评估lm
调用。如果我们对它进行评估,它就适合一个线性模型,而R的目标是找到dat_train
,这将在你的新R会话中不存在。
&#34; showAttributes&#34;是另一个强制性的选择,因为&#34; lm&#34; object具有类属性。你当然不想丢弃所有的类属性,只能导出一个简单的列表&#34;对象,对吗?而且,&#34; lm&#34;对象,如model
(模型框架),qr
(紧凑QR矩阵)和terms
(术语信息)等都具有属性。你想要保留所有这些。
如果您未设置control
,则默认设置为:
control = c("keepNA", "keepInteger", "showAttributes")
将被使用。正如你所看到的,没有&#34; quoteExpressions&#34;,所以你会遇到麻烦。
你也可以指定&#34; keepInteger&#34;并且&#34; keepNA&#34;,但我不认为需要&#34; lm&#34;对象
第2步:
上述步骤将使source
正常工作。您可以恢复您的模型:
fit1 <- source("model.R")$value
但是,尚未准备好使用summary
和predict
这样的通用函数。为什么呢?
关键问题是terms
中的fit1
对象实际上不是&#34;术语&#34;对象,但只有一个公式(它甚至不是一个公式,而只是一个&#34;语言&#34;对象没有&#34;公式&#34;类!)。只需比较fit$terms
和fit1$terms
,您就会看到差异。不要感到惊讶;我们已经设定了&#34; quoteExpressions&#34;早。虽然这对于防止call
的评估肯定有帮助,但它对terms
有副作用。所以我们需要尽可能地重建terms
。
幸运的是,这样做足够了:
fit1$terms <- terms.formula(fit1$terms)
虽然这仍然无法恢复fit$terms
中的所有信息(如缺少变量类),但它很容易成为有效的&#34;术语&#34;对象
为什么是&#34;术语&#34;对象关键?因为所有通用功能都依赖于它。你可能不需要了解更多,因为它真的是技术性的,所以我会在这里停下来。
完成此操作后,我们也可以成功使用predict
(以及summary
):
predict(fit1) ## no `newdata` given, using model frame `fit1$model`
# 1 2 3 4
#1.03 2.01 2.99 3.97
predict(fit1, dat_score) ## with `newdata`
# 1 2
#1.52 3.48
结论备注:
虽然我已经告诉你如何让事情发挥作用,但我并不是真的建议你这样做。一个&#34; lm&#34;将模型拟合到大型数据集时,对象将非常大,例如,residuals
,fitted.values
是长向量,qr
和model
是巨大的矩阵/数据帧。所以想想这个。
答案 1 :(得分:2)
这是一次重要的更新!
正如前面的答案中所提到的,最具挑战性的一点是尽可能地恢复$terms
。使用terms.formula
的建议方法适用于OP的示例,但不适用于bs()
和poly()
的以下内容:
dat <- data.frame(x1 = runif(20), x2 = runif(20), x3 = runif(20), y = rnorm(20))
library(splines)
fit <- lm(y ~ bs(x1, df = 3) + poly(x2, degree = 3) + x3, data = dat)
rm(dat)
如果我们按照上一个答案:
dput(fit, control = c("quoteExpressions", "showAttributes"), file = "model.R")
fit1 <- source("model.R")$value
fit1$terms <- terms.formula(fit1$terms)
我们会看到summary.lm
和anova.lm
正常工作,但不是predict.lm
:
predict(fit1, newdata = data.frame(x1 = 0.5, x2 = 0.5, x3 = 0.5))
bs(x1,df = 3)出错:找不到函数“bs”
这是因为缺少".Environment"
的{{1}}属性。我们需要
$terms
现在再次高于environment(fit1$terms) <- .GlobalEnv
,我们会看到另一个错误:
poly(x2,degree = 3)中的错误:
'degree'必须小于唯一点数
这是因为我们缺少predict
属性来安全/正确预测"predvars"
和bs()
。
补救措施是我们需要poly()
此类特殊属性另外:
dput
然后阅读并添加
dput(attr(fit$terms, "predvars"), control = "quoteExpressions", file = "predvars.R")
现在正在运行attr(fit1$terms, "predvars") <- source("predvars.R")$value
。
请注意,predict
的{{1}}属性也会丢失,但这似乎不会对任何通用函数造成任何问题。