211 lines
5.6 KiB
R
211 lines
5.6 KiB
R
#' Make dumbbell chart.
|
|
#'
|
|
#' @param df A data frame.
|
|
#' @param col A numeric column.
|
|
#' @param group_x The grouping column on the x-axis; only two groups.
|
|
#' @param group_y The grouping column on the y-axis.
|
|
#' @param point_size Point size.
|
|
#' @param point_alpha Point alpha.
|
|
#' @param segment_size Segment size.
|
|
#' @param segment_color Segment color.
|
|
#' @param group_x_title X-group and legend title.
|
|
#' @param group_y_title Y-axis and group title.
|
|
#' @param x_title X-axis title.
|
|
#' @param title Title.
|
|
#' @param subtitle Subtitle.
|
|
#' @param caption Caption.
|
|
#' @param line_to_y_axis TRUE or FALSE; add a line connected points and Y-axis.
|
|
#' @param line_to_y_axis_type Line to Y-axis type.
|
|
#' @param line_to_y_axis_width Line to Y-axis width.
|
|
#' @param line_to_y_axis_color Line to Y-axis color.
|
|
#' @param add_text TRUE or FALSE; add text at the points.
|
|
#' @param add_text_vjust Vertical adjustment.
|
|
#' @param add_text_size Text size.
|
|
#' @param add_text_color Text color.
|
|
#' @param theme_fun A ggplot2 theme, default to `theme_dumbbell()`
|
|
#' @param scale_fill_fun A ggplot2 scale_fill function, default to `scale_fill_visualizer_discrete()`
|
|
#' @param scale_color_fun A ggplot2 scale_color function, default to `scale_color_visualizer_discrete()`
|
|
#'
|
|
#' @return A dumbbell chart.
|
|
#' @export
|
|
#'
|
|
dumbbell <- function(
|
|
df,
|
|
col,
|
|
group_x,
|
|
group_y,
|
|
point_size = 5,
|
|
point_alpha = 1,
|
|
segment_size = 2.5,
|
|
segment_color = color("light_blue_grey"),
|
|
group_x_title = NULL,
|
|
group_y_title = NULL,
|
|
x_title = NULL,
|
|
title = NULL,
|
|
subtitle = NULL,
|
|
caption = NULL,
|
|
line_to_y_axis = FALSE,
|
|
line_to_y_axis_type = 3,
|
|
line_to_y_axis_width = 0.5,
|
|
line_to_y_axis_color = color("dark_grey"),
|
|
add_text = FALSE,
|
|
add_text_vjust = 2,
|
|
add_text_size = 3.5,
|
|
add_text_color = color("dark_grey"),
|
|
theme_fun = theme_dumbbell(),
|
|
scale_fill_fun = scale_fill_visualizer_discrete(),
|
|
scale_color_fun = scale_color_visualizer_discrete()
|
|
) {
|
|
#------ Checks
|
|
|
|
# df is a data frame
|
|
checkmate::assert_data_frame(df)
|
|
|
|
# col, group_x, group_y are character
|
|
checkmate::assert_character(col, len = 1)
|
|
checkmate::assert_character(group_x, len = 1)
|
|
checkmate::assert_character(group_y, len = 1)
|
|
|
|
# col, group_x, group_y are columns in df
|
|
checkmate::assert_choice(col, colnames(df))
|
|
checkmate::assert_choice(group_x, colnames(df))
|
|
checkmate::assert_choice(group_y, colnames(df))
|
|
|
|
# Check numeric/logical values
|
|
checkmate::assert_numeric(point_size, len = 1)
|
|
checkmate::assert_numeric(point_alpha, lower = 0, upper = 1, len = 1)
|
|
checkmate::assert_numeric(segment_size, len = 1)
|
|
checkmate::assert_logical(line_to_y_axis, len = 1)
|
|
checkmate::assert_numeric(line_to_y_axis_type, len = 1)
|
|
checkmate::assert_numeric(line_to_y_axis_width, len = 1)
|
|
checkmate::assert_logical(add_text, len = 1)
|
|
checkmate::assert_numeric(add_text_vjust, len = 1)
|
|
checkmate::assert_numeric(add_text_size, len = 1)
|
|
|
|
# Get group keys
|
|
group_x_keys <- df |>
|
|
dplyr::group_by(!!rlang::sym(group_x)) |>
|
|
dplyr::group_keys() |>
|
|
dplyr::pull()
|
|
|
|
# Check if only two groups
|
|
if (length(group_x_keys) > 2) {
|
|
rlang::abort(
|
|
"Cannot draw a dumbbell plot for `group_x` with more than 2 groups"
|
|
)
|
|
}
|
|
|
|
# Pivot long data
|
|
df_pivot <- df |>
|
|
tidyr::pivot_wider(
|
|
id_cols = c(!!rlang::sym(group_y)),
|
|
values_from = !!rlang::sym(col),
|
|
names_from = !!rlang::sym(group_x)
|
|
)
|
|
|
|
df_pivot <- df_pivot |>
|
|
dplyr::rowwise() |>
|
|
dplyr::mutate(
|
|
min = min(
|
|
!!rlang::sym(group_x_keys[[1]]),
|
|
!!rlang::sym(group_x_keys[[2]]),
|
|
na.rm = T
|
|
),
|
|
max = max(
|
|
!!rlang::sym(group_x_keys[[1]]),
|
|
!!rlang::sym(group_x_keys[[2]]),
|
|
na.rm = T
|
|
)
|
|
) |>
|
|
dplyr::ungroup() |>
|
|
dplyr::mutate(diff = max - min)
|
|
|
|
g <- ggplot2::ggplot(df_pivot)
|
|
|
|
# Add line
|
|
if (line_to_y_axis) {
|
|
xend <- min(dplyr::pull(df, !!rlang::sym(col)))
|
|
|
|
g <- g +
|
|
ggplot2::geom_segment(
|
|
ggplot2::aes(
|
|
x = min,
|
|
y = !!rlang::sym(group_y),
|
|
yend = !!rlang::sym(group_y)
|
|
),
|
|
xend = xend,
|
|
linetype = line_to_y_axis_type,
|
|
linewidth = line_to_y_axis_width,
|
|
color = line_to_y_axis_color
|
|
)
|
|
}
|
|
|
|
# Add segment
|
|
g <- g +
|
|
ggplot2::geom_segment(
|
|
ggplot2::aes(
|
|
x = !!rlang::sym(group_x_keys[[1]]),
|
|
y = !!rlang::sym(group_y),
|
|
xend = !!rlang::sym(group_x_keys[[2]]),
|
|
yend = !!rlang::sym(group_y)
|
|
),
|
|
linewidth = segment_size,
|
|
color = segment_color
|
|
)
|
|
|
|
# Add points
|
|
g <- g +
|
|
ggplot2::geom_point(
|
|
data = df,
|
|
ggplot2::aes(
|
|
x = !!rlang::sym(col),
|
|
y = !!rlang::sym(group_y),
|
|
color = !!rlang::sym(group_x),
|
|
fill = !!rlang::sym(group_x)
|
|
),
|
|
size = point_size,
|
|
alpha = point_alpha
|
|
)
|
|
|
|
# Add title, subtitle, caption, x_title, y_title
|
|
g <- g +
|
|
ggplot2::labs(
|
|
title = title,
|
|
subtitle = subtitle,
|
|
caption = caption,
|
|
x = x_title,
|
|
y = group_y_title,
|
|
color = group_x_title,
|
|
fill = group_x_title
|
|
)
|
|
|
|
# Add stat labels to points
|
|
if (add_text) {
|
|
g <- g +
|
|
ggrepel::geom_text_repel(
|
|
data = df,
|
|
ggplot2::aes(
|
|
x = !!rlang::sym(col),
|
|
y = !!rlang::sym(group_y),
|
|
label = !!rlang::sym(col)
|
|
),
|
|
vjust = add_text_vjust,
|
|
size = add_text_size,
|
|
color = add_text_color
|
|
)
|
|
}
|
|
|
|
# Add theme
|
|
g <- g + theme_fun
|
|
|
|
# Add scale fun
|
|
if (!is.null(scale_fill_fun)) {
|
|
g <- g + scale_fill_fun
|
|
}
|
|
|
|
if (!is.null(scale_color_fun)) {
|
|
g <- g + scale_color_fun
|
|
}
|
|
|
|
return(g)
|
|
}
|