如何根据省略/包含变量的模式在 R 中以编程方式创建公式

问题描述 投票:0回答:2

我经常需要为一次分析创建 10 多个模型,有时甚至超过 20 个模型。这些模型的基础公式通常遵循一个清晰的模式,但我常常难以想出一种编程方式来创建它们。如果我需要在许多模型中添加或更改某些内容,那么不要为每个模型重复自己就变得更加重要。

这是我的意思的一个例子。

假设我们有这个数据:

mydata <- mtcars %>%
  mutate(standardized_mpg = scale(mpg)%>%as.numeric(),
         logged_mpg = log(mpg))

现在我们要为三个 DV

mpg
standaridzed_mpg
logged_mpg
创建回归公式。出于某种任意原因,我们总是想要两个 IV,其中一个是
gear
cyl
,另一个是
disp
hp
。换句话说,我们想要这个:

mpg ~ gear + dips
mpg ~ cyl + dips
mpg ~ gear + hp
mpg ~ cyl + hp
logged_mpg ~ gear + dips
etc.
etc.
etc.
standardized_mpg ~ gear + dips
etc.
etc.
etc.

可以通过

expand.grid()
实现这一点:

specs_dv <- c("mpg", "standardized_mpg", "logged_mpg")
specs_iv1 <- c("gear", "cyl")
specs_iv2 <- c("disp", "hp")

f <-  expand.grid(
  specs_dv = specs_dv,
  " ~ ",
  specs_iv1 = specs_iv1,
  " + ",
  specs_iv2 = specs_iv2
) %>%
  arrange(specs_dv)

# Collapse each df row to char vector, then convert them to formulas
# At the end we will have a list-type object where every list item is a formula
f <- apply(f, 1, function(x) {
  as.formula(
    paste0(
      as.character(x), collapse = ""
    )
    )})

从这里开始,我可以使用 for 循环和函数来创建模型和表格。我不会在这里添加它们,因为它偏离了主要观点。

现在,当模型之间的变量数量固定时,上述方法效果很好。然而,更常见的是,一些模型会忽略其他模型的变量。这就是我现在被困的地方。这是一些示例数据:

mydata <- ggplot2::economics %>%
  mutate(psavert_l1 = lag(psavert),
         standardized_psavert = scale(psavert)%>%as.numeric(),
         standardized_psavert_l1 = scale(psavert_l1)%>%as.numeric(),
         logged_psavert = log(psavert),
         logged_psavert_l1 = log(psavert_l1)
         )

这些是我想要生成的公式:

uempmed ~ psavert + psavert_l1 + unemploy
uempmed ~ psavert + psavert_l1           
uempmed ~ psavert +              unemploy
uempmed ~           psavert_l1 + unemploy

uempmed ~ standardized_psavert + standardized_psavert_l1 + unemploy
uempmed ~ standardized_psavert + standardized_psavert_l1           
uempmed ~ standardized_psavert +                           unemploy
uempmed ~                        standardized_psavert_l1 + unemploy

uempmed ~ log_psavert + log_psavert_l1 + unemploy
uempmed ~ log_psavert + log_psavert_l1           
uempmed ~ log_psavert +                  unemploy
uempmed ~               log_psavert_l1 + unemploy

如您所见,这次我有两个或三个自变量。这里的模式是让一个模型包含所有三个 IV,然后再添加三个模型,每次省略一个 IV。最后,我需要这三个变量中的两个的三个修改版本。

像这样增减变量是建模时很正常的事情。因此,我想知道是否有更快的方法来创建这些公式,就像变量的数量是否固定一样。您处理这个问题的方法是什么,特别是如果有更多的变量需要处理更多的模型?手写一切似乎......只是错误的。

r modeling
2个回答
2
投票

您可以在前缀上使用

combn
lapply

lapply(c('', 'standardized_', 'log_'), \(z) {
  combn(c('psavert', 'psavert_l1', 'unemploy'), 2, FUN=\(x) 
        reformulate(paste0(z, x), 'uempmed'), simplify=FALSE)
})
# [[1]]
# [[1]][[1]]
# uempmed ~ psavert + psavert_l1
# <environment: 0x55c229491980>
#   
# [[1]][[2]]
# uempmed ~ psavert + unemploy
# <environment: 0x55c22948f078>
#   
# [[1]][[3]]
# uempmed ~ psavert_l1 + unemploy
# <environment: 0x55c2294883c8>
#   
#   
# [[2]]
# [[2]][[1]]
# uempmed ~ standardized_psavert + standardized_psavert_l1
# <environment: 0x55c22821cc60>
#  
# [[2]][[2]]
# uempmed ~ standardized_psavert + standardized_unemploy
# <environment: 0x55c22820f3d0>
#   
# [[2]][[3]]
# uempmed ~ standardized_psavert_l1 + standardized_unemploy
# <environment: 0x55c228206758>
#   
#   
# [[3]]
# [[3]][[1]]
# uempmed ~ log_psavert + log_psavert_l1
# <environment: 0x55c2281fed40>
#   
# [[3]][[2]]
# uempmed ~ log_psavert + log_unemploy
# <environment: 0x55c2281fc438>
#   
# [[3]][[3]]
# uempmed ~ log_psavert_l1 + log_unemploy
# <environment: 0x55c2281f37f8>

0
投票

经过更多测试,我找到了一种合理编程的方式。

如果您有一个公式,并希望基于减去该公式的各个变量和变量名称的一些更改来创建许多新公式,则可以使用我的方法。

请注意,我正在使用各种 tidyverse 函数来实现此功能。

  1. 创建一个 df,其中一行包含公式,公式元素作为列。如果您需要修改某些变量的开头(例如“logged_”或“standardized_”),请添加前缀。
f <- tibble(
  dv = "uempmed"
  tilde = "~",
  iv1 = "PREFIXpsavert",
  plus = "+",
  iv2 = "PREFIXpsavert_l1",
  plus2 = "+",
  iv3 = "unemploy"
)
  1. 重复行的次数与所需的公式数一样多。这里我们需要 3 组 4 个公式,所以 12.
f <- f %>% slice(rep(1:n(), each = 12))
  1. 将某些公式中应该省略的变量替换为空格
# This replaces the third IV with a blank space for every fourth model, counting from the second
# (Set "by" to the number of formulas per set of formulas. In this case it's three sets of four, so it's four.)
f[seq(2, nrow(f), by=4), c("iv3", "plus2")] <- " "

# This replace the second IV with a blank space for every fourth model, counting from the third
f[seq(3, nrow(f), by=4), c("iv2", "plus")] <- " "

# This replace the first IV with a blank space for every fourth model, counting from the fourth
f[seq(4, nrow(f), by=4), c("iv1", "plus")] <- " "
  1. 将数据框行折叠为字符串
f <- apply(f, 1, function(x) {
    paste0(
      as.character(x), collapse = ""
    )})
  1. 更改第一、二、三组四个模型的变量前缀
f[1:4] <- str_replace_all(f[1:4], "PREFIX", "")
f[5:8] <- str_replace_all(f[5:8], "PREFIX", "standardized_")
f[9:12] <- str_replace_all(f[9:12], "PREFIX", "log_")
  1. 最后,将字符串向量改为公式列表
f <- lapply(f, as.formula)

我们最终得到了一个公式列表

f
,可以与
lm(formula = f[[1]], data = mydata)
之类的东西一起使用。

基于这种结构,您可以轻松地将所有内容包装在一个函数中,从而使创建公式更加程序化。当然,实际模型也可以在循环中创建,例如:

for(i in length(f)) {
  assign(
    x     = paste0("m", str_pad(i, width=2, pad=0)),
    value = lm(
      formula = f[[i]],
      data    = mydata
    )
  )}

据此创建观星表也是可以的,但与原题相差太远,这里不再多说。希望这篇文章能帮助遇到与我类似问题的人!

© www.soinside.com 2019 - 2024. All rights reserved.