Skip to content

Commit

Permalink
example pins
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewallenbruce committed May 22, 2024
1 parent e6fabd1 commit b51d565
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 56 deletions.
2 changes: 2 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Imports:
Suggests:
covr,
tidyr,
ggthemes,
ggforce,
svglite,
janitor,
ggplot2,
Expand Down
2 changes: 0 additions & 2 deletions R/generated-globals.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ utils::globalVariables(c(
"max_nmon",
# <avg_dar>
"month",
# <generate_data>
"NA_Date_",
# <avg_dar>
"ndip",
# <predict_net>
Expand Down
77 changes: 45 additions & 32 deletions data-raw/healthy_ex.R
Original file line number Diff line number Diff line change
@@ -1,63 +1,76 @@
library(tidyverse)
library(clock)

healthr <- healthyR.data::healthyR_data |>
mutate(
date_of_service = as_date(visit_end_date_time),
visit_end_date_time = NULL
) |>
select(
patient_id = mrn,
date_of_service,
reframe(
pid = mrn,
dos = as_date(visit_end_date_time),
visit_end_date_time = NULL,
payer = payer_grouping,
charges = total_charge_amount,
adjustment = total_adjustment_amount,
payment = total_payment_amount,
balance = total_amount_due
) |>
arrange(patient_id) |>
group_by(patient_id) |>
mutate(visit = row_number(),
.after = patient_id) |>
#------ If you alter the years, you'll
# have to redo this visit calculation:
arrange(pid) |>
group_by(pid) |>
mutate(
visit = row_number(),
.after = pid
) |>
ungroup() |>
arrange(date_of_service) |>
arrange(dos) |>
#------ End
mutate(
payer = case_match(
payer,
# "Blue Cross"
# "Medicaid"
# "Commercial"
"Medicare A" ~ "Medicare Part A",
"Medicaid HMO" ~ "Anthem",
# "Blue Cross"
"Medicare B" ~ "Medicare Part B",
"Medicare HMO" ~ "Aetna",
"HMO" ~ "Humana",
# "Medicaid"
# "Commercial"
"Self Pay" ~ "Patient",
"Compensation" ~ "Worker's Comp",
"Exchange Plans" ~ "Medicare Advantage",
c("No Fault", "?") ~ "Commercial",
.default = payer
),
payer = forcats::as_factor(payer))
payer = as_factor(payer))

healthr |>
mutate(
date_of_service = add_years(date_of_service, 4, invalid = "previous"),
dos = add_years(dos, 4, invalid = "previous"),
closed = if_else(balance == 0, TRUE, FALSE),
date_of_service = case_when(
year(date_of_service) < 2015 & !closed ~ add_years(date_of_service, 8, invalid = "previous"),
year(date_of_service) < 2016 & !closed ~ add_years(date_of_service, 7, invalid = "previous"),
year(date_of_service) < 2017 & !closed ~ add_years(date_of_service, 6, invalid = "previous"),
year(date_of_service) < 2018 & !closed ~ add_years(date_of_service, 5, invalid = "previous"),
year(date_of_service) < 2019 & !closed ~ add_years(date_of_service, 4, invalid = "previous"),
year(date_of_service) < 2020 & !closed ~ add_years(date_of_service, 3, invalid = "previous"),
year(date_of_service) < 2021 & !closed ~ add_years(date_of_service, 2, invalid = "previous"),
TRUE ~ date_of_service
dos = case_when(
year(dos) < 2015 & !closed ~ add_years(dos, 8, invalid = "previous"),
year(dos) < 2016 & !closed ~ add_years(dos, 7, invalid = "previous"),
year(dos) < 2017 & !closed ~ add_years(dos, 6, invalid = "previous"),
year(dos) < 2018 & !closed ~ add_years(dos, 5, invalid = "previous"),
year(dos) < 2019 & !closed ~ add_years(dos, 4, invalid = "previous"),
year(dos) < 2020 & !closed ~ add_years(dos, 3, invalid = "previous"),
year(dos) < 2021 & !closed ~ add_years(dos, 2, invalid = "previous"),
dos >= clock::date_today("") ~ add_years(dos, -1, invalid = "previous"),
.default = dos
),
date_of_reconciliation = if_else(closed, add_months(date_of_service, 3, invalid = "previous"), NA_Date_),
days_in_ar = if_else(closed, as.integer(date_of_reconciliation - date_of_service), NA_integer_),
today = clock::date_today("")
) |>
count_days(start = date_of_service, end = today, name = "days") |>
date_recon = if_else(closed, add_months(dos, 3, invalid = "previous"), NA),
date_recon = case_when(
date_recon >= clock::date_today("") ~ add_years(dos, -1, invalid = "previous")),
days_in_ar = if_else(closed, as.integer(date_recon - dos), NA)
) |>
fuimus::count_days(
start = dos,
end = today,
name = "days"
) |>
dplyr::arrange(dplyr::desc(dos))

filter(closed == FALSE) |>
group_by(year = year(date_of_service)) |>
group_by(year = year(dos)) |>
count(payer) |>
print(n = 200)

Expand Down
44 changes: 44 additions & 0 deletions data-raw/nmooresheets.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
library(googlesheets4)
library(tidyverse)
library(janitor)
library(clock)

nm_collect <- read_sheet(
"1LM9juL8JJ1zihOMGV6kMeJ9Fe1tpmf9ka-thYpiWzt0",
sheet = "Collections") |>
clean_names()

nm_em <- read_sheet(
"1LM9juL8JJ1zihOMGV6kMeJ9Fe1tpmf9ka-thYpiWzt0",
sheet = "EM") |>
clean_names()

nm_reimburse <- read_sheet(
"1LM9juL8JJ1zihOMGV6kMeJ9Fe1tpmf9ka-thYpiWzt0",
sheet = "Reimbursement") |>
clean_names()

nm_refer <- read_sheet(
"1LM9juL8JJ1zihOMGV6kMeJ9Fe1tpmf9ka-thYpiWzt0",
sheet = "LastReferral") |>
clean_names()


# Nate Moore Medicare Rate Example
#
nm_rate <- dplyr::tibble(
payer = c("Aetna", "BCBS", "Cigna", "United", "Humana", "Anthem", "Centene"),
rate = c(1.31, 1.3, 1.1, 1.68, 1.66, 1.55, 1.48),
rvus = c(8100, 6000, 5700, 4000, 1990, 1000, 799))

ggplot(nm_rate, aes(x = rvus, y = rate)) +
geom_point() +
stat_smooth(method = "lm", se = FALSE, fullrange = TRUE, color = "red", span = 1, n = 100) +
ggrepel::geom_label_repel(aes(label = paste0(rate * 100, "%, ", format(rvus, big.mark = ","), " RVUs"))) +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
scale_x_continuous(labels = scales::comma) +
labs(
title = "Percentage of Reimbursement Compared to RVU Volume",
x = "RVU Volume",
y = "Rate as A Pct% of Medicare Reimbursement") +
ggthemes::theme_fivethirtyeight()
14 changes: 14 additions & 0 deletions vignettes/articles/aging-of_accounts.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,17 @@ 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.)


## Percentage of Accounts Receivable Beyond X Days (PARBX)

PARBX resolves the sensitivity issues of the DAR metric. It offers a simple billing process metric that’s not dependent on the charge. Its graphic representation has a skewed bell shape (Figures 12 and 13.) Its steepness represents billing process quality; a steep curve and thin tail mean a healthy billing process, while a flat bell and fat tail also mean billing problems. According to the MGMA survey, 25%-35% of the average family practice’s accounts receivables were more than 120 days old in 1997. This number has improved down to 17.7% in 2004.

## Patient Liability

The percentage of patient liability is the ratio of patient responsibility to total billed charges, and it roughly reflects patient deductibles. This metric is important in measuring front office function because it has little to do with clean claim submission or effective follow-up.


## First-Pass Pay (FPP) Rate and Denial Rate

The FPP Rate is the percentage of claims paid in full upon the first submission (subject to federal or state timely payment regulations; most state laws require 15 days for electronic submission and 30 days for paper submission.) The denial rate is the complementary metric to the FPP rate. It counts the percentage of claims that require follow-up and therefore cost more to process. Follow-up may take the form of a phone call to the payer to discover a lost claim, receive an interpretation of a denial message, correct earlier submitted data, resubmit the original claim, and consult with the provider and medical note, or denial appeal. Both FPP and denial rates are very important metrics often used for billing process improvement. The upside of the FPP/denial metric is that it’s not affected by the charge, but its downside is that it hides the differences between process imperfections on the claim submission and claim payment sides. To identify patterns of problem CPT codes or payers, the FPP/denial metric needs to be computed and compared across all payer-CPT code pairs, which is a standard feature for modern billing technologies.
8 changes: 8 additions & 0 deletions vignettes/articles/dar-formulas.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ Days in Accounts Receivable (also known as Days in AR or simply DAR) is a common

As its name implies, the unit of measurement employed by this particular metric is days, or rather the average number of days from the moment a physician provides a service until the patient or guarantor pays for that service. This number can tell you much about the financial health of the business.

Days in Accounts Receivable (DAR)
A growing number of days in AR is symptomatic of a faulty billing process. One way to determine DAR is to count days from the date of service to the date of payment for every claim, and then average that across all claims. A simpler way to compute average days in AR is by taking a ratio of AR to average daily charges, or

Days in AR = (Accounts Receivable / Average Charge) x 365

This metric also depends on the medical specialty, patient demographics, payer mix, and CPT sample. Another downside is that this metric is sensitive to the provider because it counts the lag time of unsubmitted claims for services already delivered. This lag time roughly averages across all payers, making DAR an effective comparison metric between payers for an individual provider, but invalidating it across multiple providers. One obvious advantage of the DAR metric is its independence of charges. The averaging feature eliminates sensitivity to a specific day or CPT, but it also hides the behavior shape of the AR curve.


The formula for calculating $dar$ is:

${dar} = \dfrac {earb} {{gct} \div {ndip}}$
Expand Down
50 changes: 28 additions & 22 deletions vignettes/getting-started.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -471,26 +471,32 @@ binned |>
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") |>
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()
## Nathan Moore Medicare Reimbursement Rate Example

```{r message=FALSE, warning=FALSE}
nm_rate <- dplyr::tibble(
payer = c("Aetna", "BCBS", "Cigna", "United", "Humana", "Anthem", "Centene"),
rate = c(1.31, 1.3, 1.1, 1.68, 1.66, 1.55, 1.48),
rvus = c(8100, 6000, 5700, 4000, 1990, 1000, 799),
desc = c(paste0(rate * 100, "%, ", format(rvus, big.mark = ","), " RVUs"))
)
ggplot(nm_rate, aes(x = rvus, y = rate)) +
ggforce::geom_mark_circle(aes(fill = payer, label = payer, description = desc)) +
geom_point() +
geom_smooth(method = lm,
formula = y ~ x,
se = FALSE,
color = "red",
linetype = "dashed",
linewidth = 1.5,
alpha = 0.5) +
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
scale_x_continuous(labels = scales::comma) +
ggthemes::theme_fivethirtyeight() +
labs(
title = "Percentage of Reimbursement Compared to RVU Volume",
x = "RVU Volume",
y = "Rate as A Pct% of Medicare Reimbursement") +
theme(legend.position = "none")
```

0 comments on commit b51d565

Please sign in to comment.