Skip to contents

Forager (noun)

A person that goes from place to place searching for things that they can eat or use.1

Ager (noun)

A person that calls from place to place searching for payment before insurance can refuse.2


forager is a work-in-progress, the goal of which is to become a suite of integrated analytics tools focused on a comprehensive overview of a healthcare organization’s operational and financial performance areas. Build your own rule-based, automated reporting pipeline to monitor:

  • Patient Scheduling
  • Coding / Billing
  • Productivity
  • Collections & A/R
  • Denial Management

Installation

You can install the development version of forager from GitHub with:

# install.packages("devtools")
devtools::install_github("andrewallenbruce/forager")

# install.packages("remotes")
remotes::install_github("andrewallenbruce/forager")

Foundation: Time Measurement

Everything in a Healthcare RCM workflow is built upon the bedrock of time measurement.

  • Task a is created at time t.
  • Subtask a1 is assigned at time t1 to responsible party x1.
  • Subtask a2 is assigned at time t2 to responsible party x2.
  • So on, and so forth until…
  • Task ai is completed at time ti.

Measuring the amount of time between each step becomes crucial in identifying workflow issues.

Example: The Lifecycle of a Claim

  • Provider Lag: Days between Date of Service and Date of Release
  • Billing Lag: Days between Date of Release and Date of Submission
  • Acceptance Lag: Days between Date of Submission and Date of Acceptance
  • Payment Lag: Days between Date of Acceptance and Date of Adjudication
  • Days in AR: Days between Date of Release and Date of Adjudication


x <- forager::generate_data(15000)
x |> head(n = 10)
#> # A tibble: 10 × 5
#>    claim_id   payer        ins_class balance    dates           
#>    <variable> <chr>        <chr>     <variable> <list>          
#>  1 00001      Medicaid     Secondary 152.85747  <tibble [1 × 5]>
#>  2 00002      UnitedHealth Secondary  36.05447  <tibble [1 × 5]>
#>  3 00003      Centene      Secondary  90.33910  <tibble [1 × 5]>
#>  4 00004      UnitedHealth Secondary  51.64573  <tibble [1 × 5]>
#>  5 00005      Medicaid     Secondary 154.65047  <tibble [1 × 5]>
#>  6 00006      Humana       Secondary 132.19150  <tibble [1 × 5]>
#>  7 00007      Centene      Primary    77.79067  <tibble [1 × 5]>
#>  8 00008      Humana       Secondary 139.89570  <tibble [1 × 5]>
#>  9 00009      Anthem       Primary    71.28777  <tibble [1 × 5]>
#> 10 00010      Centene      Secondary 141.13423  <tibble [1 × 5]>


x |> tidyr::unnest(dates) |> 
     tidyr::pivot_longer(cols = tidyr::starts_with("date"), 
                         names_to = "date_type", 
                         values_to = "date") |> 
                         head(n = 10) |> 
                         gluedown::md_table()
claim_id payer ins_class balance date_type date
00001 Medicaid Secondary 152.85747 date_of_service 2020-09-19
00001 Medicaid Secondary 152.85747 date_of_release 2020-09-30
00001 Medicaid Secondary 152.85747 date_of_submission 2020-10-02
00001 Medicaid Secondary 152.85747 date_of_acceptance 2020-10-05
00001 Medicaid Secondary 152.85747 date_of_adjudication 2020-11-09
00002 UnitedHealth Secondary 36.05447 date_of_service 2020-07-19
00002 UnitedHealth Secondary 36.05447 date_of_release 2020-08-02
00002 UnitedHealth Secondary 36.05447 date_of_submission 2020-08-04
00002 UnitedHealth Secondary 36.05447 date_of_acceptance 2020-08-04
00002 UnitedHealth Secondary 36.05447 date_of_adjudication 2020-09-09


x |> tidyr::unnest(dates) |> 
  count_days(date_of_service, date_of_release, provider_lag) |> 
  count_days(date_of_release, date_of_submission, billing_lag) |> 
  count_days(date_of_submission, date_of_acceptance, processing_lag) |> 
  count_days(date_of_submission, date_of_adjudication, payer_lag) |> 
  count_days(date_of_release, date_of_adjudication, days_in_ar) |> 
  dplyr::group_by(month = clock::date_month_factor(date_of_service)) |> 
  dplyr::summarise(no_of_claims = dplyr::n(), 
                   balance_total = sum(balance),
                   avg_prov_lag = round(mean(provider_lag), 2), 
                   avg_bill_lag = round(mean(billing_lag), 2),
                   avg_accept_lag = round(mean(processing_lag), 2),
                   avg_pay_lag = round(mean(payer_lag), 2),
                   avg_days_in_ar = round(mean(days_in_ar), 2), .groups = "drop") |> 
  gluedown::md_table()
month no_of_claims balance_total avg_prov_lag avg_bill_lag avg_accept_lag avg_pay_lag avg_days_in_ar
January 1277 174559.7 10.79 2.20 3.05 33.18 35.37
February 1263 171249.5 11.22 2.32 3.11 33.01 35.34
March 1225 156436.5 11.05 2.31 3.14 33.05 35.36
April 1257 168064.5 11.11 2.39 3.08 33.19 35.58
May 1281 167327.5 11.22 2.31 3.09 33.20 35.51
June 1251 163328.0 10.84 2.28 3.20 33.26 35.54
July 1238 166397.6 10.92 2.31 3.06 33.17 35.47
August 1239 162255.0 10.82 2.32 3.09 33.10 35.41
September 1281 173373.6 10.99 2.22 3.12 33.09 35.31
October 1200 160917.7 11.12 2.28 3.09 33.25 35.53
November 1236 165392.2 11.00 2.35 3.16 33.16 35.51
December 1252 167007.3 10.92 2.39 3.11 33.21 35.60


x |> tidyr::unnest(dates) |> 
  count_days(date_of_service, date_of_release, provider_lag) |> 
  count_days(date_of_release, date_of_submission, billing_lag) |> 
  count_days(date_of_submission, date_of_acceptance, processing_lag) |> 
  count_days(date_of_submission, date_of_adjudication, payer_lag) |> 
  count_days(date_of_release, date_of_adjudication, days_in_ar) |> 
  dplyr::group_by(qtr = lubridate::quarter(date_of_service)) |> 
  dplyr::summarise(no_of_claims = dplyr::n(), balance_total = sum(balance), avg_prov_lag = round(mean(provider_lag), 2), avg_bill_lag = round(mean(billing_lag), 2),
                   avg_accept_lag = round(mean(processing_lag), 2),
                      avg_pay_lag = round(mean(payer_lag), 2),
                      avg_days_in_ar = round(mean(days_in_ar), 2), .groups = "drop") |> 
  gluedown::md_table()
qtr no_of_claims balance_total avg_prov_lag avg_bill_lag avg_accept_lag avg_pay_lag avg_days_in_ar
1 3765 502245.7 11.02 2.28 3.10 33.08 35.36
2 3789 498720.0 11.06 2.33 3.12 33.22 35.54
3 3758 502026.2 10.91 2.28 3.09 33.12 35.40
4 3688 493317.2 11.01 2.34 3.12 33.21 35.55

Aging Calculation

x |> 
  tidyr::unnest(dates) |> 
  forager:::count_days(date_of_service, date_of_adjudication, days_in_ar) |> 
  dplyr::group_by(aging_bucket = cut(days_in_ar, breaks = seq(0, 500, by = 30))) |> 
  dplyr::summarise(no_of_claims = dplyr::n(),
                   balance_total = sum(balance), .groups = "drop") |> 
  gluedown::md_table()
aging_bucket no_of_claims balance_total
(0,30] 19 2776.459
(30,60] 14899 1984261.643
(60,90] 82 9271.023

Days in AR Monthly Calculation

y <- tibble::tibble(
  date = clock::date_build(2022, 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))))

y |> 
  forager::dar_month(date, gct, earb, dart = 35) |> 
  gluedown::md_table()
date month nmon ndip gct earb earb_trg earb_dc earb_pct adc dar pass actual ideal radiff
2022-01-01 January 1 31 365000.5 182770.0 412097.3 -229327.35 -125.47319 11774.210 15.52291 TRUE 0.5007390 1.129032 -0.6282932
2022-02-01 February 2 28 169090.8 169632.9 211363.5 -41730.61 -24.60054 6038.958 28.08977 TRUE 1.0032060 1.250000 -0.2467940
2022-03-01 March 3 31 297734.2 179348.2 336151.6 -156803.34 -87.42955 9604.330 18.67368 TRUE 0.6023769 1.129032 -0.5266554
2022-04-01 April 4 30 364997.1 182774.3 425829.9 -243055.66 -132.98132 12166.570 15.02266 TRUE 0.5007554 1.166667 -0.6659112
2022-05-01 May 5 31 169096.5 169633.6 190915.4 -21281.81 -12.54576 5454.725 31.09846 TRUE 1.0031762 1.129032 -0.1258560
2022-06-01 June 6 30 297726.8 179345.0 347347.9 -168002.94 -93.67584 9924.227 18.07143 TRUE 0.6023811 1.166667 -0.5642856
2022-07-01 July 7 31 365002.2 182771.0 412099.3 -229328.24 -125.47298 11774.264 15.52292 TRUE 0.5007395 1.129032 -0.6282928
2022-08-01 August 8 31 169092.2 169627.4 190910.6 -21283.18 -12.54702 5454.588 31.09811 TRUE 1.0031650 1.129032 -0.1258673
2022-09-01 September 9 30 297729.7 179344.1 347351.3 -168007.25 -93.67872 9924.324 18.07116 TRUE 0.6023722 1.166667 -0.5642945
2022-10-01 October 10 31 364999.9 182773.2 412096.7 -229323.51 -125.46892 11774.191 15.52320 TRUE 0.5007485 1.129032 -0.6282837
2022-11-01 November 11 30 169094.4 169636.7 197276.8 -27640.12 -16.29372 5636.479 30.09621 TRUE 1.0032069 1.166667 -0.1634597
2022-12-01 December 12 31 297725.8 179346.6 336142.0 -156795.39 -87.42591 9604.057 18.67405 TRUE 0.6023886 1.129032 -0.5266437


Days in AR Quarterly Calculation

y |> forager::dar_qtr(date, gct, earb, 35) |> 
     gluedown::md_table()
date nqtr ndip gct_qtr earb earb_trg earb_dc earb_pct adc dar pass actual ideal radiff
2022-03-01 1 90 831825.6 179348.2 323487.7 -144139.5 -80.37 9242.51 19.40 TRUE 0.22 0.39 -0.17
2022-06-01 2 91 831820.4 179345.0 319930.9 -140585.9 -78.39 9140.88 19.62 TRUE 0.22 0.38 -0.16
2022-09-01 3 92 831824.2 179344.1 316454.8 -137110.7 -76.45 9041.57 19.84 TRUE 0.22 0.38 -0.16
2022-12-01 4 92 831820.1 179346.6 316453.3 -137106.7 -76.45 9041.52 19.84 TRUE 0.22 0.38 -0.16


Presentation Examples



Code of Conduct

Please note that the forager project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.