Skip to content
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

Removing direction constraint from geoms #3506

Merged
merged 52 commits into from Oct 1, 2019
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
676f596
cursory study on geom_bar and geom_histogram
thomasp85 Aug 28, 2019
35514a7
Make direction detection much more granualated
thomasp85 Aug 28, 2019
9902e68
fix geom_col... There are some ambiguities here that should get discu…
thomasp85 Aug 28, 2019
697f9a6
refine direction detection
thomasp85 Aug 28, 2019
0a8c8cf
add geom_ribbon, geom_area, and geom_density
thomasp85 Aug 28, 2019
fd3fa77
Add errorbar
thomasp85 Aug 28, 2019
771042a
Fix warning
thomasp85 Aug 29, 2019
a1bc136
Add crossbar
thomasp85 Aug 29, 2019
1529852
clean up
thomasp85 Aug 29, 2019
91f0319
undo last changes
thomasp85 Aug 29, 2019
1584dfd
more alignment of approaches
thomasp85 Aug 29, 2019
a47b1e0
Add boxplot
thomasp85 Aug 29, 2019
dd21a62
add linerange and pointrange
thomasp85 Aug 29, 2019
4dbd9ad
add violin
thomasp85 Aug 29, 2019
a13ae07
add dodge
thomasp85 Aug 29, 2019
efcc999
Merge branch 'master' of https://github.com/tidyverse/ggplot2 into no…
thomasp85 Aug 30, 2019
acb29d8
Align approach across stats/geoms/positions
thomasp85 Sep 2, 2019
6ef79c5
Add remaining positions
thomasp85 Sep 2, 2019
6147ec8
fix crossbar/boxplot issues
thomasp85 Sep 2, 2019
f964e8f
imporved sniffing - allow geoms/stats to indicate features
thomasp85 Sep 12, 2019
f379f87
Add orientation parameter to stats and geoms
thomasp85 Sep 12, 2019
54183f1
Add line and smooth
thomasp85 Sep 12, 2019
4a58c3f
Allow required_aes to take two distinct sets of aesthetics using "|" …
thomasp85 Sep 12, 2019
f0518a9
fix sniffing if x and/or y are missing
thomasp85 Sep 12, 2019
2cbd1d4
bring back required_aes
thomasp85 Sep 12, 2019
68ebed4
revert style
thomasp85 Sep 12, 2019
7f5dad1
update aesthetic doc generation to recognise "|"
thomasp85 Sep 13, 2019
ca16b2c
Update docs to reflect orientation param etc
thomasp85 Sep 16, 2019
538db55
Merge branch 'master' of https://github.com/tidyverse/ggplot2 into no…
thomasp85 Sep 17, 2019
3784134
Fix aesthetic spliiting when required_aes is NULL
thomasp85 Sep 18, 2019
03b6318
Further refine sniffing to not break edge cases
thomasp85 Sep 18, 2019
57a09c3
put \emph{} outside code block
thomasp85 Sep 18, 2019
e6b41c4
Refine error reporting
thomasp85 Sep 18, 2019
4cdce8f
Don't pass in non-existing params variable
thomasp85 Sep 18, 2019
71542a6
Update docs and examples to show new orientation features
thomasp85 Sep 18, 2019
ff2c240
Fix expected errors in tests
thomasp85 Sep 18, 2019
ff7b19f
Fix typo in required aes
thomasp85 Sep 18, 2019
e39a9f9
geom_area is ambiguous
thomasp85 Sep 18, 2019
b701af7
Use # for comments (-‸ლ)
thomasp85 Sep 18, 2019
98786e7
stat_density can be used with both positions mapped
thomasp85 Sep 18, 2019
f805f18
fix docs
thomasp85 Sep 18, 2019
0bd3f52
Further tweaks to sniffing... a few layers has a continuous main axis
thomasp85 Sep 18, 2019
881ec19
Add stat_summary[_bin]()
thomasp85 Sep 18, 2019
1932acc
Test for correct orientation handling
thomasp85 Sep 19, 2019
64c1a01
export utilities for omnidirectionality
thomasp85 Sep 19, 2019
0430426
add section on how to create stats and geoms that handle two orientat…
thomasp85 Sep 19, 2019
5ba49d5
change name to bidirection
thomasp85 Sep 26, 2019
0854ee8
Better examples of has_flipped_aes() behaviour
thomasp85 Sep 26, 2019
f712e87
Merge branch 'master' of https://github.com/tidyverse/ggplot2 into no…
thomasp85 Sep 30, 2019
3e7ad39
hanging indent on params
thomasp85 Sep 30, 2019
c2f7a01
fix merge
thomasp85 Oct 1, 2019
6f0380c
Fix cross linking
thomasp85 Oct 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions NAMESPACE
Expand Up @@ -298,6 +298,8 @@ export(facet_grid)
export(facet_null)
export(facet_wrap)
export(find_panel)
export(flip_data)
export(flipped_names)
export(fortify)
export(geom_abline)
export(geom_area)
Expand Down Expand Up @@ -366,6 +368,7 @@ export(guide_legend)
export(guide_merge)
export(guide_train)
export(guides)
export(has_flipped_aes)
export(is.Coord)
export(is.facet)
export(is.ggplot)
Expand Down
7 changes: 6 additions & 1 deletion R/geom-.r
Expand Up @@ -153,7 +153,12 @@ Geom <- ggproto("Geom",
},

aesthetics = function(self) {
c(union(self$required_aes, names(self$default_aes)), self$optional_aes, "group")
if (is.null(self$required_aes)) {
required_aes <- NULL
} else {
required_aes <- unlist(strsplit(self$required_aes, '|', fixed = TRUE))
}
c(union(required_aes, names(self$default_aes)), self$optional_aes, "group")
}

)
Expand Down
29 changes: 24 additions & 5 deletions R/geom-bar.r
Expand Up @@ -19,6 +19,8 @@
#' [position_fill()] shows relative proportions at each `x` by stacking the bars
#' and then standardising each bar to have the same height.
#'
#' @eval rd_orientation()
#'
#' @eval rd_aesthetics("geom", "bar")
#' @eval rd_aesthetics("geom", "col")
#' @eval rd_aesthetics("stat", "count")
Expand All @@ -29,6 +31,10 @@
#' @export
#' @inheritParams layer
#' @inheritParams geom_point
#' @param orientation The orientation of the layer. The default (`NA`)
#' automatically determines the orientation from the aesthetic mapping. In the
#' rare event that this fails it can be given explicitly by setting `orientation`
#' to either `"x"` or `"y"`. See the *Orientation* section for more detail.
#' @param width Bar width. By default, set to 90\% of the resolution of the data.
#' @param binwidth `geom_bar()` no longer has a binwidth argument - if
#' you use it you'll get an warning telling to you use
Expand All @@ -43,17 +49,18 @@
#' g + geom_bar()
#' # Total engine displacement of each class
#' g + geom_bar(aes(weight = displ))
#' # Map class to y instead to flip the orientation
#' ggplot(mpg) + geom_bar(aes(y = class))
#'
#' # Bar charts are automatically stacked when multiple bars are placed
#' # at the same location. The order of the fill is designed to match
#' # the legend
#' g + geom_bar(aes(fill = drv))
#'
#' # If you need to flip the order (because you've flipped the plot)
#' # If you need to flip the order (because you've flipped the orientation)
#' # call position_stack() explicitly:
#' g +
#' ggplot(mpg, aes(y = class)) +
#' geom_bar(aes(fill = drv), position = position_stack(reverse = TRUE)) +
#' coord_flip() +
#' theme(legend.position = "top")
#'
#' # To show (e.g.) means, you need geom_col()
Expand All @@ -77,6 +84,7 @@ geom_bar <- function(mapping = NULL, data = NULL,
width = NULL,
binwidth = NULL,
na.rm = FALSE,
orientation = NA,
show.legend = NA,
inherit.aes = TRUE) {

Expand All @@ -99,6 +107,7 @@ geom_bar <- function(mapping = NULL, data = NULL,
params = list(
width = width,
na.rm = na.rm,
orientation = orientation,
...
)
)
Expand All @@ -117,16 +126,26 @@ GeomBar <- ggproto("GeomBar", GeomRect,
# limits, not just those for which x and y are outside the limits
non_missing_aes = c("xmin", "xmax", "ymin", "ymax"),

setup_params = function(data, params) {
params$flipped_aes <- has_flipped_aes(data, params, range_is_orthogonal = FALSE)
params
},

extra_params = c("na.rm", "orientation"),

setup_data = function(data, params) {
data$flipped_aes <- params$flipped_aes
data <- flip_data(data, params$flipped_aes)
data$width <- data$width %||%
params$width %||% (resolution(data$x, FALSE) * 0.9)
transform(data,
data <- transform(data,
ymin = pmin(y, 0), ymax = pmax(y, 0),
xmin = x - width / 2, xmax = x + width / 2, width = NULL
)
flip_data(data, params$flipped_aes)
},

draw_panel = function(self, data, panel_params, coord, width = NULL) {
draw_panel = function(self, data, panel_params, coord, width = NULL, flipped_aes = FALSE) {
# Hack to ensure that width is detected as a parameter
ggproto_parent(GeomRect, self)$draw_panel(data, panel_params, coord)
}
Expand Down
36 changes: 26 additions & 10 deletions R/geom-boxplot.r
Expand Up @@ -4,6 +4,8 @@
#' It visualises five summary statistics (the median, two hinges
#' and two whiskers), and all "outlying" points individually.
#'
#' @eval rd_orientation()
#'
#' @section Summary statistics:
#' The lower and upper hinges correspond to the first and third quartiles
#' (the 25th and 75th percentiles). This differs slightly from the method used
Expand All @@ -28,7 +30,7 @@
#' [geom_violin()] for a richer display of the distribution, and
#' [geom_jitter()] for a useful technique for small data.
#' @inheritParams layer
#' @inheritParams geom_point
#' @inheritParams geom_bar
#' @param geom,stat Use to override the default connection between
#' `geom_boxplot` and `stat_boxplot`.
#' @param outlier.colour,outlier.color,outlier.fill,outlier.shape,outlier.size,outlier.stroke,outlier.alpha
Expand Down Expand Up @@ -60,7 +62,8 @@
#' @examples
#' p <- ggplot(mpg, aes(class, hwy))
#' p + geom_boxplot()
#' p + geom_boxplot() + coord_flip()
#' # Orientation follows the discrete axis
#' ggplot(mpg, aes(hwy, class)) + geom_boxplot()
#'
#' p + geom_boxplot(notch = TRUE)
#' p + geom_boxplot(varwidth = TRUE)
Expand Down Expand Up @@ -116,6 +119,7 @@ geom_boxplot <- function(mapping = NULL, data = NULL,
notchwidth = 0.5,
varwidth = FALSE,
na.rm = FALSE,
orientation = NA,
show.legend = NA,
inherit.aes = TRUE) {

Expand Down Expand Up @@ -148,6 +152,7 @@ geom_boxplot <- function(mapping = NULL, data = NULL,
notchwidth = notchwidth,
varwidth = varwidth,
na.rm = na.rm,
orientation = orientation,
...
)
)
Expand All @@ -161,9 +166,16 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,

# need to declare `width` here in case this geom is used with a stat that
# doesn't have a `width` parameter (e.g., `stat_identity`).
extra_params = c("na.rm", "width"),
extra_params = c("na.rm", "width", "orientation"),

setup_params = function(data, params) {
params$flipped_aes <- has_flipped_aes(data, params)
params
},

setup_data = function(data, params) {
data$flipped_aes <- params$flipped_aes
data <- flip_data(data, params$flipped_aes)
data$width <- data$width %||%
params$width %||% (resolution(data$x, FALSE) * 0.9)

Expand All @@ -173,8 +185,8 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
out_max <- vapply(data$outliers, max, numeric(1))
})

data$ymin_final <- pmin(out_min, data$ymin)
data$ymax_final <- pmax(out_max, data$ymax)
data$ymin_final <- pmin(out_min, data$ymin)
data$ymax_final <- pmax(out_max, data$ymax)
}

# if `varwidth` not requested or not available, don't use it
Expand All @@ -190,16 +202,16 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
data$width <- NULL
if (!is.null(data$relvarwidth)) data$relvarwidth <- NULL

data
flip_data(data, params$flipped_aes)
},

draw_group = function(data, panel_params, coord, fatten = 2,
outlier.colour = NULL, outlier.fill = NULL,
outlier.shape = 19,
outlier.size = 1.5, outlier.stroke = 0.5,
outlier.alpha = NULL,
notch = FALSE, notchwidth = 0.5, varwidth = FALSE) {

notch = FALSE, notchwidth = 0.5, varwidth = FALSE, flipped_aes = FALSE) {
data <- flip_data(data, flipped_aes)
# this may occur when using geom_boxplot(stat = "identity")
if (nrow(data) != 1) {
stop(
Expand All @@ -226,6 +238,7 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
),
common
), n = 2)
whiskers <- flip_data(whiskers, flipped_aes)

box <- new_data_frame(c(
list(
Expand All @@ -241,6 +254,7 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
),
common
))
box <- flip_data(box, flipped_aes)

if (!is.null(data$outliers) && length(data$outliers[[1]] >= 1)) {
outliers <- new_data_frame(list(
Expand All @@ -254,6 +268,8 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
fill = NA,
alpha = outlier.alpha %||% data$alpha[1]
), n = length(data$outliers[[1]]))
outliers <- flip_data(outliers, flipped_aes)

outliers_grob <- GeomPoint$draw_panel(outliers, panel_params, coord)
} else {
outliers_grob <- NULL
Expand All @@ -262,7 +278,7 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
ggname("geom_boxplot", grobTree(
outliers_grob,
GeomSegment$draw_panel(whiskers, panel_params, coord),
GeomCrossbar$draw_panel(box, fatten = fatten, panel_params, coord)
GeomCrossbar$draw_panel(box, fatten = fatten, panel_params, coord, flipped_aes = flipped_aes)
))
},

Expand All @@ -271,5 +287,5 @@ GeomBoxplot <- ggproto("GeomBoxplot", Geom,
default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = 0.5,
alpha = NA, shape = 19, linetype = "solid"),

required_aes = c("x", "lower", "upper", "middle", "ymin", "ymax")
required_aes = c("x|y", "lower|xlower", "upper|xupper", "middle|xmiddle", "ymin|xmin", "ymax|xmax")
)
18 changes: 14 additions & 4 deletions R/geom-col.r
Expand Up @@ -37,16 +37,26 @@ GeomCol <- ggproto("GeomCol", GeomRect,
# limits, not just those for which x and y are outside the limits
non_missing_aes = c("xmin", "xmax", "ymin", "ymax"),

setup_params = function(data, params) {
params$flipped_aes <- has_flipped_aes(data, params)
params
},

extra_params = c("na.rm", "orientation"),

setup_data = function(data, params) {
data$flipped_aes <- params$flipped_aes
data <- flip_data(data, params$flipped_aes)
data$width <- data$width %||%
params$width %||% (resolution(data$x, FALSE) * 0.9)
transform(data,
ymin = pmin(y, 0), ymax = pmax(y, 0),
xmin = x - width / 2, xmax = x + width / 2, width = NULL
data <- transform(data,
ymin = pmin(y, 0), ymax = pmax(y, 0),
xmin = x - width / 2, xmax = x + width / 2, width = NULL
)
flip_data(data, params$flipped_aes)
},

draw_panel = function(self, data, panel_params, coord, width = NULL) {
draw_panel = function(self, data, panel_params, coord, width = NULL, flipped_aes = FALSE) {
# Hack to ensure that width is detected as a parameter
ggproto_parent(GeomRect, self)$draw_panel(data, panel_params, coord)
}
Expand Down
16 changes: 14 additions & 2 deletions R/geom-crossbar.r
Expand Up @@ -5,6 +5,7 @@ geom_crossbar <- function(mapping = NULL, data = NULL,
...,
fatten = 2.5,
na.rm = FALSE,
orientation = NA,
show.legend = NA,
inherit.aes = TRUE) {
layer(
Expand All @@ -18,6 +19,7 @@ geom_crossbar <- function(mapping = NULL, data = NULL,
params = list(
fatten = fatten,
na.rm = na.rm,
orientation = orientation,
...
)
)
Expand All @@ -28,18 +30,26 @@ geom_crossbar <- function(mapping = NULL, data = NULL,
#' @usage NULL
#' @export
GeomCrossbar <- ggproto("GeomCrossbar", Geom,
setup_params = function(data, params) {
GeomErrorbar$setup_params(data, params)
},

extra_params = c("na.rm", "orientation"),

setup_data = function(data, params) {
GeomErrorbar$setup_data(data, params)
},

default_aes = aes(colour = "black", fill = NA, size = 0.5, linetype = 1,
alpha = NA),

required_aes = c("x", "y", "ymin", "ymax"),
required_aes = c("x", "y", "ymin|xmin", "ymax|xmax"),

draw_key = draw_key_crossbar,

draw_panel = function(data, panel_params, coord, fatten = 2.5, width = NULL) {
draw_panel = function(data, panel_params, coord, fatten = 2.5, width = NULL, flipped_aes = FALSE) {
data <- flip_data(data, flipped_aes)

middle <- transform(data, x = xmin, xend = xmax, yend = y, size = size * fatten, alpha = NA)

has_notch <- !is.null(data$ynotchlower) && !is.null(data$ynotchupper) &&
Expand Down Expand Up @@ -85,6 +95,8 @@ GeomCrossbar <- ggproto("GeomCrossbar", Geom,
group = rep(seq_len(nrow(data)), 5) # each bar forms it's own group
))
}
box <- flip_data(box, flipped_aes)
middle <- flip_data(middle, flipped_aes)

ggname("geom_crossbar", gTree(children = gList(
GeomPolygon$draw_panel(box, panel_params, coord),
Expand Down
9 changes: 8 additions & 1 deletion R/geom-density.r
Expand Up @@ -4,18 +4,23 @@
#' the histogram. This is a useful alternative to the histogram for continuous
#' data that comes from an underlying smooth distribution.
#'
#' @eval rd_orientation()
#'
#' @eval rd_aesthetics("geom", "density")
#' @seealso See [geom_histogram()], [geom_freqpoly()] for
#' other methods of displaying continuous distribution.
#' See [geom_violin()] for a compact density display.
#' @inheritParams layer
#' @inheritParams geom_point
#' @inheritParams geom_bar
#' @param geom,stat Use to override the default connection between
#' `geom_density` and `stat_density`.
#' @export
#' @examples
#' ggplot(diamonds, aes(carat)) +
#' geom_density()
#' # Map the values to y to flip the orientation
#' ggplot(diamonds, aes(y = carat)) +
#' geom_density()
#'
#' ggplot(diamonds, aes(carat)) +
#' geom_density(adjust = 1/5)
Expand Down Expand Up @@ -49,6 +54,7 @@ geom_density <- function(mapping = NULL, data = NULL,
stat = "density", position = "identity",
...,
na.rm = FALSE,
orientation = NA,
show.legend = NA,
inherit.aes = TRUE) {

Expand All @@ -62,6 +68,7 @@ geom_density <- function(mapping = NULL, data = NULL,
inherit.aes = inherit.aes,
params = list(
na.rm = na.rm,
orientation = orientation,
...
)
)
Expand Down