Related
I'm trying to avoid the bottom annotation being clipped. It's the descender on the "p" that gets chopped off. I've used the "inward" option on vjust.
df <- data.frame(x=c(as.Date("2020-01-01"),as.Date("2022-01-01"))
,y=c(0,1))
df
ggplot(df) +
geom_point(mapping=aes(x=x,y=y)) +
annotate("text",x=mean(df$x),y=-Inf,label="Clipped",hjust=0.5,vjust="inward",size=12,colour="red") +
annotate("text",x=mean(df$x),y=Inf,label="Not Clipped",hjust=0.5,vjust="inward",size=12,colour="blue")
A possible approach would be to use the min and max y values:
library(tidyverse)
df <- data.frame(
x = c(as.Date("2020-01-01"), as.Date("2022-01-01")),
y = c(0, 1)
)
ggplot(df) +
geom_point(aes(x, y)) +
annotate("text", x = mean(df$x), y = min(df$y), label = "Clipped", hjust = 0.5, vjust = "inward", size = 12, colour = "red") +
annotate("text", x = mean(df$x), y = max(df$y), label = "Not Clipped", hjust = 0.5, vjust = "inward", size = 12, colour = "blue")
Created on 2022-07-02 by the reprex package (v2.0.1)
Interesting. Looks like this issue is related to what is chosen as the base line to align the text labels. This could be seen clearly when switching to geom_label where we see that for the clipped label the base line chosen for the alignment is not the end of the "p". Hence the "p"s get clipped off:
ggplot(df) +
geom_point(mapping = aes(x = x, y = y)) +
annotate("label", x = mean(df$x), y = -Inf, label = "Clipped",
hjust = 0.5, vjust = "inward", size = 12, colour = "red", label.padding = unit(0, "lines")) +
annotate("label", x = mean(df$x), y = Inf, label = "Not Clipped",
hjust = 0.5, vjust = "inward", size = 12, colour = "blue", label.padding = unit(0, "lines"))
One possible fix would be to switch to ggtext::GeomRichtext:
library(ggplot2)
library(ggtext)
ggplot(df) +
geom_point(mapping = aes(x = x, y = y)) +
annotate(ggtext::GeomRichtext, x = mean(df$x), y = -Inf, label = "Clipped",
hjust = 0.5, vjust = "inward", size = 12, colour = "red",
label.size = 0, fill = NA, label.padding = unit(0, "lines")) +
annotate(ggtext::GeomRichtext, x = mean(df$x), y = Inf, label = "Not Clipped",
hjust = 0.5, vjust = "inward", size = 12, colour = "blue",
label.size = 0, fill = NA, label.padding = unit(0, "lines"))
If you don't want it to be clipped on the same position, you can use coord_cartesian(clip = "off"):
df <- data.frame(x=c(as.Date("2020-01-01"),as.Date("2022-01-01"))
,y=c(0,1))
library(ggplot2)
ggplot(df) +
geom_point(mapping=aes(x=x,y=y)) +
annotate("text",x=mean(df$x),y=-Inf,label="Clipped",hjust=0.5,vjust="inward",size=12,colour="red") +
annotate("text",x=mean(df$x),y=Inf,label="Not Clipped",hjust=0.5,vjust="inward",size=12,colour="blue") +
coord_cartesian(clip = 'off')
Created on 2022-07-02 by the reprex package (v2.0.1)
I have my labels roughly aligned to each side of my stacked bar chart. The problem is that they look like a mess because they aren't right and left justified on either side of the bar. How do I fix this so that they look professional?
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
)
# Sort order
level_order <- df3 %>%
arrange(desc(Amount))
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = -3.1,
size = 5) +
geom_text(aes(label = Label),
position = position_stack(vjust = 0.5),
hjust = 5,
size = 5) +
theme(legend.position = "none") +
theme(plot.title = element_text(size = 50, hjust = .5, vjust = 0)) +
ggtitle("Food Costs by Reindeer")
hjust determines the text alignment (with 0 being left-aligned, and 1 right-aligned). The x co-ordinate of your geom_text at the moment is defaulted to 1, so changing this will change the position of the text.
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(x=0.6, label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = 0.5,
size = 5) +
geom_text(aes(x=1.4, label = Label),
position = position_stack(vjust = 0.5),
hjust = 0.5,
size = 5) +
theme(legend.position = "none") +
theme(plot.title = element_text(size = 50, hjust = .5, vjust = 0)) +
ggtitle("Food Costs by Reindeer")
You can also pass hjust as an aesthetic. In order to do that, you will need to prepare the labelling as a separate data frame. Then, you only need to call geom_text once. I don't say this is necessarily better, but just pointing out that this is possible. A few more comments in the code, also regarding a few common pitfalls.
library(tidyverse)
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
) %>%
## arrange step here
arrange(desc(Amount))
## I like to prepare the data outside ggplot
label_df <- df3 %>%
mutate(Amount_lab = paste0("$", Amount)) %>%
pivot_longer(-Amount) %>%
## this adds a column for your adjustment, and the x position compared with the central column
mutate(hjust = rep(0:1, nrow(.)/2),
x = rep(c(1.21, .79), nrow(.)/2))
ggplot(mapping = aes(y = Amount)) +
## geom_col is geom_bar(stat = "identity"), stack is default, so you can omit it
## call data in the geom layers
## set x to 1
## width = .4 so it matches your selected x from above
geom_col(data = df3, aes(x = 1, fill=fct_inorder(Label)), width = .4) +
scale_fill_brewer(palette = "Blues", direction = -1) +
## need to reverse both y and value, weirdly
geom_text(data = label_df, aes(x, y = rev(Amount), label = rev(value),
## this is the main trick
hjust = hjust),
position = position_stack(vjust = 0.5) ) +
## sadly, need to turn clip off
coord_cartesian(clip = "off") +
theme_void() +
## call theme only once!!
theme(legend.position = "none",
plot.title = element_text(size = 20, hjust = .5, vjust = 0),
## you need to add a margin
plot.margin = margin(r = .6, l = .6, unit = "in")) +
ggtitle("Food Costs by Reindeer")
Created on 2021-12-20 by the reprex package (v2.0.1)
Try fixing the x co-ordinate in the call to geom_text and managing alignment with hjust...
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
)
library(ggplot2)
library(dplyr)
library(forcats)
level_order <- df3 %>%
arrange(desc(Amount))
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(x = 1.3, label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = 0,
size = 5) +
geom_text(aes(x = 0.6, label = Label),
position = position_stack(vjust = 0.5),
hjust = 0,
size = 5) +
theme(legend.position = "none") +
theme(plot.title = element_text(size = 50, hjust = .5, vjust = 0)) +
ggtitle("Food Costs by Reindeer")
Created on 2021-12-19 by the reprex package (v2.0.1)
Using Peter's answer above (reminding me of the "x" position argument I forgot existed), this was the final fix that got what I was looking for. hjust = 0 is left-justification and hjust = 1 is right justification.
library(tidyverse)
library(grid)
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
)
# Sort order
level_order <- df3 %>%
arrange(desc(Amount))
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(x = 1.3, label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = 0,
size = 5) +
geom_text(aes(x = 0.7, label = Label),
position = position_stack(vjust = 0.5),
hjust = 1,
size = 5) +
theme(legend.position = "none",
plot.margin = unit(c(0,0,2,0), "cm"))
grid.text("Food Costs by Reindeer", x = unit(0.5, "npc"), y = unit(0, "npc"),
vjust = -0.5, gp = gpar(cex=4))
I am having issues positionning my labels for my grouped bar chart.
Below a reproducible example, and on the image you can see where I would like the labels to be placed.
Not all on the same line, but each label aligned with each of the 3 bar chart, and ideally each label placed right above each corresponding bar chart.
Any idea how to achieve this? I want to keep a grouped bar chart and not a stacked bar chart.
data_F <- "https://raw.githubusercontent.com/max9nc9/Temp/main/temp.csv"
data_F <- read.csv(data_F, sep = ";")
colnames(data_F) <- c("Month_Year", "Type", "Amount")
data_F$Amount <- as.numeric(data_F$Amount)
Final_Graph <-
ggplot(data_F, aes(x = Month_Year, y = Amount, label = Amount, fill = Type)) +
geom_bar(position="dodge", stat = "identity") +
geom_text(size = 3, position = position_stack(vjust = 0.5), color = "black") +
theme(axis.text.x = element_text(angle = 90)) +
scale_fill_manual(values = c("#e62200", "#00c41d", "#f7b21e")) +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.subtitle = element_text(hjust = 0.5))
Final_Graph
As you use position_dodge for the bars use it for the labels as well, where I use width = .9, which is the default for bars:
data_F <- "https://raw.githubusercontent.com/max9nc9/Temp/main/temp.csv"
data_F <- read.csv(data_F, sep = ";")
colnames(data_F) <- c("Month_Year", "Type", "Amount")
data_F$Amount <- as.numeric(data_F$Amount)
library(ggplot2)
ggplot(data_F, aes(x = Month_Year, y = Amount, label = Amount, fill = Type)) +
geom_bar(position="dodge", stat = "identity") +
geom_text(size = 3, position = position_dodge(width = .9), vjust = 0, color = "black") +
theme(axis.text.x = element_text(angle = 90)) +
scale_fill_manual(values = c("#e62200", "#00c41d", "#f7b21e")) +
theme(plot.title = element_text(hjust = 0.5)) +
theme(plot.subtitle = element_text(hjust = 0.5))
I did a graph with ggplot. My aim is to add labels at the top of the vertical lines I put in the graph. The problem is that these labels appear not above enough. They intersect the vertical line. Instead, I would like them not to touch the vertical lines.
For instance:
date <- seq(2000, 2010, by = 1)
value <- seq(1, 11, by=1)
df <- data.frame(Date = date, Value = value)
ggplot(df, aes(x = df$Date)) + geom_line(aes(y = df$Value, colour = "Value"), size = 1) + theme_classic() + theme(axis.line = element_line(colour = "grey", size = 0.8)) + labs(y = "\n \n", x = "", colour = "") + theme(axis.title = element_text(family = "Times New Roman", face = "plain", size = 12, colour = "black")) +
geom_vline(xintercept = df$Date[c(3)], linetype="dashed", colour = "black", size=1) +
geom_text(aes(x=df$Date[3], y= Inf, label = "Label"), colour = "black", vjust = 1, size = 5)
How can I push the label in a way that they don't intersect the dashed lines?
Thanks a lot for your help!
This'll be hard with your current approach because geom_vline draws a line that goes on forever in both directions. You could push your text to the right or left in your call to geom_text though. If you really want to have the text right above the vertical line, I'd suggest using annotate to draw the line instead:
library(ggplot2)
date <- seq(2000, 2010, by = 1)
value <- seq(1, 11, by=1)
df <- data.frame(Date = date, Value = value)
ggplot(df, aes(x = Date)) +
geom_line(aes(y = Value, colour = Value), size = 1) +
theme_classic() + theme(axis.line = element_line(colour = "grey", size = 0.8)) +
labs(y = "\n \n", x = "", colour = "") +
theme(
axis.title = element_text(family = "Times New Roman", face = "plain", size = 12, colour = "black")) +
geom_text(aes(x=df$Date[3], y=max(df$Value), label = "Label"), colour = "black", vjust = 1, size = 5, nudge_y = 1) +
annotate("segment", x=df$Date[3], xend=df$Date[3], y=0, yend=max(df$Value), linetype="dashed", size=1)
Look at the last two lines above: I use nudge_y in the call to geom_text to move the label up and then I use annotate to draw a fixed-length line.
I also made some minor edits to other parts of your code. Notably, you don't have to preface column names with df$ if they're in your aes() calls and the colour argument of aes should always be an unquoted (no ") column name.
Perhaps inserting a geom_segment is what you are looking for:
ggplot(df, aes(x = Date)) +
geom_line(aes(y = Value, colour = "Value"), size = 1) +
geom_segment(aes(x = 2005, y = 0, xend = 2005, yend = 9, colour = "segment"), data = df) +
geom_text(aes(x=df$Date[6], y= 10, label = "Label"), colour = "black", vjust = 1, size = 5)
I would suggest to go with geom_segment. I also made similar edits to your code as amoeba did
ggplot(df, aes(x = Date)) +
geom_line(aes(y = Value, colour = "Value"), size = 1) +
labs(y = "\n \n", x = "", colour = "") +
theme_classic() +
theme(axis.line = element_line(colour = "grey", size = 0.8),
axis.title = element_text(family = "Times New Roman",
face = "plain",
size = 12,
colour = "black")) +
geom_segment(aes(x = df$Date[3],xend=df$Date[3],y=0,yend=max(value*0.97)),
linetype="dashed", colour = "black", size=1) +
geom_text(aes(x=df$Date[3], y= Inf, label = "Label"), colour = "black", vjust = 1, size = 5)+
ylim(0,NA)
Perhaps there are better options out there, but here is one approach.
Add additional margin space above the plot with plot.margin in your theme.
Add coord_cartesian to turn off clipping (allowing drawing outside the extent of the plot panel).
Use annotation_custom to add the text.
library(ggplot2)
date <- seq(2000, 2010, by = 1)
value <- seq(1, 11, by=1)
df <- data.frame(Date = date, Value = value)
ggplot(df, aes(x = Date)) +
geom_line(aes(y = Value, colour = Value), size = 1) +
theme_classic() +
theme(plot.margin = unit(c(2,1,1,1), "cm"), axis.line = element_line(colour = "grey", size = 0.8)) +
labs(y = "\n \n", x = "", colour = "") +
theme(axis.title = element_text(family = "Times New Roman", face = "plain", size = 12, colour = "black")) +
geom_vline(xintercept = df$Date[c(3)], linetype="dashed", colour = "black", size=1) +
annotation_custom(grob = textGrob(label = "Label", hjust = .5, gp = gpar(cex = 1.2)),
ymin = 12, ymax = 12, xmin = df$Date[c(3)], xmax = df$Date[c(3)]) +
coord_cartesian(clip = 'off')
I want to adjust make the width of the gray area and the bar plot smaller.
Now the grey area on the left and right to the plot is too much as you can see in my screenshot. Looking for a solution that also captures the scenario that I have 3 or more bars in the chart.
ggplot(data=MyDataFrame, aes(x='Text', y=MetricColumn, fill=LegendTag))+
scale_fill_brewer(palette="Set3")+
geom_bar(stat="identity", width=0.2)+
geom_text(aes(label=paste0(MetricColumn,"%")), position = position_stack(vjust = .5), size = 3.5, color = "black")+
labs(title="Text of the title")+
theme(plot.title = element_text(hjust = 0.5, face="bold"))+
theme(legend.position="left")
What James Martherus says is definitly part of the solution. But since a plot always fills out the whole plotting window it will look weird. So youc an either set the plot margins:
ggplot(data=diamonds, aes(x= "Text", y= ..count../sum(..count..), fill = cut,
label = scales::percent(..count../sum(..count..)))) +
scale_fill_brewer(palette="Set3")+
geom_bar(stat="count", show.legend = F) +
geom_text(stat = 'count', position = position_stack(vjust = .5), size = 3, color = "black") +
labs(x = NULL) +
scale_y_continuous(labels = scales::percent, name = "Percent") +
theme(plot.margin = unit(c(0.5,7, 0.5, 7), "cm"))
Or you just save it the way you want it to be:
p <- ggplot(data=diamonds, aes(x= "Text", y= ..count../sum(..count..), fill = cut,
label = scales::percent(..count../sum(..count..)))) +
scale_fill_brewer(palette="Set3")+
geom_bar(stat="count", show.legend = F) +
geom_text(stat = 'count', position = position_stack(vjust = .5), size = 6, color = "black") +
labs(x = NULL) +
scale_y_continuous(labels = scales::percent, name = "Percent") +
theme(axis.title.y = element_text(size = 20),
axis.text = element_text(size = 15))
ggsave("D:/R/plot.png", width = 5, height = 15, dpi = 200)
This way you get it without the margins.
Remove the width=.2 from geom_bar as you're telling R to make your bar take up only 20% of the plot. geom_bar sets the default width at 90% of the plot (see the documentation).
ggplot(data=MyDataFrame, aes(x='Text', y=MetricColumn, fill=LegendTag))+
scale_fill_brewer(palette="Set3")+
geom_bar(stat="identity")+
geom_text(aes(label=paste0(MetricColumn,"%")), position = position_stack(vjust = .5), size = 3.5, color = "black")+
labs(title="Text of the title")+
theme(plot.title = element_text(hjust = 0.5, face="bold"))+
theme(legend.position="left")