Hi I'm looking for a way to scale a ggplot2's legend (independently of the plot and label sizes), and ideally an automated way to do this scaling so that it fits on the page.
It is very easy to scale the whole plot, including the legend, just by changing the output size but sometimes (especially if I am pasting the plots into a report at a fairly small size) I might need the axis sizes to be fairly large.
I can't find any way to scale the legend as a whole. The options seem to be to manually split it over several rows or to change each element of the legend independently (as I have attempted below); but some of these can be set using rel and some can't its hard to know what you'll end up with and it still looks a bit funny.
It seems strange to me that the default behaviour for ggplot2 is to allow legends to go off the page.
Basically I want the plot like this (which was made in paint using a combination of test.png and test2.png from my example below):
Ideally I'd like it if during the save process it figures out the plot widow and then applies some scaling automatically. Otherwise if I could scale down with a percentage that what be second best option.
Reprex of a simple example. WARNING it saves to a c:\temp folder
library(ggplot2)
testplot <- ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length, colour = Species))+
geom_point()+geom_hline(aes(yintercept = 6, colour = "example line"))+theme(legend.position = "bottom")
ggsave(testplot,filename = "C:/temp/test.png", width = 3, height = 3)
ggsave(testplot,filename = "C:/temp/test2.png", width = 6, height = 6)
test2 <- testplot + theme(legend.key.size = unit(0.5,"lines"), legend.text = element_text(size = rel(0.5)),
legend.title = element_text(size = rel(0.5)))+
guides(colour = guide_legend(override.aes = list(size = 0.6)))
ggsave(test2,filename = "C:/temp/test3.png", width = 3, height = 3)
Created on 2019-09-04 by the reprex package (v0.3.0)
Related
I use R for most of my data analysis. Until now I used to export the results as a CSV and visualized them using Macs Numbers.
The reason: The Graphs are embeded in documents and there is a rather large border on the right side reserved for annotations (tufte handout style). Between the acutal text and the annotations column there is white space. The plot of the graphs needs to fit the width of text while the legend should be placed in the annotation column.
I would prefer to also create the plots within R for a better workflow and higher efficiency. Is it possible to create such a layout using plotting with R?
Here is an example of what I would like to achieve:
And here is some R Code as a starter:
library(tidyverse)
data <- midwest %>%
head(5) %>%
select(2,23:25) %>%
pivot_longer(cols=2:4,names_to="Variable", values_to="Percent") %>%
mutate(Variable=factor(Variable, levels=c("percbelowpoverty","percchildbelowpovert","percadultpoverty"),ordered=TRUE))
ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
geom_col(position=position_dodge(width=0.85),width=0.8) +
labs(x="County") +
theme(text=element_text(size=9),
panel.background = element_rect(fill="white"),
panel.grid = element_line(color = "black",linetype="solid",size= 0.3),
panel.grid.minor = element_blank(),
panel.grid.major.x=element_blank(),
axis.line.x=element_line(color="black"),
axis.ticks= element_blank(),
legend.position = "right",
legend.title = element_blank(),
legend.box.spacing = unit(1.5,"cm") ) +
scale_y_continuous(breaks= seq(from=0, to=50,by=5),
limits=c(0,51),
expand=c(0,0)) +
scale_fill_manual(values = c("#CF232B","#942192","#000000"))
I know how to set a custom font, just left it out for easier saving.
Using ggsave
ggsave("Graph_with_R.jpeg",plot=last_plot(),device="jpeg",dpi=300, width=18, height=9, units="cm")
I get this:
This might resample the result aimed for in the actual case, but the layout and sizes do not fit exact. Also recognize the different text sizes between axis titles, legend and tick marks on y-axes. In addition I assume the legend width depends on the actual labels and is not fixed.
Update
Following the suggestion of tjebo I posted a follow-up question.
Can it be done? Yes. Is it convenient? No.
If you're working in ggplot2 you can translate the plot to a gtable, a sort of intermediate between the plot specifications and the actual drawing. This gtable, you can then manipulate, but is messy to work with.
First, we need to figure out where the relevant bits of our plot are in the gtable.
library(ggplot2)
library(gtable)
library(grid)
plt <- ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
geom_bar(position = position_dodge2(preserve = "single"))
# Making gtable
gt <- ggplotGrob(plt)
gtable_show_layout(gt)
Then, we can make a new gtable with prespecified dimensions and place the bits of our old gtable into it.
# Making a new gtable
new <- gtable(widths = unit(c(12.5, 1.5, 4), "cm"),
heights = unit(9, "cm"))
# Adding main panel and axes in first cell
new <- gtable_add_grob(
new,
gt[7:9, 3:5], # If you see the layout above as a matrix, the main bits are in these rows/cols
t = 1, l = 1
)
# Finding the legend
legend <- gt$grobs[gt$layout$name == "guide-box"][[1]]
legend <- legend$grobs[legend$layout$name == "guides"][[1]]
# Adding legend in third cell
new <- gtable_add_grob(
new, legend, t = 1, l = 3
)
# Saving as raster
ragg::agg_png("test.png", width = 18, height = 9, units = "cm", res = 300)
grid.newpage(); grid.draw(new)
dev.off()
#> png
#> 2
Created on 2021-04-02 by the reprex package (v1.0.0)
The created figure should match the dimensions you're looking for.
Another option is to draw the three components as separate plots and stitch them together in the desired ratio.
The below comes quite close to the desired ratio, but not exactly. I guess you'd need to fiddle around with the values given the exact saving dimensions. In the example I used figure dimensions of 7x3.5 inches (which is similar to 18x9cm), and have added the black borders just to demonstrate the component limits.
library(tidyverse)
library(patchwork)
data <- midwest %>%
head(5) %>%
select(2,23:25) %>%
pivot_longer(cols=2:4,names_to="Variable", values_to="Percent") %>%
mutate(Variable=factor(Variable, levels=c("percbelowpoverty","percchildbelowpovert","percadultpoverty"),ordered=TRUE))
p1 <-
ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
geom_col() +
scale_fill_manual(values = c("#CF232B","#942192","#000000"))
p_legend <- cowplot::get_legend(p1)
p_main <- p1 <-
ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
geom_col(show.legend = FALSE) +
scale_fill_manual(values = c("#CF232B","#942192","#000000"))
p_main + plot_spacer() + p_legend +
plot_layout(widths = c(12.5, 1.5, 4)) &
theme(plot.margin = margin(),
plot.background = element_rect(colour = "black"))
Created on 2021-04-02 by the reprex package (v1.0.0)
update
My solution is only semi-satisfactory as pointed out by the OP. The problem is that one cannot (to my knowledge) define the position of the grob in the third panel.
Other ideas for workarounds:
One could determine the space needed for text (but this seems not so easy) and then to size the device accordingly
Create a fake legend - however, this requires the tiles / text to be aligned to the left with no margin, and this can very quickly become very hacky.
In short, I think teunbrand's solution is probably the most straight forward one.
Update 2
The problem with the left alignment should be fixed with Stefan's suggestion in this thread
I have the dataset with values for different keys for three independent groups and I would like to create a bar plot with these values for 3 different groups using facet_grid. This is what I did so far, however I can not find the way to fix spacing between the bars (it is clearly observed form the picture that they are different). I tried changing arguments width and position for geom_col, but it did not help. How can I fix it?
library(ggplot2)
# Loading
groups = c(rep("q", 8), rep("w", 8), rep("e", 8))
keys = c(c(1:8), c(1:8), c(1:8))
values = c(rep(8, 8), rep(8, 8), rep(8, 8))
data = data.frame(groups, values, keys)
ggplot(data, aes(x = keys, y = values)) +
geom_col(width=0.9375) +
facet_grid(~groups)
My guess is that it is an issue with the size/resolution of the image. If the size/resolution is small, you may get this result. Your image is 614 x 362 and #Leonardo is 1362x699. My guess is that this is what is causing this weird spacing.
g <- ggplot(data, aes(x = keys, y = values)) +
geom_col() + facet_grid(~groups)
png(filename = "Rplotsmall.png", width=614, height = 362)
print(g)
dev.off()
png(filename = "Rplotlarge.png", width=1362, height = 699)
print(g)
dev.off()
Small image (with weird spacing):
Large image (spacing appears ok):
If you are using Rstudio and printing to the plot window, you should see this weird effect appear and disappear when you click "zoom" and increase or reduce the size of the window (because then Rstudio is redrawing the image with different size).
Try this:
ggplot(data) +
geom_bar(aes(x = keys, y = values), stat = "identity") +
facet_grid(~groups)
I have made plots in R (RStudio) with ggplot2. When I export them via export::graph2office, the labels are moved around. However, this only happens when I specify the font for the labels.
library (ggplot2)
library (export)
plot_data <- data.frame (a = runif (1:20), b = seq (1:20))
x11 (width = 3, height = 3)
ggplot (data = plot_data, mapping = aes (x = a, y = b)) +
geom_point () +
labs (x = "my x-label", y = "my y-label") +
theme (panel.background = element_blank(),
panel.border = element_rect (fill = NA, size = 0.7),
axis.ticks = element_line (color = "black", lineend = "round"),
axis.ticks.length = unit (2, "mm"),
axis.text = element_text (color = "black"),
plot.margin = unit(rep (0, 4), "cm"),
text = element_text (size=18,
family="ChantillyLH",
color = "black")
)
graph2office (file = "my_graph", type = "DOC")
Here, you can see the graph in R (to the right) and the exported graph in word (to the left):
The undesired behaviour is more obvious for the y-label in this example, but also the x-label is moved a bit. I wonder if there is a way to fix this.
The same happens when I specify another font family, for example family="Comic Sans MS":
EDIT: it even happens when no textcommand is given:
The answer probably is: yes, export::graph2office moves axis labels around (so do export::graph2pptand export::graph2doc). There is no way to fix this. If you want to style your graphs in R and export them as-is into Office, the export::graph2office function, unfortunately, is not your way to go. However, the function can of course be used as a quick-and-dirty option to produce editable office-graphs.
If your goal is to export graphs in a more reliable manner, CairoSVG might be a much better option (see my answer here: Producing a vector graphics image (i.e. metafile) in R suitable for printing in Word 2007).
Look I will put 2 images of the same plot, the only thing I made was changing the size of it, expanding the size of the plot to the sizes before saving it as an image. But the code is the same in both plots. How can I make this not to happen?
here I attach same plot with different size, see how much change the columns, now there is a new column with máximum of 15 that was not before !!!:
Here I attach my code for the plot:
plot<-ggplot(DT,aes(x=Date,y=n_per_week,fill=var2))+
geom_bar(stat="identity", size= 4)+
theme(plot.title = element_text(hjust = 0.5))+
ylab("N")+xlab("Date (Month-Year)")+
scale_x_date(date_labels = "%m-%y")+
theme_minimal(base_size = 11, base_family = "")
How can I make this changes not to happen !!
Thanks in advance !
Can I adjust the point size, alpha, font, and axis ticks in a plotmatrix?
Here is an example:
library(ggplot2)
plotmatrix(iris)
How can I:
make the points twice as big
set alpha = 0.5
have no more than 5 ticks on each axis
set font to 1/2 size?
I have fiddled with the mapping = aes() argument to plotmatrix as well as opts() and adding layers such as + geom_point(alpha = 0.5, size = 14), but none of these seem to do anything. I have hacked a bit of a fix to the size by writing to a large pdf (pdf(file = "foo.pdf", height = 10, width = 10)), but this provides only a limited amount of control.
Pretty much all of the ggplot2 scatterplot matrix options are still fairly new and can be a bit experimental.
But the facilities in GGally do allows you to construct this kind of plot manually, though:
custom_iris <- ggpairs(iris,upper = "blank",lower = "blank",
title = "Custom Example")
p1 <- ggplot(iris,aes(x = Sepal.Length,y = Sepal.Width)) +
geom_point(size = 1,alpha = 0.3)
p2 <- ggplot(iris,aes(x = Sepal.Width,y = Sepal.Length)) +
geom_point()
custom_iris <- putPlot(custom_iris,p1,2,1)
custom_iris <- putPlot(custom_iris,p2,3,2)
custom_iris
I did that simply by directly following the last example in ?ggpairs.