The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

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:

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

License

MIT

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.