如何正确地“拟合”一个拟合的线性模型(通过`lm`)到ASCII文件并稍后重新创建它?

时间:2017-01-13 23:39:48

标签: r regression linear-regression lm

我想将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

在上述两种情况下,我都希望fit3fit4都可以正常工作,但是他们不会重新编译成lm对象,我可以使用predict() }。

任何人都可以告诉我如何将模型持久化到具有structure(...)类似ASCII的结构的文件,然后将其重新读回作为lm对象,我可以在{ {1}}?为什么我目前的方法不起作用?

2 个答案:

答案 0 :(得分:13)

第1步:

您需要控制解析选项:

dput(fit, control = c("quoteExpressions", "showAttributes"), file = "model.R") 

您可以在?.deparseOpts中了解有关所有可能选项的更多信息。

&#34; quoteExpressions&#34;使用quote包装所有调用/表达式/语言,以便在以后重新解析它们时不会对它们进行评估。注意:

  • source正在进行解析;
  • 你装好的&#34; lm&#34;
  • 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

但是,尚未准备好使用summarypredict这样的通用函数。为什么呢?

关键问题是terms中的fit1对象实际上不是&#34;术语&#34;对象,但只有一个公式(它甚至不是一个公式,而只是一个&#34;语言&#34;对象没有&#34;公式&#34;类!)。只需比较fit$termsfit1$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;将模型拟合到大型数据集时,对象将非常大,例如,residualsfitted.values是长向量,qrmodel是巨大的矩阵/数据帧。所以想想这个。

答案 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.lmanova.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}}属性也会丢失,但这似乎不会对任何通用函数造成任何问题。