Fuzzy Survival Analysis with Vague Event Times and Fuzzy Censoring

Fuzzy survival analysis for data in which event times and/or failure indicators are vague, for instance when a failure is only known to have occurred during the second quarter of a year, or when it is uncertain whether a subject lost to follow-up had in fact failed. Vague information is represented by fuzzy numbers. The package provides a fuzzy product-limit estimator obtained from nested alpha-cuts of the fuzzy survival function, a fuzzy Cox proportional hazards model with fuzzy coefficient and hazard ratio estimates, and a fuzzy log-rank test. Type-reduction utilities for summarizing fuzzy estimates by single numbers, a simulation engine for validation studies, plotting, print and summary methods, and alpha-cut based confidence bands are provided. The bounds are computed at the alpha-cuts of the fuzzy event times, so the alpha-cut intervals returned for the survival function, the regression coefficients and the test statistic are nested by construction.


fuzzySurvival

Fuzzy survival analysis for data with vague event times and vague failure indicators.

Survival records are often imprecise: a failure is known to have happened "during the second quarter", between two follow-up visits, or not at all - a subject lost to follow-up may or may not have failed. fuzzySurvival models that imprecision directly instead of discarding it or forcing an arbitrary choice, and reports the whole consequence on the estimation rather than a single number.

The package implements the alpha-cut extension of the Kaplan-Meier estimator, of the Cox proportional hazards model and of the log-rank test, together with the type-reduction (defuzzification) rules needed to turn a fuzzy result into a single interpretable quantity.

Installation

# from a local checkout
install.packages(".", repos = NULL, type = "source")

Why another package?

Survival analysis with fuzzy numbers is not covered by any CRAN package. FuzzyNumbers and friends provide the fuzzy-set machinery, but nothing connects it to censoring and to the partial likelihood; survival covers crisp data only. The two ingredients are trivially combined once the algebra is right, but the result has to satisfy properties that ordinary code breaks:

  • the reported bounds must be nested alpha-cuts, so that they describe one fuzzy number rather than a family of unrelated intervals;
  • the estimator must reduce exactly to the classical one when the data are crisp, which makes the implementation checkable against survival.

Both properties are covered by the test suite.

The two kinds of vagueness

Vague event times. The membership function of the event time says how plausible each value is. A failure observed between two examinations gives a flat fuzzy number on the interval between them (the value is entirely plausible anywhere in it); a rounded recollection gives a trapezoid.

library(fuzzySurvival)

# "the failure occurred in the second quarter" (months 4 to 6)
fuzzy_number_interval(4, 6)
#> <fuzzy_number> with 1 element
#> [1] "I[4, 6]"

# certainly between 3 and 9, plausibly concentrated in 5-7
fuzzy_number_trapezoid(3, 5, 7, 9)

Vague failure indicators. A status strictly between 0 and 1 is read as the degree of belief that the subject failed. Such a subject contributes a fractional number of events, and stays in the risk set until they are censored:

fuzzy_surv(c(2, 5, 7, 9), c(1, 0.5, 1, 1))

Fuzzy Kaplan-Meier

At level alpha the data compatible with the information are the box of times between the alpha-cuts. Replacing each time by the lower or the upper end of its cut makes the failures happen as early or as late as possible, so the two Kaplan-Meier curves bound the survival function:

data(fuzzy_dfs)

fit <- fkm(fuzzy_surv(ft, fs) ~ 1, data = fuzzy_dfs)
fit
#> Fuzzy Kaplan-Meier estimate
#>   n = 240, events = 81, uncertain = 40, alpha levels = 21
#>   mean width of the alpha-cuts at level 0: 0.675

summary(fit, times = c(6, 12, 24))[, c("time", "n.risk", "lower", "surv", "upper")]
#>   time n.risk   lower   surv  upper
#> 1    6    212 0.00000 0.9781 0.9781
#> 2   12    187 0.9360 0.9410 0.9460
#> 3   24    129 0.7668 0.7784 0.7900

lower and upper are the bounds at the requested level, surv the defuzzified curve. On precise data the two bounds coincide and the estimator reproduces survival::survfit() exactly.

Fuzzy Cox model

The same construction is applied to the partial likelihood: the alpha-cut of a coefficient spans every value obtained at a level at or below it, which is what makes the intervals nested. Hazard ratios keep their membership structure because the exponential is monotone.

fcox(fuzzy_surv(ft, fs) ~ age + arm, data = fuzzy_dfs)
#> Fuzzy Cox proportional hazards model
#>
#>        coef         p fuzzy.lower fuzzy.upper
#> age -0.0465 7.914e-05     -0.0696     -0.0234
#> arm  0.5211  0.005565      0.1527      0.8895

Fuzzy log-rank test

flogrank(fuzzy_surv(ft, fs) ~ arm, data = fuzzy_dfs)
#> Fuzzy log-rank test
#>   n = 240, groups: Control vs Treated
#>   observed / expected events in group 1:  49 / 65.66
#>   crisp chi-square (level 1):  9.3273
#>   crisp p-value: 0.002258

On crisp data this reproduces survival::survdiff().

Defuzzification

A fuzzy result carries a whole family of alpha-cuts; a single number is obtained by a type-reduction rule. Nine classical rules are available - midrange, centroid, gmirl, sgmrl, ms, msm, yager, min, max:

defuzz(fuzzy_number_trapezoid(3, 5, 7, 9), "centroid")
#> [1] 6

# the fuzzy survival probability itself, reduced at three levels
defuzz(fkm_fuzzy_surv(fit, times = c(6, 12, 24)), "midrange", level = 0.5)

Every rule of a symmetric fuzzy number returns its centre, which makes the rules easy to check:

defuzz(fuzzy_number_triangle(2, 5, 8), c("centroid", "gmirl", "sgmrl"))
#>      centroid        gmirl        sgmrl
#> [1,]        5            5            5

Simulating your own data

sim_fuzzy_surv() generates exact event times and then coarsens them the way a record does, so that a study can be validated against a known truth:

set.seed(1)
d <- sim_fuzzy_surv(n = 200, beta = c(age = 0.4, arm = -0.5))
head(d[, c("arm", "fs")])
fit <- fkm(fuzzy_surv(ft, fs) ~ arm, data = d)
plot(fit, type = "diff")

Documentation

vignette("fuzzy-survival", package = "fuzzySurvival") walks through the construction, the estimation, and the caveats. ?fkm, ?fcox, ?flogrank and ?fuzzy_number give the reference.

Caveats

  • The bounds are computed at the end points and the centre of the data box at each level. Because the Kaplan-Meier estimator is not monotone in the event times - moving one subject past another changes both risk sets - those three representatives do not in general bracket every possible dataset. The resulting intervals are a good but not provably exhaustive bound; the tests check the properties that are guaranteed (nesting, containment of the representative curves, exact reduction on crisp data).
  • Coarsening failures to examination times is biasing. Fuzzy bounds widen to reflect the imprecision; they do not remove that bias.
  • An interval fuzzy number has a flat core, so all of its alpha-cuts are equal and the bounds do not shrink with alpha. Use a trapezoid when the plausibility of the values should be graded.

License

MIT

Reference manual

It appears you don't have a PDF plugin for this browser. You can click here to download the reference manual.

install.packages("fuzzySurvival")

0.1.0 by Mukul Bijalwan, a day ago


Browse source code at https://github.com/cran/fuzzySurvival


Authors: Mukul Bijalwan [aut, cre] (ORCID: , Gunjan Aggrwal [aut] , Mukul Jain [aut]


Documentation:   PDF Manual  


MIT + file LICENSE license


Imports stats, graphics, grDevices, survival

Suggests testthat, knitr, rmarkdown, covr


See at CRAN