creating custom annotations in only one facet of a ggplot - r

Is there a way to specify that a custom_annotation only applies to one facet of a ggplot?
For example, if I run the following code
library(tidyverse)
library(grid)
text_grob=grobTree(textGrob("text",x=0.5, y=0.6, rot=90,
gp=gpar(col="red")))
ggplot(mtcars, aes(x=mpg, y =drat))+
geom_point() +
facet_wrap(~cyl) +
annotation_custom(overrep_grob)
I get this
How can I only keep the rightmost red "text" annotation and not add the "text" annotation to the first two facets? Note I can't use geom_text or annotate because I need to make use of textGrob's relative text positioning

egg has geom_custom,
library(ggplot2)
library(grid)
library(egg)
d = data.frame(cyl=6, drat = 4, mpg = 15)
d$grob <- list(textGrob("text",rot=90, hjust = 0, gp=gpar(col="red")))
ggplot(mtcars, aes(x=mpg, y=drat))+
geom_point() +
facet_wrap(~cyl) +
geom_custom(data = d, aes(data = grob), grob_fun = identity)

You could also do this using geom_text by calculating the relative position of the text needed. Note that here, the relative position is slightly different than the one you use above because here I define the relative position as some proportion of the dynamic range. You can choose a different value for rel to get the position you need. I find that this way makes the positioning less arbitrary.
library(tidyverse)
rel_pos <- function(.data, var, rel){
var <- enquo(var)
.data %>%
summarise(x = sum(max(!!var), min(!!var))* rel) %>% .[1, "x"]
}
my_text <- data_frame(mpg = rel_pos(mtcars, mpg, 0.5),
drat = rel_pos(mtcars, drat, 0.6) ,
cyl = 8, lab = "text")
ggplot(mtcars, aes(x=mpg, y =drat))+
geom_point() +
facet_wrap(~cyl)+
geom_text(data = my_text, aes(label = lab), color = "red", angle = 90)
Created on 2018-08-15 by the reprex
package (v0.2.0).

Related

Colour rows by one factor in geom_table

I am trying to make a ggplot2 graph in which I have a "classic" dot plot together with a table.
This is an example of the plot I am making:
library(dplyr)
library(tibble)
library(ggplot2)
library(ggpmisc)
mtcars[1:10,] %>%
select(cyl, mpg, wt) -> tb
df <- tibble(x = 5.45, y = 34, tb = list(tb))
# using defaults
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
geom_point() +
geom_table(data = df, aes(x = x, y = y, label = tb))
This is the output of the code:
I would however the rows of the table be coloured according to the factor cyl. So every rows which has carb==4 is red, ==6 is green and ==8 is blue.
I don't need the column cyl to appear in the table, so that can be removed (if possible).
Is there a way to do it?
Thanks a lot in advance for your precious help,
Luca
Unlike most of the ggplot ecosystem, you can't just use a fill aesthetic inside geom_table, and instead need to create a table theme which gets passed to table.theme inside geom_table. This means it is somewhat inconvenient to achieve the desired effect, but it is possible with something like the following:
thm <- gridExtra::ttheme_default()
fills <- scales::hue_pal()(3)[as.numeric(factor(df$tb[[1]]$cyl))]
thm$core$bg_params$fill <- fills
df$tb[[1]] <- df$tb[[1]][-1]
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl))) +
geom_point() +
geom_table(data = df, aes(x = x, y = y, label = tb), table.theme = thm)
It is a bit hacky, but using the table.theme argument, you can supply the table with your own background colors (see Controlling the colors of rows in ggplot table annotation).
First, we construct a vector which contains a required background color for each row. Then, we use those colors in the table.theme argument of geom_table.
library(dplyr)
library(tibble)
library(ggplot2)
library(ggpmisc)
mtcars[1:10,] %>%
select(cyl, mpg, wt) -> tb
df <- tibble(x = 5.45, y = 34, tb = list(tb))
# Construct vector of required background colors (from default ggplot2 theme)
colors <- scales::hue_pal()(n_distinct(tb$cyl))
colors <- colors[factor(tb$cyl)]
# using defaults
ggplot(mtcars, aes(wt, mpg, colour = factor(cyl), fill = factor(cyl))) +
geom_point() +
geom_table(data = df, aes(x = x, y = y, label = tb),
table.theme = gridExtra::ttheme_default(core = list(bg_params=list(fill=colors))))

Add additional labels from a DataFrame to a facet_grid with existing label

I have a set data that I need to add to levels of labels. One on a single chart within the facet grid, and one from a small dataframe with entries for for each chart.
In the example below you'll see that I can add to a single chart no problem but when I try to add from the df I get the error -
Error in FUN(X[[i]], ...) : object 'wt' not found
Preparation:
library(ggplot2)
p <- ggplot(mtcars, aes(mpg, wt)) + geom_line()
p <- p + facet_grid(. ~ cyl)
ann_text <- data.frame(mpg = 30,wt = 5,lab = "Text",
cyl = factor(8,levels = c("4","6","8")))
dfl <- data.frame(name = c('Jim',"Bob", "Sue"), r = c(-0.2, 0.5, -0.4))
Single Label:
p + geom_text(data = ann_text,label = "Text")
Multiple Labels:
p + geom_text(data = ann_text,label = "Text") +
geom_text(data = dfl, mpg = 30,wt = 5, aes(label = r))
The method I'm using is trying to recreate other examples I've found here on SO and elsewhere but I seem to be missing something.
It's not working in your second code because in the second geom_text, your mpg and wt in not in aes(). Also, these two variables are absent in your dfl.
If you wish to have better control of the labelling of your r variable, you can create extra columns in dfl specifying the x and y location of the label, and use these variables in geom_text(aes()).
Note that I have modified the y position in the second geom_text to avoid overlapping "0,2" with "Text".
library(ggplot2)
p <- ggplot(mtcars, aes(mpg, wt)) + geom_line()
p <- p + facet_grid(. ~ cyl)
ann_text <- data.frame(mpg = 30,wt = 5,lab = "Text",
cyl = factor(8,levels = c("4","6","8")))
dfl <- data.frame(name = c('Jim',"Bob", "Sue"), r = c(-0.2, 0.5, -0.4))
p + geom_text(data = ann_text,label = "Text") +
geom_text(data = dfl, aes(30, 4, label = r), check_overlap = T)
Created on 2022-05-06 by the reprex package (v2.0.1)

Adding a nudge_y to the data labels of a "Jittered" R ggplot2 graph

I am facing a smal issue concerning positioning labels on an R graph. I made an example with the data available in ggplot2 package to make it executable.
library(ggrepel)
library(ggplot2)
library(tidyverse)
pos <- position_jitter(seed = 1, height = 0.25)
ggplot(mtcars, aes(vs, wt, group = am, label = wt)) +
geom_jitter(position = pos) +
geom_text_repel(aes(label = ifelse(wt > 3, wt, "")), position = pos)
I used the command position_jitter to define jitter and geom_text_repel to set data labels. Using position_jitter I cannot define a position nudge_y of data labels but I woukld like to add to the defined position (position_jitter(seed = 1, height = 0.25)) a fixed value, let's say +0.5, so as to shift up systematically every label of a defined value.
Is it possible to make in R something like this?
Thank you in advance for every your reply!
RE: comments
You can manually add jitter (random uniform) noise to your datapoints beforehand. Nudging the text then becomes trivial.
library(ggrepel)
library(ggplot2)
library(tidyverse)
mtcars %>%
mutate(jit_vs = vs + runif(length(vs), -0.4, 0.4),
jit_wt = wt + runif(length(wt), -0.25, 0.25)) %>%
ggplot(aes(jit_vs, jit_wt, group = am, label = wt)) +
geom_point() +
geom_text_repel(aes(label = ifelse(wt > 3, wt, "")), nudge_y = 0.5)
Created on 2021-09-03 by the reprex package (v2.0.1)

automagically using labels (haven semantics) in ggplot2 plots

I'm plotting data marked up using haven semantics, i.e. variables and values have labels defined via attributes.
Often, these labels are also what I want in my axis titles and ticks.
library(ggplot2)
mtcars$mpg = haven::labelled(mtcars$mpg, labels = c("low" = 10, "high" = 30))
attributes(mtcars$mpg)$label = "miles per gallon"
ggplot(mtcars, aes(mpg, cyl)) + geom_point() +
scale_x_continuous(attributes(mtcars$mpg)$label,
breaks = attributes(mtcars$mpg)$labels,
labels = names(attributes(mtcars$mpg)$labels))
Could I write a helper that replaces that laborious scale_x_continuous statement with something that can more easily be iterated? E.g. something like
scale_x_continuous(label_from_attr, breaks = breaks_from_attr, labels = value_labels_from_attr). Or maybe even + add_labels_from_attributes() to replace the whole thing?
I'm aware that I can write/use helpers like Hmisc::label to slightly shorten the attribute-code above, but that's not what I want here.
I don't have a good scale, but you can use a function like this:
label_x <- function(p) {
b <- ggplot_build(p)
x <- b$plot$data[[b$plot$labels$x]]
p + scale_x_continuous(
attributes(x)$label,
breaks = attributes(x)$labels,
labels = names(attributes(x)$labels)
)
}
Then use as (+ won't do):
p <- ggplot(mtcars, aes(mpg, cyl)) + geom_point()
label_x(p)
Alternatively, use a pipe:
mtcars %>% { ggplot(., aes(mpg, cyl)) + geom_point() } %>% label_x()
Old solution
use_labelled <- function(l, axis = "x") {
if (axis == "x") {
scale_x_continuous(attributes(l)$label,
breaks = attributes(l)$labels,
labels = names(attributes(l)$labels))
}
if (axis == "y") {
scale_y_continuous(attributes(l)$label,
breaks = attributes(l)$labels,
labels = names(attributes(l)$labels))
}
}
Then you just give:
ggplot(mtcars, aes(mpg, cyl)) + geom_point() + use_labelled(mtcars$cyl)
Or for the y-axis:
ggplot(mtcars, aes(cyl, mpg)) + geom_point() + use_labelled(mtcars$cyl, "y")
Another approach is to write a wrapper for ggplot() that has its own class. Then attributes have full visibility when the corresponding print method is called. See ?ag.print from package 'yamlet' (0.2.1).
library(ggplot2)
library(yamlet)
library(magrittr)
mtcars$disp %<>% structure(label = 'displacement', unit = 'cu. in.')
mtcars$mpg %<>% structure(label = 'mileage', unit = 'miles/gallon')
mtcars$am %<>% factor(levels = c(0,1), labels = c('automatic','manual'))
mtcars$am %<>% structure(label = 'transmission')
agplot(mtcars, aes(disp, mpg, color = am)) + geom_point()

ggplot2: Create an empty space on the x-axis

On the below plot, I'd like to create an empty space on the x-axis at position 3. In other word, I'd like that the dots for wt>=3 and the axis scale when wt>=3 are shifted to the right side by some arbitrary chosen value. Does it make sense?
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
I could simply modify the my data and add 0.5 to each value of mpg where wt>=3 but it doesn't solve the issue with the x-axis.
I might rephrase my question saying that on the below graph I'd like that the vertical line does not overlap the data and therefore all the data (and also the x-axis) should be shifted to the left by the thickness of the vertical line.
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + geom_vline(xintercept=3, size=30)
I was thinking about facet_wrap or viewport. Maybe something like adding a constant to each value of mpg where wt>=3 and then, manually set the values for the x-axis.
Not entirely sure what you are looking for, and I get confused by watching the weird axis on my own plot... Something like this?
mtcars$wt2 <- with(mtcars, ifelse(wt > 3, wt+1, wt))
ggplot(mtcars, aes(x = wt2, y = mpg)) +
geom_point() +
annotate("rect", xmin = 3, xmax = 4, ymin = 0, ymax = 35, alpha = 0.2) +
scale_x_continuous(breaks = round(mtcars$wt2), label = round(mtcars$wt))
Similar to # Frank, and tested...
x <- mtcars
x$group <- ""
x[x$wt<3,]$group <- "1.LIGHT"
x[x$wt>=3,]$group <- "2.HEAVY"
library(ggplot2)
library(grid) #for unit(...)
ggplot(x, aes(x=wt,y=mpg)) +
geom_point() +
facet_grid(~group,scales="free",space="free") +
theme(panel.margin = unit(2, "lines"))
Produces this:
what about something like this?
mtcars <- transform(mtcars, split = ifelse(wt >= 3, "b", "a" ))
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
facet_wrap(~split, scales = "free_x")

Resources