tests/testthat/test_custom.R

context("Custom distributions in flexsurvreg")

## These previously didn't work when the d or h functions are
## defined in functions or testthat environments.  To find the h or d
## function in form.dp, get() and exists() would need to look in
## parent.frame(2), i.e. the grandparent calling environment, but
## inherits=TRUE searches back through _enclosing_ (definition)
## environments.
## Even with d/h functions defined at top level, these still didn't
## work when called from R CMD check.

## Solve by adding dfns argument to flexsurvreg, passing functions through.

if (is.element("eha", installed.packages()[,1])) {
    test_that("Custom distributions from another package",{
            library(eha)
            dllogis2 <- eha::dllogis; pllogis2 <- eha::pllogis       
            custom.llogis <- list(name="llogis2",
                                  pars=c("shape","scale"),
                                  location="scale",
                                  transforms=c(log, log),
                                  inv.transforms=c(exp, exp),
                                  inits=function(t){ c(1, median(t)) })
            fitll <- flexsurvreg(formula = Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.llogis, dfns=list(d=dllogis2, p=pllogis2))
    
            custom.ev <- list(name="EV",
                              pars=c("shape","scale"),
                              location="scale",
                              transforms=c(log, log),
                              inv.transforms=c(exp, exp),
                              inits=function(t){ c(1, median(t)) })
            fitev <- flexsurvreg(formula = Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.ev, dfns=list(d=dEV, p=pEV))
    ###   h(x) = (b/a)(x/a)^(b-1)exp((x / a)^b)
    ###   H(x) = exp( (x / a)^b) ) - 1
    
    
            detach("package:eha")
    
            fitll.builtin <- flexsurvreg(formula = Surv(futime, fustat) ~ 1, data = ovarian, dist="llogis")
            expect_equal(fitll$loglik, fitll.builtin$loglik)
    })
}
    
test_that("Custom distributions: hazard and cumulative hazard",{
    hfoo <- function(x, rate=1){ rate }
    Hfoo <- function(x, rate=1){ rate*x }
    custom.foo <- list(name="foo", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp), inits=function(t)1/median(t))
    fitf <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo))
    fite <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist="exp")
    expect_equal(fitf$loglik, fite$loglik)
})

test_that("Custom distributions: hazard only",{
    hbar <- function(x, rate=1){ rate }
    hbar <- Vectorize(hbar)
    custom.bar <- list(name="bar", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp), inits=function(t)1/mean(t))
    fitf <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.bar, dfns=list(h=hbar))

### FIXME q is Inf.  is it Surv object bug?  time2 being used
### it's trying to get p from exp(-H(t)) with t = Inf, should get 


    fite <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist="exp")
    expect_equal(fitf$loglik, fite$loglik)

    ## with covariates.  Approximation is less good.  Many more integrations needed
    fitf <- flexsurvreg(Surv(futime, fustat) ~ age, data = ovarian, dist=custom.bar, dfns=list(h=hbar))
    fite <- flexsurvreg(Surv(futime, fustat) ~ age, data = ovarian, dist="exp")
    expect_equal(fitf$loglik, fite$loglik, tolerance=1e-05)

    ## options to integrate()
    flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.bar, dfns=list(h=hbar), integ.opts=list(rel.tol=0.01))
    flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.bar, dfns=list(h=hbar), integ.opts=list(subdivisions=200))

    ## OK to omit inits from custom list if supply it to flexsurvreg
    custom.bar <- list(name="bar", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp))
    flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.bar, dfns=list(h=hbar), inits=0.001)
})

test_that("Custom distributions: density only",{
    custom.baz <- list(name="baz", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp), inits=function(t)1/mean(t))
    dbaz <- function(x, rate=1, log=FALSE){
        if (log) {log(rate) - x*rate} else rate*exp(-rate*x)
    }
    dbaz <- Vectorize(dbaz)
    fitf <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.baz, dfns=list(d=dbaz))
    fite <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist="exp")
    expect_equal(fitf$loglik, fite$loglik)

    ## with covariates
    fitf <- flexsurvreg(Surv(futime, fustat) ~ age, data = ovarian, dist=custom.baz, dfns=list(d=dbaz), inits=c(9e-07, 0.12))
    fite <- flexsurvreg(Surv(futime, fustat) ~ age, data = ovarian, dist="exp")
    expect_equal(fitf$loglik, fite$loglik, tolerance=1e-05)
})

test_that("Custom distributions: summary", {
    custom.baz <- list(name="baz", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp), inits=function(t)1/mean(t))
    dbaz <- function(x, rate=1, log=FALSE){
        if (log) {log(rate) - x*rate} else rate*exp(-rate*x)
    }
    dbaz <- Vectorize(dbaz)
    fitd <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.baz, dfns=list(d=dbaz))
    expect_no_error(summary(fitd, type='quantile'))

    hfoo <- function(x, rate=1){ rate }
    Hfoo <- function(x, rate=1){ rate*x }
    custom.foo <- list(name="foo", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp), inits=function(t)1/median(t))
    fith <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo))
    expect_no_error(summary(fith, type='quantile'))
})

## Integration breaks for the Gompertz

if (0) {
    hbar2 <- function(x, a=1, b=1){ b*exp(a*x) }
    hbar2 <- Vectorize(hbar2)
    custom.bar2 <- list(name="bar2", pars=c("a","b"), location="b", transforms=c(identity,log), inv.transforms=c(identity,exp), inits=function(t)c(0.001, 1/mean(t)))
    fitf <- flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.bar2, control=list(trace=1,REPORT=1,reltol=1e-16,maxit=10000))
    fitgo <- flexsurvreg(formula = Surv(futime, fustat) ~ 1,
                         data = ovarian, dist="gompertz",
                         control=list(trace=1,REPORT=1,reltol=1e-16))
}

## test_that("Custom distributions: hazard only, unknown function, more than one arg",{
## })
## TODO a more complicated one

test_that("Errors in custom distributions",{
    hfoo <- function(x, rate=1){ rate }
    Hfoo <- function(x, rate=1){ rate*x }
    custom.foo <- list(pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp))
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "\"name\" element of custom distribution list not given")
    custom.foo <- list(name=0, pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp))
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "\"name\" element of custom distribution list should be a string")
    custom.foo <- list(name="foo", location="rate", transforms=c(log), inv.transforms=c(exp))
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "parameter names \"pars\" not given")
    custom.foo <- list(name="foo", pars=2, location="rate", transforms=c(log), inv.transforms=c(exp))
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "parameter names \"pars\" should be a character vector")
    custom.foo <- list(name="foo", pars="rate", transforms=c(log), inv.transforms=c(exp))
    expect_warning(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo), fixedpars=TRUE, inits=1),
                 "location parameter not given, assuming it is the first one")
    custom.foo <- list(name="foo", pars="rate", location="bar", transforms=c(log), inv.transforms=c(exp))
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo), fixedpars=TRUE, inits=1),
                 "location parameter \"bar\" not in list of parameters")
    custom.foo <- list(name="foo", pars=c("rate"), location="rate", transforms=c(log), inv.transforms=c(exp))
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "\"inits\" not supplied, and no function to estimate them found")
    custom.foo <- list(name="foo", pars=c("rate"), location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "transforms not given")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=c(log), location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "transforms not given")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=log, inv.transforms=exp, location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "\"transforms\" must be a list")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=c(log), inv.transforms=exp, location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "\"inv.transforms\" must be a list")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=list(2), inv.transforms=c(exp), location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "some of \"transforms\" are not functions")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=c(log), inv.transforms=list(2), location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "some of \"inv.transforms\" are not functions")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=c(log,log), inv.transforms=c(exp,exp), location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "transforms vector of length 2, parameter names of length 1")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=c(log), inv.transforms=c(exp,exp), location="rate")
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "inverse transforms vector of length 2, parameter names of length 1")
    custom.foo <- list(name="foo", pars=c("rate"), transforms=c(log), inv.transforms=c(exp), location="rate", inits=1)
    expect_error(flexsurvreg(Surv(futime, fustat) ~ 1, data = ovarian, dist=custom.foo, dfns=list(h=hfoo, H=Hfoo)),
                 "\"inits\" element of custom distribution list must be a function")
})

test_that("Errors in form.dp",{
    expect_error(form.dp(dlist=list(), dfns=list()), "Neither density function .+ nor hazard function")
})
chjackson/flexsurv-dev documentation built on April 23, 2024, 10:57 a.m.