【问题标题】:Print "pretty" tables for h2o models in R在 R 中打印 h2o 模型的“漂亮”表
【发布时间】:2016-12-18 01:20:32
【问题描述】:

R 有多个包,可帮助从统计模型输出中打印“漂亮”表格 (LaTeX/HTML/TEXT),并轻松比较替代模型规范的结果。

其中一些包是apsrtablextablememisctexregoutregstargazer(示例见此处:https://www.r-statistics.com/2013/01/stargazer-package-for-beautiful-latex-tables-from-r-statistical-models-output/)。

是否有任何类似的R 包支持h2o 包的模型?

这里有两个简单的 GLM 模型示例,带有h2o,我喜欢将它们并排打印为“漂亮”的表格。

# Load package and setup h2o
library(h2o)
localH2O <- h2o.init(ip = 'localhost', port = 54321, max_mem_size = '4g')

# Load data
prostatePath <- system.file("extdata", "prostate.csv", package = "h2o")
prostate.hex <- h2o.importFile(path = prostatePath, destination_frame = "prostate.hex")

# Run GLMs
model.output.1 <- h2o.glm(y = "CAPSULE", x = c("RACE","PSA","DCAPS"),
  training_frame = prostate.hex,family = "binomial", nfolds = 0, 
  alpha = 0.5, lambda_search = FALSE)
model.output.2 <- h2o.glm(y = "CAPSULE", x = c("AGE","RACE","PSA","DCAPS"), 
  training_frame = prostate.hex, family = "binomial", nfolds = 0, 
  alpha = 0.5, lambda_search = FALSE)

这是使用 texreg 包中的 screenreg() 的常规 GLM 对象的外观:

library(data.table)
library(texreg)
d <- fread(prostatePath)
model.output.1.glm <- glm(CAPSULE ~ RACE + PSA + DCAPS, data=d)
model.output.2.glm <- glm(CAPSULE ~ AGE + RACE + PSA + DCAPS, data=d)
screenreg(list(model.output.1.glm, model.output.2.glm))

【问题讨论】:

  • 添加一个到列表中:broom
  • “美丽”和“观星者”是相互排斥的。 It’s a seriously terrible package — 不要使用它。我建议改为‹pander›。
  • @Keith Hughitt:broom 中有一个功能可以并排比较模型吗?
  • 我认为通常的术语是“漂亮”,技术术语是“可发布”。
  • 同意。我使用“美丽”一词是因为我在我的问题中链接的博客文章。

标签: r h2o stargazer texreg


【解决方案1】:

如果您从模型输出中提取它们,您可以将 R xtable 包与 h2o 的 H2OTable 一起使用(或者 knitr,如果您使用 as.h2o(your_H2OTable) 将 H2OTable 转换为 H2OFrame)。

例如,要从模型的系数创建一个漂亮的表格,您需要先使用model.output.1@model$coefficients_table 提取系数表格,然后您可以使用 xtable:xtable(prostate.glm@model$coefficients_table) 打印出 Latex 代码。

对于并排视图,在 knitr 或 xtablextable and sweave 中有多个关于如何执行此操作的帖子

【讨论】:

  • 这指向了正确的方向。但是,并排表格 - 至少如链接的帖子中所示,不一定与相应的系数匹配。他们只是简单地把两张桌子放在一起,对吧?
【解决方案2】:

不,目前没有一个包可以做到这一点。 broom 包还不支持 H2O 模型——那太酷了!也许这可能在未来发生。一旦有一种方法可以使用 broom 或类似功能将模型输出“整理”到 R data.frame 中,那么 xtable 等就会很好地工作。

【讨论】:

  • 这似乎不是一个答案(还)。
【解决方案3】:

texreg 包作者在这里。 texreg 基于泛型函数,这意味着任何用户都可以为任意模型添加自定义 extract 方法并使其与 texreg 一起使用。我将在下面引导您完成。我希望这个详细的阐述能够帮助过去提出类似问题的其他人设计自己的texreg 扩展。

另一个示例请参见2013 paper in the Journal of Statistical Software 中的第 6 节。不过,首先,我将描述texreg 架构如何更普遍地工作,以便让您了解什么是可能的。

texreg 和通用 extract 函数

共有三个函数:texreg(用于 LaTeX 输出)、htmlreg(用于 HTML 输出,在大多数使用场景中也可以通过 Word 或 Markdown 进行解释)和screenreg(用于在控制台)。这三个函数用于将一些清理后的信息(系数、标准误差、置信区间、p 值、拟合优度统计量、模型标签等)转换为各自的输出格式。这并不完美,可能会更加灵活,但我认为此时它有很多自定义参数,包括booktabsdcolumn 支持。因此,最大的挑战是首先获得清理后的模型信息。

这是通过向这三个函数中的任何一个提供texreg 对象来完成的。 texreg 对象只是系数等的容器,并使用 S4 类正式定义。要创建texreg 对象,您可以使用构造函数createTexreg(如帮助页面中所述),它接受所有不同的信息作为参数,例如标准错误等。或者(更好)你可以使用extract 函数从某个估计模型中提取这些信息,并返回一个texreg 对象以用于三个函数中的任何一个。您通常这样做的方式是将几个模型的列表交给texregscreenreg 等函数,该函数将在内部调用extract 以创建texreg 对象,然后处理来自那些对象。

但是,将调用extract 函数的输出保存到一个对象中同样有效,可能操作此texreg 对象,然后在被操作对象上调用texreg 函数以显示它作为一张桌子。这样可以灵活地调整结果。

之前,我提到包使用泛型函数。这意味着extract 函数是通用的,因为您可以为其注册适用于任意模型类的方法。例如,如果extract 函数不知道如何处理h2o 对象以及如何从此类对象中提取相关信息,您可以编写一个方法来执行此操作并将其注册到extract 函数。下面,我将一步一步地引导你,希望人们从这个详细的阐述中学习并开始编写自己的扩展。 (注:如果有人开发了有用的方法,请发邮件给我,我可以将它包含在下一个texreg 版本中。)还值得指出的是,包的源文件包含70多个@987654354的示例@ 可以用作模板的方法。这些示例存储在文件R/extract.R中。

识别类标签并设置extract方法

第一步是识别对象的类名。在您的示例中,class(model.output.1) 返回以下类标签:“H2OBinomialModel”和“h2o”。第一个标签是更具体的标签,第二个标签是更一般的标签。如果所有h2o 模型对象的结构都类似,那么为h2o 对象编写一个扩展,然后在方法中决定如何处理特定模型是有意义的。因为我对h2o 包几乎一无所知,所以在这种情况下,我更喜欢从更具体的extract 方法开始,用于H2OBinomialModel 对象。如果我们愿意,可以稍后对其进行调整。

extract 方法的结构如下:编写一个名为extract.xyz 的函数(用类标签替换“xyz”),至少有一个名为model 的参数,它接受模型对象(例如,@ 987654366@ 在您的示例中),将一些代码放入从model 对象中提取相关信息的主体中,使用createTexreg 构造函数创建一个texreg 对象,并返回此对象。这是一个空容器:

extract.H2OBinomialModel <- function(model, ...) {
  s <- summary(model)

  # extract information from model and summary object here

  # then create and return a texreg object (replace NULL with actual values):
  tr <- createTexreg(
    coef.names = NULL,    # character vector of coefficient labels
    coef = NULL,          # numeric vector with coefficients
    se = NULL,            # numeric vector with standard error values
    pvalues = NULL,       # numeric vector with p-values
    gof.names = NULL,     # character vector with goodness-of-fit labels
    gof = NULL,           # numeric vector of goodness-of-fit statistics
    gof.decimal = NULL    # logical vector: GOF statistic has decimal points?
  )
  return(tr)
}

请注意,函数定义还包含... 参数,该参数可用于自定义参数,这些参数应移交给extract 方法中的函数调用。

另请注意,函数定义主体中的第一行将模型摘要保存在名为s 的对象中。这通常很有用,因为许多包编写者决定在摘要中以更简单的版本存储一些信息,因此通常应该将模型及其摘要视为有用的信息来源。在某些情况下,可能需要查看相应包中的摘要方法的实际定义,以了解在调用summary 命令时如何计算摘要页面上显示的信息,因为并非所有summary方法存储summary对象中显示的不同元素。

H2OBinomialModel 对象中找到正确的信息

下一步是检查对象并找到应在最终表格中显示的所有详细信息。通过查看model.output.1的输出,我猜下面的部分应该构成表格底部的GOF块:

MSE:  0.202947
R^2:  0.1562137
LogLoss:  0.5920097
Mean Per-Class Error:  0.3612191
AUC:  0.7185655
Gini:  0.4371311
Null Deviance:  512.2888
Residual Deviance:  449.9274
AIC:  457.9274

而下面的部分大概应该构成表格中间的系数块:

Coefficients: glm coefficients
      names coefficients standardized_coefficients
1 Intercept    -1.835223                 -0.336428
2      RACE    -0.625222                 -0.193052
3     DCAPS     1.314428                  0.408336
4       PSA     0.046861                  0.937107

在许多情况下,摘要包含相关信息,但在这里打印模型会产生我们需要的信息。我们需要在model.output.1 对象(或其摘要,如果适用)中找到所有这些。为此,有几个有用的命令。其中有str(model.output.1)names(summary(model.output.1))等类似命令。

让我们从系数块开始。调用str(model) 表明S4 对象中有一个名为model 的槽。我们可以通过调用model.output.1@model查看它的内容。结果是一个包含多个命名元素的列表,其中包括coefficients_table。所以我们可以通过调用model.output.1@model$coefficients_table来访问系数表。结果是一个数据框,我们可以使用$ 运算符访问其中的列。特别是,我们需要名称和系数。这里有两种类型的系数,标准化的和非标准化的,我们可以稍后在我们的提取方法中添加一个参数,让用户决定他或她想要什么。以下是我们提取系数及其标签的方法:

coefnames <- model.output.1@model$coefficients_table$names
coefs <- model.output.1@model$coefficients_table$coefficients
coefs.std <- model.output.1@model$coefficients_table$standardized_coefficients

据我所知,对象中没有存储标准错误或 p 值。如果我们希望这样做,我们可以编写一些额外的代码来计算它们,但这里我们将关注那些容易作为模型输出的一部分提供的东西。

重要的是,我们不应覆盖R 中的任何现有函数名称,例如namescoef。虽然这样做在技术上应该可行,因为代码稍后会在函数中执行,但这很容易在尝试时导致混淆,因此您最好避免这种情况。

接下来,我们需要定位拟合优度统计数据。通过仔细检查str(model.output.1) 的输出,我们看到拟合优度统计信息包含在model@model$training_metrics@metrics 下的几个槽中。让我们将它们保存到一些更容易访问的对象中:

mse <- model.output.1@model$training_metrics@metrics$MSE
r2 <- model.output.1@model$training_metrics@metrics$r2
logloss <- model.output.1@model$training_metrics@metrics$logloss
mpce <- model.output.1@model$training_metrics@metrics$mean_per_class_error
auc <- model.output.1@model$training_metrics@metrics$AUC
gini <- model.output.1@model$training_metrics@metrics$Gini
nulldev <- model.output.1@model$training_metrics@metrics$null_deviance
resdev <- model.output.1@model$training_metrics@metrics$residual_deviance
aic <- model.output.1@model$training_metrics@metrics$AIC

在某些情况下,包的作者为通用函数编写方法,这些方法可用于提取一些常见信息,例如观察次数 (nobs(model))、AIC (AIC(model))、BIC ( BIC(model))、偏差 (deviance(model)) 或对数似然 (logLik(model)[[1]])。所以这些是你可能想先尝试的事情;但是h2o 包似乎没有提供这样的便捷方法。

将信息添加到extract.H2OBinomialModel 函数

现在我们已经找到了我们需要的所有信息,我们可以将它们添加到我们上面定义的extract.H2OBinomialModel 函数中。

但是,我们想让用户决定他或她更喜欢原始系数还是标准化系数,并且我们想让用户决定应该报告哪些拟合优度统计数据,因此我们添加了各种逻辑参数函数头,然后在函数内部使用 if 条件来检查我们是否应该将各自的统计信息嵌入到生成的 texreg 对象中。

在这种情况下,我们还删除了 s &lt;- summary(model) 行,因为我们实际上不需要摘要中的任何信息,因为我们在模型对象中找到了我们需要的所有信息。

完整的函数可能如下所示:

# extension for H2OBinomialModel objects (h2o package)
extract.H2OBinomialModel <- function(model, standardized = FALSE, 
      include.mse = TRUE, include.rsquared = TRUE, include.logloss = TRUE, 
      include.meanerror = TRUE, include.auc = TRUE, include.gini = TRUE, 
      include.deviance = TRUE, include.aic = TRUE, ...) {

  # extract coefficient table from model:
  coefnames <- model@model$coefficients_table$names
  if (standardized == TRUE) {
    coefs <- model@model$coefficients_table$standardized_coefficients
  } else {
    coefs <- model@model$coefficients_table$coefficients
  }

  # create empty GOF vectors and subsequently add GOF statistics from model:
  gof <- numeric()
  gof.names <- character()
  gof.decimal <- logical()
  if (include.mse == TRUE) {
    mse <- model@model$training_metrics@metrics$MSE
    gof <- c(gof, mse)
    gof.names <- c(gof.names, "MSE")
    gof.decimal <- c(gof.decimal, TRUE)
  }
  if (include.rsquared == TRUE) {
    r2 <- model@model$training_metrics@metrics$r2
    gof <- c(gof, r2)
    gof.names <- c(gof.names, "R^2")
    gof.decimal <- c(gof.decimal, TRUE)
  }
  if (include.logloss == TRUE) {
    logloss <- model@model$training_metrics@metrics$logloss
    gof <- c(gof, logloss)
    gof.names <- c(gof.names, "LogLoss")
    gof.decimal <- c(gof.decimal, TRUE)
  }
  if (include.meanerror == TRUE) {
    mpce <- model@model$training_metrics@metrics$mean_per_class_error
    gof <- c(gof, mpce)
    gof.names <- c(gof.names, "Mean Per-Class Error")
    gof.decimal <- c(gof.decimal, TRUE)
  }
  if (include.auc == TRUE) {
    auc <- model@model$training_metrics@metrics$AUC
    gof <- c(gof, auc)
    gof.names <- c(gof.names, "AUC")
    gof.decimal <- c(gof.decimal, TRUE)
  }
  if (include.gini == TRUE) {
    gini <- model@model$training_metrics@metrics$Gini
    gof <- c(gof, gini)
    gof.names <- c(gof.names, "Gini")
    gof.decimal <- c(gof.decimal, TRUE)
  }
  if (include.deviance == TRUE) {
    nulldev <- model@model$training_metrics@metrics$null_deviance
    resdev <- model@model$training_metrics@metrics$residual_deviance
    gof <- c(gof, nulldev, resdev)
    gof.names <- c(gof.names, "Null Deviance", "Residual Deviance")
    gof.decimal <- c(gof.decimal, TRUE, TRUE)
  }
  if (include.aic == TRUE) {
    aic <- model@model$training_metrics@metrics$AIC
    gof <- c(gof, aic)
    gof.names <- c(gof.names, "AIC")
    gof.decimal <- c(gof.decimal, TRUE)
  }

  # create texreg object:
  tr <- createTexreg(
    coef.names = coefnames, 
    coef = coefs, 
    gof.names = gof.names, 
    gof = gof, 
    gof.decimal = gof.decimal
  )
  return(tr)
}

对于拟合优度块,您可以看到我首先创建了空向量,然后用其他统计信息填充它们,前提是用户使用相应的参数打开了相应的统计信息。

gof.decimal 逻辑向量指示每个 GOF 统计数据是否有小数位 (TRUE)(FALSE,例如观察次数)。

最后,新函数需要注册为通用extract函数的方法。这是使用一个简单的命令完成的:

setMethod("extract", signature = className("H2OBinomialModel", "h2o"), 
definition = extract.H2OBinomialModel)

这里className的第一个参数是类标签,第二个是定义类的包。

总结起来,为了编写自定义扩展,唯一需要做的两件事是 1)编写提取方法,以及 2)注册该方法。也就是说,这段代码可以在运行时执行,不必插入到任何包中。

不过,为了您的方便,我已将 H2OBinomialModel 方法添加到 texreg 版本 1.36.13,可在 CRAN 上使用。

请注意,此处提供的解决方案不适用于任何以前版本的 texreg,因为以前的版本无法处理既没有标准误差也没有置信区间的模型。在我看来,这是一个相当专业的设置,我还没有遇到过一个只提供估计而没有任何不确定性度量的包。我现在已经在texreg 中修复了这个问题。

试用新的extract 方法

在运行时执行函数定义和setMethod 命令后,可以使用以下命令创建表:

screenreg(list(model.output.1, model.output.2), custom.note = "")

这是输出:

======================================
                      Model 1  Model 2
--------------------------------------
Intercept              -1.84    -1.11 
RACE                   -0.63    -0.62 
DCAPS                   1.31     1.31 
PSA                     0.05     0.05 
AGE                             -0.01 
--------------------------------------
MSE                     0.20     0.20 
R^2                     0.16     0.16 
LogLoss                 0.59     0.59 
Mean Per-Class Error    0.36     0.38 
AUC                     0.72     0.72 
Gini                    0.44     0.44 
Null Deviance         512.29   512.29 
Residual Deviance     449.93   449.51 
AIC                   457.93   459.51 
======================================

custom.note = "" 参数在这里是有意义的,因为我们不想要显着性图例,因为模型不报告任何不确定性度量。

也可以抑制某些 GOF 措施和/或使用标准化系数:

screenreg(list(model.output.1, model.output.2), custom.note = "", 
    include.deviance = FALSE, include.auc = FALSE, standardized = TRUE)

结果:

======================================
                      Model 1  Model 2
--------------------------------------
Intercept              -0.34    -0.34 
RACE                   -0.19    -0.19 
DCAPS                   0.41     0.41 
PSA                     0.94     0.94 
AGE                             -0.07 
--------------------------------------
MSE                     0.20     0.20 
R^2                     0.16     0.16 
LogLoss                 0.59     0.59 
Mean Per-Class Error    0.36     0.38 
Gini                    0.44     0.44 
AIC                   457.93   459.51 
======================================

其他可以和createTexreg一起使用的插槽

createTexreg 构造函数在任何extract 方法中调用。上面的示例显示了一些简单的信息。除了示例中包含的详细信息外,texreg 对象中还提供了以下插槽:

  • se:标准错误
  • pvalues:p 值
  • ci.low:置信区间的下限
  • ci.up:置信区间的上限
  • model.name:当前模型的标题

请注意,应使用置信区间或标准误差和 p 值,而不是两者。

操作texreg 对象

除了将模型直接交给screenregtexreghtmlreg函数外,还可以先将提取的信息保存到texreg对象中,然后在显示或保存表格之前对其进行操作.附加值是,即使是对表的复杂更改也很容易以这种方式应用。例如,可以重命名系数或 GOF 统计、添加新行、重命名模型、修改值或更改系数或 GOF 统计的顺序。你可以这样做:首先,调用extract 函数将信息保存到texreg 对象:

tr <- extract(model.output.1)

你可以通过调用tr来显示texreg对象的内容,输出如下:

No standard errors and p-values were defined for this texreg object.

                coef.
Intercept -1.83522343
RACE      -0.62522179
DCAPS      1.31442834
PSA        0.04686106

                             GOF dec. places
MSE                    0.2029470        TRUE
R^2                    0.1562137        TRUE
LogLoss                0.5920097        TRUE
Mean Per-Class Error   0.3612191        TRUE
AUC                    0.7185655        TRUE
Gini                   0.4371311        TRUE
Null Deviance        512.2888402        TRUE
Residual Deviance    449.9273825        TRUE
AIC                  457.9273825        TRUE

或者,这是str(tr)所示的对象结构:

Formal class 'texreg' [package "texreg"] with 10 slots
  ..@ coef.names : chr [1:4] "Intercept" "RACE" "DCAPS" "PSA"
  ..@ coef       : num [1:4] -1.8352 -0.6252 1.3144 0.0469
  ..@ se         : num(0) 
  ..@ pvalues    : num(0) 
  ..@ ci.low     : num(0) 
  ..@ ci.up      : num(0) 
  ..@ gof.names  : chr [1:9] "MSE" "R^2" "LogLoss" "Mean Per-Class Error" ...
  ..@ gof        : num [1:9] 0.203 0.156 0.592 0.361 0.719 ...
  ..@ gof.decimal: logi [1:9] TRUE TRUE TRUE TRUE TRUE TRUE ...
  ..@ model.name : chr(0) 

现在您可以以任意方式操作此对象,例如,添加 GOF 统计信息:

tr@gof.names <- c(tr@gof.names, "new statistic")
tr@gof <- c(tr@gof, 12)
tr@gof.decimal <- c(tr@gof.decimal, FALSE)

或者你可以改变系数的顺序:

tr@coef.names <- tr@coef.names[c(4, 1, 2, 3)]
tr@coef <- tr@coef[c(4, 1, 2, 3)]

完成操作后,您可以在调用时交出texreg 对象而不是原始模型,例如screenreg

screenreg(list(tr, model.output.2), custom.note = "")

新结果将如下所示:

======================================
                      Model 1  Model 2
--------------------------------------
PSA                     0.05     0.05 
Intercept              -1.84    -1.11 
RACE                   -0.63    -0.62 
DCAPS                   1.31     1.31 
AGE                             -0.01 
--------------------------------------
MSE                     0.20     0.20 
R^2                     0.16     0.16 
LogLoss                 0.59     0.59 
Mean Per-Class Error    0.36     0.38 
AUC                     0.72     0.72 
Gini                    0.44     0.44 
Null Deviance         512.29   512.29 
Residual Deviance     449.93   449.51 
AIC                   457.93   459.51 
new statistic          12             
======================================

TL;DR

texreg可由用户自定义。只需编写如上所示的提取方法并使用setMethods 调用注册它。我在 latest texreg version 1.36.13 中包含了 H2OBinomialModel 方法,以及使用没有标准错误的模型(例如这个)的错误修复。

【讨论】:

  • 让我为您大声疾呼。很棒的解释!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-20
  • 1970-01-01
  • 1970-01-01
  • 2021-06-22
  • 1970-01-01
  • 2016-07-07
相关资源
最近更新 更多