ggplot2: Adjust distance between text and axis - r

As a followup to the question of how to
Increase distance between text and title on the y-axis, I am wondering whether it is also possible to adjust the distance between an axis and its title/label. This could be useful for creating similarly styled graphs, for example when the tick marks of two graphs are of different magnitudes.
Here are two example plots:
plot_a <- ggplot(mpg, aes(cty, hwy)) + geom_point()
plot_b <- ggplot(mpg, aes(cty, hwy*100)) + geom_point()
Note the difference on the left side of the plots:
Cowplot can, somehow, do the job and set the distance of axis and label the same in two plots:
cowplot::plot_grid (plotlist = list (plot_a, plot_b),
align = "v",
nrow = 2,
ncol = 1,
greedy = T)
However, I'd rather like to adjust the distance bewteen the label and the axis manually. Also, there are cases where it's preferable to draw one single plot per panel, rendering the cowplot-solution useless. So - is there a possibility to manually adjust the distance between axis and label?

Related

Insert rectangle outside of ggplot to visualize plot segments

I hope you can help me. I have the idea of visualizing segments within a plot with a rectangle that can be placed next to the y or x-axis which means that it would be outside of the plot area. It should look similar as in the image below:
I tried to reach the mentioned output by trying two different approaches:
I created two viewports with the grid package and put the plot in one viewport that I placed at the bottom and one viewport on top of that. The big problem here is that I need the coordinates from where the grey background panel of the ggplot starts so I can place the top viewport exactly there, so that the segments conincide with the x-axis length. My code looked like following:
container_viewport <- viewport(x=0,y=0,height=1,width=1,just = c("left","bottom"))
pushViewport(container_viewport)
grid.draw(rectGrob())
popViewport()
section_viewport <- viewport(x=0.055,y=0.99,height=0.085,width=0.935,just=c("left","top"))
pushViewport(section_viewport)
plot_obj <- ggplot_build(testplot)
plot_data <- plot_obj$data[[1]]
grid.draw(rectGrob(gp = gpar(col = "red")))
popViewport()
plot_viewport <- viewport(x=0,y=0,height=0.9,width=1,just=c("left","bottom"))
pushViewport(plot_viewport)
grid.draw(ggplotGrob(testplot))
popViewport()
This looks fine but I had to hardcode the coordinates of the viewport at the top.
I used grid.arrange() to arrange to stack the plots vertically (instead of a grob for the rectangle like in the other approach I create a ggplot instead for that). Here, basically the same problem exists, since I somehow need to put the plot representing the rectangle at the top in the right position on the x-axis. My code looked like following:
p1 <- plot_data %>%
ggplot()+
geom_rect(aes(xmin=-Inf,xmax=Inf,ymin=-Inf,ymax=Inf))
p2 <- testplot
test_plot <- grid.arrange(p1,p2,heights=c(1,10))
This approach does not work that good.
Since I would like to create a solution that can be applied generally, trial and error with the coordinates of the viewport is no option since the length of the y-axis label or tick labels can vary and therefore the length and coordinates of the background panel. When this step is done the segmentation of the rectangle should be no problem anymore.
Maybe this is just not possible but if then I would appreciate any help.
Thank you!
I would probably use patchwork here. Let's start by replicating your plot:
library(ggplot2)
library(patchwork)
p <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point(color = "red") +
labs(x = "test", y = "test")
p
That looks very similar. Now we define (in our own co-ordinates) where we want the section split to occur on the x axis.
section_split <- 5.25
Using just this number, we add rectangles and text annotations that cover a copy of our original plot, and remove its axis annotations using theme_void:
p2 <- p +
annotate("rect", xmin = c(-Inf, section_split), ymin = c(-Inf, -Inf),
xmax = c(section_split, Inf), ymax = c(Inf, Inf),
fill = c("#00a2e8", "#ff7f27")) +
annotate("text", label = c("Section A", "Section B"), size = 6,
y = rep(mean(layer_scales(p)$y$range$range), 2),
x = c((min(layer_scales(p)$x$range$range) + section_split)/2,
(max(layer_scales(p)$x$range$range) + section_split)/2)) +
theme_void()
Now we just draw this second plot above our first, adjusting the relative heights to about 1:10
p2/p + plot_layout(heights = c(1, 10))
The benefit of doing it this way is that, since we copied the original plot, the positional mapping of the x axis is identical between the two plots, and patchwork will automatically line up the panels.
Created on 2023-02-04 with reprex v2.0.2

How do you remove extra space between x-axis and plot for a geom_density_ridges_gradient() plot in R ggplot

I am making a plot using ggplot2 in R. I am using the ggridges package and the geom_density_ridges_gradient() plot type. These plots create a large space between the bottom of the figure and the labels of the x-axis. Is there a way to remove or shrink this added space?
Here is the code I'm using to make the plot.
library(tidyverse)
library(ggridges)
library(scales)
sample_q <- ggplot(data, aes(x=value/1000,y=factor(category),group=category)) +
geom_density_ridges_gradient(scale=.5) +
theme_ridges(center_axis_labels = TRUE,grid=TRUE) +
xlab("Values") +
ylab("Height") +
theme(text=element_text(size=10)) +
scale_x_continuous(expand = c(0,0), limits = c(-15,15), breaks=c(-15,-10,-5,0,5,10,15))
I've tried creating the plot with different values of 'scale'. Here are three plots with scale set equal to 2, 1, and .5.
You can see that the space between the bottom of the density plot and the x-axis labels is large and does not change with scales. I've tried without the theme_ridges line as well and the plot still has the same vertical space between the density plot and the x-axis labels. I'd like the x-axis labels to be just below the 'Group A' plot.

How to prevent x axes overlap ggplotly with facet_wrap(~variable)

I have some data that looks nice when plotted with ggpglot but the x-axes and the plots underneath overlap in plotly::ggplotly().
library(gapminder)
library(plotly)
p <- ggplot(gapminder, aes(x=gdpPercap, y=lifeExp)) + geom_point() + scale_x_log10()
p <- p + aes(color=continent) + facet_wrap(~year,scale="free")
gp <- ggplotly(p)
gp
So I've been trying this: R: ggplot and plotly axis margin won't change
Basically I need to increase the white space between each graph object and the one below. Any thoughts would be much appreciated.
You can add vertical space in between your facets using:
theme(panel.spacing.y = unit(1, "line")) # adjust to taste
However, there's a snag related to axis titles and facets when converting to plotly, where the axis title becomes an annotation and needs to be shifted more manually.
See these links for ways how:
https://stackoverflow.com/a/47228372/6851825
Converting ggplot object to plotly object creates axis title that overlaps tick values
https://github.com/ropensci/plotly/issues/1224

ggplot: same bar widths with different number of bars

I would like to have barplots in which the bars have the same width across different plots, no matter how many bars are shown. I do not want to show the plots on the same page, or arrange them with facets, grid.arrange or anything like that, but just have two plots with bars of the same width.
I could do this by just multiplying the width by the number of bars in the plot divided by the number of bars in the plot with the most bars (see example). But it would be more convenient and somewhat cleaner code if I could do this without any computations before the ggplot call.
Is there a way to specify the bar widths in a unit like lines, em, centimeters?
Or can I access the number of levels of the variable mapped to the x-aesthetic in the call to geom_col? (Note the variable mapped to the x-aesthetic changes between plots)
Or is there another simple solution?
ggplot(data.frame(x=factor(1:2), y=4:5), aes(x=x, y=y)) +
geom_col(width=0.7*2/3)
ggplot(data.frame(A=factor(1:3), y=3:5), aes(x=A, y=y)) +
geom_col(width=0.7*3/3)
AFAIK, you can not set an absolute width to geom_col()/geom_bar(), so you'd either have to precalculate the proportions and aspect ratio of the bars or use geom_segment() that takes a size argument that is absolute. These aren't internally parameterised as rectangles and don't take seperate colour and fill arguments though.
library(ggplot2)
library(patchwork)
g1 <- ggplot(data.frame(x=factor(1:2), y=4:5), aes(x=x, y=y, xend = x, yend = 0)) +
geom_segment(size = 20)
g2 <- ggplot(data.frame(A=factor(1:3), y=3:5), aes(x=A, y=y, xend=A, yend = 0)) +
geom_segment(size = 20)
g1 + g2

Force scatter plot grid to be square in ggpot2

I'm trying to force the grid of a scatter plot to be composed of squares, with x and y values that have different ranges.
I tried to force a square shape of the whole plot (aspect.ratio=1), but this does not solve the problem of different ranges. Then I tried to change limits of values of my axes.
1)Here is what I tried first:
p + theme(aspect.ratio = 1) +
coord_fixed(ratio=1, xlim = c(-0.050,0.050),ylim = c(-0.03,0.03))
2) I changed the ratio by using the range of the values for each axis:
p + coord_fixed(ratio=0.06/0.10, xlim = c(-0.050,0.050), ylim = c(-0.03,0.03))
3)Then I changed the limits of y to match those of x:
p + theme(aspect.ratio = 1) +
coord_fixed(ratio=1, xlim = c(-0.050,0.050),ylim = c(-0.05,0.05))
1) The grid on the background is composed by rectangles.
2) I would expect this to change the position of the tick marks automatically in order to give me a grid composed of squares. Still triangles.
3) It obviously worked 'cause I matched the ranges of x and y. But there was a lot of empty space in the graph.
Is there something else I should try?
Thanks in advance.
If you want the plot to be square and you want the grid to be square you can do this by rescaling the y variable to be on the same scale as the x variable (or vice versa) for plotting, and then inverting the rescaling to generate the correct axis value labels for the rescaled axis.
Here's an example using the mtcars data frame, and we'll use the rescale function from the scales package.
First let's create a plot of mpg vs. hp but with the hp values rescaled to be on the same scale as mpg:
library(tidyverse)
library(scales)
theme_set(theme_bw())
p = mtcars %>%
mutate(hp.scaled = rescale(hp, to=range(mpg))) %>%
ggplot(aes(mpg, hp.scaled)) +
geom_point() +
coord_fixed() +
labs(x="mpg", y="hp")
Now we can invert the rescaling to generate the correct value labels for hp. We do that below by supplying the inverting function to the labels argument of scale_y_continuous:
p + scale_y_continuous(labels=function(x) rescale(x, to=range(mtcars$hp)))
But note that rescaling back to the original hp scale results in non-pretty breaks. We can fix that by generating pretty breaks on the hp scale, rescaling those to the mpg scale to get the locations where we want the tick marks and then inverting that to get the label values. However, in that case we won't get a square grid if we want to keep the overall plot panel square:
p + scale_y_continuous(breaks = rescale(pretty_breaks(n=5)(mtcars$hp),
from=range(mtcars$hp),
to=range(mtcars$mpg)),
labels = function(x) rescale(x, from=range(mtcars$mpg), to=range(mtcars$hp)))
I'm not sure what code you are using, it is missing in block 1 and 3. But using the mtcars data set the following works:
library(ggplot2)
ggplot(mtcars, aes(mpg, wt)) +
geom_point() +
coord_fixed(ratio = 1) +
scale_x_continuous(breaks = seq(10, 35, 1)) +
scale_y_continuous(breaks = seq(1, 6, 1))
The last two lines make it clear that 1 point on the x-axis is equal to 1 point on the y-axis.
In the documention you will further find the following advise:
ensures that the ranges of axes are equal to the specified ratio by
adjusting the plot aspect ratio

Resources