R/plot_special.R

Defines functions cred_adj_warning pivot_plot_special plot_actual_to_expected plot_termination_rates

Documented in plot_actual_to_expected plot_termination_rates

#' Additional plotting functions for termination studies
#'
#' These functions create additional experience study plots that are not
#' available or difficult to produce using the [autoplot.exp_df()] function.
#'
#' @param object An object of class `exp_df` created by the function
#' [exp_stats()].
#' @param ... Additional arguments passed to [autoplot.exp_df()].
#' @param include_cred_adj If `TRUE`, credibility-weighted termination rates
#' will be plotted as well.
#' @param add_hline If `TRUE`, a blue dashed horizontal line will be drawn at
#' 100%.
#'
#' @details
#'
#' [plot_termination_rates()] - Create a plot of observed termination rates
#' and any expected termination rates attached to an `exp_df` object.
#'
#' [plot_actual_to_expected()] - Create a plot of actual-to-expected termination
#' rates attached to an `exp_df` object.
#'
#' @return a `ggplot` object
#'
#' @examples
#'
#' study_py <- expose_py(census_dat, "2019-12-31", target_status = "Surrender")
#' expected_table <- c(seq(0.005, 0.03, length.out = 10), 0.2, 0.15, rep(0.05, 3))
#'
#' study_py <- study_py |>
#'   mutate(expected_1 = expected_table[pol_yr],
#'          expected_2 = ifelse(inc_guar, 0.015, 0.03))
#'
#' exp_res <- study_py |> group_by(pol_yr) |>
#'   exp_stats(expected = c("expected_1", "expected_2"))
#'
#' plot_termination_rates(exp_res)
#'
#' plot_actual_to_expected(exp_res)
#'
#' @seealso [autoplot.exp_df()]
#'
#' @name plot_special
#' @rdname plot_special
#' @export
plot_termination_rates <- function(object, ..., include_cred_adj = FALSE) {
  verify_exp_df(object)
  if (
    include_cred_adj &&
      (!attr(object, "xp_params")$credibility ||
        is.null(attr(object, "expected")))
  ) {
    cred_adj_warning()
  }

  .groups <- groups(object)
  piv_cols <- c(
    "q_obs",
    attr(object, "expected"),
    if (include_cred_adj) paste0("adj_", attr(object, "expected"))
  )

  object <- pivot_plot_special(object, piv_cols)

  attr(object, "groups") <- append(.groups, rlang::expr(Series), after = 1L)

  autoplot(object, y = Rate, ...)
}

#' @rdname plot_special
#' @export
plot_actual_to_expected <- function(object, ..., add_hline = TRUE) {
  verify_exp_df(object)

  piv_cols <- paste0("ae_", attr(object, "expected")) |>
    intersect(names(object))
  if (length(piv_cols) == 0) {
    rlang::abort(c(
      x = "The `exp_df` object does not have any actual-to-expected results available.",
      i = "Hint: to add expected values, use the `expected` argument in `exp_stats()`"
    ))
  }

  .groups <- groups(object)

  object <- pivot_plot_special(object, piv_cols, values_to = "A/E ratio")

  attr(object, "groups") <- append(.groups, rlang::expr(Series), after = 1L)
  p <- autoplot(object, y = `A/E ratio`, ...)

  if (add_hline) {
    p <- p +
      ggplot2::geom_hline(yintercept = 1, linetype = 2, color = "#112599")
  }

  p
}

# this function is used to pivot `exp_df` or `trx_df` objects before they're
# passed to special plotting functions
#' @param object An `exp_df` or `trx_df` object
#' @param piv_cols A primary set of columns to pivot longer
#' @param values_to Name of the values column in the pivoted object.
#' @noRd
pivot_plot_special <- function(object, piv_cols, values_to = "Rate") {
  hold_class <- class(object)
  xp_params <- attr(object, "xp_params")
  piv_cols <- intersect(piv_cols, names(object))

  object <- if (!xp_params$conf_int) {
    object |>
      tidyr::pivot_longer(
        dplyr::all_of(piv_cols),
        names_to = "Series",
        values_to = values_to
      )
  } else {
    extra_piv_cols <- c(
      piv_cols |> paste0("_upper"),
      piv_cols |> paste0("_lower")
    ) |>
      intersect(names(object))

    object |>
      dplyr::rename_at(piv_cols, \(x) paste(x, values_to, sep = "_")) |>
      tidyr::pivot_longer(
        c(
          dplyr::all_of(
            piv_cols |>
              paste(values_to, sep = "_")
          ),
          dplyr::all_of(extra_piv_cols)
        ),
        names_to = c("Series", ".value"),
        names_pattern = paste0(
          "^(",
          paste0(piv_cols, collapse = "|"),
          ")_(",
          values_to,
          "|upper|lower)"
        )
      ) |>
      dplyr::rename_at(c("lower", "upper"), \(x) paste(values_to, x, sep = "_"))
  }

  class(object) <- hold_class
  attr(object, "xp_params") <- xp_params
  object
}

# This internal function provides a common warning that is used by multiple
# functions.
cred_adj_warning <- function() {
  rlang::warn(c(
    "*" = "`object` has no credibility-weighted termination rates.",
    "i" = "Pass `credibility = TRUE` and one or more column names to `expected` when calling `exp_stats()` to calculate credibility-weighted termination rates."
  ))
}

Try the actxps package in your browser

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

actxps documentation built on Nov. 5, 2025, 5:40 p.m.