Search code examples
rerror-handlingsuppress-warningscox-regression

Suppressing specific warning in coxph leads to error with cox.zph


I would like to suppress a warning that frequently crops up in coxph analysis. The warning "Loglik converged before variable 1 ; coefficient may be infinite" is known to be oversensitive, and I have other ways of sense-checking the results. My first try was to wrap coxph in a calling handler:

coxMW <- function(...){
  withCallingHandlers(coxph(...),
                      warning=function(w) {
                        if (grepl("coefficient may be infinite", w$message))
                          invokeRestart("muffleWarning")
                      })
}

That gives the expected answers

library(survival)
coxObj1 <- coxph(Surv(futime, fustat) ~ rx, data=ovarian)
coxObj2 <- coxMW(Surv(futime, fustat) ~ rx, data=ovarian)
summary(coxObj1)
summary(coxObj2))

However, when I try to use the result in cox.zph:

zph1 <- cox.zph(coxObj1)
zph2 <- cox.zph(coxObj2)

The second case, using the new function, results in: "Error in is.data.frame(data) : ..2 used in an incorrect context, no ... to look in"

I am now struggling to find a way to suppress this particular warning. I use coxph numerous times inside a large function and don't really want to wrap every call to coxph in withCallingHandlers. The whole function is called using tryCatch with an error handler to log the error etc. and move on to the next case, which works fine. However, if I add a warning handler to tryCatch, suppressing just this warning, that exits the function and I cannot work out how to continue from where the warning occurred within the function. I have tried various combinations of restart commands in the warning handler but I don't really know what I am doing, and wonder if there is a better approach? Any suggestions welcome!


Solution

  • This is a tricky one. The reason the model produced in your wrapper doesn't work is that the coxph object keeps a record of the call that produced it, which cox.zph itself reuses. When you use the ... in your wrapper, these are stored as the parameters of the call without substitution. When this call is passed to cox.zph, it doesn't know how to deal with the ... parameters.

    You can see this by looking at the call objects stored within your two models:

    coxObj1$call
    #> coxph(formula = Surv(futime, fustat) ~ rx, data = ovarian)
    coxObj2$call
    #> coxph(formula = ..1, data = ..2)
    

    So, your wrapper needs to substitute the ... into the call slot of the model:

    coxMW <- function(...){
      withCallingHandlers(result <- coxph(...),
                          warning=function(w) {
                            if (grepl("coefficient may be infinite", w$message))
                              invokeRestart("muffleWarning")
                          })
    
      cox_call      <- match.call()   # Contains the substituted parameters we want
      cox_call[[1]] <- quote(coxph)   # Change the wrapper's name to coxph in the call
    
      if(names(cox_call)[2] == "") names(cox_call)[2] <- "formula"
      result$call <- cox_call
      return(result)
    }
    

    So now you can do:

    coxObj2 <- coxMW(Surv(futime, fustat) ~ rx, data=ovarian)
    zph2    <- cox.zph(coxObj2)
    zph2
    #>        chisq df   p
    #> rx      2.68  1 0.1
    #> GLOBAL  2.68  1 0.1