【问题标题】:Problem passing column name to sapply within a function将列名传递给函数内的问题
【发布时间】:2021-02-15 08:49:29
【问题描述】:

我需要为多个 logit 模型计算大量预测概率,并且我正在尝试编写一个函数来加速该过程。但是,我无法使我的功能正常工作。问题似乎是下面代码的“iv = x”部分。我不确定如何正确传递列名。

pp <- function(iv, model, df) {
  lev <- levels(df[[iv]])
  l.prob <- sapply(lev, FUN=function(x){
  mean(predict(model, type = "response", 
               newdata = mutate(df, iv = x)), na.rm=TRUE)
  })
  l.prob
}


test <- pp(iv="myvar", model=model1, df=mydf)
test

以下是一些示例数据,显示该功能如何不工作:

set.seed(123123)
df=data.frame(y=sample(c(0,1), replace=TRUE, size=100), x1=as.factor(rep(c("value1", "value2"), 50)), x2=rnorm(100, mean=50, sd=10))


logit1 <- glm(y ~ x1+x2, data = df, family=binomial(link="logit"))
summary(logit1)


#what the predicted probabilities should be (0.4173400, 0.4625565)
lev <- levels(df$x1)
pp <- sapply(lev, FUN=function(x){
  mean(predict(logit1, type = "response", 
               newdata = mutate(df, x1 = x)), na.rm=TRUE)
})
pp

#now running function (produces probabilities 0.44 and 0.44)

pp <- function(iv, model, df) {
  lev <- levels(df[[iv]])
  l.prob <- sapply(lev, FUN=function(x){
    mean(predict(model, type = "response", 
                 newdata = mutate(df, iv = x)), na.rm=TRUE)
  })
  l.prob
}


test <- pp(iv="x1", model=logit1, df=df)
test

【问题讨论】:

  • 你能提供一些示例数据吗?此外,您致电 glm() 获取 logit 模型将有助于重现您的工作。您可以通过将dput(mydf) 的输出粘贴到问题中的代码块中来共享您的数据。
  • 问题是它没有将该列名传递给函数,结果与我为变量输入一个无意义的名称而不是“iv”相同。预测的概率是相同的。
  • 好的,用一些示例数据进行了编辑。
  • (1) 请使用set.seed(),以便结果值保持不变。现在他们改变了; (2) 您能否准确描述您认为在df$x1 级别上的迭代应该发生的事情?
  • 抱歉,更新了。我相信它应该计算每个 x1 水平的 1 结果的平均概率。

标签: r function sapply


【解决方案1】:

考虑在预测之前使用[[动态分配列并避免mutate(尤其是如果它是dplyr中使用的唯一方法并且可以为您节省library调用)。

pp <- function(iv, model, df) {
  lev <- levels(df[[iv]])
  l.prob <- sapply(lev, FUN=function(x){
        df[[iv]] <- x
        mean(predict(model, type = "response", newdata = df), na.rm=TRUE)
  })
}

另一种基本 R 方法是使用临时名称添加新列,然后使用动态参数重命名所有列。

  l.prob <- sapply(lev, FUN=function(x){
        mean(predict(model, type = "response", 
                     newdata = setNames(transform(df, tmp = x), c(colnames(df), iv)), 
             na.rm=TRUE)
  })

【讨论】:

    【解决方案2】:

    您只需要模仿原始 pp: x1 = x 中的分配。现在您正尝试在sapply 中使用iv,但在sapply 中您的函数仅引用x

    进行此更新会重现 pptest 的结果:

    library(dplyr)
    set.seed(1L)
    
    # hard-coded df$x1
    lev <- levels(df$x1)
    pp <- sapply(lev, FUN=function(x){
      mean(predict(logit1, type = "response", 
                   newdata = mutate(df, x1 = x)), na.rm=TRUE)
    })
    pp
       value1    value2 
    0.4799503 0.5400409 
    
    
    # 'x1' passed in as :iv: arg
    pp <- function(iv, model, df) {
      lev <- levels(df[[iv]])
      l.prob <- sapply(lev, FUN=function(x){
        mean(predict(model, type = "response", 
                     newdata = mutate(df, x1 = x)), na.rm=TRUE) 
      })
      l.prob
    }
    
    test <- pp(iv="x1", model=logit1, df=df)
    
    test
       value1    value2 
    0.4799503 0.5400409 
    

    作为替代方案,您可以将x1 不带引号直接传递给pp(),并使用{{ }} (curly curly notation) 在df 中评估iv

    pp <- function(iv, model, df) {
      lev <- levels(df %>% pull({{iv}})) # <-- use {{ }}
      l.prob <- sapply(lev, FUN=function(x){
        mean(predict(model, type = "response", 
                     newdata = mutate(df, x1 = x)), na.rm=TRUE)
      })
      l.prob
    }
    
    
    test <- pp(iv=x1, model=logit1, df=df) # <-- x1 has no quotes
    test
       value1    value2 
    0.4799503 0.5400409 
    

    【讨论】:

    • 有没有办法在函数中不使用变量名 (x1) 来做到这一点?我希望能够对任何变量执行此操作。
    猜你喜欢
    • 2017-04-15
    • 2023-03-04
    • 2011-02-08
    • 1970-01-01
    相关资源
    最近更新 更多