R/cheapcombfun.R

Defines functions CheapCombFun

Documented in CheapCombFun

#' A function to calculate the cheapest drug combination given a data.frame with combination and prices
#' @param target The target dose
#' @param combi A complete dataframe with combinations and prices
#' @param total_max A maximum of possible combinations. Default = 10
#' @param only_total If `TRUE`, only display the total costs. If `FALSE`, return all information.
#' @param vial_share If `TRUE`, vial share is allowed.
#' @keywords CADTH
#' @export CheapCombFun
#' @examples
#' # Data frame of possible doses and prices per dose
#' df_combi <- data.frame(drug = rep(LETTERS[1], 3),
#'                        dose = c(10, 20, 50),
#'                        price_dose = c(11.39, 20, 56.96))
#'
#' # Return only total costs of cheapest option
#' tatooheen::CheapCombFun(target = 92.5, combi = df_combi, only_total = T)
#'
#' # Return more information as a list
#' res <- tatooheen::CheapCombFun(target = 92.5, combi = df_combi, only_total = F)

CheapCombFun <- function(target,
                         combi,
                         total_max = 10,
                         only_total = TRUE,
                         vial_share = FALSE){

        if(vial_share){
                p_dose <- combi %>%
                        mutate(p_dose = price_dose / dose) %>%
                        summarise(min = min(p_dose)) %>%
                        pull

                p_dose * target
        }else{
                # Silence dplyr summarise information
                options(dplyr.summarise.inform = FALSE)

                # Section I: Loading required packages ####
                require("RcppAlgos")
                require("tidyverse")

                # Section II: preparing function input
                # Define number from which to choose and order them if necessary
                possible_mg <- sort(combi$dose)

                # Define target value
                target_mg <- target

                # Define new target if needed

                new_target <- ifelse(any(target_mg / possible_mg <= 1),
                                     target_mg,
                                     min(ceiling(target_mg / possible_mg) * possible_mg))

                # Stop if target can never be reached with total_max
                if(new_target / max(possible_mg) > total_max) stop("Target cannot be reached with total_max.")

                # Define maximum of combinations based on the combining the smallest unit to reach target
                max_combs <- ceiling(min(new_target / min(possible_mg), total_max))

                try(if(max_combs %% 1 !=0) stop("AllCombFunFast: Minimum combination is not multiple of target. Check if target can be reached without decimals."))

                try(if(max_combs > total_max) stop("AllCombFunFast: Combination may not be reachable with setting for total_max."))

                # Section III: find all possible combinations ####
                # Make matrix with all possible combinations of possible mgs
                all_combs <- comboGeneral(c(0, possible_mg), max_combs, TRUE)

                # Only keep colums that reach target
                reduced_combs <- all_combs[rowSums(all_combs) >= new_target, , drop = FALSE] # to prevent from vectorising if only one combination is possible


                # Keep only unique combinations
                unique_combs <- t(reduced_combs)
                colnames(unique_combs) <- paste0("combi_", 1:ncol(unique_combs))

                # Convert to data frame for easy use
                df_combis <- unique_combs %>% as.data.frame() %>%
                        pivot_longer(everything(),
                                     names_to = "combi",
                                     values_to = "dose") %>%
                        filter(dose > 0) %>%
                        group_by(combi, dose) %>%
                        summarise(freq = n()) %>%
                        ungroup()

                # Create cost df
                df_cost <- left_join(df_combis, combi, by = "dose") %>%
                        mutate(cost_dosing = freq * price_dose) %>%
                        group_by(combi) %>%
                        mutate(cost_combi = sum(cost_dosing)) %>%
                        ungroup %>%
                        mutate(cost_rank = min_rank(cost_combi))

                # Select cheapest option for reference df
                df_cheapest <- df_cost %>%
                        filter(cost_rank == 1)

                # Store results
                ifelse(only_total,
                       res <- df_cheapest[[1, "cost_combi"]],
                       res <- list(unique_combis = unique_combs,
                                   df_combis = df_combis,
                                   df_cost = df_cost,
                                   df_cheapest = df_cheapest,
                                   waste = new_target - target))
                res
        }
}





#
#
# xx <- CheapCombFun(target = 15,
#              combi = df_tx_prices[df_tx_prices$inn == "bortezomib", ], only_total = T,
#              vial_share = F)
# xx
# xx$df_cost %>%
#   mutate(p_dose = price_dose / dose) %>%
#   arrange(p_dose)
#
#
# res_doxo$df_cost %>%
#   group_by(combi) %>%
#   summarise(d = sum(dose * freq))
# es_doxo$df_cheapest %>% mutate(dose * freq)
#
#
# p_dose <- combi %>%
#   mutate(p_dose = price_dose / dose) %>%
#   summarise(min = min(p_dose)) %>%
#   pull
fthielen/tatooheen documentation built on Nov. 8, 2023, 4:37 a.m.