I have begun to extend ggplot2
and I'm still getting a feel for how the package calls all of its internal functions.
I have a new ggproto class that extends one of the current Geom
environments. The current model of the class will plot something along the discrete x axis, ideally touching the x axis ticks. This model works well when the y axis is already on a discrete scale, because the default expansion values only adds .6 padding. However on a continuous y scale, the default padding can make these new plotted objects seem far.
In summary, how can I make my Geom
class override the default expansion without just adding either scale_y_continuous(expand = c(0,0,.05,0)
or scale_y_discrete(expand = c(0, 0, .6,0)
to my layer function...
Consider the following reproducible example
library(dplyr)
library(tidyr)
library(ggplot2)
library(stringr)
mtcars0 <- as_tibble(mtcars, rownames = "CarNames") %>%
gather(key = "qualities", value = "value", -CarNames) %>%
group_by(qualities) %>%
mutate(scaledValue = scale(value)) %>%
ungroup() %>%
mutate(carCase = case_when(str_detect(CarNames, "^[A-M]") ~ "A-M",
TRUE ~ "N-Z"))
"%||%" <- function(a, b) {
if (!is.null(a)) a else b
}
MyText <- ggproto("GeomMyText",
GeomText,
extra_params = c("na.rm","padDist"),
setup_data = function(data, params){
#find bottom of plot with sufficent space
minpadding <- params$padDist %||% diff(range(data$y))*.05
data$y <- min(data$y) - minpadding
data
})
geom_mytext <- function (mapping = NULL, data = NULL, stat = "identity", position = "identity",
..., parse = FALSE, nudge_x = 0, nudge_y = 0, check_overlap = FALSE,
na.rm = FALSE, show.legend = NA, inherit.aes = TRUE, padDist = NULL)
{
if (!missing(nudge_x) || !missing(nudge_y)) {
if (!missing(position)) {
abort("You must specify either `position` or `nudge_x`/`nudge_y`.")
}
position <- position_nudge(nudge_x, nudge_y)
}
layer(data = data, mapping = mapping, stat = stat, geom = MyText,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(parse = parse, check_overlap = check_overlap,
na.rm = na.rm, padDist = padDist, ...))
}
result <- ggplot(mtcars0, aes(x = CarNames, value)) +
geom_point() +
geom_mytext(aes(label = carCase)) +
theme(axis.text.x = element_text(angle=90))
#Default
result
#Desired Result without having to call scale_y_continuous
result + scale_y_continuous(expand = c(0,0,0.05,0))
I'm assuming I need to extend the ScaleContinuous
environment but I have no idea how to connect the MyText
environment with it.
Any suggestions?
---- EDIT ----
Thanks for the quick replies! A few things -
- I am aware there is over plotting and clipping of labels, this isn't my actual Geom environment, just something I could put together that demonstrates my question.
- As I was afraid, it seems that everyone so far is providing the solution that was raised for this question. While supplying my own scale is less than ideal - because I have to put logic in to discern if they associated y axis is discrete/continuous, when
ggplot2
already knows this, I figured that there might be a trick I was missing. For now I will continue development with the suggestions given. Thanks!
---- EDIT 2 ----
I took another look at the solution given here. The exact parameters I need to modify is
panel_params$y$continuous_range[1] <- panel_params$y$limits[1]
And I need to do this somewhere in draw_panel
. It seems like the associated scales are contained there and the coord$transform(data, panel_params)
is responsible for including the padding on the rescaled axis depending on what is set for panel_params$y$limits
and panel_params$y$continuous_range
.
Thanks again to everyone who contributed!