diff --git a/DESCRIPTION b/DESCRIPTION index d14f45f..c7adae6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -27,6 +27,7 @@ Suggests: covr, tidyr, svglite, + janitor, ggplot2, headliner, forcats, diff --git a/NAMESPACE b/NAMESPACE index 71afa30..ec396f9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,7 @@ export(avg_dar) export(bin_aging) export(dar_ex) +export(days_between) export(generate_data) export(load_ex) export(net_ex) diff --git a/R/aging.R b/R/aging.R index 5ca0982..afdf186 100644 --- a/R/aging.R +++ b/R/aging.R @@ -2,89 +2,115 @@ #' #' @template args-df-default #' -#' @template args-date-col +#' @param ndays `` column of counts of days elapsed to bin by #' #' @param bin_type `` string specifying the bin type; one of "chop", "cut" or "ivs" #' #' @template returns-default #' #' @examples -#' binned <- bin_aging( -#' df = load_ex("aging_ex"), -#' date = dos -#' ) |> -#' dplyr::select( -#' dos:ins_class, -#' dar:aging_bin -#' ) +#' generate_data(10)[c( +#' "date_srvc", +#' "charges", +#' "payer")] |> +#' days_between(date_srvc) |> +#' bin_aging(days_in_ar) #' -#' head(binned) -#' -#' binned |> +#' load_ex("aging_ex") |> +#' dplyr::select(dos, charges, ins_name) |> +#' days_between(dos) |> +#' bin_aging(days_in_ar) |> #' dplyr::arrange(aging_bin) |> -#' dplyr::summarise(n_claims = dplyr::n(), -#' balance = sum(charges), -#' .by = aging_bin) |> -#' dplyr::mutate( -#' tot_claims = sum(n_claims), -#' tot_balance = sum(balance), -#' pct_claims = n_claims / tot_claims, -#' pct_balance = balance / tot_balance) |> -#' print(n = 50) -#' -#' binned |> -#' dplyr::arrange(aging_bin, ins_name) |> -#' dplyr::summarise( -#' n_claims = dplyr::n(), -#' balance = sum(charges), -#' .by = c(aging_bin, ins_name) -#' ) |> -#' dplyr::mutate( -#' tot_claims = sum(n_claims), -#' tot_balance = sum(balance), -#' pct_claims = n_claims / tot_claims, -#' pct_balance = balance / tot_balance) |> -#' print(n = 50) -#' -#' binned |> -#' dplyr::arrange(ins_name, aging_bin) |> -#' dplyr::summarise( -#' n_claims = dplyr::n(), -#' balance = sum(charges), -#' .by = c(aging_bin, ins_name) +#' dplyr::group_by( +#' year = clock::get_year(dos), +#' month = clock::date_month_factor(dos), #' ) |> -#' dplyr::mutate( -#' tot_claims = sum(n_claims), -#' tot_balance = sum(balance), -#' pct_claims = n_claims / tot_claims, -#' pct_balance = balance / tot_balance, -#' .by = ins_name) |> -#' print(n = 50) +#' janitor::tabyl(ins_name, aging_bin, year) #' #' @autoglobal #' #' @export -bin_aging <- function(df, date, bin_type = "chop") { +bin_aging <- function(df, ndays, bin_type = c("case", "chop")) { - df <- df |> - dplyr::mutate( - dar = clock::date_count_between( - {{ date }}, - lubridate::today(), - "day")) + bin_type <- match.arg(bin_type) if (bin_type == "chop") { - df <- df |> - dplyr::mutate( - aging_bin = santoku::chop_width( - dar, - 30, - start = 0, - left = FALSE, - close_end = FALSE + df <- df |> + dplyr::mutate( + aging_bin = santoku::chop_width( + {{ ndays }}, + 30, + start = 0, + left = FALSE, + close_end = FALSE + ) + ) + } + + if (bin_type == "case") { + + df <- df |> + dplyr::mutate( + aging_bin = dplyr::case_when( + dplyr::between({{ ndays }}, 0, 30) ~ "0-30", + dplyr::between({{ ndays }}, 31, 60) ~ "31-60", + dplyr::between({{ ndays }}, 61, 90) ~ "61-90", + dplyr::between({{ ndays }}, 91, 120) ~ "91-120", + {{ ndays }} >= 121 ~ "121+" + ), + aging_bin = suppressWarnings( + forcats::fct_relevel( + aging_bin, + c("0-30", "31-60", "61-90", "91-120", "121+"), + after = Inf + ) + ) ) - ) } return(df) } + +#' Calculate Number of Days Between Two Dates +#' +#' @template args-df-default +#' +#' @param from `[character]` column of dates to calculate days between +#' +#' @param to `[character]` column of dates to calculate days between +#' +#' @template returns-default +#' +#' @examples +#' generate_data(10)[c( +#' "date_srvc", +#' "charges", +#' "payer")] |> +#' days_between(date_srvc) +#' +#' @autoglobal +#' +#' @export +days_between <- function(df, from, to = NULL) { + + if (is.null(to)) { + + df |> + dplyr::mutate( + days_in_ar = clock::date_count_between( + {{ from }}, + clock::date_today(""), + "day") + ) + + } else { + + df |> + dplyr::mutate( + days_in_ar = clock::date_count_between( + {{ from }}, + {{ to }}, + "day") + ) + } +} diff --git a/R/dar.R b/R/dar.R index 07853e3..16280bd 100644 --- a/R/dar.R +++ b/R/dar.R @@ -8,9 +8,9 @@ #' #' @template args-earb-col #' -#' @param dart `` Target Days in AR, default is `35` days +#' @param dart `[numeric]` Target Days in AR, default is `35` days #' -#' @param period `` string specifying the calculation period; one of +#' @param by `[character]` string specifying the calculation period; one of #' `"month"`, `"quarter"`, or `"year"`; defaults to `"month"` #' #' @template returns-default @@ -21,14 +21,14 @@ #' gct = gross_charges, #' earb = ending_ar, #' dart = 35, -#' period = "month") +#' by = "month") #' #' avg_dar(df = dar_ex(), #' date = date, #' gct = gross_charges, #' earb = ending_ar, #' dart = 35, -#' period = "quarter") +#' by = "quarter") #' #' @autoglobal #' @@ -38,9 +38,9 @@ avg_dar <- function(df, gct, earb, dart = 35, - period = c("month", "quarter")) { + by = c("month", "quarter")) { - period <- match.arg(period) + by <- match.arg(by) datecol <- rlang::englue("{{ date }}") earbcol <- rlang::englue("{{ earb }}") gctcol <- rlang::englue("{{ gct }}") @@ -63,7 +63,7 @@ avg_dar <- function(df, ndip = lubridate::days_in_month({{ date }}) ) - if (period == "quarter") { + if (by == "quarter") { qtr_max_nmons <- df |> dplyr::summarise( diff --git a/R/generated-globals.R b/R/generated-globals.R index 5403cfd..db45d36 100644 --- a/R/generated-globals.R +++ b/R/generated-globals.R @@ -7,16 +7,25 @@ utils::globalVariables(c( # "adc", # + "aging_bin", + # + "balance", + # + "charges", # "dar", # - "date_of_acceptance", + "date_accept", + # + "date_adjud", # - "date_of_release", + "date_recon", # - "date_of_service", + "date_rlse", # - "date_of_submission", + "date_srvc", + # + "date_submit", # "earb_gt120", # @@ -29,6 +38,8 @@ utils::globalVariables(c( "max_nmon", # "month", + # + "NA_Date_", # "ndip", # diff --git a/R/predict_net.R b/R/predict_net.R index 5b21e21..05fc3c9 100644 --- a/R/predict_net.R +++ b/R/predict_net.R @@ -30,16 +30,16 @@ predict_net <- function(df, date, gct, earb, net, parb_120) { df <- df |> dplyr::mutate( - date = lubridate::ymd({{ date }}), - pct_paid = {{ net }} / {{ gct }}, - parl_120 = 1 - {{ parb_120 }}, - net_pred = ({{ gct }} * pct_paid) * parl_120, + date = lubridate::ymd({{ date }}), + pct_paid = {{ net }} / {{ gct }}, + parl_120 = 1 - {{ parb_120 }}, + net_pred = ({{ gct }} * pct_paid) * parl_120, earb_gt120 = {{ earb }} * parb_120, earb_lt120 = {{ earb }} * parl_120 ) pred <- dplyr::tibble( - date = df$date[nrow(df)] + months(1), + date = df$date[nrow(df)] + months(1), net_pred = df$net_pred[nrow(df)] ) @@ -75,11 +75,12 @@ predict_net <- function(df, date, gct, earb, net, parb_120) { net_ex <- function() { load_ex("monthly_raw") |> - dplyr::select(date, - gct = gross_charges, - earb = ending_ar, - net = net_payment,) |> - dplyr::mutate(date = lubridate::ymd(date), - parb_120 = rep(c(0.021, 0.047, 0.075), 4) - ) + dplyr::select( + date, + gct = gross_charges, + earb = ending_ar, + net = net_payment,) |> + dplyr::mutate( + date = lubridate::ymd(date), + parb_120 = rep(c(0.021, 0.047, 0.075), 4)) } diff --git a/R/utils.R b/R/utils.R index a6bcf6e..b69376c 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,6 +1,6 @@ #' Mount [pins][pins::pins-package] board #' -#' @param source `` `"local"` or `"remote"` +#' @param source `[character]` string: `"local"` (default) or `"remote"` #' #' @returns `` or `` #' @@ -25,11 +25,11 @@ mount_board <- function(source = c("local", "remote")) { #' Get a pinned dataset from a [pins][pins::pins-package] board #' -#' @param pin `` string name of pinned dataset +#' @param pin `[character]` string name of pinned dataset #' #' @template args-dots #' -#' @returns `` +#' @returns A [tibble][tibble::tibble-package] #' #' @noRd get_pin <- function(pin, ...) { @@ -46,7 +46,7 @@ get_pin <- function(pin, ...) { #' #' @param ... arguments to pass to [mount_board()] #' -#' @returns `` of [pins][pins::pins-package] +#' @returns A `[list]` of [pins][pins::pins-package] #' #' @noRd list_pins <- function(...) { @@ -59,7 +59,7 @@ list_pins <- function(...) { #' Load example data #' -#' @param name `` name of example dataset +#' @param name `[character]` name of example dataset #' #' @returns A [tibble][tibble::tibble-package] #' @@ -74,7 +74,11 @@ load_ex <- function(name) { #' Generate mock coding/billing data frame #' -#' @param rows number of rows to generate; default is 100 +#' @param rows `[integerish]` rows number of rows to generate; default is `100` +#' +#' @param add_day_counts `[logical]` add_day_counts add columns for days between events; default is `TRUE` +#' +#' @param ... `[dots]` additional arguments #' #' @returns A [tibble][tibble::tibble-package] #' @@ -84,28 +88,64 @@ load_ex <- function(name) { #' @autoglobal #' #' @export -generate_data <- function(rows = 100){ - - dplyr::tibble( - claim_id = wakefield::id(n = rows), - date_of_service = wakefield::date_stamp( - n = rows, - start = lubridate::today() - lubridate::dyears(1), - random = TRUE), - payer = fixtuRes::set_vector( - rows, - set = c("Medicare", "Medicaid", "Cigna", "Humana", "UnitedHealth", "Anthem", "BCBS", "Centene")), - ins_class = fixtuRes::set_vector(rows, set = c("Primary", "Secondary")), - balance = wakefield::income(n = rows, digits = 2) / 300) |> - dplyr::mutate( - date_of_service = lubridate::as_date(date_of_service), - date_of_release = date_of_service + round(abs(stats::rnorm(length(date_of_service), 11, 4))), - date_of_submission = date_of_release + round(abs(stats::rnorm(length(date_of_release), 2, 2))), - date_of_acceptance = date_of_submission + round(abs(stats::rnorm(length(date_of_submission), 3, 2))), - date_of_adjudication = date_of_acceptance + round(abs(stats::rnorm(length(date_of_acceptance), 30, 3)))) - # |> - # tidyr::nest(dates = tidyr::contains("date")) +generate_data <- function(rows = 100, add_day_counts = TRUE, ...) { + + payer_names <- c( + "Medicare", + "Medicaid", + "Cigna", + "Humana", + "United Health", + "Anthem", + "BCBS", + "Centene" + ) + rsmpl <- sample(1:rows, size = (75 * rows / 100)) + + df <- dplyr::tibble( + clm_id = wakefield::id_factor(n = rows), + payer = forcats::as_factor(fixtuRes::set_vector(size = rows, set = payer_names)), + charges = as.double(wakefield::income(n = rows, digits = 2) / 300), + date_srvc = lubridate::as_date(wakefield::date_stamp( + n = rows, + start = lubridate::today() - lubridate::dyears(1), + random = TRUE) + ), + date_rlse = date_srvc + stats::rpois(rows, 1:15), + date_submit = date_rlse + stats::rpois(rows, 1:5), + date_accept = date_submit + stats::rpois(rows, 5:20), + date_adjud = date_accept + stats::rpois(rows, 30:120), + date_recon = date_adjud + stats::rpois(rows, 1:10) + ) |> + dplyr::mutate( + balance = charges, + balance = dplyr::if_else(date_adjud == date_recon, 0, balance), + date_recon = dplyr::if_else( + lubridate::year(date_srvc) == max( + lubridate::year(date_srvc)) & balance > 0 & dplyr::row_number(date_recon) %in% rsmpl, NA_Date_, date_recon), + balance = dplyr::if_else(!is.na(date_recon), 0, balance), + .after = charges + ) |> + dplyr::arrange(dplyr::desc(date_srvc)) + + if (add_day_counts) { + + df <- df |> + dplyr::mutate( + days_rlse = as.integer(date_rlse - date_srvc), + days_submit = as.integer(date_submit - date_rlse), + days_accept = as.integer(date_accept - date_submit), + days_adjud = as.integer(date_adjud - date_accept), + days_recon = as.integer(date_recon - date_adjud), + days_in_ar = dplyr::if_else( + is.na(date_recon), + as.integer(date_adjud - date_srvc), + as.integer(date_recon - date_srvc) + ) + ) + } + return(df) } #' Sorted Bar Chart diff --git a/README.Rmd b/README.Rmd index 8527ac7..533fdd7 100644 --- a/README.Rmd +++ b/README.Rmd @@ -57,7 +57,7 @@ You can install the development version of `forager` from pak::pak("andrewallenbruce/forager") ``` -```{r, echo=TRUE, warning=FALSE, message=FALSE} +```{r setup, echo=TRUE, message=FALSE, warning=FALSE} library(tidyverse) library(clock) library(forager) @@ -87,59 +87,78 @@ Measuring the amount of time between each step becomes crucial in identifying wo
```{r, echo=TRUE, warning=FALSE} -x <- forager::generate_data(15000) -x |> head(n = 10) +x <- forager::generate_data(1500) + +mean(x$days_in_ar, na.rm = TRUE) ```
```{r echo=TRUE} -x |> +x_pvt <- x |> + select(clm_id:date_recon) |> pivot_longer( cols = starts_with("date"), names_to = "date_type", values_to = "date") |> - mutate(days_diff = lead(date) - date, - .by = claim_id) -``` -
- -```{r echo=TRUE} -x_days <- x |> - count_days(date_of_service, date_of_release, prov_lag) |> - count_days(date_of_release, date_of_submission, bill_lag) |> - count_days(date_of_submission, date_of_acceptance, proc_lag) |> - count_days(date_of_submission, date_of_adjudication, payer_lag) |> - count_days(date_of_release, date_of_adjudication, dar) + mutate(date_lag = lead(date) - date, + date_lag = lag(date_lag, order_by = date), + date_type = str_remove_all(date_type, "date_"), + date_type = case_match( + date_type, + "srvc" ~ "Service", + "rlse" ~ "Release", + "submit" ~ "Submission", + "accept" ~ "Acceptance", + "adjud" ~ "Adjudication", + "recon" ~ "Reconciliation"), + date_type = fct_relevel( + date_type, + "Service", + "Release", + "Submission", + "Acceptance", + "Adjudication", + "Reconciliation"), + .by = clm_id) + +x_pvt ``` +
```{r echo=TRUE} -x_days |> - group_by(month = date_month_factor(date_of_service)) |> +x |> + group_by( + year = get_year(date_srvc), + month = date_month_factor(date_srvc)) |> summarise( - n_claims = n(), - balance = sum(balance), - prov_lag = mean(prov_lag), - bill_lag = mean(bill_lag), - proc_lag = mean(proc_lag), - payer_lag = mean(payer_lag), - dar = mean(dar), + n_claims = n(), + balance = sum(balance, na.rm = TRUE), + days_rlse = mean(days_rlse, na.rm = TRUE), + days_submit = mean(days_submit, na.rm = TRUE), + days_accept = mean(days_accept, na.rm = TRUE), + days_adjud = mean(days_adjud, na.rm = TRUE), + days_recon = mean(days_recon, na.rm = TRUE), + days_in_ar = mean(days_in_ar, na.rm = TRUE), .groups = "drop") ```
```{r echo=TRUE} -x_days |> - group_by(qtr = quarter(date_of_service)) |> +x |> + group_by( + year = get_year(date_srvc), + nqtr = quarter(date_srvc)) |> summarise( - n_claims = n(), - balance = sum(balance), - prov_lag = mean(prov_lag), - bill_lag = mean(bill_lag), - proc_lag = mean(proc_lag), - payer_lag = mean(payer_lag), - dar = mean(dar), + n_claims = n(), + balance = sum(balance, na.rm = TRUE), + days_rlse = mean(days_rlse, na.rm = TRUE), + days_submit = mean(days_submit, na.rm = TRUE), + days_accept = mean(days_accept, na.rm = TRUE), + days_adjud = mean(days_adjud, na.rm = TRUE), + days_recon = mean(days_recon, na.rm = TRUE), + days_in_ar = mean(days_in_ar, na.rm = TRUE), .groups = "drop") ``` @@ -148,48 +167,54 @@ x_days |> ```{r echo=TRUE} x |> - count_days(date_of_service, date_of_adjudication, dar) |> - group_by(aging_bin = cut(dar, breaks = seq(0, 500, by = 30))) |> + bin_aging(days_in_ar, bin_type = "chop") |> + group_by(aging_bin) |> summarise( n_claims = n(), - balance = roundup(sum(balance)), + balance = roundup(sum(balance, na.rm = TRUE)), .groups = "drop") -``` -```{r} x |> - bin_aging(date_of_service) + bin_aging(days_in_ar, bin_type = "case") |> + group_by(aging_bin) |> + summarise( + n_claims = n(), + balance = roundup(sum(balance, na.rm = TRUE)), + .groups = "drop") ``` - ## Days in AR Monthly Calculation ```{r echo=TRUE} tibble( date = date_build(2024, 1:12), - gct = abs(rnorm(12, c(365000.567, 169094.46, 297731.74), c(2:3))), - earb = abs(rnorm(12, c(182771.32, 169633.64, 179347.72), c(2:3)))) |> + gct = rpois(12, 250000:400000), + earb = rpois(12, 290000:400000) + ) |> avg_dar( date, gct, earb, - dart = 35) + dart = 35, + by = "month") ```
## Days in AR Quarterly Calculation -```{r results='asis', echo=TRUE} +```{r echo=TRUE} tibble( date = date_build(2024, 1:12), - gct = abs(rnorm(12, c(365000.567, 169094.46, 297731.74), c(2:3))), - earb = abs(rnorm(12, c(182771.32, 169633.64, 179347.72), c(2:3)))) |> - avg_dar(date, - gct, - earb, - dart = 35, - period = "quarter") + gct = rpois(12, 250000:400000), + earb = rpois(12, 285500:400000) + ) |> + avg_dar( + date, + gct, + earb, + dart = 35, + by = "quarter") ``` diff --git a/README.md b/README.md index 0adba3d..a709a08 100644 --- a/README.md +++ b/README.md @@ -74,151 +74,158 @@ identifying workflow issues.
``` r -x <- forager::generate_data(15000) -x |> head(n = 10) -#> # A tibble: 10 × 9 -#> claim_id date_of_service payer ins_class balance date_of_release -#> -#> 1 00001 2022-06-20 Medicaid Secondary 191.92213 2022-07-05 -#> 2 00002 2022-12-20 BCBS Primary 239.44870 2022-12-29 -#> 3 00003 2022-06-20 Cigna Secondary 150.90283 2022-07-04 -#> 4 00004 2023-02-20 BCBS Primary 100.98583 2023-03-03 -#> 5 00005 2023-03-20 Medicaid Secondary 103.16400 2023-04-02 -#> 6 00006 2022-10-20 BCBS Primary 26.85437 2022-11-06 -#> 7 00007 2022-07-20 Cigna Secondary 116.19070 2022-08-01 -#> 8 00008 2022-12-20 Humana Secondary 180.63007 2022-12-30 -#> 9 00009 2022-06-20 Medicare Secondary 179.69207 2022-07-02 -#> 10 00010 2022-10-20 Anthem Primary 141.31267 2022-11-06 -#> # ℹ 3 more variables: date_of_submission , date_of_acceptance , -#> # date_of_adjudication +x <- forager::generate_data(1500) + +mean(x$days_in_ar, na.rm = TRUE) +#> [1] 101.8013 ```
``` r -x |> +x_pvt <- x |> + select(clm_id:date_recon) |> pivot_longer( cols = starts_with("date"), names_to = "date_type", values_to = "date") |> - mutate(days_diff = lead(date) - date, - .by = claim_id) -#> # A tibble: 75,000 × 7 -#> claim_id payer ins_class balance date_type date days_diff -#> -#> 1 00001 Medicaid Secondary 191.9221 date_of_service 2022-06-20 15 days -#> 2 00001 Medicaid Secondary 191.9221 date_of_release 2022-07-05 4 days -#> 3 00001 Medicaid Secondary 191.9221 date_of_submis… 2022-07-09 1 days -#> 4 00001 Medicaid Secondary 191.9221 date_of_accept… 2022-07-10 31 days -#> 5 00001 Medicaid Secondary 191.9221 date_of_adjudi… 2022-08-10 NA days -#> 6 00002 BCBS Primary 239.4487 date_of_service 2022-12-20 9 days -#> 7 00002 BCBS Primary 239.4487 date_of_release 2022-12-29 1 days -#> 8 00002 BCBS Primary 239.4487 date_of_submis… 2022-12-30 5 days -#> 9 00002 BCBS Primary 239.4487 date_of_accept… 2023-01-04 27 days -#> 10 00002 BCBS Primary 239.4487 date_of_adjudi… 2023-01-31 NA days -#> # ℹ 74,990 more rows + mutate(date_lag = lead(date) - date, + date_lag = lag(date_lag, order_by = date), + date_type = str_remove_all(date_type, "date_"), + date_type = case_match( + date_type, + "srvc" ~ "Service", + "rlse" ~ "Release", + "submit" ~ "Submission", + "accept" ~ "Acceptance", + "adjud" ~ "Adjudication", + "recon" ~ "Reconciliation"), + date_type = fct_relevel( + date_type, + "Service", + "Release", + "Submission", + "Acceptance", + "Adjudication", + "Reconciliation"), + .by = clm_id) + +x_pvt +#> # A tibble: 9,000 × 7 +#> clm_id payer charges balance date_type date date_lag +#> +#> 1 0030 Cigna 107. 107. Service 2023-05-21 NA days +#> 2 0030 Cigna 107. 107. Release 2023-06-06 16 days +#> 3 0030 Cigna 107. 107. Submission 2023-06-10 4 days +#> 4 0030 Cigna 107. 107. Acceptance 2023-06-22 12 days +#> 5 0030 Cigna 107. 107. Adjudication 2023-09-02 72 days +#> 6 0030 Cigna 107. 107. Reconciliation NA NA days +#> 7 0046 Anthem 181. 181. Service 2023-05-21 NA days +#> 8 0046 Anthem 181. 181. Release 2023-05-24 3 days +#> 9 0046 Anthem 181. 181. Submission 2023-05-28 4 days +#> 10 0046 Anthem 181. 181. Acceptance 2023-06-15 18 days +#> # ℹ 8,990 more rows ```
``` r -x_days <- x |> - count_days(date_of_service, date_of_release, prov_lag) |> - count_days(date_of_release, date_of_submission, bill_lag) |> - count_days(date_of_submission, date_of_acceptance, proc_lag) |> - count_days(date_of_submission, date_of_adjudication, payer_lag) |> - count_days(date_of_release, date_of_adjudication, dar) -``` - -``` r -x_days |> - group_by(month = date_month_factor(date_of_service)) |> +x |> + group_by( + year = get_year(date_srvc), + month = date_month_factor(date_srvc)) |> summarise( - n_claims = n(), - balance = sum(balance), - prov_lag = mean(prov_lag), - bill_lag = mean(bill_lag), - proc_lag = mean(proc_lag), - payer_lag = mean(payer_lag), - dar = mean(dar), + n_claims = n(), + balance = sum(balance, na.rm = TRUE), + days_rlse = mean(days_rlse, na.rm = TRUE), + days_submit = mean(days_submit, na.rm = TRUE), + days_accept = mean(days_accept, na.rm = TRUE), + days_adjud = mean(days_adjud, na.rm = TRUE), + days_recon = mean(days_recon, na.rm = TRUE), + days_in_ar = mean(days_in_ar, na.rm = TRUE), .groups = "drop") -#> # A tibble: 12 × 8 -#> month n_claims balance prov_lag bill_lag proc_lag payer_lag dar -#> -#> 1 January 1190 163636. 11.1 2.34 3.06 33.1 35.4 -#> 2 February 1262 161645. 11.0 2.24 3.12 33.0 35.3 -#> 3 March 1339 171348. 10.9 2.33 3.22 33.3 35.7 -#> 4 April 1164 162699. 11.0 2.41 3.13 33.2 35.6 -#> 5 May 1203 161203. 11.0 2.32 3.13 33.0 35.3 -#> 6 June 1269 173170. 10.9 2.36 3.03 33.1 35.5 -#> 7 July 1284 174301. 11.2 2.31 3.25 33.2 35.6 -#> 8 August 1243 163962. 10.9 2.39 3.12 33.2 35.5 -#> 9 September 1237 167538. 11.0 2.33 3.14 33.1 35.4 -#> 10 October 1287 176752. 11.0 2.31 3.10 33.2 35.5 -#> 11 November 1252 166784. 11.1 2.28 3.13 33.2 35.5 -#> 12 December 1270 167237. 11.3 2.35 3.12 33.2 35.6 +#> # A tibble: 12 × 10 +#> year month n_claims balance days_rlse days_submit days_accept days_adjud +#> +#> 1 2022 June 120 0 8.06 3.06 12.8 74.0 +#> 2 2022 July 136 0 8.77 3.12 12.5 73.3 +#> 3 2022 August 127 0 8.08 2.96 13.0 75.2 +#> 4 2022 September 131 0 8.07 3.11 13 73.5 +#> 5 2022 October 124 0 7.60 2.75 12.6 74.5 +#> 6 2022 November 114 0 7.86 3.07 11.9 71.7 +#> 7 2022 December 142 0 7.84 3.04 12.2 77.0 +#> 8 2023 January 124 10815. 7.78 2.63 12.4 73.7 +#> 9 2023 February 122 12522. 7.98 2.99 12.5 77.1 +#> 10 2023 March 139 13533. 7.90 2.78 11.8 74.2 +#> 11 2023 April 123 14256. 7.98 2.98 12.9 76.8 +#> 12 2023 May 98 10116. 8.33 3.03 12.2 72.3 +#> # ℹ 2 more variables: days_recon , days_in_ar ```
``` r -x_days |> - group_by(qtr = quarter(date_of_service)) |> +x |> + group_by( + year = get_year(date_srvc), + nqtr = quarter(date_srvc)) |> summarise( - n_claims = n(), - balance = sum(balance), - prov_lag = mean(prov_lag), - bill_lag = mean(bill_lag), - proc_lag = mean(proc_lag), - payer_lag = mean(payer_lag), - dar = mean(dar), + n_claims = n(), + balance = sum(balance, na.rm = TRUE), + days_rlse = mean(days_rlse, na.rm = TRUE), + days_submit = mean(days_submit, na.rm = TRUE), + days_accept = mean(days_accept, na.rm = TRUE), + days_adjud = mean(days_adjud, na.rm = TRUE), + days_recon = mean(days_recon, na.rm = TRUE), + days_in_ar = mean(days_in_ar, na.rm = TRUE), .groups = "drop") -#> # A tibble: 4 × 8 -#> qtr n_claims balance prov_lag bill_lag proc_lag payer_lag dar -#> -#> 1 1 3791 496629. 11.0 2.31 3.14 33.1 35.4 -#> 2 2 3636 497072. 11.0 2.36 3.09 33.1 35.5 -#> 3 3 3764 505800. 11.0 2.34 3.17 33.2 35.5 -#> 4 4 3809 510773. 11.1 2.31 3.12 33.2 35.5 +#> # A tibble: 5 × 10 +#> year nqtr n_claims balance days_rlse days_submit days_accept days_adjud +#> +#> 1 2022 2 120 0 8.06 3.06 12.8 74.0 +#> 2 2022 3 394 0 8.31 3.07 12.8 74.0 +#> 3 2022 4 380 0 7.77 2.96 12.2 74.6 +#> 4 2023 1 385 36869. 7.89 2.80 12.2 74.9 +#> 5 2023 2 221 24373. 8.13 3.00 12.6 74.8 +#> # ℹ 2 more variables: days_recon , days_in_ar ``` ## Aging Calculation ``` r x |> - count_days(date_of_service, date_of_adjudication, dar) |> - group_by(aging_bin = cut(dar, breaks = seq(0, 500, by = 30))) |> + bin_aging(days_in_ar, bin_type = "chop") |> + group_by(aging_bin) |> summarise( n_claims = n(), - balance = roundup(sum(balance)), + balance = roundup(sum(balance, na.rm = TRUE)), .groups = "drop") -#> # A tibble: 3 × 3 -#> aging_bin n_claims balance -#> -#> 1 (0,30] 16 2836. -#> 2 (30,60] 14880 1994347. -#> 3 (60,90] 104 13091. +#> # A tibble: 5 × 3 +#> aging_bin n_claims balance +#> +#> 1 (30, 60] 124 5934. +#> 2 (60, 90] 453 20147. +#> 3 (90, 120] 492 19992. +#> 4 (120, 150] 345 13099. +#> 5 (150, 180] 86 2070. ``` ``` r + x |> - bin_aging(date_of_service) -#> # A tibble: 15,000 × 11 -#> claim_id date_of_service payer ins_class balance date_of_release -#> -#> 1 00001 2022-06-20 Medicaid Secondary 191.92213 2022-07-05 -#> 2 00002 2022-12-20 BCBS Primary 239.44870 2022-12-29 -#> 3 00003 2022-06-20 Cigna Secondary 150.90283 2022-07-04 -#> 4 00004 2023-02-20 BCBS Primary 100.98583 2023-03-03 -#> 5 00005 2023-03-20 Medicaid Secondary 103.16400 2023-04-02 -#> 6 00006 2022-10-20 BCBS Primary 26.85437 2022-11-06 -#> 7 00007 2022-07-20 Cigna Secondary 116.19070 2022-08-01 -#> 8 00008 2022-12-20 Humana Secondary 180.63007 2022-12-30 -#> 9 00009 2022-06-20 Medicare Secondary 179.69207 2022-07-02 -#> 10 00010 2022-10-20 Anthem Primary 141.31267 2022-11-06 -#> # ℹ 14,990 more rows -#> # ℹ 5 more variables: date_of_submission , date_of_acceptance , -#> # date_of_adjudication , dar , aging_bin + bin_aging(days_in_ar, bin_type = "case") |> + group_by(aging_bin) |> + summarise( + n_claims = n(), + balance = roundup(sum(balance, na.rm = TRUE)), + .groups = "drop") +#> # A tibble: 4 × 3 +#> aging_bin n_claims balance +#> +#> 1 31-60 124 5934. +#> 2 61-90 453 20147. +#> 3 91-120 492 19992. +#> 4 121+ 431 15169. ``` ## Days in AR Monthly Calculation @@ -226,32 +233,34 @@ x |> ``` r tibble( date = date_build(2024, 1:12), - gct = abs(rnorm(12, c(365000.567, 169094.46, 297731.74), c(2:3))), - earb = abs(rnorm(12, c(182771.32, 169633.64, 179347.72), c(2:3)))) |> + gct = rpois(12, 250000:400000), + earb = rpois(12, 290000:400000) + ) |> avg_dar( date, gct, earb, - dart = 35) + dart = 35, + by = "month") #> # A tibble: 12 × 27 -#> date gct earb nmon mon month nqtr yqtr dqtr year ymon -#> -#> 1 2024-01-01 364999. 182771. 1 Jan January 1 2024. 1Q24 2024 2024. -#> 2 2024-02-01 169097. 169635. 2 Feb February 1 2024. 1Q24 2024 2024. -#> 3 2024-03-01 297734. 179347. 3 Mar March 1 2024. 1Q24 2024 2024. -#> 4 2024-04-01 365001. 182773. 4 Apr April 2 2024. 2Q24 2024 2024. -#> 5 2024-05-01 169093. 169632. 5 May May 2 2024. 2Q24 2024 2024. -#> 6 2024-06-01 297731. 179344. 6 Jun June 2 2024. 2Q24 2024 2024. -#> 7 2024-07-01 365000. 182773. 7 Jul July 3 2024. 3Q24 2024 2024. -#> 8 2024-08-01 169092. 169635. 8 Aug August 3 2024. 3Q24 2024 2024. -#> 9 2024-09-01 297731. 179348. 9 Sep Septemb… 3 2024. 3Q24 2024 2024. -#> 10 2024-10-01 365002. 182771. 10 Oct October 4 2024. 4Q24 2024 2024. -#> 11 2024-11-01 169094. 169631. 11 Nov November 4 2024. 4Q24 2024 2024. -#> 12 2024-12-01 297729. 179347. 12 Dec December 4 2024. 4Q24 2024 2024. +#> date gct earb nmon mon month nqtr yqtr dqtr year ymon +#> +#> 1 2024-01-01 249971 290387 1 Jan January 1 2024. 1Q24 2024 2024. +#> 2 2024-02-01 249609 289524 2 Feb February 1 2024. 1Q24 2024 2024. +#> 3 2024-03-01 250604 290556 3 Mar March 1 2024. 1Q24 2024 2024. +#> 4 2024-04-01 249649 290402 4 Apr April 2 2024. 2Q24 2024 2024. +#> 5 2024-05-01 250781 290856 5 May May 2 2024. 2Q24 2024 2024. +#> 6 2024-06-01 250043 290617 6 Jun June 2 2024. 2Q24 2024 2024. +#> 7 2024-07-01 249686 289370 7 Jul July 3 2024. 3Q24 2024 2024. +#> 8 2024-08-01 250669 289316 8 Aug August 3 2024. 3Q24 2024 2024. +#> 9 2024-09-01 249563 290971 9 Sep September 3 2024. 3Q24 2024 2024. +#> 10 2024-10-01 249992 290669 10 Oct October 4 2024. 4Q24 2024 2024. +#> 11 2024-11-01 249929 289751 11 Nov November 4 2024. 4Q24 2024 2024. +#> 12 2024-12-01 250540 289247 12 Dec December 4 2024. 4Q24 2024 2024. #> # ℹ 16 more variables: myear , nhalf , yhalf , dhalf , #> # ndip , adc , dar , dar_pass , dar_diff , #> # ratio_actual , ratio_ideal , ratio_diff , earb_target , -#> # earb_dec_abs , earb_dec_pct , earb_gct_diff +#> # earb_dec_abs , earb_dec_pct , earb_gct_diff ```
@@ -261,26 +270,27 @@ tibble( ``` r tibble( date = date_build(2024, 1:12), - gct = abs(rnorm(12, c(365000.567, 169094.46, 297731.74), c(2:3))), - earb = abs(rnorm(12, c(182771.32, 169633.64, 179347.72), c(2:3)))) |> - avg_dar(date, - gct, - earb, - dart = 35, - period = "quarter") + gct = rpois(12, 250000:400000), + earb = rpois(12, 285500:400000) + ) |> + avg_dar( + date, + gct, + earb, + dart = 35, + by = "quarter") +#> # A tibble: 4 × 18 +#> date earb nmon nqtr month gct ndip adc dar dar_pass dar_diff +#> +#> 1 2024-03-01 285101 3 1 March 748742 91 8228. 34.7 TRUE -0.350 +#> 2 2024-06-01 285522 6 2 June 750220 91 8244. 34.6 TRUE -0.367 +#> 3 2024-09-01 285733 9 3 Sept… 749819 92 8150. 35.1 FALSE 0.0584 +#> 4 2024-12-01 284571 12 4 Dece… 750898 92 8162. 34.9 TRUE -0.134 +#> # ℹ 7 more variables: ratio_actual , ratio_ideal , ratio_diff , +#> # earb_target , earb_dec_abs , earb_dec_pct , +#> # earb_gct_diff ``` -# A tibble: 4 × 18 - -date earb nmon nqtr month gct ndip adc dar dar_pass dar_diff - 1 2024-03-01 -1.79e5 3 1 March 8.32e5 91 9141. 19.6 TRUE -15.4 2 2024-06-01 1.79e5 6 2 -June 8.32e5 91 9141. 19.6 TRUE -15.4 3 2024-09-01 1.79e5 9 3 Sept… -8.32e5 92 9042. 19.8 TRUE -15.2 4 2024-12-01 1.79e5 12 4 Dece… 8.32e5 92 -9042. 19.8 TRUE -15.2 \# ℹ 7 more variables: ratio_actual , -ratio_ideal , ratio_diff , \# earb_target , earb_dec_abs -, earb_dec_pct , \# earb_gct_diff - ## Code of Conduct Please note that the `forager` project is released with a [Contributor diff --git a/data-raw/mermaid_diagram.R b/data-raw/mermaid_diagram.R new file mode 100644 index 0000000..74454c3 --- /dev/null +++ b/data-raw/mermaid_diagram.R @@ -0,0 +1,12 @@ +# ```{mermaid} +# %%| label: tx-mermaid +# %%| fig-cap: "Example of Taxonomy Hierarchy" +# %%| echo: false +# +# flowchart TB +# A(((Date of Service))) == Provider Signs Chart ==> B(Date of Release) +# B(Date of Release) == Biller Submits Claim ==> C([Date of Submission]) +# C([Date of Submission]) == Payer Acknowledges Receipt of Claim ==> D[[Date of Acceptance]] +# E([Date of Submission]) --> L>Addiction Medicine] +# L>Addiction Medicine] --o M[[207LA0401X]] +# ``` diff --git a/man/avg_dar.Rd b/man/avg_dar.Rd index 636347e..2f2af40 100644 --- a/man/avg_dar.Rd +++ b/man/avg_dar.Rd @@ -4,7 +4,7 @@ \alias{avg_dar} \title{Calculate Average Days in AR} \usage{ -avg_dar(df, date, gct, earb, dart = 35, period = c("month", "quarter")) +avg_dar(df, date, gct, earb, dart = 35, by = c("month", "quarter")) } \arguments{ \item{df}{\verb{} or \verb{}} @@ -15,9 +15,9 @@ avg_dar(df, date, gct, earb, dart = 35, period = c("month", "quarter")) \item{earb}{\verb{} column of ending accounts receivable balances} -\item{dart}{\verb{} Target Days in AR, default is \code{35} days} +\item{dart}{\verb{[numeric]} Target Days in AR, default is \code{35} days} -\item{period}{\verb{} string specifying the calculation period; one of +\item{by}{\verb{[character]} string specifying the calculation period; one of \code{"month"}, \code{"quarter"}, or \code{"year"}; defaults to \code{"month"}} } \value{ @@ -32,13 +32,13 @@ avg_dar(df = dar_ex(), gct = gross_charges, earb = ending_ar, dart = 35, - period = "month") + by = "month") avg_dar(df = dar_ex(), date = date, gct = gross_charges, earb = ending_ar, dart = 35, - period = "quarter") + by = "quarter") } diff --git a/man/bin_aging.Rd b/man/bin_aging.Rd index 19d0fee..bb2bdea 100644 --- a/man/bin_aging.Rd +++ b/man/bin_aging.Rd @@ -4,12 +4,12 @@ \alias{bin_aging} \title{Apply 30-Day Aging Bins} \usage{ -bin_aging(df, date, bin_type = "chop") +bin_aging(df, ndays, bin_type = c("case", "chop")) } \arguments{ \item{df}{\verb{} or \verb{}} -\item{date}{column of \verb{}s} +\item{ndays}{\verb{} column of counts of days elapsed to bin by} \item{bin_type}{\verb{} string specifying the bin type; one of "chop", "cut" or "ivs"} } @@ -20,56 +20,22 @@ a \link[tibble:tibble-package]{tibble} Apply 30-Day Aging Bins } \examples{ -binned <- bin_aging( - df = load_ex("aging_ex"), - date = dos -) |> - dplyr::select( - dos:ins_class, - dar:aging_bin - ) +generate_data(10)[c( + "date_srvc", + "charges", + "payer")] |> + days_between(date_srvc) |> + bin_aging(days_in_ar) -head(binned) - -binned |> +load_ex("aging_ex") |> + dplyr::select(dos, charges, ins_name) |> + days_between(dos) |> + bin_aging(days_in_ar) |> dplyr::arrange(aging_bin) |> - dplyr::summarise(n_claims = dplyr::n(), - balance = sum(charges), - .by = aging_bin) |> - dplyr::mutate( - tot_claims = sum(n_claims), - tot_balance = sum(balance), - pct_claims = n_claims / tot_claims, - pct_balance = balance / tot_balance) |> - print(n = 50) - -binned |> - dplyr::arrange(aging_bin, ins_name) |> - dplyr::summarise( - n_claims = dplyr::n(), - balance = sum(charges), - .by = c(aging_bin, ins_name) - ) |> - dplyr::mutate( - tot_claims = sum(n_claims), - tot_balance = sum(balance), - pct_claims = n_claims / tot_claims, - pct_balance = balance / tot_balance) |> - print(n = 50) - -binned |> - dplyr::arrange(ins_name, aging_bin) |> - dplyr::summarise( - n_claims = dplyr::n(), - balance = sum(charges), - .by = c(aging_bin, ins_name) + dplyr::group_by( + year = clock::get_year(dos), + month = clock::date_month_factor(dos), ) |> - dplyr::mutate( - tot_claims = sum(n_claims), - tot_balance = sum(balance), - pct_claims = n_claims / tot_claims, - pct_balance = balance / tot_balance, - .by = ins_name) |> - print(n = 50) + janitor::tabyl(ins_name, aging_bin, year) } diff --git a/man/days_between.Rd b/man/days_between.Rd new file mode 100644 index 0000000..b99c8ee --- /dev/null +++ b/man/days_between.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/aging.R +\name{days_between} +\alias{days_between} +\title{Calculate Number of Days Between Two Dates} +\usage{ +days_between(df, from, to = NULL) +} +\arguments{ +\item{df}{\verb{} or \verb{}} + +\item{from}{\verb{[character]} column of dates to calculate days between} + +\item{to}{\verb{[character]} column of dates to calculate days between} +} +\value{ +a \link[tibble:tibble-package]{tibble} +} +\description{ +Calculate Number of Days Between Two Dates +} +\examples{ +generate_data(10)[c( + "date_srvc", + "charges", + "payer")] |> + days_between(date_srvc) + +} diff --git a/man/generate_data.Rd b/man/generate_data.Rd index 5cba3c3..d8fe494 100644 --- a/man/generate_data.Rd +++ b/man/generate_data.Rd @@ -4,10 +4,14 @@ \alias{generate_data} \title{Generate mock coding/billing data frame} \usage{ -generate_data(rows = 100) +generate_data(rows = 100, add_day_counts = TRUE, ...) } \arguments{ -\item{rows}{number of rows to generate; default is 100} +\item{rows}{\verb{[integerish]} rows number of rows to generate; default is \code{100}} + +\item{add_day_counts}{\verb{[logical]} add_day_counts add columns for days between events; default is \code{TRUE}} + +\item{...}{\verb{[dots]} additional arguments} } \value{ A \link[tibble:tibble-package]{tibble} diff --git a/man/load_ex.Rd b/man/load_ex.Rd index f9d535a..9155209 100644 --- a/man/load_ex.Rd +++ b/man/load_ex.Rd @@ -7,7 +7,7 @@ load_ex(name) } \arguments{ -\item{name}{\verb{} name of example dataset} +\item{name}{\verb{[character]} name of example dataset} } \value{ A \link[tibble:tibble-package]{tibble} diff --git a/vignettes/articles/aging-of_accounts.Rmd b/vignettes/articles/aging-of_accounts.Rmd index 9c3e177..c79d97a 100644 --- a/vignettes/articles/aging-of_accounts.Rmd +++ b/vignettes/articles/aging-of_accounts.Rmd @@ -26,83 +26,3 @@ An account is a *billable episode of care*. It begins to age once it is billed t As well, aging should be broken down by many metrics, such as Provider, Patient, Insurance Types (Commercial, Primary, Secondary, Worker’s Compensation, Managed Care), Facility, Diagnosis/Procedure code, Specialty, etc. The older the account or the longer the account remains unpaid, the less likely it will be reimbursed. Most claims are originally billed to insurance and, until the insurance makes a payment, the responsibility for the payment continues to be with the insurance payer. After the payer makes or denies a payment (with no just cause for an appeal), the responsibility for the balance of goes to the patient (to be sent an invoice) or the physician (to be written off.) - - -```{r} -binned <- bin_aging( - df = load_ex("aging_ex"), - date = dos) |> - select( - dos:ins_class, - dar:aging_bin) - -binned -``` - -```{r} -binned |> - arrange(aging_bin) |> - summarise(n_claims = n(), - balance = sum(charges), - .by = aging_bin) |> - mutate(pct_claims = n_claims / sum(n_claims), - pct_balance = balance / sum(balance)) |> - gt(rowname_col = "aging_bin") |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> - fmt_percent(columns = pct_claims:pct_balance, decimals = 0) |> - fmt_currency(columns = balance, decimals = 0) |> - fmt_number(columns = n_claims, decimals = 0) |> - opt_stylize() |> - cols_label(n_claims = "Claims", balance = "Charges") |> - cols_move_to_start(c(n_claims, pct_claims, balance, pct_balance)) |> - cols_merge(c(n_claims, pct_claims), pattern = "{1} ({2})") |> - cols_merge(c(balance, pct_balance), pattern = "{1} ({2})") |> - tab_header(title = "Aging Report", ) |> - tab_options(heading.align = "left", - quarto.disable_processing = TRUE) -``` - - -```{r} -binned |> - arrange(aging_bin, ins_name) |> - summarise( - n_claims = n(), - balance = sum(charges), - .by = c(aging_bin, ins_name) - ) |> - mutate(pct_claims = n_claims / sum(n_claims), - pct_balance = balance / sum(balance)) |> - gt(groupname_col = "aging_bin", rowname_col = "ins_name") |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> - fmt_percent(columns = pct_claims:pct_balance) |> - fmt_currency(columns = balance, decimals = 0) |> - fmt_number(columns = n_claims, decimals = 0) |> - cols_label(n_claims = "Claims", balance = "Charges") |> - cols_merge(c(n_claims, pct_claims), pattern = "{1} ({2})") |> - cols_merge(c(balance, pct_balance), pattern = "{1} ({2})") |> - opt_stylize() -``` - - -```{r} -binned |> - arrange(ins_name, aging_bin) |> - summarise( - n_claims = n(), - balance = sum(charges), - .by = c(aging_bin, ins_name) - ) |> - mutate(pct_claims = n_claims / sum(n_claims), - pct_balance = balance / sum(balance), - .by = ins_name) |> - gt(groupname_col = "aging_bin", rowname_col = "ins_name") |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> - fmt_percent(columns = pct_claims:pct_balance) |> - fmt_currency(columns = balance) |> - fmt_number(columns = n_claims, decimals = 0) |> - cols_label(n_claims = "Claims", balance = "Charges") |> - cols_merge(c(n_claims, pct_claims), pattern = "{1} ({2})") |> - cols_merge(c(balance, pct_balance), pattern = "{1} ({2})") |> - opt_stylize() -``` diff --git a/vignettes/articles/dar-formulas.Rmd b/vignettes/articles/dar-formulas.Rmd index daf3dc5..7e21f95 100644 --- a/vignettes/articles/dar-formulas.Rmd +++ b/vignettes/articles/dar-formulas.Rmd @@ -36,9 +36,9 @@ ${dar} = \dfrac {earb} {{gct} \div {ndip}}$ Where: - * $ndip = $ Number of Days in Period - * $gct = $ Total Gross Charges - * $earb = $ Ending AR Balance + - ${ndip} = $ Number of Days in Period + - ${gct} = $ Total Gross Charges + - ${earb} = $ Ending AR Balance [^1]: A list of fees physicians establish as the fair price for the services they provide. Keep in mind, this is *not* the same as a payment schedule. @@ -60,9 +60,9 @@ This is the Accounts Receivables balance at the close of business (COB) on the f Let's say you have the following data: - * Number of Days in Period: 30 - * Total Gross Charges: $180,000 - * Ending AR Balance: $77,400 + * Number of Days in Period = __30__ + * Total Gross Charges = __$180,000__ + * Ending AR Balance = __$77,400__ The calculation would look like this: @@ -83,10 +83,7 @@ dar(earb = 77400, ## DAR Targets - * Ending AR $earb_t = (dar_t \times gct) \div ndip$ -
- * Gross Charges $gct_t = (earb \times ndip) \div dar_t$ -
- * Days in Period: $ndip_t = (dar_t \times gct) \div earb$ -
- * DAR: $dar_t = (earb \times ndip) \div gct$ + - $earb_t = (dar_t \times gct) \div ndip$ + - $gct_t = (earb \times ndip) \div dar_t$ + - $ndip_t = (dar_t \times gct) \div earb$ + - $dar_t = (earb \times ndip) \div gct$ diff --git a/vignettes/getting-started.Rmd b/vignettes/getting-started.Rmd index 42c2a74..c4c93d6 100644 --- a/vignettes/getting-started.Rmd +++ b/vignettes/getting-started.Rmd @@ -49,7 +49,7 @@ dar_mon <- avg_dar( gct = gross_charges, earb = ending_ar, dart = 35, - period = "month" + by = "month" ) ``` @@ -58,24 +58,27 @@ dar_mon <- avg_dar( ```{r} dar_mon |> gt(rowname_col = "mon") |> - cols_hide(c(date, - nmon, - month, - nqtr, - yqtr, - dqtr, - year, - ymon, - myear, - nhalf, - yhalf, - dhalf, - ndip, - ending_ar_target, - dar_diff, - ratio_ideal, - ratio_actual)) |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> + cols_hide( + c( + date, + nmon, + month, + nqtr, + yqtr, + dqtr, + year, + ymon, + myear, + nhalf, + yhalf, + dhalf, + ndip, + ending_ar_target, + dar_diff, + ratio_ideal, + ratio_actual + ) + ) |> fmt_currency(columns = c(gross_charges, ending_ar, adc, @@ -122,7 +125,6 @@ dar_mon |> ```{r} dar_mon |> ggplot(aes(x = mon, y = dar)) + - # geom_point() + geom_line(group = 1, linetype = "dashed", alpha = 0.7) + geom_hline(yintercept = 35, color = "red") + labs(title = "Days in AR by Month", x = NULL, y = NULL) + @@ -139,10 +141,7 @@ dar_ex() |> aes(x = date, y = gross_charges), color = "red", alpha = 0.7, - linewidth = 1.5 - ) + - # geom_point(aes(x = date, y = ending_ar), size = 2) + - # geom_point(aes(x = date, y = gross_charges), color = "red", size = 2) + + linewidth = 1.5) + scale_y_continuous(labels = dollar_format(prefix = "$")) + scale_x_date(date_breaks = "1 month", date_labels = "%b") + labs(title = NULL, x = NULL, y = NULL) @@ -203,7 +202,7 @@ avg_dar( gct = gross_charges, earb = ending_ar, dart = 35, - period = "month" + by = "month" ) |> select( month, @@ -219,34 +218,40 @@ avg_dar( headline = "{delta_p}% {trend} than Target", trend_phrases = headliner::trend_terms(more = "HIGHER", less = "Lower"), n_decimal = 0) |> - gt(rowname_col = "month") |> + gt(rowname_col = "month") |> cols_label( - gross_charges = "Gross Charges", - ending_ar = "Ending AR", - ending_ar_target = "Target AR", - dar = "Days in AR", - dar_pass = "Pass", + gross_charges = "Gross Charges", + ending_ar = "Ending AR", + ending_ar_target = "Target AR", + dar = "Days in AR", + dar_pass = "Pass", headline = "Ending AR Trend" - ) |> + ) |> tab_row_group(label = "Q4", rows = c(10:12)) |> tab_row_group(label = "Q3", rows = c(7:9)) |> tab_row_group(label = "Q2", rows = c(4:6)) |> - tab_row_group(label = "Q1", rows = c(1:3)) |> + tab_row_group(label = "Q1", rows = c(1:3)) |> fmt_number(columns = dar) |> fmt_currency(columns = c(gross_charges, ending_ar, ending_ar_target)) |> - tab_style(style = cell_text(font = c( - google_font(name = "IBM Plex Mono"), - default_fonts())), - locations = cells_body(columns = c(gross_charges, ending_ar, ending_ar_target, dar))) |> - opt_stylize(style = 6, color = "cyan") |> + # tab_style(style = cell_text(font = c( + # google_font(name = "IBM Plex Mono"), + # default_fonts())), + # locations = cells_body(columns = c(gross_charges, ending_ar, ending_ar_target, dar))) |> + opt_stylize(style = 6, color = "cyan") |> tab_header( - title = md("Example **Days in AR Analysis** with the **{forager}** Package"), - subtitle = md("**May** saw the *highest* Days in AR of 2022 *(51.2)*. This coincided with the largest
month-to-month increase in AR & highest percentage over the AR Target *(46%)*.")) |> - opt_all_caps() |> + title = md("Example **Days in AR Analysis** with the **{forager}** Package"), + subtitle = md( + "**May** saw the *highest* Days in AR of 2022 *(51.2)*. This coincided with the largest
month-to-month increase in AR & highest percentage over the AR Target *(46%)*." + ) + ) |> + opt_all_caps() |> grand_summary_rows( columns = c(gross_charges, ending_ar, ending_ar_target, dar), - fns = list(Mean = ~mean(., na.rm = TRUE), Median = ~median(., na.rm = TRUE))) |> - opt_table_font(font = list(gt::google_font(name = "Roboto"))) |> + fns = list( + Mean = ~ mean(., na.rm = TRUE), + Median = ~ median(., na.rm = TRUE) + ) + ) |> opt_align_table_header(align = "left") ``` @@ -258,14 +263,13 @@ dar_month <- avg_dar( gct = gross_charges, earb = ending_ar, dart = 35, - period = "month" + by = "month" ) # Create df for gt_plt_bar_stack dar_month_pct <- dar_month |> - mutate( - gct_pct = (gross_charges / (gross_charges + ending_ar) * 100), - earb_pct = (ending_ar / (gross_charges + ending_ar) * 100)) |> + mutate(gct_pct = (gross_charges / (gross_charges + ending_ar) * 100), + earb_pct = (ending_ar / (gross_charges + ending_ar) * 100)) |> select(month, gct_pct, earb_pct) |> pivot_longer(-month, names_to = "measure", values_to = "percentage") |> group_by(month) |> @@ -279,21 +283,17 @@ dar_month_join <- right_join( # Create new copy cols for gt_plt_bullet dar_month_gt <- dar_month_join |> - select( - month, - gross_charges, - ending_ar, - ending_ar_target, - dar, - dar_pass, - list_data - ) |> - mutate( - target_col = ending_ar, - plot_col = ending_ar_target) + select(month, + gross_charges, + ending_ar, + ending_ar_target, + dar, + dar_pass, + list_data) |> + mutate(target_col = ending_ar, plot_col = ending_ar_target) # Create gt table -dar_month_gt |> +dar_month_gt |> gt(rowname_col = "month") |> cols_label( gross_charges = "Gross Charges", @@ -301,41 +301,45 @@ dar_month_gt |> ending_ar_target = "Optimal AR", dar = "Days in AR", dar_pass = "Pass", - plot_col = "Optimal AR Threshold") |> + plot_col = "Optimal AR Threshold" + ) |> tab_row_group(label = "Q4", rows = c(10:12)) |> tab_row_group(label = "Q3", rows = c(7:9)) |> tab_row_group(label = "Q2", rows = c(4:6)) |> tab_row_group(label = "Q1", rows = c(1:3)) |> - gt_theme_nytimes() |> + gt_theme_nytimes() |> fmt_number(columns = dar) |> fmt_currency(columns = c(gross_charges, ending_ar, ending_ar_target)) |> - gt_plt_bullet(column = plot_col, - target = target_col, - palette = c("#8ca0aa", "black"), - width = 65 - ) |> - gt_plt_bar_stack(list_data, - width = 50, - labels = c("Charges (%) ", " AR (%)"), - palette = c("#2c3e50", "#8ca0aa") - ) |> - gt_badge(dar_pass, - palette = c("FALSE" = "#8ca0aa") - ) |> - tab_style(style = cell_text(color = "#2c3e50", weight = "bolder"), - locations = cells_body(columns = dar_pass, rows = dar_pass == "FALSE") - ) |> - tab_style(style = cell_text(color = "#8ca0aa", weight = "normal"), - locations = cells_body(columns = dar_pass, rows = dar_pass == "TRUE") - ) |> - data_color(columns = c(gross_charges, ending_ar, dar), - colors = col_numeric(palette = c("#2c3e50", "#8ca0aa") |> - as.character(), - domain = NULL) - ) |> - tab_footnote(footnote = "Horizontal bar indicates Optimal AR, vertical bar is Actual.", - locations = cells_column_labels(columns = plot_col) - ) |> + gt_plt_bullet( + column = plot_col, + target = target_col, + palette = c("#8ca0aa", "black"), + width = 65 + ) |> + gt_plt_bar_stack( + list_data, + width = 50, + labels = c("Charges (%) ", " AR (%)"), + palette = c("#2c3e50", "#8ca0aa") + ) |> + gt_badge(dar_pass, palette = c("FALSE" = "#8ca0aa")) |> + tab_style( + style = cell_text(color = "#2c3e50", weight = "bolder"), + locations = cells_body(columns = dar_pass, rows = dar_pass == "FALSE") + ) |> + tab_style( + style = cell_text(color = "#8ca0aa", weight = "normal"), + locations = cells_body(columns = dar_pass, rows = dar_pass == "TRUE") + ) |> + data_color( + columns = c(gross_charges, ending_ar, dar), + colors = col_numeric( + palette = c("#2c3e50", "#8ca0aa") |> + as.character(), + domain = NULL + ) + ) |> + tab_footnote(footnote = "Horizontal bar indicates Optimal AR, vertical bar is Actual.", locations = cells_column_labels(columns = plot_col)) |> tab_header(title = md("Example **Days in AR Analysis** with the **{forager}** Package")) ``` @@ -349,46 +353,49 @@ avg_dar( gct = gross_charges, earb = ending_ar, dart = 39, - period = "quarter" + by = "quarter" ) |> - gt(rowname_col = "nqtr") |> - cols_hide(c(date, - nmon, - month, - ndip, - ending_ar_target, - dar_diff, - ratio_ideal, - ratio_actual)) |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> - fmt_currency(columns = c(gross_charges, - ending_ar, - adc, - ending_ar_target, - ending_ar_dec_abs, - earb_gct_diff)) |> - # fmt_tf(columns = c(dar_pass), tf_style = "check-mark") |> - fmt_percent(columns = c(ending_ar_dec_pct)) |> - fmt_number(columns = c(dar, - dar_diff, - ratio_actual, - ratio_ideal, - ratio_diff)) |> - cols_move_to_start(c(nqtr, - gross_charges, - ending_ar, - earb_gct_diff, - adc, - dar_pass, - dar, - dar_diff, - ratio_actual, - ratio_ideal, - ratio_diff, - ending_ar_target, - ending_ar_dec_abs, - ending_ar_dec_pct - )) |> + gt(rowname_col = "nqtr") |> + cols_hide(c( + date, + nmon, + month, + ndip, + ending_ar_target, + dar_diff, + ratio_ideal, + ratio_actual + )) |> + fmt_currency( + columns = c( + gross_charges, + ending_ar, + adc, + ending_ar_target, + ending_ar_dec_abs, + earb_gct_diff + ) + ) |> + fmt_percent(columns = c(ending_ar_dec_pct)) |> + fmt_number(columns = c(dar, dar_diff, ratio_actual, ratio_ideal, ratio_diff)) |> + cols_move_to_start( + c( + nqtr, + gross_charges, + ending_ar, + earb_gct_diff, + adc, + dar_pass, + dar, + dar_diff, + ratio_actual, + ratio_ideal, + ratio_diff, + ending_ar_target, + ending_ar_dec_abs, + ending_ar_dec_pct + ) + ) |> cols_label( gross_charges = "Gross Charges", ending_ar = "Ending AR", @@ -399,21 +406,17 @@ avg_dar( ending_ar_dec_abs = "AR Decrease Needed", ending_ar_dec_pct = "%", earb_gct_diff = "AR - GC" - ) |> + ) |> opt_stylize() ``` ## Aging Bins ```{r} -binned <- bin_aging( - df = load_ex("aging_ex"), - date = dos - ) |> - dplyr::select( - dos:ins_class, - dar:aging_bin - ) +binned <- load_ex("aging_ex") |> + select(dos:ins_name) |> + days_between(dos) |> + bin_aging(days_in_ar) binned ``` @@ -434,7 +437,6 @@ binned |> mutate(pct_claims = n_claims / sum(n_claims), pct_balance = balance / sum(balance)) |> gt(rowname_col = "aging_bin") |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> fmt_percent(columns = pct_claims:pct_balance, decimals = 0) |> fmt_currency(columns = balance, decimals = 0) |> fmt_number(columns = n_claims, decimals = 0) |> @@ -460,7 +462,6 @@ binned |> mutate(pct_claims = n_claims / sum(n_claims), pct_balance = balance / sum(balance)) |> gt(groupname_col = "aging_bin", rowname_col = "ins_name") |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> fmt_percent(columns = pct_claims:pct_balance) |> fmt_currency(columns = balance, decimals = 0) |> fmt_number(columns = n_claims, decimals = 0) |> @@ -479,14 +480,15 @@ binned |> balance = sum(charges), .by = c(aging_bin, ins_name) ) |> - mutate(pct_claims = n_claims / sum(n_claims), - pct_balance = balance / sum(balance), - .by = ins_name) |> + mutate( + pct_claims = n_claims / sum(n_claims), + pct_balance = balance / sum(balance), + .by = ins_name + ) |> gt(groupname_col = "aging_bin", rowname_col = "ins_name") |> - opt_table_font(font = google_font(name = "JetBrains Mono")) |> - fmt_percent(columns = pct_claims:pct_balance) |> - fmt_currency(columns = balance) |> - fmt_number(columns = n_claims, decimals = 0) |> + fmt_percent(columns = pct_claims:pct_balance) |> + fmt_currency(columns = balance) |> + fmt_number(columns = n_claims, decimals = 0) |> cols_label(n_claims = "Claims", balance = "Charges") |> cols_merge(c(n_claims, pct_claims), pattern = "{1} ({2})") |> cols_merge(c(balance, pct_balance), pattern = "{1} ({2})") |>