Drawing 60 minute isochrones around each transplant center
The following function uses the mapboxapi package to calculate 60-minute travel isochrones around each transplant center.
The function assumes the that you have an API token configured in R. For details of how to do this, see the package documentation.
Its inputs are as follows:
SFObject: This is an object of class sf that contains the location of each transplant center
year: year as a character. Must be a year in yearlist defined in R/setup.R.
Click to show/hide R Code
#Define a function that takes an SF data object and returns 60 minute isochronesset_60_min_isochrone_at_7AM<-function(SFObject, year) {if (nrow(SFObject) ==0) {return( SFObject %>%st_transform(5070) ) }else{ tempDF<-SFObject%>%mutate(TimeZone=tz_lookup(., method="accurate"))%>%st_transform(4326) FinalDF <-vector("list", length(timezones))for(i in1:length(timezones)) { TZ<-converted_times[[year]][[i,1]] UTCTime<-format(converted_times[[year]][[i,3]], "%Y-%m-%dT%H:%M:%SZ")if (nrow(filter(tempDF,TimeZone==TZ))>0) { temp<-tempDF%>%filter(TimeZone==TZ)%>%mb_isochrone(time =60, profile ="driving-traffic",depart_at = UTCTime,id_column ="OTCCode")%>%rename(OTCCode=id) FinalDF[[i]]<-temp } } FinalDF%>%bind_rows()%>%st_transform(5070)%>%return() }}
Converting data frame of summary results into a gt format table
This function takes a summary results data frame and formats it into a publication-ready table using the gt package. The input data are assumed to contain one row per characteristic and year, with separate columns for the numerator, denominator, and percentage of the population meeting the specified access criterion.
The function first reshapes the data from long to wide format so that each study year appears as a grouped set of columns. It then converts the Characteristic column into a factor with a user-specified ordering and labeling, ensuring that rows appear in a meaningful and reproducible sequence rather than default alphabetical order.
After restructuring the data, the function applies a consistent gt table layout. Columns are grouped under year-specific spanner headers, counts are displayed with thousands separators, and percentages are rounded to one decimal place. The table title is supplied as an argument, allowing the same function to be reused across multiple result tables.
To improve readability, the function also applies subtle background shading to distinguish columns belonging to different years and adds vertical borders between year groupings. The result is a standardized comparative table that can be reused throughout the project for presenting access estimates across organs, center classifications, and distance definitions.
This function also makes repeated use of purrr::reduce(), which is a functional programming tool for applying the same transformation cumulatively across a sequence of values. Here, the sequence is the set of study years. Each iteration takes the current gt table and adds another layer of formatting, such as a year-specific spanner, fill color, or border. This avoids repetitive code and makes the table-building process both more compact and more generalizable to analyses involving different numbers of years.
Click to show/hide R Code
convert_resultsdf_to_table<-function(resultsdf, year_list_fn=year_list, row_values, row_labels, table_title){ resultsdf%>%#Reshape data setpivot_wider(names_from = Year,values_from =c(Numerator, Denominator, Percentage),names_vary ="slowest" )%>%#Use the table defined above to turn Characteristic into a factor variablemutate(Characteristic=factor(Characteristic,levels = row_values,labels = row_labels))%>%arrange(Characteristic)%>%#Convert to a gt format tablegt()%>%#Groups columns by year with a spanner label { purrr::reduce( year_list_fn,~ .x %>%tab_spanner(label = .y,columns =matches(paste0("_", .y, "$")) ),.init = . ) } %>%#Sets coluns to round to 0 decimal places for numbers and 1 decimal place for percentagesfmt_number(columns =matches(paste0("Numerator_|Denominator_")),decimals =0,use_seps =TRUE ) %>%fmt_number(columns =matches("Percentage_"),decimals =1 )%>%tab_header(title = table_title )%>%#Removes "_year" from columnscols_label_with(~"Numerator", columns =matches("^Numerator_")) %>%cols_label_with(~"Denominator", columns =matches("^Denominator_")) %>%cols_label_with(~"Percentage", columns =matches("^Percentage_"))%>%#Shades columns by year for easier reading { purrr::reduce(seq_along(year_list_fn),~ .x %>%tab_style(style =cell_fill(color =colorRampPalette(c("lightgray","lightblue"))(length(year_list_fn) )[.y] ),locations =cells_body(columns =matches(paste0("_", year_list_fn[.y], "$")) ) ),.init = . ) }%>%#Add separators between years { purrr::reduce(seq_len(length(year_list_fn) -1),~ .x %>%tab_style(style =cell_borders(sides ="right",weight =px(2),color ="black" ),locations =cells_body(columns =matches(paste0("Percentage_", year_list_fn[.y], "$")) ) ) %>%tab_style(style =cell_borders(sides ="left",weight =px(2),color ="black" ),locations =cells_body(columns =matches(paste0("Numerator_", year_list_fn[.y +1], "$")) ) ),.init = . ) }}}
Creates paired plots of maps with percentages in the plot
This function creates side-by-side map panels comparing geographic access in two different years, while also embedding the corresponding summary percentages directly into the plot titles. For a given organ, distance definition, and center category, the function pulls the relevant summary statistics from the precomputed results tables and combines them with the geographic buffer objects used to draw the maps. This allows each figure to show both the spatial distribution of access and the headline percentage values in a single visual.
The function is designed to compare two years, by default 2017 and 2022. For each year, it overlays transplant center catchment areas in red on a national base map, while blue dots represent the estimated distribution of people living with HIV. The title above each panel reports the percentage, numerator, and denominator for the selected outcome, allowing the reader to interpret the geographic pattern alongside the underlying quantitative summary.
An optional argument, include_nonHIV, allows the figure title to also display the corresponding results for people without HIV. This makes it possible to present HIV and non-HIV comparisons within the same figure when desired, while keeping the map itself visually simple. The function also performs basic checks to ensure that the requested years and outcome labels are valid and that the buffer object supplied is likely to match the intended analysis.
Finally, the two maps are combined into a single annotated figure using cowplot, with a shared title and a brief explanatory note. The result is a presentation-style comparative graphic that is useful for showing changes over time in transplant center catchment areas and in the proportion of people with HIV who live within those areas.
Click to show/hide R Code
make_paired_plot<-function(organ, distance, outcome, buffer_list,year1="2017",year2="2022", plottitle,include_nonHIV=FALSE){# ----Error checking----# Validate include_nonHIVif (!is.logical(include_nonHIV) ||length(include_nonHIV) !=1||is.na(include_nonHIV)) {stop("include_nonHIV must be a single TRUE or FALSE value.") }#Access name of object passed to `buffer_list` buffer_name <-deparse(substitute(buffer_list))#Make sure name of outcome matches buffer_listif (!stringr::str_detect( stringr::str_to_lower(buffer_name), stringr::str_to_lower(outcome) )) {warning(glue::glue("Buffer object ('{buffer_name}') may not match outcome '{outcome}'." )) }if (!year1 %in% year_list) {stop(glue::glue("year1 ({year1}) is not in year_list.")) }if (!year2 %in% year_list) {stop(glue::glue("year2 ({year2}) is not in year_list.")) }if (!outcome %in%c("active", "HIV", "HOPE")) {stop(glue::glue("outcome ({outcome}) is not in c('active', 'HIV', 'HOPE').")) }# ----Main function----#Keep only elements of Results_HIV_df necessary for plot summary_df_HIV<-Results_HIV_df[[organ]]%>%filter(str_detect(Characteristic, outcome))%>%filter(str_detect(Characteristic, distance))#parameters of plot assigned parameter_plot<-list() parameter_plot$y_title<-0.9 parameter_plot$y_footnote<-0.1if (include_nonHIV) { parameter_plot$y_title<-0.97 parameter_plot$y_footnote<-0.01 }#Keep only elements of Results_nonHIV_df necessary for plot, if even needed in the first placeif (include_nonHIV) { summary_df_nonHIV<-Results_nonHIV_df[[organ]]%>%filter(str_detect(Characteristic, outcome))%>%filter(str_detect(Characteristic, distance)) }#Creates text for nonHIV (is NULL if ) title_text_nonHIV<-list()if (include_nonHIV==TRUE) { title_text_nonHIV[[year1]]<-glue("<br>","<span style='font-size:30pt; font-weight:normal;'>HIV negative:</span> ","<span style='font-size:30pt; font-weight:bold;'>","{summary_df_nonHIV$Percentage[summary_df_nonHIV$Year == as.numeric(year1)]}%","</span>","<br>","{comma(summary_df_nonHIV$Numerator[summary_df_nonHIV$Year == as.numeric(year1)])}"," / ","{comma(summary_df_nonHIV$Denominator[summary_df_nonHIV$Year == as.numeric(year1)])}") title_text_nonHIV[[year2]]<-glue("<br>","<span style='font-size:30pt; font-weight:normal;'>HIV negative:</span> ","<span style='font-size:30pt; font-weight:bold;'>","{summary_df_nonHIV$Percentage[summary_df_nonHIV$Year == as.numeric(year2)]}%","</span>","<br>","{comma(summary_df_nonHIV$Numerator[summary_df_nonHIV$Year == as.numeric(year2)])}"," / ","{comma(summary_df_nonHIV$Denominator[summary_df_nonHIV$Year == as.numeric(year2)])}") }else { title_text_nonHIV[[year1]]<-"" title_text_nonHIV[[year2]]<-"" }#Left graph Plot1<-ggplot() +geom_sf(data = county_dots_transformed[[year1]], size =0.3, color ="navy", alpha =0.5) +theme_void() +theme(axis.title.y =element_text(size =36, angle =90, vjust =0.5),plot.title =element_textbox(size =16, hjust =0.5,halign =0.5) )+labs(y=year1,title =glue("<span style='font-size:30pt; font-weight:normal;'>PLWH:</span> ","<span style='font-size:30pt; font-weight:bold;'>","{summary_df_HIV$Percentage[summary_df_HIV$Year == as.numeric(year1)]}%","</span>","<br>","{comma(summary_df_HIV$Numerator[summary_df_HIV$Year == as.numeric(year1)])}"," / ","{comma(summary_df_HIV$Denominator[summary_df_HIV$Year == as.numeric(year1)])}", title_text_nonHIV[[year1]] ))+geom_sf(data=buffer_list[[organ]][[distance]][[year1]], color="red", fill=NA)+geom_sf(data=states_sf_transformed, fill=NA)#Right plot Plot2<-ggplot() +geom_sf(data = county_dots_transformed[[year2]], size =0.3, color ="navy", alpha =0.5) +theme_void() +theme(axis.title.y =element_text(size =36, angle =90, vjust =0.5),plot.title =element_textbox(size =16, hjust =0.5,halign =0.5) )+labs(y=year2,title =glue("<span style='font-size:30pt; font-weight:normal;'>PLWH:</span> ","<span style='font-size:30pt; font-weight:bold;'>","{summary_df_HIV$Percentage[summary_df_HIV$Year == as.numeric(year2)]}%","</span>","<br>","{comma(summary_df_HIV$Numerator[summary_df_HIV$Year == as.numeric(year2)])}"," / ","{comma(summary_df_HIV$Denominator[summary_df_HIV$Year == as.numeric(year2)])}", title_text_nonHIV[[year2]] ))+geom_sf(data=buffer_list[[organ]][[distance]][[year2]], color="red", fill=NA)+geom_sf(data=states_sf_transformed, fill=NA)#Combine plots combined<-cowplot::plot_grid(Plot1, Plot2,ncol =2, align ="hv") final_plot <-ggdraw() +draw_plot(combined) +draw_label( plottitle,x =0.5, y = parameter_plot$y_title, # top centerhjust =0.5, vjust =1, # anchor to the top edgefontface ='bold',size =25 )+# draw_label("Each blue dot represents 100 people with HIV\nRed outlines represent the catchment area for each transplant center",x =0.02, y = parameter_plot$y_footnote, # left margin positionhjust =0, vjust =0, # align text to the left edgesize =14,fontface ="italic" )return(final_plot)}
Plot the number of people with HIV in the catchment area of each transplant center
This function uses the ggbeeswarm package to make GraphPad Prism-type plots comparing the number of people living with HIV in the catchment area of each transplant center in the years specified.
Click to show/hide R Code
plot_center_HIV_catchment<-function(organ, distance, buffer_list,year1="2017",year2="2022"){# ----Error checking----#Access name of object passed to `buffer_list` buffer_name <-deparse(substitute(buffer_list))if (!year1 %in% year_list) {stop(glue::glue("year1 ({year1}) is not in year_list.")) }if (!year2 %in% year_list) {stop(glue::glue("year2 ({year2}) is not in year_list.")) } temp_DF<-list() temp_DF[[year1]]<-buffer_list%>%pluck(year1)%>%st_drop_geometry()%>%group_by(OTCCode)%>%summarize(total_cases=round(sum(tract_cases), 0))%>%mutate(year=as.numeric(year1))%>%mutate(Center_classification=case_when( OTCCode %in% HOPE_center_volumes[[organ]][[year1]]$REC_CTR_CD~"HOPE center", OTCCode %in% HIV__center_volumes[[organ]][[year1]]$REC_CTR_CD~"≥1 HIV R+ transplants", OTCCode %in% Active_tx_centers[[organ]][[year1]]~"No HIV R+ transplants" ))%>%filter(!is.na(OTCCode)) temp_DF[[year2]]<-buffer_list%>%pluck(year2)%>%st_drop_geometry()%>%group_by(OTCCode)%>%summarize(total_cases=round(sum(tract_cases), 0))%>%mutate(year=as.numeric(year2))%>%mutate(Center_classification=case_when( OTCCode %in% HOPE_center_volumes[[organ]][[year2]]$REC_CTR_CD~"HOPE center", OTCCode %in% HIV__center_volumes[[organ]][[year2]]$REC_CTR_CD~"≥1 HIV R+ transplants", OTCCode %in% Active_tx_centers[[organ]][[year2]]~"No HIV R+ transplants" ))%>%filter(!is.na(OTCCode)) combined_DF<-bind_rows(temp_DF)%>%mutate(Center_classification=factor(Center_classification,levels=c("HOPE center","≥1 HIV R+ transplants", "No HIV R+ transplants"),labels=c("HOPE center","≥1 HIV R+ transplants", "No HIV R+ transplants") ))%>%mutate(Center_classification_color=case_when( OTCCode %in% HOPE_center_volumes[[organ]][[year1]]$REC_CTR_CD~"HOPE center", OTCCode %in% HIV__center_volumes[[organ]][[year1]]$REC_CTR_CD~"≥1 HIV R+ transplants", OTCCode %in% Active_tx_centers[[organ]][[year1]]~"No HIV R+ transplants",TRUE~glue("No transplants in {year1}") )) combined_DF %>%ggplot()+geom_violin(mapping =aes(x=Center_classification, y=total_cases), color =NA, alpha=0.5,fill ="grey", draw_quantiles =1)+stat_summary(mapping =aes(x=Center_classification, y=total_cases),fun = median, fun.min = median, fun.max = median, geom ="crossbar", width =0.9, fatten=1.5)+geom_quasirandom(mapping =aes(x=Center_classification, y=total_cases, color=Center_classification_color), cex=2, alpha=0.7)+stat_compare_means(method ="kruskal.test",mapping =aes(x = Center_classification, y = total_cases)) +scale_y_continuous(labels = scales::comma)+guides(color =guide_legend(title.position ="top", nrow =1))+theme_classic()+theme(legend.position ="bottom",legend.title =element_text(hjust =0.5), # centers titlelegend.box ="horizontal")+theme(axis.text.x =element_text(angle =45,hjust =1,vjust =1))+facet_grid(.~year)+labs(x ="Center HIV R+ volume", y="PLWH in catchment area", color=glue("{year1} status"))+scale_color_discrete(breaks =c("HOPE center","≥1 HIV R+ transplants", "No HIV R+ transplants",glue("No transplants in {year1}")) )}
---title: "Functions"format: html---The code in this script performs a number of useful tasks required for the analysis.::: {.Rcode title="Source code"}The full R script is available at:- [`R/functions.R`](https://github.com/VagishHemmige/HOPE-CDC-analysis-2026/blob/master/R/functions.R)This R script file is itself reliant on the following helper files:- [`R/setup.R`](https://github.com/VagishHemmige/HOPE-CDC-analysis-2026/blob/master/R/setup.R):::## Drawing 60 minute isochrones around each transplant centerThe following function uses the [`mapboxapi` package](https://walker-data.com/mapboxapi/) to calculate 60-minute travel isochrones around eachtransplant center.The function assumes the that you have an API token configured in R. For details of how to do this, see the package documentation.Its inputs are as follows:- `SFObject`: This is an object of class `sf` that contains the location of each transplant center- `year`: year as a character. Must be a year in `yearlist` defined in `R/setup.R`.```{r, eval=FALSE}#Define a function that takes an SF data object and returns 60 minute isochronesset_60_min_isochrone_at_7AM<-function(SFObject, year) { if (nrow(SFObject) == 0) { return( SFObject %>% st_transform(5070) ) }else{ tempDF<-SFObject%>% mutate(TimeZone=tz_lookup(., method="accurate"))%>% st_transform(4326) FinalDF <- vector("list", length(timezones)) for(i in 1:length(timezones)) { TZ<-converted_times[[year]][[i,1]] UTCTime<-format(converted_times[[year]][[i,3]], "%Y-%m-%dT%H:%M:%SZ") if (nrow(filter(tempDF,TimeZone==TZ))>0) { temp<-tempDF%>% filter(TimeZone==TZ)%>% mb_isochrone(time = 60, profile = "driving-traffic", depart_at = UTCTime, id_column = "OTCCode")%>% rename(OTCCode=id) FinalDF[[i]]<-temp } } FinalDF%>% bind_rows()%>% st_transform(5070)%>% return() }}```## Converting data frame of summary results into a `gt` format tableThis function takes a summary results data frame and formats it into a publication-ready table using the `gt` package. The input data are assumed to contain one row per characteristic and year, with separate columns for the numerator, denominator, and percentage of the population meeting the specified access criterion.The function first reshapes the data from long to wide format so that each study year appears as a grouped set of columns. It then converts the `Characteristic` column into a factor with a user-specified ordering and labeling, ensuring that rows appear in a meaningful and reproducible sequence rather than default alphabetical order.After restructuring the data, the function applies a consistent `gt` table layout. Columns are grouped under year-specific spanner headers, counts are displayed with thousands separators, and percentages are rounded to one decimal place. The table title is supplied as an argument, allowing the same function to be reused across multiple result tables.To improve readability, the function also applies subtle background shading to distinguish columns belonging to different years and adds vertical borders between year groupings. The result is a standardized comparative table that can be reused throughout the project for presenting access estimates across organs, center classifications, and distance definitions.This function also makes repeated use of `purrr::reduce()`, which is a functional programming tool for applying the same transformation cumulatively across a sequence of values. Here, the sequence is the set of study years. Each iteration takes the current `gt` table and adds another layer of formatting, such as a year-specific spanner, fill color, or border. This avoids repetitive code and makes the table-building process both more compact and more generalizable to analyses involving different numbers of years.```{r, eval=FALSE}convert_resultsdf_to_table<-function(resultsdf, year_list_fn=year_list, row_values, row_labels, table_title){ resultsdf%>% #Reshape data set pivot_wider( names_from = Year, values_from = c(Numerator, Denominator, Percentage), names_vary = "slowest" )%>% #Use the table defined above to turn Characteristic into a factor variable mutate(Characteristic=factor(Characteristic, levels = row_values, labels = row_labels))%>% arrange(Characteristic)%>% #Convert to a gt format table gt()%>% #Groups columns by year with a spanner label { purrr::reduce( year_list_fn, ~ .x %>% tab_spanner( label = .y, columns = matches(paste0("_", .y, "$")) ), .init = . ) } %>% #Sets coluns to round to 0 decimal places for numbers and 1 decimal place for percentages fmt_number( columns = matches(paste0("Numerator_|Denominator_")), decimals = 0, use_seps = TRUE ) %>% fmt_number( columns = matches("Percentage_"), decimals = 1 )%>%tab_header( title = table_title )%>% #Removes "_year" from columns cols_label_with(~ "Numerator", columns = matches("^Numerator_")) %>% cols_label_with(~ "Denominator", columns = matches("^Denominator_")) %>% cols_label_with(~ "Percentage", columns = matches("^Percentage_"))%>% #Shades columns by year for easier reading { purrr::reduce( seq_along(year_list_fn), ~ .x %>% tab_style( style = cell_fill( color = colorRampPalette(c("lightgray","lightblue"))( length(year_list_fn) )[.y] ), locations = cells_body( columns = matches(paste0("_", year_list_fn[.y], "$")) ) ), .init = . ) }%>% #Add separators between years { purrr::reduce( seq_len(length(year_list_fn) - 1), ~ .x %>% tab_style( style = cell_borders( sides = "right", weight = px(2), color = "black" ), locations = cells_body( columns = matches(paste0("Percentage_", year_list_fn[.y], "$")) ) ) %>% tab_style( style = cell_borders( sides = "left", weight = px(2), color = "black" ), locations = cells_body( columns = matches(paste0("Numerator_", year_list_fn[.y + 1], "$")) ) ), .init = . ) }}}```## Creates paired plots of maps with percentages in the plotThis function creates side-by-side map panels comparing geographic access in two different years, while also embedding the corresponding summary percentages directly into the plot titles. For a given organ, distance definition, and center category, the function pulls the relevant summary statistics from the precomputed results tables and combines them with the geographic buffer objects used to draw the maps. This allows each figure to show both the spatial distribution of access and the headline percentage values in a single visual.The function is designed to compare two years, by default 2017 and 2022. For each year, it overlays transplant center catchment areas in red on a national base map, while blue dots represent the estimated distribution of people living with HIV. The title above each panel reports the percentage, numerator, and denominator for the selected outcome, allowing the reader to interpret the geographic pattern alongside the underlying quantitative summary.An optional argument, `include_nonHIV`, allows the figure title to also display the corresponding results for people without HIV. This makes it possible to present HIV and non-HIV comparisons within the same figure when desired, while keeping the map itself visually simple. The function also performs basic checks to ensure that the requested years and outcome labels are valid and that the buffer object supplied is likely to match the intended analysis.Finally, the two maps are combined into a single annotated figure using `cowplot`, with a shared title and a brief explanatory note. The result is a presentation-style comparative graphic that is useful for showing changes over time in transplant center catchment areas and in the proportion of people with HIV who live within those areas.```{r, eval=FALSE}make_paired_plot<-function(organ, distance, outcome, buffer_list, year1="2017", year2="2022", plottitle, include_nonHIV=FALSE){ # ----Error checking---- # Validate include_nonHIV if (!is.logical(include_nonHIV) || length(include_nonHIV) != 1 || is.na(include_nonHIV)) { stop("include_nonHIV must be a single TRUE or FALSE value.") } #Access name of object passed to `buffer_list` buffer_name <- deparse(substitute(buffer_list)) #Make sure name of outcome matches buffer_list if (!stringr::str_detect( stringr::str_to_lower(buffer_name), stringr::str_to_lower(outcome) )) { warning(glue::glue( "Buffer object ('{buffer_name}') may not match outcome '{outcome}'." )) } if (!year1 %in% year_list) { stop(glue::glue("year1 ({year1}) is not in year_list.")) } if (!year2 %in% year_list) { stop(glue::glue("year2 ({year2}) is not in year_list.")) } if (!outcome %in% c("active", "HIV", "HOPE")) { stop(glue::glue("outcome ({outcome}) is not in c('active', 'HIV', 'HOPE').")) } # ----Main function---- #Keep only elements of Results_HIV_df necessary for plot summary_df_HIV<-Results_HIV_df[[organ]]%>% filter(str_detect(Characteristic, outcome))%>% filter(str_detect(Characteristic, distance)) #parameters of plot assigned parameter_plot<-list() parameter_plot$y_title<-0.9 parameter_plot$y_footnote<-0.1 if (include_nonHIV) { parameter_plot$y_title<-0.97 parameter_plot$y_footnote<-0.01 } #Keep only elements of Results_nonHIV_df necessary for plot, if even needed in the first place if (include_nonHIV) { summary_df_nonHIV<-Results_nonHIV_df[[organ]]%>% filter(str_detect(Characteristic, outcome))%>% filter(str_detect(Characteristic, distance)) } #Creates text for nonHIV (is NULL if ) title_text_nonHIV<-list() if (include_nonHIV==TRUE) { title_text_nonHIV[[year1]]<- glue("<br>", "<span style='font-size:30pt; font-weight:normal;'>HIV negative:</span> ", "<span style='font-size:30pt; font-weight:bold;'>", "{summary_df_nonHIV$Percentage[summary_df_nonHIV$Year == as.numeric(year1)]}%", "</span>", "<br>", "{comma(summary_df_nonHIV$Numerator[summary_df_nonHIV$Year == as.numeric(year1)])}", " / ", "{comma(summary_df_nonHIV$Denominator[summary_df_nonHIV$Year == as.numeric(year1)])}") title_text_nonHIV[[year2]]<- glue("<br>", "<span style='font-size:30pt; font-weight:normal;'>HIV negative:</span> ", "<span style='font-size:30pt; font-weight:bold;'>", "{summary_df_nonHIV$Percentage[summary_df_nonHIV$Year == as.numeric(year2)]}%", "</span>", "<br>", "{comma(summary_df_nonHIV$Numerator[summary_df_nonHIV$Year == as.numeric(year2)])}", " / ", "{comma(summary_df_nonHIV$Denominator[summary_df_nonHIV$Year == as.numeric(year2)])}") } else { title_text_nonHIV[[year1]]<-"" title_text_nonHIV[[year2]]<-"" } #Left graph Plot1<-ggplot() + geom_sf(data = county_dots_transformed[[year1]], size = 0.3, color = "navy", alpha = 0.5) + theme_void() + theme( axis.title.y = element_text(size = 36, angle = 90, vjust = 0.5), plot.title = element_textbox(size = 16, hjust = 0.5, halign = 0.5) )+ labs(y=year1, title = glue("<span style='font-size:30pt; font-weight:normal;'>PLWH:</span> ", "<span style='font-size:30pt; font-weight:bold;'>", "{summary_df_HIV$Percentage[summary_df_HIV$Year == as.numeric(year1)]}%", "</span>", "<br>", "{comma(summary_df_HIV$Numerator[summary_df_HIV$Year == as.numeric(year1)])}", " / ", "{comma(summary_df_HIV$Denominator[summary_df_HIV$Year == as.numeric(year1)])}", title_text_nonHIV[[year1]] ))+ geom_sf(data=buffer_list[[organ]][[distance]][[year1]], color="red", fill=NA)+ geom_sf(data=states_sf_transformed, fill=NA) #Right plot Plot2<-ggplot() + geom_sf(data = county_dots_transformed[[year2]], size = 0.3, color = "navy", alpha = 0.5) + theme_void() + theme( axis.title.y = element_text(size = 36, angle = 90, vjust = 0.5), plot.title = element_textbox(size = 16, hjust = 0.5, halign = 0.5) )+ labs(y=year2, title = glue("<span style='font-size:30pt; font-weight:normal;'>PLWH:</span> ", "<span style='font-size:30pt; font-weight:bold;'>", "{summary_df_HIV$Percentage[summary_df_HIV$Year == as.numeric(year2)]}%", "</span>", "<br>", "{comma(summary_df_HIV$Numerator[summary_df_HIV$Year == as.numeric(year2)])}", " / ", "{comma(summary_df_HIV$Denominator[summary_df_HIV$Year == as.numeric(year2)])}", title_text_nonHIV[[year2]] ))+ geom_sf(data=buffer_list[[organ]][[distance]][[year2]], color="red", fill=NA)+ geom_sf(data=states_sf_transformed, fill=NA) #Combine plots combined<-cowplot::plot_grid(Plot1, Plot2, ncol = 2, align = "hv") final_plot <- ggdraw() + draw_plot(combined) + draw_label( plottitle, x = 0.5, y = parameter_plot$y_title, # top center hjust = 0.5, vjust = 1, # anchor to the top edge fontface = 'bold', size = 25 )+ # draw_label( "Each blue dot represents 100 people with HIV\nRed outlines represent the catchment area for each transplant center", x = 0.02, y = parameter_plot$y_footnote, # left margin position hjust = 0, vjust = 0, # align text to the left edge size = 14, fontface = "italic" ) return(final_plot)}```## Plot the number of people with HIV in the catchment area of each transplant centerThis function uses the `ggbeeswarm` package to make GraphPad Prism-type plots comparing the number of people living with HIV in the catchment area of each transplant center in the years specified.```{r, eval=FALSE}plot_center_HIV_catchment<-function(organ, distance, buffer_list, year1="2017", year2="2022"){ # ----Error checking---- #Access name of object passed to `buffer_list` buffer_name <- deparse(substitute(buffer_list)) if (!year1 %in% year_list) { stop(glue::glue("year1 ({year1}) is not in year_list.")) } if (!year2 %in% year_list) { stop(glue::glue("year2 ({year2}) is not in year_list.")) } temp_DF<-list() temp_DF[[year1]]<-buffer_list%>% pluck(year1)%>% st_drop_geometry()%>% group_by(OTCCode)%>% summarize(total_cases=round(sum(tract_cases), 0))%>% mutate(year=as.numeric(year1))%>% mutate(Center_classification=case_when( OTCCode %in% HOPE_center_volumes[[organ]][[year1]]$REC_CTR_CD~"HOPE center", OTCCode %in% HIV__center_volumes[[organ]][[year1]]$REC_CTR_CD~"≥1 HIV R+ transplants", OTCCode %in% Active_tx_centers[[organ]][[year1]]~"No HIV R+ transplants" ))%>% filter(!is.na(OTCCode)) temp_DF[[year2]]<-buffer_list%>% pluck(year2)%>% st_drop_geometry()%>% group_by(OTCCode)%>% summarize(total_cases=round(sum(tract_cases), 0))%>% mutate(year=as.numeric(year2))%>% mutate(Center_classification=case_when( OTCCode %in% HOPE_center_volumes[[organ]][[year2]]$REC_CTR_CD~"HOPE center", OTCCode %in% HIV__center_volumes[[organ]][[year2]]$REC_CTR_CD~"≥1 HIV R+ transplants", OTCCode %in% Active_tx_centers[[organ]][[year2]]~"No HIV R+ transplants" ))%>% filter(!is.na(OTCCode)) combined_DF<-bind_rows(temp_DF)%>% mutate(Center_classification=factor(Center_classification, levels=c("HOPE center","≥1 HIV R+ transplants", "No HIV R+ transplants"), labels=c("HOPE center","≥1 HIV R+ transplants", "No HIV R+ transplants") ))%>% mutate(Center_classification_color=case_when( OTCCode %in% HOPE_center_volumes[[organ]][[year1]]$REC_CTR_CD~"HOPE center", OTCCode %in% HIV__center_volumes[[organ]][[year1]]$REC_CTR_CD~"≥1 HIV R+ transplants", OTCCode %in% Active_tx_centers[[organ]][[year1]]~"No HIV R+ transplants", TRUE~glue("No transplants in {year1}") )) combined_DF %>% ggplot()+ geom_violin(mapping = aes(x=Center_classification, y=total_cases), color = NA, alpha=0.5,fill = "grey", draw_quantiles = 1)+ stat_summary(mapping = aes(x=Center_classification, y=total_cases),fun = median, fun.min = median, fun.max = median, geom = "crossbar", width = 0.9, fatten=1.5)+ geom_quasirandom(mapping = aes(x=Center_classification, y=total_cases, color=Center_classification_color), cex=2, alpha=0.7)+ stat_compare_means(method = "kruskal.test", mapping = aes(x = Center_classification, y = total_cases)) + scale_y_continuous(labels = scales::comma)+ guides(color = guide_legend(title.position = "top", nrow = 1))+ theme_classic()+ theme(legend.position = "bottom", legend.title = element_text(hjust = 0.5), # centers title legend.box = "horizontal")+ theme(axis.text.x = element_text( angle = 45, hjust = 1, vjust = 1))+ facet_grid(.~year)+ labs(x = "Center HIV R+ volume", y="PLWH in catchment area", color=glue("{year1} status"))+ scale_color_discrete(breaks = c("HOPE center", "≥1 HIV R+ transplants", "No HIV R+ transplants", glue("No transplants in {year1}")) )}```