Annotate Ggplot with an Extra Tick and Label

Annotate ggplot with an extra tick and label

Four solutions.

The first uses scale_x_continuous to add the additional element then uses theme to customize the new text and tick mark (plus some additional tweaking).

The second uses annotate_custom to create new grobs: a text grob, and a line grob. The locations of the grobs are in data coordinates. A consequence is that the positioning of the grob will change if the limits of y-axis changes. Hence, the y-axis is fixed in the example below. Also, annotation_custom is attempting to plot outside the plot panel. By default, clipping of the plot panel is turned on. It needs to be turned off.

The third is a variation on the second (and draws on code from here). The default coordinate system for grobs is 'npc', so position the grobs vertically during the construction of the grobs. The positioning of the grobs using annotation_custom uses data coordinates, so position the grobs horizontally in annotation_custom. Thus, unlike the second solution, the positioning of the grobs in this solution is independent of the range of the y values.

The fourth uses viewports. It sets up a more convenient unit system for locating the text and tick mark. In the x direction, the location uses data coordinates; in the y direction, the location uses "npc" coordinates. Thus, in this solution too, the positioning of the grobs is independent of the range of the y values.

First Solution

## scale_x_continuous then adjust colour for additional element 
## in the x-axis text and ticks
library(ggplot2)
df <- data.frame(x=seq(1:100), y=sort(rexp(100, 2), decreasing = T))

p = ggplot(df, aes(x=x, y=y)) + geom_point() +
scale_x_continuous(breaks = c(0,25,30,50,75,100), labels = c("0","25","xyz","50","75","100")) +
theme(axis.text.x = element_text(color = c("black", "black", "red", "black", "black", "black")),
axis.ticks.x = element_line(color = c("black", "black", "red", "black", "black", "black"),
size = c(.5,.5,1,.5,.5,.5)))

# y-axis to match x-axis
p = p + theme(axis.text.y = element_text(color = "black"),
axis.ticks.y = element_line(color = "black"))

# Remove the extra grid line
p = p + theme(panel.grid.minor = element_blank(),
panel.grid.major.x = element_line(color = c("white", "white", NA, "white", "white", "white")))
p

Sample Image

Second Solution

## annotation_custom then turn off clipping
library(ggplot2)
library(grid)
df <- data.frame(x=seq(1:100), y=sort(rexp(100, 2), decreasing = T))

p = ggplot(df, aes(x=x, y=y)) + geom_point() +
scale_y_continuous(limits = c(0, 4)) +
annotation_custom(textGrob("xyz", gp = gpar(col = "red")),
xmin=30, xmax=30,ymin=-.4, ymax=-.4) +
annotation_custom(segmentsGrob(gp = gpar(col = "red", lwd = 2)),
xmin=30, xmax=30,ymin=-.25, ymax=-.15)

g = ggplotGrob(p)
g$layout$clip[g$layout$name=="panel"] <- "off"
grid.draw(g)

Sample Image

Third Solution

library(ggplot2)
library(grid)
df <- data.frame(x=seq(1:100), y=sort(rexp(100, 2), decreasing = T))

p = ggplot(df, aes(x=x, y=y)) + geom_point()

gtext = textGrob("xyz", y = -.05, gp = gpar(col = "red"))
gline = linesGrob(y = c(-.02, .02), gp = gpar(col = "red", lwd = 2))

p = p + annotation_custom(gtext, xmin=30, xmax=30, ymin=-Inf, ymax=Inf) +
annotation_custom(gline, xmin=30, xmax=30, ymin=-Inf, ymax=Inf)

g = ggplotGrob(p)
g$layout$clip[g$layout$name=="panel"] <- "off"
grid.draw(g)

Sample Image

Fourth Solution

Updated to ggplot2 v3.0.0

## Viewports
library(ggplot2)
library(grid)
df <- data.frame(x=seq(1:100), y=sort(rexp(100, 2), decreasing = T))

(p = ggplot(df, aes(x=x, y=y)) + geom_point())

# Search for the plot panel using regular expressions
Tree = as.character(current.vpTree())
pos = gregexpr("\\[panel.*?\\]", Tree)
match = unlist(regmatches(Tree, pos))
match = gsub("^\\[(panel.*?)\\]$", "\\1", match) # remove square brackets
downViewport(match)

#######
# Or find the plot panel yourself
# current.vpTree() # Find the plot panel
# downViewport("panel.6-4-6-4")
#####

# Get the limits of the ggplot's x-scale, including the expansion.
x.axis.limits = ggplot_build(p)$layout$panel_params[[1]][["x.range"]]

# Set up units in the plot panel so that the x-axis units are, in effect, "native",
# but y-axis units are, in effect, "npc".
pushViewport(dataViewport(yscale = c(0, 1), xscale = x.axis.limits, clip = "off"))
grid.text("xyz", x = 30, y = -.05, just = "center", gp = gpar(col = "red"), default.units = "native")
grid.lines(x = 30, y = c(.02, -.02), gp = gpar(col = "red", lwd = 2), default.units = "native")

upViewport(0)

Sample Image

How to have ggplot show a label for each and every tick mark

Use scale_x_date to format a date axis. Adapting your code, we can do the following:

cesyoy %>% 
mutate(year = substr(as.character(Month1),1,4),
month = substr(as.character(Month1),5,7),
date = as.Date(paste(year,month,"1",sep ="-"))) %>%
ggplot() +
geom_col(aes(x = date, y = annualjobgrowth)) +
scale_x_date(date_breaks="1 month", date_labels="%b\n%Y")

Sample Image

I've chosen a particular format for the date labels, but you can adjust this by tweaking the date_labels argument. See the help for strftime for details on formatting codes and options.

scale_x_date is in place of scale_x_continuous, rather than in addition to it. You can only use one scale function for a given scale. If you add a new scale for, say, the x-axis, it will replace the previous scale_x_***. So, however you want to customize the x-axis scale, you would do all of it in a single call to scale_x_date.

ggplot annotate with italics and variables not working in R

Your code is erroring on eta_plot and cohenD_plot for me but try this.

   annotate(
"text",
x = 1.5,
y = c(85, 82, 79),
size = 7.5,
label = c(
paste0("~italic(p)== ", aov_interaction_p),
paste0("~italic(eta)== ", "eta_plot"),
paste0("~italic(d)== ", "cohenD_plot")
),
parse = TRUE
)

Sample Image

ggplot: add manually labelled tick marks on top of automatic tick marks

It's unclear exactly what you want this to look like but you could do one of two options. You could either use geom_vline() or geom_segment(). Vline will do a line from the bottom to the top, but it sounds like you may prefer to use segment. Try this:

+ geom_segment(x = min(x), xend = min(x), y = 0, yend = 1)

If you change the yend argument you could make the tick smaller or larger. Drawing one for the max value should be as simple as swapping the min() arguments for max() arguments. Or you could just input the values manually. Alternatively, you could add a vline to go the full height of the panel with:

+ geom_vline(xintercept = min(x))

You can read more about both here. If this doesn't help much, you can provide a proper reprex and maybe a sketch of your desired output we can modify that code to get a bit closer to what you want.

edit:

Writing outside of the plot window is a bit more difficult, but this link may help you. I've tried it on a few and always found that in my cases it was easier to use a different solution. Here's one option:

library(ggplot2)

set.seed(123) # so we have the same toy data

df <- data.frame(x=1:100,y=rnorm(100,10,1))

ggplot(df) +
geom_point(aes(x=x,y=y)) +
geom_segment(x=0, xend=18, y=8.033383, yend=8.033383) + # draw to x axis
geom_segment(x=18, xend=18, y=0, yend=8.033383) + # draw to y axis
annotate("text", 18.2, 8.2, label="(8, 8.03)", size=3) # ordered pair just above it

If you didn't want to draw all the way to the point you could just change the first xend and yend arguments where the x/y start at zero to be come just above the edge of the plot window.

image as axis tick ggplot

cowplot package has made this somewhat easier.

Build the plot:

library(ggridges)
library(ggplot2)

p <- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges()

Load the images and use axis_canvas() to build a strip of vertical images:

library(cowplot)

pimage <- axis_canvas(p, axis = 'y') +
draw_image("https://upload.wikimedia.org/wikipedia/commons/thumb/9/9f/Iris_virginica.jpg/295px-Iris_virginica.jpg", y = 2.5, scale = 0.5) +
draw_image("https://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Iris_versicolor_3.jpg/320px-Iris_versicolor_3.jpg", y = 1.5, scale = 0.5) +
draw_image("https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/Kosaciec_szczecinkowaty_Iris_setosa.jpg/450px-Kosaciec_szczecinkowaty_Iris_setosa.jpg", y = 0.5, scale = 0.5)

# insert the image strip into the plot
ggdraw(insert_yaxis_grob(p, pimage, position = "left"))

Sample Image

Without the axis.text.y:

p <- ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges() +
theme(axis.text.y = element_blank())
ggdraw(insert_yaxis_grob(p, pimage, position = "left"))

Sample Image

You could remove also the vertical line, currently I can't find a way of having the image strip on the left-side of the axis line.



Related Topics



Leave a reply



Submit