tests/testthat/test-unsurv.R

test_that("unsurv fits and returns expected structure", {
  skip_if_not_installed("cluster")

  dat <- make_toy_curves(n = 30, Q = 30, seed = 1)
  fit <- unsurv(dat$S, dat$times, K = 2, eps_jitter = 0)

  expect_s3_class(fit, "unsurv")
  expect_true(is.integer(fit$clusters))
  expect_equal(length(fit$clusters), nrow(dat$S))
  expect_equal(fit$K, 2L)
  expect_equal(length(fit$times), ncol(dat$S))
  expect_equal(dim(fit$medoids), c(2, ncol(dat$S)))

  # sanity: silhouette in [-1, 1]
  expect_true(is.finite(fit$silhouette_mean))
  expect_gte(fit$silhouette_mean, -1)
  expect_lte(fit$silhouette_mean, 1)
})

test_that("unsurv enforces monotonicity when requested", {
  skip_if_not_installed("cluster")

  dat <- make_toy_curves(n = 20, Q = 25, seed = 2)
  # Introduce a monotonicity violation
  dat$S[1, 10] <- 0.1
  dat$S[1, 11] <- 0.9

  fit <- unsurv(dat$S, dat$times, K = 2, enforce_monotone = TRUE, eps_jitter = 0)

  # medoids should be non-increasing if enforce_monotone=TRUE was applied before PAM
  med <- fit$medoids
  diffs <- apply(med, 1, diff)
  expect_true(all(diffs <= 1e-12))
})

test_that("unsurv can leave curves non-monotone when requested", {
  skip_if_not_installed("cluster")

  S <- rbind(
    c(1, 0.9, 0.8, 0.7, 0.6),
    c(1, 0.85, 0.95, 0.9, 0.75), # has upward bump
    c(1, 0.9, 0.78, 0.65, 0.55)
  )
  times <- seq_along(S[1, ])

  fit <- unsurv(S, times, K = 2, enforce_monotone = FALSE, eps_jitter = 0)
  expect_false(fit$enforce_monotone)

  # one medoid should retain the upward bump
  bump_sizes <- apply(fit$medoids, 1, function(x) max(diff(x)))
  expect_gt(max(bump_sizes), 0)
})

test_that("unsurv errors on bad inputs", {
  skip_if_not_installed("cluster")

  dat <- make_toy_curves(n = 10, Q = 10, seed = 3)

  expect_error(unsurv(dat$S, rev(dat$times), K = 2), "strictly increasing")
  expect_error(unsurv(dat$S[, -1, drop = FALSE], dat$times, K = 2), "ncol\\(S\\) must equal")
  expect_error(unsurv(dat$S[1, , drop = FALSE], dat$times, K = 2), "at least 2 rows")
  expect_error(unsurv(dat$S, dat$times, K = 0), "must be")
  expect_error(unsurv(dat$S, dat$times, K = 1000), "cannot exceed")
})

test_that("unsurv handles distance, weights, and automatic K selection", {
  skip_if_not_installed("cluster")

  dat <- make_toy_curves(n = 50, Q = 30, seed = 8)
  custom_w <- seq_len(ncol(dat$S))

  fit <- unsurv(
    dat$S, dat$times,
    K = NULL, K_max = 5,
    distance = "L1",
    weights = custom_w,
    standardize_cols = TRUE,
    eps_jitter = 0
  )

  expect_equal(fit$distance, "L1")
  expect_true(abs(sum(fit$weights) - 1) < 1e-8)
  expect_equal(length(fit$weights), ncol(dat$S))
  expect_true(fit$K >= 2 && fit$K <= 5)
  expect_true(is.finite(fit$silhouette_mean))
})

Try the unsurv package in your browser

Any scripts or data that you put into this service are public.

unsurv documentation built on Sept. 1, 2026, 1:06 a.m.