This question already has answers here:
Wrap long axis labels via labeller=label_wrap in ggplot2
(4 answers)
Closed 2 years ago.
I know hjust is used for the x title axis, but how would I go about centering and multilining the x axis labels? Here is my plotting function:
gg_fun<-function(){
ggplot(tab,
aes(x = Var1, y = Percent)) +
#theme_light() +
theme(panel.background = element_rect(fill = NA),
axis.title.y=element_text(angle=0, vjust=0.5, face="bold"),
axis.title.x=element_blank(),
axis.text.y = element_text(size = 10),
axis.text.x = element_text(size = 12),
axis.ticks.x = element_blank(),
axis.ticks.y = element_blank(),
#panel.grid.minor = element_line(colour = "dark gray"),
panel.grid.major.x = element_blank() ,
# explicitly set the horizontal lines (or they will disappear too)
panel.grid.major.y = element_line(size=.1, color="dark gray" ),
axis.line = element_line(size=.1, colour = "black"),
plot.background = element_rect(colour = "black",size = 1)) +
geom_bar(stat = "Identity", fill="#5596E6") + #"cornflower" blue
ggtitle(element_blank()) +
scale_y_continuous(expand = c(0, 0), breaks = round(seq(0, 1, by = .1), digits = 2),
labels = percent(round(seq(0, 1, by = .1), digits = 2), digits = 0),
limits = c(0,.6)) #+
#scale_x_discrete()
}
Here is an example graph it produces:
I am aware of n.dodge argument for scale_x_discrete(), but this is not what I am looking for. I also do not want to simply abbreviate using labels = abbreviate or specifying precisely as this is time consuming. I have also seen for example levels(birds$effect) <- gsub(" ", "\n", levels(birds$effect)), but this skips every line and makes some labels far too long. How would I go about centering the x label text as well as having it multiline to prevent overlap? Example of what I am going for:
You can use stringr::str_wrap as a labelling function in scale_x_discrete.
Let's take some sample data:
tab <- data.frame(Var1 = c("Video of presentation incl visuals",
"Video of presentation, written text and visuals",
"Written text, plus visuals",
"Other (please specify)"),
Percent = c(0.33, 0.34, 0.16, 0.17))
With your original function, this gives the following plot:
gg_fun()
But with the following modification:
gg_fun<-function(){
ggplot(tab,
aes(x = Var1, y = Percent)) +
#theme_light() +
theme(panel.background = element_rect(fill = NA),
axis.title.y=element_text(angle=0, vjust=0.5, face="bold"),
axis.title.x=element_blank(),
axis.text.y = element_text(size = 10),
axis.text.x = element_text(size = 12),
axis.ticks.x = element_blank(),
axis.ticks.y = element_blank(),
#panel.grid.minor = element_line(colour = "dark gray"),
panel.grid.major.x = element_blank() ,
# explicitly set the horizontal lines (or they will disappear too)
panel.grid.major.y = element_line(size=.1, color="dark gray" ),
axis.line = element_line(size=.1, colour = "black"),
plot.background = element_rect(colour = "black",size = 1)) +
geom_bar(stat = "Identity", fill="#5596E6") + #"cornflower" blue
ggtitle(element_blank()) +
scale_y_continuous(expand = c(0, 0),
breaks = round(seq(0, 1, by = .1), digits = 2),
labels = scales::percent(round(seq(0, 1, by = .1),
digits = 2), digits = 0),
limits = c(0,.6)) +
scale_x_discrete(labels = function(x) stringr::str_wrap(x, width = 16))
}
We get:
gg_fun()
Typically, you have to manually place the newline character '\n' within your labels. However, someone wrote a function to do this automatically, which is provided in this thread.
Related
I have recently started using the facet_nested function from the ggh4x package and I really like the look of the nested axis. I would like to annotate the plot to show stats that I have run. I have created a dummy dataset to illustrate my problem.
library(tidyverse)
library(markdown)
library(ggtext)
library(ggh4x)
df <- data.frame(pretreatment = c("10NA", "10NA","10NA", "NT", "NT", "NT"),
timepoint = c("0 h", "6 h","6 h", "0 h", "6 h", "6 h"),
treatment = c("baseline", "10NA", "NT","baseline", "10NA", "NT"),
mean_copy_no = c(1000, 1500, 1200, 600, 700, 400),
sample_id = c(1, 2, 3, 4, 5, 6))
df %>%
ggplot(aes(x=sample_id, y = mean_copy_no, fill = treatment)) +
geom_col(colour = "black") +
facet_nested(.~ pretreatment + timepoint + treatment, scales = "free", nest_line = TRUE, switch = "x") +
ylim(0,2000) +
theme_bw() +
theme(strip.text.x = element_text(size = unit(10, "pt")),
legend.position = "none",
axis.title.y = element_markdown(size = unit(13, "pt")),
axis.text.y = element_text(size = 11),
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
strip.text = element_markdown(size = unit(12, "pt")),
strip.background = element_blank(),
panel.spacing.x = unit(0.05,"line"),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.border = element_blank())
This generates the following plot.
Now my problem is that each of the bars is located within its own facet and not all on one x axis (if you run the code without the theme, it shows it more clearly).
I have drawn how I would like the plot to look.
I would like to add lines and stars to indicate significant differences.
I can easilly add the stars, however I am struggling to add the lines and I understand that this may not even be possible because I am using facets to generate the plot. I just wanted to post the question and see if someone has any suggestion on how to do it in R. Or if there is a way to achieve the nested look without using facets.
*Edited for clarity.
One option is to use cowplot after making the ggplot object, where we can add the lines and text.
library(ggplot2)
library(cowplot)
results <- df %>%
ggplot(aes(x=sample_id, y = mean_copy_no, fill = treatment)) +
geom_col(colour = "black") +
facet_nested(.~ pretreatment + timepoint + treatment, scales = "free", nest_line = TRUE, switch = "x") +
ylim(0,2000) +
theme_bw() +
theme(strip.text.x = element_text(size = unit(10, "pt")),
legend.position = "none",
axis.title.y = element_markdown(size = unit(13, "pt")),
axis.text.y = element_text(size = 11),
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
strip.text = element_markdown(size = unit(12, "pt")),
strip.background = element_blank(),
panel.spacing.x = unit(0.05,"line"),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.border = element_blank())
ggdraw(results) +
draw_line(
x = c(0.07, 0.36),
y = c(0.84, 0.84),
color = "black", size = 1
) +
annotate("text", x = 0.215, y = 0.85, label = "*", size = 15) +
draw_line(
x = c(0.7, 0.98),
y = c(0.55, 0.55),
color = "black", size = 1
) +
annotate("text", x = 0.84, y = 0.56, label = "**", size = 15)
Output
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
My question is very similar to this question, but it's not the same.
I am looking for a way to create an empty ggplot with just the legend. However, in contrast to the autohor of the question I linked at the top, I actually need to create just the legend with no plot area included in the image.
I tried the following code:
ggplot(NULL, aes(color = ""))+
geom_blank()+
scale_color_manual(values = "black", labels = "Something")+
guides(color = guide_legend())+
theme(legend.box.background = element_rect(color = "black"))
But I'm getting the opposite of what I want - I am getting an empty plot area with no legend, like this:
And I would like my end result to look like this (I drew this in Paint):
Any help would be appreciated!
You can make a normal plot, then play with theme to achieve the desired result.
library(ggplot2)
ggplot(data.frame(x = 1, y = 1, colour = 'Something'), aes(x, y, fill = colour))+
geom_point(alpha=0, shape = 0)+ # completely transparent rectangular point
scale_fill_manual(values='black', drop=FALSE) +
guides(fill = guide_legend(override.aes = list(alpha=1, size = 40)))+ # showing the point in the legend
theme(axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
legend.position = c(0.5, 0.5), # move the legend to the center
legend.title = element_blank(),
legend.text = element_text(size = 40),
legend.key = element_rect(fill='NA'),
panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill='white', size=1)
)
Get the legend how you want it to look, then extract it with cowplot::get_legend:
library(grid)
library(cowplot)
library(ggplot2)
grid.newpage()
grid.draw(get_legend(
ggplot(data.frame(x = 1, y = 1), aes(x, y, fill = "Something")) +
geom_col(size = 20)+
scale_fill_manual(values = "white", labels = "Something", name = "") +
theme_bw() +
theme(legend.box.background = element_rect(color = "black"),
legend.title = element_text(size = 30),
legend.key.size = unit(60, "points"),
legend.text = element_text(size = 24),
legend.key = element_rect(colour = "black"),
legend.box.margin = margin(20, 20, 20, 20))))
I have implemented a density plot in R using ggplot2 (updated version):
library(ggplot2)
densityplot <- function(values, pointsInTime, title) {
df <- data.frame(x = 1:length(pointsInTime), y = values)
p <-
ggplot(df, aes(x = x, y = y)) +
geom_area(alpha = 0.5, fill = '#42a5ed') +
geom_line(color = '#42a5ed') +
geom_point(
color = '#42a5ed',
fill = 'white',
shape = 21,
size = 3
) +
scale_x_continuous(labels = pointsInTime, breaks = 1:length(df$x)) +
theme(
panel.background = element_blank(),
panel.grid.major.y = element_line(color = '#dddddd'),
panel.grid.major.x = element_blank(),
axis.text.x = element_text(margin = margin(10, 0, 0, 0)),
axis.text.y = element_text(margin = margin(0, 10, 0, 0)),
plot.title = element_text(size = 18),
axis.title = element_blank(),
axis.ticks = element_blank()
) +
coord_cartesian(expand = FALSE, xlim=min(df$x):max(df$x), ylim=0:max(df$y)+1) +
labs(title = title)
p
}
densityplot(sample(10:25, size = s),
format(ISOdate(2017, 1:12, 1), "%b"),
"Number of Fatal Accidents")
Update: Now the 'Dec' label on the bottom right is cut off. Is there a way to add margin or spacing?
Initial Problem: The horizontal grid lines in the background are too wide. I want them to just begin where the 'Jan' tick resp. to end where the 'Dec' tick would be; just covering the actual plot area, not the whole panel. However, I want to have the space around the panel to stay the same, i.e. the axis labels should not move towards the data.
I created a ggplot2 object:
a <- replicate(8,rnorm(100))
colnames(a) <- letters[1:8]
b < -melt(a,id.vars=1:1)
colnames(b) <- c("c","variable","value")
ggplot(b,aes(x = c,y = value, colour = variable, linetype = variable)) +
geom_line()+
geom_point(aes(shape = factor(variable)), size = 1.7) +
scale_x_continuous(limits = c(-1, 1),
breaks = seq(-1, 1, 0.1),
expand=c(0.01, 0.01)) +
scale_y_continuous(limits = c(-1, 1),
breaks = seq(-1, 1, 0.1),
expand = c(0.01, 0.01))+
theme_bw(base_size = 12, base_family = "Helvetica") +
theme(axis.text=element_text(size = 10),
axis.title=element_text(size = 10),
text = element_text(size = 10),
axis.line = element_line(size = 0.25),
axis.ticks=element_line(size = 0.25),
panel.grid.major = element_blank(),
#panel.grid.minor = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, size = 0.5),
panel.background = element_blank(),
legend.position = "top" ,
legend.direction = "vertical",
legend.title = element_blank(),
legend.text = element_text(size = 13),
legend.background = element_blank(),
legend.key = element_blank()) +
labs(x = '', y = '', title = "") +
theme(plot.title = element_text(size=10)) +
theme(strip.text.x = element_text(size = 8,color="black"),
strip.background = element_blank()) +
theme(strip.text.x = element_text(size = 8, colour = "black"))
My problem is the following:
when I create the legend, there is a separate legend for the colors and a separate one for the points.
How can I create a single legend for each of the 8 variables?
Let me minimise your code and focus on the legend issue. This is what you have now.
ggplot(b,aes(x = c, y = value, colour = variable, linetype = variable)) +
geom_line() +
geom_point(aes(shape = factor(variable)),size=1.7)
Your data frame, b has variable as factor. You use this in two ways here; variable and factor(variable). You can simply use variable for shape in geom_point; make all variable identical.
ggplot(b,aes(x = c, y = value, colour = variable, linetype = variable)) +
geom_line()+
geom_point(aes(shape = variable),size = 1.7)
I saw some warning messages related to colours and other things. You may want to take care of them. But, for legend, this is one way to go.
Take from the ideas on this page: http://www.cookbook-r.com/Graphs/Legends_(ggplot2)/#modifying-the-text-of-legend-titles-and-labels
I edited your code to make the data visible (you had problems with your x-axis limits. Note the final three lines. These commands tell ggplot to create only one legend.
a<-replicate(6,rnorm(100))
colnames(a)<-letters[1:6]
b<-melt(a,id.vars=1:1)
colnames(b)<-c("c","variable","value")
ggplot(b,aes(x=c,y=value,colour=variable,linetype=variable)) +
geom_line() + geom_point(aes(shape=factor(variable)),size=1.7)+
scale_x_continuous(limits=c(0,100))+
scale_y_continuous(limits=c(-2,2),breaks=seq(-2,2,0.1),expand=c(0.01,0.01))+
theme_bw(base_size=12, base_family="Helvetica") +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=10),
text = element_text(size=10),
axis.line = element_line(size=0.25),
axis.ticks=element_line(size=0.25),
panel.grid.major = element_blank(),
#panel.grid.minor = element_blank(),
panel.border = element_rect(colour="black",fill=NA,size=0.5),
panel.background = element_blank(),
legend.position="top" ,
legend.direction="vertical",
legend.title=element_blank(),
legend.text=element_text(size=13),
legend.background=element_blank(),
legend.key=element_blank())+
labs(x='', y='',title="")+
theme(plot.title=element_text(size=10))+
theme(strip.text.x = element_text(size = 8,color="black"),strip.background=element_blank())+
theme(strip.text.x = element_text(size = 8,color="black"))+
scale_colour_discrete(name ="Factor")+
scale_linetype_discrete(name ="Factor") +
scale_shape_discrete(name ="Factor")