New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New dodging algorithm for box plots #2196
Changes from 13 commits
512fb33
4be63d9
54c1a2a
1f4a984
698ced7
10942d1
8e058b9
6c5dd86
133ed71
1e08220
502ad20
4707fc3
e6e6f00
e2b1ded
59c014e
327597f
d169dc9
64c3688
f916986
8213782
0efaed7
3da2f49
b689a4c
03e3d46
029a5d0
dd78a80
ae594df
99fe422
30b189a
9f7dcbc
d05a7df
dbdc9a9
79aedfb
7c9aec8
3706b49
10e8616
b059e39
4e2052f
09ee427
04666e7
8398e7e
b7553ac
34002fa
b437848
a05c3e0
1cdcc09
9cea3ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#' Position dodge for box plots | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to give this a name that reflects that it works for any geom with variable widths - i.e. it would also work for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or maybe |
||
#' | ||
#' Dodging preserves the vertical position of an geom while adjusting the | ||
#' horizontal position. `position_boxdodge` is a special case of | ||
#' `position_dodge` for arranging box plots, which can have variable widths. | ||
#' | ||
#' @include position-dodge.r | ||
#' @inheritParams position_dodge | ||
#' @param padding Padding between boxes at the same position. Boxes are shrunk | ||
#' by this proportion to make room for space between them. | ||
#' @family position adjustments | ||
#' @export | ||
#' @examples | ||
#' ggplot(data = iris, aes(Species, Sepal.Length)) + | ||
#' geom_boxplot(aes(colour = Sepal.Width < 3.2)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing indent |
||
#' | ||
#' ggplot(data = iris, aes(Species, Sepal.Length)) + | ||
#' geom_boxplot(aes(colour = Sepal.Width < 3.2), varwidth = TRUE) | ||
position_boxdodge <- function(width = NULL, preserve = c("single", "total"), | ||
padding = 0.1) { | ||
ggproto(NULL, PositionBoxdodge, | ||
width = width, | ||
preserve = match.arg(preserve), | ||
padding = padding | ||
) | ||
} | ||
|
||
#' @rdname ggplot2-ggproto | ||
#' @format NULL | ||
#' @usage NULL | ||
#' @export | ||
PositionBoxdodge <- ggproto("PositionBoxdodge", PositionDodge, | ||
preserve = "single", | ||
padding = 0.1, | ||
setup_params = function(self, data) { | ||
if (is.null(data$xmin) && is.null(data$xmax) && is.null(self$width)) { | ||
warning("Width not defined. Set with `position_boxdodge(width = ?)`", | ||
call. = FALSE) | ||
} | ||
|
||
if (identical(self$preserve, "total")) { | ||
n <- NULL | ||
} else { | ||
n <- max(table(data$x)) | ||
} | ||
|
||
list( | ||
width = self$width, | ||
n = n, | ||
padding = self$padding | ||
) | ||
}, | ||
|
||
compute_panel = function(data, params, scales) { | ||
collide_box( | ||
data, | ||
params$width, | ||
name = "position_boxdodge", | ||
strategy = pos_boxdodge, | ||
n = params$n, | ||
padding = params$padding, | ||
check.width = FALSE | ||
) | ||
} | ||
) | ||
|
||
pos_boxdodge <- function(df, width, n = NULL, padding = 0.1) { | ||
|
||
if (!all(c("xmin", "xmax") %in% names(df))) { | ||
df$xmin <- df$x | ||
df$xmax <- df$x | ||
} | ||
|
||
# xid represents groups of boxes that share the same position | ||
df$xid <- match(df$x, sort(unique(df$x))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh this is why it won't work with |
||
|
||
if (is.null(n)) { | ||
# If n is null, preserve total widths of boxes at each position by dividing | ||
# widths by the number of elements at that position | ||
n <- table(df$xid) | ||
df$new_width <- (df$xmax - df$xmin) / n[df$xid] | ||
} else { | ||
df$new_width <- (df$xmax - df$xmin) / n | ||
} | ||
|
||
df$xmin <- df$x - (df$new_width / 2) | ||
df$xmax <- df$x + (df$new_width / 2) | ||
|
||
# Find the total width of each group of boxes | ||
group_sizes <- plyr::ddply(df, "xid", plyr::summarize, size = sum(new_width)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be replaced by a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yeah sure. |
||
|
||
# Starting xmin for each group of boxes | ||
starts <- group_sizes$xid - (group_sizes$size / 2) | ||
|
||
# Set the boxes in place | ||
for (i in seq_along(starts)) { | ||
divisions <- cumsum(c(starts[i], df[df$xid == i, "new_width"])) | ||
df[df$xid == i, "xmin"] <- divisions[-length(divisions)] | ||
df[df$xid == i, "xmax"] <- divisions[-1] | ||
} | ||
|
||
# x values get moved to between xmin and xmax | ||
df$x <- rowMeans(df[, c("xmin", "xmax")]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
|
||
# If no boxes occupy the same position, there is no need to add padding | ||
if (!any(duplicated(df$xid))) { | ||
return(df) | ||
} | ||
|
||
# Shrink boxes to add space between them | ||
df$pad_width <- df$new_width * (1 - padding) | ||
df$xmin <- df$x + (df$pad_width / 2) | ||
df$xmax <- df$x - (df$pad_width / 2) | ||
|
||
df | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And
bar
. And you should add a brief descrption.