I am using ggplot in R to generate a heatmap. I would like to align the width of the legend in "bottom" position to the width of the ggplot itself.
Example code:
# Load packages
library(tidyverse)
# Create dataframe
df <- expand.grid(x = seq(1,100), y = seq(1,100))
# Add variable
set.seed(123)
df$z <- rnorm(nrow(df))
# Generate plot
ggplot(data = df, aes(x = x, y = y)) +
geom_tile(aes(fill = z)) +
theme(legend.position = "bottom")
With the option legend.key.width = unit(2, "cm") I was able to set the width manually. However, I am wondering if there is a way to automatically align the width of the legend to the width of the plot?
Related
I am attempting to accomplish two tasks:
Cohesively combine two figures that share the same y axis, but one which has a categorical x axis variable and the other that has a continuous x axis variable. I would like to display them as contiguous, only separated by a solid black line (i.e. the right edge of the left plot and the left edge of the right plot).
Modify freely the dimensions of the figures, so that I can i. extend the x axis on the left figure to better demonstrate the spread of the data, and to ii. idealize the ratio of the size of the two figures.
Below is my attempt:
#libraries used:
library(ggplot2)
library(dplyr)
#Pulling in example dataset:
data_1 <- iris
#Building my left figure, which has a continuous x and y axis, and establishing y axis limits to match between the two figures:
object_1 <- ggplot(data_1, aes(x = Sepal.Width, y = Sepal.Length)) + geom_point() + ylim(0, 10)
#Building my second data table:
data_2 <- iris %>% group_by(Species) %>% summarize(av_petal_length = mean(Petal.Length))
#Building my right hand figure, with empty y axis titles and text to provide space to combine the two figures on the left y axis:
object_2 <- ggplot(data_2, aes(x = Species, y = av_petal_length)) + geom_point() + ylim(0, 10) +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank())
#Attempt to grid.arrange:
grid.arrange(object_1, object_2, nrow = 1)
As you can see, a simple grid.arrange does not combine them completely. I have attempted to modify the panel margins in the two figures by tinkering with plot.margin() under theme(), but this requires a lot of tinkering and if the figures get resized at all the relationship between the two figures can become distorted. Is it possible to cleanly, simply combine these two figures into one cohesive rectangle, separated by a line, and to manually modify the dimensions of the figures?
Below, we're using seperate themes for the left and right plots that delete the relevant plot margins and the y-axis of the right plot.
I'm sure you can do it with grid.arrange() too, but {patchwork} allows you to set figure widths as well.
library(ggplot2)
library(dplyr)
library(patchwork)
# As before
data_1 <- iris
object_1 <- ggplot(data_1, aes(x = Sepal.Width, y = Sepal.Length)) + geom_point() + ylim(0, 10)
data_2 <- iris %>% group_by(Species) %>% summarize(av_petal_length = mean(Petal.Length))
object_2 <- ggplot(data_2, aes(x = Species, y = av_petal_length)) + geom_point() + ylim(0, 10)
# Remove relevant margins from theme, including y-axis elements on the right
theme_left <- theme(plot.margin = margin(5.5, 0, 5.5, 5.5))
theme_right <- theme(plot.margin = margin(5.5, 5.5, 5.5, 0),
axis.ticks.length.y = unit(0, "pt"),
axis.title.y = element_blank(),
axis.text.y = element_blank())
black_line <- annotate("segment", x = Inf, xend = Inf, y = -Inf, yend = Inf, size = 2)
# Patchwork everything together
(object_1 + theme_left + black_line) +
(object_2 + theme_right) +
plot_layout(widths = c(2, 1))
Created on 2022-02-01 by the reprex package (v2.0.1)
Using ggplot2's legend.position (and legend.justification), the two available parameters indicate the relative position of the legend, but what if I want to position the legend based on the coordinates of the plot?
I can't find a way to do it.
This is strange as annotate gives an x and y argument that allows such things.
Here is some toy data
library(ggplot2)
ggplot(data = mtcars, aes(x = mpg,y = disp,color = factor(cyl))) +
geom_point() +
theme(legend.position = c(0.01,0.01),
legend.justification = c(0,0))
Which gives:
What about if I want the bottom-left corner of the legend to be on coordinates (10,100)?
I don't think there is an easy way to do it. The only approach i could think of is to build the plot object to extract the ranges of the axes in order to convert (10, 100) into a relative coordinate that can be used with legend position. Admittedly, this is very hacky...
library(tidyverse)
p <- ggplot(data = mtcars, aes(x = mpg, y = disp, color = factor(cyl))) +
geom_point()
ranges <- ggplot_build(p) %>%
pluck("layout", "panel_params", 1) %>%
`[`(c("x.range", "y.range"))
x <- (10 - ranges$x.range[1]) / (ranges$x.range[2] - ranges$x.range[1])
y <- (100 - ranges$y.range[1]) / (ranges$y.range[2] - ranges$y.range[1])
p + theme(legend.position = c(x, y), legend.justification = c(0, 0))
Created on 2021-07-21 by the reprex package (v1.0.0)
I am using plotly 4.8 with ggplot2 3.0.0, and trying to add and align text labels to my scatter plots. However, it seems the hjust parameter is being ignored by plotly in geom_text(aes(....), hjust = "left"). (Also tried hjust = 0.)
GGPLOT OUTPUT
See it renders fine in plot window as a ggplot with labels left aligned.
PLOTLY OUTPUT
But the alignment is lost in conversion, and the text is centered.
So the question, is fixing this alignment possible with plotly?
TEST EXAMPLE CODE
library(ggplot2)
library(data.table)
library(plotly)
data(mtcars)
plotdata <- as.data.table(mtcars)
plotdata$carname <- rownames(mtcars)
# take a small demo subset
plotdata <- plotdata[1:10,]
gg <- ggplot(plotdata, aes(x = wt, y = mpg, label = carname)) +
geom_point() + theme_minimal()
gg <- gg + geom_text(aes(label = carname),
size = 2,
hjust = "left")
print(gg)
# convert ggplot
p <- ggplotly(gg)
p
You just need to add text position textposition = "right":
ggplotly(p) %>% style(textposition = "right")
Output:
Ref: https://github.com/ropensci/plotly/issues/769
I am using plotly 4.8 with ggplot2 3.0.0, and trying to add and align text labels to my scatter plots. However, it seems the hjust parameter is being ignored by plotly in geom_text(aes(....), hjust = "left"). (Also tried hjust = 0.)
GGPLOT OUTPUT
See it renders fine in plot window as a ggplot with labels left aligned.
PLOTLY OUTPUT
But the alignment is lost in conversion, and the text is centered.
So the question, is fixing this alignment possible with plotly?
TEST EXAMPLE CODE
library(ggplot2)
library(data.table)
library(plotly)
data(mtcars)
plotdata <- as.data.table(mtcars)
plotdata$carname <- rownames(mtcars)
# take a small demo subset
plotdata <- plotdata[1:10,]
gg <- ggplot(plotdata, aes(x = wt, y = mpg, label = carname)) +
geom_point() + theme_minimal()
gg <- gg + geom_text(aes(label = carname),
size = 2,
hjust = "left")
print(gg)
# convert ggplot
p <- ggplotly(gg)
p
You just need to add text position textposition = "right":
ggplotly(p) %>% style(textposition = "right")
Output:
Ref: https://github.com/ropensci/plotly/issues/769
Is there any way to line up the points of a line plot with the bars of a bar graph using ggplot when they have the same x-axis? Here is the sample data I'm trying to do it with.
library(ggplot2)
library(gridExtra)
data=data.frame(x=rep(1:27, each=5), y = rep(1:5, times = 27))
yes <- ggplot(data, aes(x = x, y = y))
yes <- yes + geom_point() + geom_line()
other_data = data.frame(x = 1:27, y = 50:76 )
no <- ggplot(other_data, aes(x=x, y=y))
no <- no + geom_bar(stat = "identity")
grid.arrange(no, yes)
Here is the output:
The first point of the line plot is to the left of the first bar, and the last point of the line plot is to the right of the last bar.
Thank you for your time.
Extending #Stibu's post a little: To align the plots, use gtable (Or see answers to your earlier question)
library(ggplot2)
library(gtable)
data=data.frame(x=rep(1:27, each=5), y = rep(1:5, times = 27))
yes <- ggplot(data, aes(x = x, y = y))
yes <- yes + geom_point() + geom_line() +
scale_x_continuous(limits = c(0,28), expand = c(0,0))
other_data = data.frame(x = 1:27, y = 50:76 )
no <- ggplot(other_data, aes(x=x, y=y))
no <- no + geom_bar(stat = "identity") +
scale_x_continuous(limits = c(0,28), expand = c(0,0))
gYes = ggplotGrob(yes) # get the ggplot grobs
gNo = ggplotGrob(no)
plot(rbind(gNo, gYes, size = "first")) # Arrange and plot the grobs
Edit To change heights of plots:
g = rbind(gNo, gYes, size = "first") # Combine the plots
panels <- g$layout$t[grepl("panel", g$layout$name)] # Get the positions for plot panels
g$heights[panels] <- unit(c(0.7, 0.3), "null") # Replace heights with your relative heights
plot(g)
I can think of (at least) two ways to align the x-axes in the two plots:
The two axis do not align because in the bar plot, the geoms cover the x-axis from 0.5 to 27.5, while in the other plot, the data only ranges from 1 to 27. The reason is that the bars have a width and the points don't. You can force the axex to align by explicitly specifying an x-axis range. Using the definitions from your plot, this can be achieved by
yes <- yes + scale_x_continuous(limits=c(0,28))
no <- no + scale_x_continuous(limits=c(0,28))
grid.arrange(no, yes)
limits sets the range of the x-axis. Note, though, that the alginment is still not quite perfect. The y-axis labels take up a little more space in the upper plot, because the numbers have two digits. The plot looks as follows:
The other solution is a bit more complicated but it has the advantage that the x-axis is drawn only once and that ggplot makes sure that the alignment is perfect. It makes use of faceting and the trick described in this answer. First, the data must be combined into a single data frame by
all <- rbind(data.frame(other_data,type="other"),data.frame(data,type="data"))
and then the plot can be created as follows:
ggplot(all,aes(x=x,y=y)) + facet_grid(type~.,scales = "free_y") +
geom_bar(data=subset(all,type=="other"),stat="identity") +
geom_point(data=subset(all,type=="data")) +
geom_line(data=subset(all,type=="data"))
The trick is to let the facets be constructed by the variable type which was used before to label the two data sets. But then each geom only gets the subset of the data that should be drawn with that specific geom. In facet_grid, I also used scales = "free_y" because the two y-axes should be independent. This plot looks as follows:
You can change the labels of the facets by giving other names when you define the data frame all. If you want to remove them alltogether, then add the following to your plot:
+ theme(strip.background = element_blank(), strip.text = element_blank())