ggplot2 image with too big legend when converted to jpg format - r

I am making a plot to be submitted to an academic journal.
I used png() function for anti-aliasing, but the legend in the plot is too big.
Please refer to the sample code and plot below.
posn.d <- position_dodge(0.1)
plot_test <- ggplot(mtcars, aes(factor(gear), hp, shape = factor(am)))+
ylab("HP")+
stat_summary(fun.y = mean, geom = "point", position = posn.d, aes(shape = factor(am)), size = 1)+
stat_summary(fun.y = mean, geom = "line", position = posn.d, aes(group = factor(am), linetype = factor(am)), size = 0.4)+
stat_summary(fun.data = mean_se, fun.args = list(mult = 1), geom = "errorbar", width = 0.1, position =posn.d, size=0.3)+
theme(legend.key = element_rect(colour = "white"), panel.border = element_blank(), panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), axis.line = element_line(colour = "black"),
axis.title.y=element_text(vjust=1,face="bold",size=6), axis.title.x = element_blank(),
axis.text.x=element_text(size=6, face = "bold"), axis.text.y=element_text(size=6, face = "bold"),
axis.line.x=element_line(size=0.2),axis.line.y=element_line(size=0.2),axis.ticks=element_line(size=0.2),
plot.title=element_text(hjust=0,vjust=2,face="bold", size=3))+
scale_linetype_manual(name = "AM", labels = c("am0", "am1"), values = c("solid", "dotted"))+
scale_shape_manual(name = "AM", labels = c("am0", "am1"), values = c(16, 15))+
scale_x_discrete(labels = c("three", "four", "five"))
png(width = 100, height = 35, units="mm", filename = "test.jpg", type = "cairo", antialias = "subpixel", family = "malgun", res = 300)
plot_test
dev.off()
I tried to reduce the size of the legend title and text, by adding
legend.title = element_text(size = 4, face = "bold"), legend.text = element_text(size = 4)
in the theme().
But the space between legend texts and title are still too large and look difficult to control.
Could anyone help me without changing the whole image size or resolution?
(Because the size and resolution can not be controlled freely to conform to the guidelines of the journal.)

Related

How can I completely remove legend.key from ggplot2 legend?

I'm plotting a graph and I need to completely remove the legend.key from my ggplot legend. Why I need to do this? The legend starts with a number that reference the X axis breaks, and the label its too large and I don't want to keep it in the X axis. So, in the X axis i put breaks=1:15, and in legend the label starts with this numbers.
In resume, I just want to remove the legend.key from my graph. Is it possible? I have tried legend.key=element_blank(), but without sucess.
Obs.: In the code is it possible to see that I don't want the fill=legto change the colors of each bar. Everything is set to be gray and I just want to remove de legend.key.
ggplot(IC_QS, aes(x=ind,y=values))+
geom_boxplot(aes(fill=leg),color="black", outlier.colour = "red")+
labs(title = "XXXXXXXXXX",
subtitle = "XXXXXXXXXXX",
caption = "XXXXXXXXXXXXX")+
scale_x_discrete(name = "", labels=1:15)+
scale_y_continuous(name = "XXX", breaks = seq(0,10,1), expand = c(0,0.08*max(IC_QS$values)))+
scale_fill_manual(name="Sectors", values = rep("gray", 15), labels=str_wrap(IC_QS_leg,25))+
theme(legend.position = "right", legend.background = element_blank(),
legend.key = element_blank(),legend.text = element_text(face = "bold", size = 8,),
panel.background = element_blank(), panel.grid.major = element_line(colour = "gray", linetype = "dashed"),
axis.title.x = element_text(face = "bold",vjust = -1), axis.title.y = element_text(face="bold", vjust = +1.5),
axis.text = element_text(colour="black", face = "bold"), title = element_text(face = "bold"))
Obviously we don't have your data, but here's an idea using the iris built-in data set
ggplot(iris, aes(Species, Petal.Width, fill = Species)) +
geom_boxplot() +
scale_x_discrete(labels = seq(length(levels(iris$Species)))) +
scale_fill_manual(values = rep("grey", length(levels(iris$Species))),
labels = paste(seq(length(levels(iris$Species))),
levels(iris$Species), sep = " - ")) +
guides(fill = guide_legend(override.aes = list(color = NA, fill = NA))) +
theme_light(base_size = 16) +
theme(legend.key.width = unit(0, "mm"))

Controlling legend key spacing and color in ggplot2 [duplicate]

This question already has answers here:
Smaller gap between two legends in one plot (e.g. color and size scale)
(2 answers)
Modify spacing between key glyphs in vertical legend whilst keeping key glyph border [duplicate]
(1 answer)
How to set multiple legends / scales for the same aesthetic in ggplot2?
(2 answers)
How to manually change text color of ggplot2 legend in R?
(2 answers)
Match legend text color in geom_text to symbol [duplicate]
(4 answers)
Closed last year.
Issue
I am sure this is a very fix for someone who has done it before. I want to have a different spacing between legend key box 2 and 3 in ggplot2. Is there any trick to have a different spacing between the boxes of the legend? I also want to match the color of the legend box and the text. Currently, the legend text is black. So, I have two questions here
How I add a different spacing between the legend keys: bigger spacing between box 2 and 3, keep the spacing between other legends key boxes similar).
How to match the color of the legend key box with the text?
Here is my data and code. I have also added the design of the graph I want to produce from this code.
Packages
library(tidyverse)
library(ggplot2)
library(ggtext)
Sample Data
Food = c("meat", "meat", "meat", "meat", "wheat","wheat","wheat",
"wheat", "maize","maize","maize","maize")
Subgroup = c("Male", "Female", "Urban", "Rural", "Male", "Female",
"Urban", "Rural", "Male", "Female","Urban", "Rural")
mean = c(8.66, 10.45, 9.88, 7.32, 21.04, 19.65, 20.26, 20.87, 51.06 , 44.51, 47.60, 48.40)
df <- data.frame(Food, Subgroup, mean)
df$Subgroup[df$Subgroup == "Urban"] <- 1
df$Subgroup[df$Subgroup == "Rural"] <- 2
df$Subgroup[df$Subgroup == "Female"] <- 3
df$Subgroup[df$Subgroup == "Male"] <- 4
df$Subgroup <- factor(df$Subgroup,
levels = c(1, 2, 3, 4),
labels = c("Urban", "Rural", "Female", "Male"))
#Color code
colorPanel = c( '#42235e', '#7d103d', '#007da5', '#003b5d' )
# bar chart
Plot_FBGDS <- ggplot(df, aes(x = Food, y = mean, fill = Subgroup)) +
geom_col(stat = "identity", position = position_dodge(-0.84), width = 0.82) +
scale_y_continuous(breaks = c(0,20, 40, 60,80), expand = c(0,0),
limits = c(0,100),
labels = function(x) paste0(x, "%")) +
geom_text(aes(label = paste0(mean,"%"), y = mean + 2, color = Subgroup), stat = "identity",
size = 3, vjust = 0.5, face = "bold", family = "sans", position = position_dodge(-0.88)) +
scale_color_manual(values = colorPanel) +
scale_x_discrete(limits = c("meat",
"wheat",
"maize")) +
coord_flip() +
scale_fill_manual(values = colorPanel) +
labs( x= " ",
y = " ") +
theme(text = element_text(size = 14, color = "black", family = "sans"),
panel.background = element_rect(fill = "white"),
panel.border = element_blank(),
axis.text.y = element_text(family = "sans", color = "black", size = 14),
axis.text.x = element_blank(),
axis.line.x = element_blank(),
axis.line.y = element_line(),
axis.ticks.y.left = element_line(colour = "green"),
axis.ticks.length=unit(0, "cm"),
axis.title.x = element_text(size = 10, color = "black", family = "sans"),
axis.text = element_text(size = 10, color = "black",family = "sans"),
legend.key = element_rect(colour = NA, fill=NA, size= 7),
legend.text = element_text(size = 10, family = "sans"),
legend.margin=margin(t= -1, r= 2, b= 2, l= 2),
legend.title = element_blank(),
legend.key.height = unit(0.03, "npc"),
legend.key.width = unit(0.03, "npc"),
legend.position = c(0.85, 0.70),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.minor.x = element_blank())
Plot_FBGDS
Current Plot
One option would be the ggnewscale package which allows to add a second fill legend. Doing so we could draw separate legends for Urban/Rural and for Male/Female for which the spacing could be set via legend.spacing.y and legend.margin. To make this work we have to add a duplicated geom_col where we have to explicitly map on the fill aes.
To color the legend text according to the bars you could make use of the ggtext package which allows styling of text via markdown, HTML and CSS. To this end use ggtext::element_markdown for legend.text and add labels to the legends where you set your desired colors using some HTML and CSS.
EDIT Following this answer by #teunbrand we could increase or set the spacing between legend keys via legend.spacing.y if we set byrow=TRUE in guide_legend. However, as legend.spacing.y also effects the spacing between legends you probably have to adjust the top and/or bottom legend.margin as well to get your desired spacing between the legends for each group.
library(ggplot2)
library(ggtext)
# Color code
colorPanel <- c("#42235e", "#7d103d", "#007da5", "#003b5d")
names(colorPanel) <- c("Urban", "Rural", "Female", "Male")
labels <- paste0("<span style='color:", colorPanel, ";'>", names(colorPanel), "</span>")
names(labels) <- names(colorPanel)
# bar chart
library(ggnewscale)
Plot_FBGDS <- ggplot(df, aes(x = Food, y = mean, fill = Subgroup)) +
geom_col(position = position_dodge(-0.84), width = 0.82) +
geom_text(aes(label = paste0(mean, "%"), y = mean + 1, color = Subgroup),
size = 3, hjust = 0, vjust = 0.5, fontface = "bold", family = "sans", position = position_dodge(-0.88), show.legend = FALSE
) +
scale_fill_manual(values = colorPanel, breaks = c("Urban", "Rural"), labels = labels[c("Urban", "Rural")],
aesthetics = c("color", "fill"),
guide = guide_legend(order = 1, byrow = TRUE)) +
new_scale_fill() +
new_scale_color() +
geom_col(aes(fill = Subgroup), position = position_dodge(-0.84), width = 0.82) +
scale_fill_manual(values = colorPanel, breaks = c("Female", "Male"), labels = labels[c("Female", "Male")],
aesthetics = c("color", "fill"),
guide = guide_legend(order = 2, byrow = TRUE)) +
scale_y_continuous(
breaks = c(0, 20, 40, 60, 80), expand = c(0, 0),
limits = c(0, 100),
labels = function(x) paste0(x, "%")
) +
scale_x_discrete(limits = c(
"meat",
"wheat",
"maize"
)) +
coord_flip() +
labs(x = " ", y = " ") +
theme(
text = element_text(size = 14, color = "black", family = "sans"),
panel.background = element_rect(fill = "white"),
panel.border = element_blank(),
axis.text.y = element_text(family = "sans", color = "black", size = 14),
axis.text.x = element_blank(),
axis.line.x = element_blank(),
axis.line.y = element_line(),
axis.ticks.y.left = element_line(colour = "green"),
axis.ticks.length = unit(0, "cm"),
axis.title.x = element_text(size = 10, color = "black", family = "sans"),
axis.text = element_text(size = 10, color = "black", family = "sans"),
legend.text = element_markdown(size = 10, family = "sans"),
legend.key = element_rect(colour = NA, fill = NA, size = 7),
legend.key.height = unit(0.03, "npc"),
legend.key.width = unit(0.03, "npc"),
legend.margin = margin(t = 4, r = 2, b = 4, l = 2),
legend.title = element_blank(),
legend.position = c(0.85, 0.70),
legend.spacing.y = unit(8, "pt"),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.minor.x = element_blank()
)
Plot_FBGDS
#> Warning: position_dodge requires non-overlapping x intervals

Facet_Wrap title overlapping with y-axis in ggplotly?

I am currently using a facet_wrap to knit data (html) to an Rmarkdown document. I am looking to use facet_wrap and ggplot to show this data.
This is how the data looks just in ggplot (not plotly):
However, when I use plotly the titles of each plot intersect with the y-axis making interpretability difficult
Here is the code to the first plot not using plotly
plot <- ggplot(dataframe, aes(x = week, y = measure))+
geom_line(size = 1.25, aes(color = "orange"))+
geom_point(size = 1.50)+
theme_economist()+
geom_smooth(method = 'auto', se = FALSE, size = .50)+
scale_x_continuous(breaks=seq(0,13,1))+
scale_y_continuous(labels = scales::dollar_format(scale = .0001, suffix = "K"))+
facet_wrap(vars(category), scales = "free", ncol = 3, nrow = 6)+
theme(legend.position = "none")+
ggtitle('Title')+
theme(
panel.grid.major = element_line(linetype = "dotted"),
axis.title.x = element_text( size = 10, margin=margin(30,0,0,0)),
axis.title.y = element_text( size = 10, margin=margin(0,30,0,0)),
axis.text = element_text( size = 10),
plot.title = element_text( size = 14, margin=margin(0,0,35,0), hjust = 0.5),
panel.background = element_rect(fill = NA),
strip.text = element_text(size=10)
)
Then here is what I do to conver it to plotly
ggplotly(plot)
Some random data:
require(stats)
r <- function(x) {abs(as.numeric(arima.sim(n=x, list(order=c(1,0,0),ar=0.95)))+10)}
dataframe = data.frame(week = rep(1:13,6), measure = r(13*6), category = rep(as.character(1:6),each=13))
Although I was not able to reproduce the overlapping y-axis labels, you can set the margins between the faceted plots by setting panel.margin.y and panel.margin.x inside the theme():
plot <- ggplot(dataframe, aes(x = week, y = measure))+
geom_line(size = 1.25, aes(color = "orange"))+
geom_point(size = 1.50)+
theme_economist() +
geom_smooth(method = 'auto', se = FALSE, size = .50)+
scale_x_continuous(breaks=seq(0,13,1))+
scale_y_continuous(labels = scales::dollar_format(scale = .0001, suffix = "K"))+
facet_wrap(vars(category), scales = "free", ncol = 3, nrow = 6)+
theme(legend.position = "none")+
ggtitle('Title')+
theme(
panel.grid.major = element_line(linetype = "dotted"),
axis.title.x = element_text( size = 10, margin=margin(30,0,0,0)),
axis.title.y = element_text( size = 10, margin=margin(0,30,0,0)),
axis.text = element_text( size = 10),
plot.title = element_text( size = 14, margin=margin(0,0,35,0), hjust = 0.5),
panel.background = element_rect(fill = NA),
strip.text = element_text(size=10), panel.margin.x = unit(1,"lines"), panel.margin.y = unit(0,"lines")
)
ggplotly(plot)
You can fine tune the margins to suit your plot.
Before:
> ggplotly(plot)
After:
> ggplotly(plot)

How to change size of points of legend when 2 legends are present

I have a graph where two legends are present. I need to change the size of the points of one of the legends.
I need to change the bullet size of "market type" in the legend. I use the example here but does not work for my graph.
My code is below:
k <- ggplot(subsetdf) + theme_bw() +
geom_point( aes(y=y, x=x, size =Total.Unit.Count, fill = region), shape=21)+
scale_colour_hue(aes(y=y, x=x),l=50) + # Use a slightly darker palette than normal
geom_text_repel (aes(y=y, x=x, label = rownames(subsetdf))) +
geom_smooth(aes(x=x, y=y),method=lm, # Add linear regression lines
se=FALSE) +
labs(y = "title", x = "title",
title = "title",
size = "size", fill = "fill")+
theme(plot.title = element_text (face = 'bold',size = 21,hjust = 0.5),
legend.text = element_text(size = 16),
legend.title = element_text(size = 18),
axis.title.x = element_text(size=20),
axis.title.y = element_text(size=20),
axis.text.x = element_text(size = 18,angle=45, hjust=1),
axis.text.y = element_text(size = 18,hjust = 1),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())+
scale_size_continuous(range = c(3,8))+
guides(colour = guide_legend(override.aes = list(size=10)))
You used the fill aesthetic guide, not color. So that is the guide to override.
Below is an example with iris dataset, as you code is not reproducible.
library(ggplot2)
ggplot(iris) +
geom_point(aes(Sepal.Length, Petal.Length, size = Sepal.Width, fill = Species), shape = 21) +
guides(fill = guide_legend(override.aes = list(size=8)))

Adjusting white space between titles and the edge of the plot

I want to create space between the titles (the axis title and the plot title) and the edge of the plot. I tried vjust on axis.title and plot.title with no luck. Nothing really changed in the plot when I tried various values for vjust. I also tried plot.margin, but nothing seemed to happen with it either.
Data:
data = data.frame(Category = c(0,1), value = c(40000, 120000))
data$Category = factor(data$Category, levels = c(0,1), labels = c("One-time", "Repeat"))
Plot:
p = ggplot(data, aes(Category, Value)) +
geom_bar(stat = "identity", width = 0.5, position=position_dodge(width=0.9)) +
geom_text(aes(label=Value), position=position_dodge(width=0.9), family = "mono", vjust=-0.5) +
ggtitle("Title") +
scale_y_continuous(expand = c(0,0), limits = c(0,150000)) +
scale_x_discrete(expand = c(0,0), limits = c("One-time", "Repeat")) +
xlab("X axis Title") +
ylab("Y axis Title")
Theme:
p + theme(
panel.grid.major = element_line(linetype = "blank"),
panel.grid.minor = element_line(linetype = "blank"),
axis.title = element_text(family = "sans", size = 15),
axis.text = element_text(family = "mono", size = 12),
plot.title = element_text(family = "sans", size = 18),
panel.background = element_rect(fill = NA)
)
You want to do this using margin
p + theme(
panel.grid.major = element_line(linetype = "blank"),
panel.grid.minor = element_line(linetype = "blank"),
axis.title.x = element_text(family = "sans", size = 15, margin=margin(30,0,0,0)),
axis.title.y = element_text(family = "sans", size = 15, margin=margin(0,30,0,0)),
axis.text = element_text(family = "mono", size = 12),
plot.title = element_text(family = "sans", size = 18, margin=margin(0,0,30,0)),
panel.background = element_rect(fill = NA)
)
Note that margin requires a four inputs and they specify the space in the order top,right,bottom,left. Also note I'm using the developmental ggplot2 version so my title default is left justified. 'hjust' and 'vjust' worked in older versions of ggplot2.
You can also give a "-ve" value to margin argument to pull the title closer to the plot.
gg_1 +
theme(plot.title = element_text(size = 12,
hjust = 0.5,
family = fam_3,
face = "bold",
margin = margin(0,0,-10,0))

Resources