【问题标题】:Specifying positive class of an outcome variable in caret train()在插入符号 train() 中指定结果变量的正类
【发布时间】:2020-02-14 17:36:25
【问题描述】:

我想知道是否有办法在插入符号的train() 函数中指定结果变量的哪个类是正数。一个最小的例子:

# Settings
ctrl <- trainControl(method = "repeatedcv", number = 10, savePredictions = TRUE, summaryFunction = twoClassSummary, classProbs = TRUE)

# Data
data <- mtcars %>% mutate(am = factor(am, levels = c(0,1), labels = c("automatic", "manual"), ordered = T))

# Train
set.seed(123)
model1 <- train(am ~ disp + wt, data = data, method = "glm", family = "binomial", trControl = ctrl, tuneLength = 5)

# Data (factor ordering switched)
data <- mtcars %>% mutate(am = factor(am, levels = c(1,0), labels = c("manual", "automatic"), ordered = T))

# Train
set.seed(123)
model2 <- train(am ~ disp + wt, data = data, method = "glm", family = "binomial", trControl = ctrl, tuneLength = 5)

# Specifity and Sensitivity is switched
model1
model2

如果您运行代码,您会注意到特异性和敏感性指标在两个模型中都“切换”了。看起来train() 函数将因子结果变量的第一级作为正结果。有没有办法在函数本身中指定一个正类,所以无论结果因子排序如何,我都会得到相同的结果?我尝试添加positive = "manual",但这会导致错误。

【问题讨论】:

    标签: r r-caret


    【解决方案1】:

    我相信@Johannes 是过度设计简单流程的例子。

    只需还原因子的顺序:

       df$target <- factor(df$target, levels=rev(levels(df$target)))
    

    【讨论】:

    • 公平点。我已经在我的答案中添加了这句话,以避免让人们寻找绕道而行的快速解决方案。我确实认为 OP 知道该修复程序,但想明确指定肯定级别以使代码更健壮。
    【解决方案2】:

    问题不在于函数train(),而在于函数twoClassSummary,如下所示:

    function (data, lev = NULL, model = NULL) 
    {
      lvls <- levels(data$obs)
    
      [...]    
    
      out <- c(rocAUC, 
               sensitivity(data[, "pred"], data[, "obs"], 
                 lev[1]),  # Hard coded positive class
               specificity(data[, "pred"], data[, "obs"], 
                 lev[2])) # Hard coded negative class
      names(out) <- c("ROC", "Sens", "Spec")
      out
    }
    

    将它们传递给sensitivity()specificity() 的级别顺序在这里是硬编码的。

    正如@Seymour 非常正确地指出的那样,颠倒结果变量级别的顺序可以解决问题。

    df$target <- factor(df$target, levels=rev(levels(df$target)))
    

    如果您不愿意更改关卡的顺序,可以通过一种非侵入性的方式来更改 twoClassSummary() 函数。

    sensitivity()specificity() 分别采用 positivenegative 级别名称(次优设计选择)。所以我们将这两个参数包含在我们的自定义函数中。 再往下,我们将这些参数传递给相应的函数来解决问题。

    customTwoClassSummary <- function(data, lev = NULL, model = NULL, positive = NULL, negative=NULL) 
    {
      lvls <- levels(data$obs)
      if (length(lvls) > 2) 
        stop(paste("Your outcome has", length(lvls), "levels. The twoClassSummary() function isn't appropriate."))
      caret:::requireNamespaceQuietStop("ModelMetrics")
      if (!all(levels(data[, "pred"]) == lvls)) 
        stop("levels of observed and predicted data do not match")
      rocAUC <- ModelMetrics::auc(ifelse(data$obs == lev[2], 0, 
                                         1), data[, lvls[1]])
      out <- c(rocAUC, 
               # Only change happens here!
               sensitivity(data[, "pred"], data[, "obs"], positive=positive), 
               specificity(data[, "pred"], data[, "obs"], negative=negative))
      names(out) <- c("ROC", "Sens", "Spec")
      out
    }
    

    但是如何在不更改包内更多代码的情况下指定这些选项呢?默认情况下,caret 不会将选项传递给汇总函数。我们在调用trainControl()时将函数封装在一个匿名函数中:

    ctrl <- trainControl(method = "repeatedcv", number = 10, savePredictions = TRUE, 
                         # This is a trick how to fix arguments for a function call
                         summaryFunction = function(...) customTwoClassSummary(..., 
                                           positive = "manual", negative="automatic"), 
                         classProbs = TRUE)
    

    ... 参数确保caret 传递给匿名函数的所有其他参数都传递给customTwoClassSummary()

    【讨论】:

      猜你喜欢
      • 2018-11-11
      • 2019-06-02
      • 2016-01-30
      • 2017-05-28
      • 2018-05-15
      • 2017-05-19
      • 1970-01-01
      • 1970-01-01
      • 2019-03-27
      相关资源
      最近更新 更多