Show two measurement units on axis ticks in ggplot2 - r

How it is possible (if at all) to show two alternative units on axis ticks in ggplot2?
What I would like to achieve is something like this:

Here's a hacky way of doing that:
d = data.frame(x = 1:20, y = rnorm(20, 5, 5))
ggplot(data = d, aes(x = x, y = y)) +
scale_x_continuous(breaks = c(1:20, seq(2.54, 20, 2.54)),
labels = c(1:20, paste0("\n", 1:as.integer(20/2.54), "\""))) +
geom_point()

Related

How to keep default axis labels but add an additional label in ggplot2

I would like to keep the default labels ggplot2 provides for Y-axis below, but always have a Y-axis tick and/or label at y = 100 to highlight the horizontal line intercept.
library(ggplot2)
maxValue <- 1000
df <- data.frame(
var1 = seq(1, maxValue, by = 25),
var2 = seq(1, maxValue, by = 50)
)
ggplot(df, aes(x = var1, y = var2)) +
geom_point() +
geom_hline(yintercept = 100, color = "red")
Created on 2022-04-09 by the reprex package (v2.0.1.9000)
Expected output:
Note that maxValue can be anything. So the solution to just increase in steps of 100 doesn't work. For example:
plot <- plot +
scale_y_continuous(
breaks = seq(0, max(df$y) + 100, 100),
labels = as.character(seq(0, max(df$y) + 100, 100))
)
This is because if the max value is 10000 or a similar big number like that, the number of labels will be overwhelming. This is why I would like to stay with the default Y-axis labels that ggplot2 provides and only add a single additional label at y = 100.
By default ggplot2 will compute the default axis breaks in the following manner (Refer to this answer for more details):
labeling::extended(min(df$var1), max(df$var1), m = 5))
We can just add your custom value 100 to this vector and pass it to scale_y_continous
def_breaks <- labeling::extended(min(df$var1), max(df$var1), m = 5)
ggplot(df, aes(x = var1, y = var2)) +
geom_point() +
geom_hline(yintercept = 100, color = "red") +
scale_y_continuous(breaks = c(100, def_breaks),
# pass to minor breaks so that they are not messed up
minor_breaks = def_breaks)

How to annotate lines like this in ggplot2?

See example:
I hope I don't need to manually assign the coordinators of the texts. If this is too complicated to achieve in ggplot2, what are the alternatives in R? Or maybe even not in R?
As #Axeman says, ggrepel is a decent option. Unfortunately it will only avoid overlap with other labels, and not the lines, so the solution isn't quite perfect.
library(ggplot2)
install.packages("ggrepel")
library(ggrepel)
set.seed(50)
d <- data.frame(y = c(rnorm(50), rnorm(50, 5), rnorm(50, 10)),
x = rep(seq(50), times = 3),
group = rep(LETTERS[seq(3)], each = 50))
ggplot(d, aes(x, y, group = group, label = group)) +
geom_line() +
geom_text_repel(data = d[d$x == sample(d$x, 1), ], size = 10)

Mix color and fill aesthetics in ggplot

I wonder if there is the possibility to change the fill main colour according to a categorical variable
Here is a reproducible example
df = data.frame(x = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
y = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
grp = c(rep('a', times = 10),
rep('b', times = 10)),
val = rep(1:10, times = 2))
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(color = grp,
fill = val,
size = val))
Of course it is easy to change the circle colour/shape, according to the variable grp, but I'd like to have the a group in shades of red and the b group in shades of blue.
I also thought about using facets, but don't know if the fill gradient can be changed for the two panels.
Anyone knows if that can be done, without gridExtra?
Thanks!
I think there are two ways to do this. The first is using the alpha aesthetic for your val column. This is a quick and easy way to accomplish your goal but may not be exactly what you want:
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(alpha=val,
fill = grp,
size = val)) + theme_minimal()
The second way would be to do something similar to this post: Vary the color gradient on a scatter plot created with ggplot2. I edited the code slightly so its not a range from white to your color of interest but from a lighter color to a darker color. This requires a little bit of work and using the scale_fill_identity function which basically takes a variable that has the colors you want and maps them directly to each point (so it doesn't do any scaling).
This code is:
#Rescale val to [0,1]
df$scaled_val <- rescale(df$val)
low_cols <- c("firebrick1","deepskyblue")
high_cols <- c("darkred","deepskyblue4")
df$col <- ddply(df, .(grp), function(x)
data.frame(col=apply(colorRamp(c(low_cols[as.numeric(x$grp)[1]], high_cols[as.numeric(x$grp)[1]]))(x$scaled_val),
1,function(x)rgb(x[1],x[2],x[3], max=255)))
)$col
df
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(
fill = col,
size = val)) + theme_minimal() +scale_fill_identity()
Thanks to this other post I found a way to visualize the fill bar in the legend, even though that wasn't what I meant to do.
Here's the ouptup
And the code
df = data.frame(x = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
y = c(rnorm(10, mean = 0),
rnorm(10, mean = 3)),
grp = factor(c(rep('a', times = 10),
rep('b', times = 10)),
levels = c('a', 'b')),
val = rep(1:10, times = 2)) %>%
group_by(grp) %>%
mutate(scaledVal = rescale(val)) %>%
ungroup %>%
mutate(scaledValOffSet = scaledVal + 100*(as.integer(grp) - 1))
scalerange <- range(df$scaledVal)
gradientends <- scalerange + rep(c(0,100,200), each=2)
ggplot(data = df,
aes(x = x,
y = y)) +
geom_point(pch = 21,
aes(fill = scaledValOffSet,
size = val)) +
scale_fill_gradientn(colours = c('white',
'darkred',
'white',
'deepskyblue4'),
values = rescale(gradientends))
Basically one should rescale fill values (e.g. between 0 and 1) and separate them using another order of magnitude, provided by the categorical variable grp.
This is not what I wanted though: the snippet can be improved, of course, to make the whole thing less manual, but still lacks the simple usual discrete fill legend.

Make ggplot2 write the order of magnitude of the axis label only once at the top

I would like to make ggplot2 write only the first part of the scientific notation onto the axis and then add a $x 10^n$ atop the axis for the order of magnitude. Is there a function to do this?
Here is a MWE with a hack to show what I mean:
ggplot(data = data.frame(x = 1:10, y = seq(1, 2, l = 10)*1000), aes(x,y)) + geom_line()
while I'd something like:
ggplot(data = data.frame(x = 1:10, y = seq(1, 2, l = 10)*1000), aes(x,y)) + geom_line() +
scale_y_continuous(breaks = c(1, 1.25, 1.5, 1.75, 2, 2.05)*1000, label = c(1, 1.25, 1.5, 1.75, 2, "x 10^3"))
As a side question, I have noticed that the axis label becomes quickly to close to the tick labels when they are large. Is there a way to set an automatic spacing in between them ?
Here's a more automated way to execute your hack, in case you want to use similar labeling rules for different data. It will figure out an appropriate power of 10 to use and apply that to the labeling:
y_breaks = pretty_breaks()(data$y)
y_max_exp = floor(log10(max(y_breaks)))
y_breaks = c(y_breaks, max(y_breaks) * 1.025)
y_labels = if_else(y_breaks == max(y_breaks),
paste0("x 10^", y_max_exp),
as.character(y_breaks / (10^y_max_exp)))
ggplot(data, aes(x,y)) + geom_line() +
scale_y_continuous(breaks = y_breaks, label = y_labels, minor_breaks = NULL)

Creating Multiple y-axes scales (or alternatives?) using ggplot2 in R [duplicate]

How it is possible (if at all) to show two alternative units on axis ticks in ggplot2?
What I would like to achieve is something like this:
Here's a hacky way of doing that:
d = data.frame(x = 1:20, y = rnorm(20, 5, 5))
ggplot(data = d, aes(x = x, y = y)) +
scale_x_continuous(breaks = c(1:20, seq(2.54, 20, 2.54)),
labels = c(1:20, paste0("\n", 1:as.integer(20/2.54), "\""))) +
geom_point()

Resources