Adding visual embellishment in a custom ggplot theme - r

I'm currently prototyping custom ggplot themes for use within my organisation. Currently, the theme looks like this (using mtcars data):
I would like to add a solid-colored bar (in the org's primary color) underlining the chart title region to act as an easily scalable type of branding (rather than a jpeg logo which restricts the aspect ratio and size of the charts). I made a mockup of the effect I'm trying to achieve in paint:
I'm aware of annotate() but as far as I understand it that function only accepts arguments corresponding to x and y coordinates in the plotting area, so I don't know how to create an annotation that is bound to a point outside of the plotting area

I would use annotation_custom using a grid::linesGrob here. This allows you to place the line relative to the panel without having to use the plot limits, which can produce inconsistent results. It also allows the line to extend beyond the left and right limits of the plotting area.
Suppose your plot is created a bit like this:
library(ggplot2)
p <- ggplot(mpg, aes(displ, hwy, col = class)) +
geom_point() +
labs(title = "Test 1, theme 1", subtitle = "R default dataset",
caption = "Organization caption here",
y = "Fuel efficiency (mpg)",
x = "Engine displacement (litres)") +
scale_color_brewer(palette = "Set2", name = NULL) +
theme(panel.grid = element_line(color = "gray50"),
panel.border = element_rect(fill = NA),
legend.position = "top")
p
To add the line you can do:
p + coord_cartesian(clip = "off") +
annotation_custom(grid::linesGrob(
x = unit(c(-1, 2), "npc"), y = unit(c(1.2, 1.2), "npc"),
gp = grid::gpar(col = "orange2", lwd = 5)))

It is a bit hard without reproducible example, but you can use annotate with "segment" and define the x-values with Inf and the y-values with max(y) + some value depending on your theme layout like this:
library(ggplot2)
library(dplyr)
mtcars %>%
ggplot(aes(x = mpg, y = wt)) +
geom_point() +
annotate("segment", x = -Inf, xend = Inf, y = max(mtcars$wt) + 0.5, yend = max(mtcars$wt) + 0.5, colour = "orange", size = 2) +
coord_cartesian(clip = "off", ylim = c(min(mtcars$wt), max(mtcars$wt))) +
theme(plot.margin = unit(c(3,3,1,1), "lines"))
Created on 2022-07-27 by the reprex package (v2.0.1)

Related

Add small arrows instead of axis to UMAP

More often I see in publications that instead of printing the UMAP axis in scRNAseq experiments (or even t-SNE or PCA) they just add two small arrows in the bottom left corner.
Something like this:
I really like the aesthetics of it but I don´t know how to replicate this in R. I guess this is normally done separately with some image editor but it can probably be done with ggplot2 package to make it more reproducible.
So far I only got the arrows in the axis:
x <- data.frame(UMAP1=rnorm(300),UMAP2=rnorm(300))
ggplot(x, aes(UMAP1,UMAP2)) + geom_point() + theme_minimal() +
theme(axis.line = element_line(arrow = arrow(type='closed',
length = unit(10,'pt'))))
But I don't know how to make them smaller and with the title underneath. Does anyone have any suggestions on how to do this?
In code below, adjust unit(3, "cm") and hjust = 0 to taste.
Disclaimer: I wrote ggh4x.
library(ggplot2)
axis <- ggh4x::guide_axis_truncated(
trunc_lower = unit(0, "npc"),
trunc_upper = unit(3, "cm")
)
x <- data.frame(UMAP1=rnorm(300),UMAP2=rnorm(300))
ggplot(x, aes(UMAP1, UMAP2)) +
geom_point() +
guides(x = axis, y = axis) +
theme(axis.line = element_line(arrow = arrow()),
axis.title = element_text(hjust = 0))
Created on 2022-12-07 by the reprex package (v2.0.1)
Optionally, add the code below if you want to get rid of the ticks and labels (which don't make any sense in terms of UMAP/tSNE anyway)
scale_x_continuous(breaks = NULL) +
scale_y_continuous(breaks = NULL)
I'd suggest faking it with an annotation:
library(dplyr); library(umap); library(ggplot2)
arr <- list(x = -10, y = -15, x_len = 5, y_len = 5)
ggplot(storms_umap_extract, aes(x,y, color = category, alpha = wind)) +
geom_point() +
annotate("segment",
x = arr$x, xend = arr$x + c(arr$x_len, 0),
y = arr$y, yend = arr$y + c(0, arr$y_len),
arrow = arrow(type = "closed", length = unit(10, 'pt'))) +
theme_void()
Here's the umap data:
storms_umap <- storms |>
select(lat,long, wind, pressure) |>
umap() # this took about a minute to run
storms_umap_extract <- tibble(
x = storms_umap$layout[,1],
y = storms_umap$layout[,2],
wind = storms_umap$data[,3],
category = storms$category
)

scale_fill_discrete with breaks and custom color scale?

I have an issue choosing the custom colouring of my plot, I am trying to add my own values to the plot but I can's seem to understand where should I put the custom color vector:
col <- c("#004d8d", "#cc2701", "#e5b400")
This is the plot code right now:
p1 <- ggplot(data = densdf1, mapping = aes(x = x, y = y)) +
geom_area(data = densdf1[densdf1$CI,],
aes(fill = Electrode, color = Electrode),
outline.type = "full", alpha = 0.3, size = 1) +
geom_line(aes(color = Electrode), size = 1) +
scale_fill_discrete(breaks=c("Fz","Cz","Pz")) +
guides(colour = "none") +
geom_vline(xintercept = 0) +
lims(x = c(-3, 2), y = c(0, 2.25)) +
labs(title="INTERVAL 225-275ms", x="VALUES", y="DENSITY") +
theme_bw() +
theme(axis.text=element_text(size=10),
axis.title=element_text(size=12),
plot.title=element_text(size=14))
This is the plot with default colours and it looks perfectly fine, but I'd like to customise the colours.
As you can see there is a contour line and the area from the density that need to be coloured.
I tried changing the aesthetics but I think I am not understanding the logic. I tried also using scale_manual_fill(values = col) removing scale_fill_discrete(breaks=c("Fz","Cz","Pz")) but it only works with the area inside.
Can any ggplot2 expert give me a hint? Thanks!
in the aes(...) section you define scales, and the color must be adjusted for each scale. In your case it would be the "fill" and the "color" scale, hence the following two lines must be added to adjust both scales:
scale_color_manual(values = col, breaks = c("Fz", "Cz", "Pz")) +
scale_fill_manual(values = col, breaks = c("Fz", "Cz", "Pz")) +
If you want to make sure, that a certain category gets a certain color, you have to specify the breaks, and the order the vector corresponds to the order of the color vector.

Draw a rectangle around bars and categories in (R, ggplot2)

I would like to create a box/rectangle around a single level of a category and include the axis category text and the bar itself:
As you can see in the photo, the rectangle extends beyond the grid and into the plot area to encompass the axis text. I'm hoping for something customizable so I can draw rounded corners or not, change the color, and specify where it goes.
Here's some generic code I used to produce a plot:
ggplot(mtcars, aes(x=factor(cyl)))+
geom_bar(stat="count", width=0.7, fill="steelblue")+
theme_minimal()
Hopefully, this isn't answered somewhere already!
For rectangle use annotate with "rect"
to go over the x axis you can set the x axis to blank
then add new quasi axis with geom_text setting y to 0 or -1. play around to fit:
p <- ggplot(mtcars, aes(x=factor(cyl)))+
geom_bar(stat="count", width=0.7, fill="steelblue")+
theme_minimal()
p + annotate("rect", xmin = 0.5, xmax = 1.5, ymin = -1, ymax = 12,
alpha = 0, color= "green") +
theme(axis.text.x = element_blank(),
axis.line.x = element_blank(),
axis.ticks.x = element_blank()) +
geom_text(aes(y = -0.5, x = factor(cyl),
label = cyl)) +
labs(title="Rectangle over x axis!",
x ="cyl", y = "count")
That's what the ggforce package is great for. Here with a semi-programmatic approach to define x/y coordinates of your shape. If you intend to mark specific areas / data points, you might also want to look into ggforce::geom_mark_rect
I have also un-factorised the x.
library(tidyverse)
library(ggforce)
cyl <- 4
n_cyl4 <- table(mtcars$cyl)[1]
df_rect <- data.frame(x = c(cyl - .5, rep(cyl + .5, 2), cyl - .5), y = c(rep(-2, 2), rep(n_cyl4 + .5, 2)))
ggplot(mtcars, aes(x = cyl)) +
geom_shape(data = df_rect, aes(x, y), fill = NA, color = "black", radius = .01) +
geom_bar(stat = "count", width = 0.7, fill = "steelblue") +
scale_x_continuous(breaks = seq(4, 8, 2)) +
coord_cartesian(ylim = c(0, NA), clip = "off") +
theme_minimal()
Created on 2021-08-03 by the reprex package (v2.0.0)

R 4.0.0 : I am trying to change symbols of legend for a plot in ggplot2, at the moment I have overlapping symbols

I'm making a plot in ggplot2 with a line and a segment, the symbols for these appear overlapped. I can make it so that their symbols are either both an arrow or both a line using show.legend, but I'd like to make the segment an arrow and the line a line. I tried using override.aes to alter it after looking for a solution. changing the shape doesn't appear to do anything. setting the line to 0 makes everything disappear for that symbol.
library(tidyverse)
ggplot(NULL, aes(x=0, y=0)) + geom_point(alpha = 0) +
coord_cartesian(xlim = c(-2.5,2.5),ylim = c(-2.5,2.5)) +
geom_segment(aes(x=0, xend=1, y=0, yend=-2, color ="Vector a"), arrow = arrow(length = unit(0.5, "cm")), show.legend = T) +
geom_abline(aes(intercept=1, slope=1/2, color = "Line 1"), show.legend = T) +
scale_y_continuous(breaks=c(-2:2)) +
scale_x_continuous(breaks=c(-2:2)) +
scale_color_manual(values = c("green", "blue"), guide = guide_legend(override.aes = list(linetype = c(1,1),
shape = c(1,1)))) +
labs(
title = "plot (a) line",
x = "X Axis",
y = "Y Axis"
)
Thank you in advance for any advice you have in fixing my problem :).
EDIT: looking around I found out the symbol for either can be changed through key_glyph = draw_key_rect with various other draw_keys_ working. they'll still both display at the same time.
Try this:
library(ggplot2)
ggplot(NULL, aes(x=0, y=0)) + geom_point(alpha = 0) +
coord_cartesian(xlim = c(-2.5,2.5),ylim = c(-2.5,2.5)) +
geom_segment(aes(x=0, xend=1, y=0, yend=-2, linetype ="Vector a"),
colour = "blue",
arrow = arrow(length = unit(0.5, "cm"))) +
geom_abline(aes(intercept=1, slope=1/2, color = "Line 1")) +
scale_y_continuous(breaks=c(-2:2)) +
scale_x_continuous(breaks=c(-2:2)) +
scale_color_manual(values = c("green", "blue")) +
labs(title = "plot (a) line",
x = "X Axis",
y = "Y Axis",
linetype = NULL,
colour = "colour")
Commentary
You cannot easily use one aesthetic for two different geom levels in ggplot; so you need to fiddle with the build up of your data and call to ggplot to use different aesthetics to create the legend you want. I can't find an explicit statement to this effect. In Hadley Wickham (2015) ggplot2 Elegant Graphics for Data Analysis it states:
“A legend may need to draw symbols from multiple layers. For example,
if you’ve mapped colour to both points and lines, the keys will show
both points and lines.”
and
"ggplot2 tries to use the fewest number of legends to accurately convey
the aesthetics used in the plot. It does this by combining legends
where the same variable is mapped to different aesthetics."
Which goes some way to explaining the issues you were having with your plot.
Created on 2020-05-24 by the reprex package (v0.3.0)

Adding legend for combo bar and line graph -- ggplot ignoring commands

I am trying to make a bar chart with line plots as well. The graph has created fine but the legend does not want to add the line plots to the legend.
I have tried so many different ways of adding these to the legend including:
ggplot Legend Bar and Line in Same Graph
None of which have worked. show.legend also seems to have been ignored in the geom_line aes.
My code to create the graph is as follows:
ggplot(first_q, aes(fill = Segments)) +
geom_bar(aes(x= Segments, y= number_of_new_customers), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
ylab('Number of Customers') + xlab('Segments') +
ggtitle('Number Customers in Q1 by Segments') +theme(plot.title =
element_text(hjust = 0.5)) +
geom_line(aes(x= Segments, y=count) ,stat="identity",
group = 1, size = 1.5, colour = "darkred", alpha = 0.9, show.legend =
TRUE) +
geom_line(aes(x= Segments, y=bond_count)
,stat="identity", group = 1, size = 1.5, colour = "blue", alpha =
0.9) +
geom_line(aes(x= Segments, y=variable_count)
,stat="identity", group = 1, size = 1.5, colour = "darkgreen",
alpha = 0.9) +
geom_line(aes(x= Segments, y=children_count)
,stat="identity", group = 1, size = 1.5, colour = "orange", alpha
= 0.9) +
guides(fill=guide_legend(title="Segments")) +
scale_color_discrete(name = "Prod", labels = c("count", "bond_count", "variable_count", "children_count)))
I am fairly new to R so if any further information is required or if this question could be better represented then please let me know.
Any help is greatly appreciated.
Alright, you need to remove a little bit of your stuff. I used the mtcars dataset, since you did not provide yours. I tried to keep your variable names and reduced the plot to necessary parts. The code is as follows:
first_q <- mtcars
first_q$Segments <- mtcars$mpg
first_q$val <- seq(1,nrow(mtcars))
first_q$number_of_new_costumers <- mtcars$hp
first_q$type <- "Line"
ggplot(first_q) +
geom_bar(aes(x= Segments, y= number_of_new_costumers, fill = "Bar"), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
geom_line(aes(x=Segments,y=val, linetype="Line"))+
geom_line(aes(x=Segments,y=disp, linetype="next line"))
The answer you linked already gave the answer, but i try to explain. You want to plot the legend by using different properties of your data. So if you want to use different lines, you can declare this in your aes. This is what get's shown in your legend. So i used two different geom_lines here. Since the aes is both linetype, both get shown at the legend linetype.
the plot:
You can adapt this easily to your use. Make sure you using known keywords for the aesthetic if you want to solve it this way. Also you can change the title names afterwards by using:
labs(fill = "costum name")
If you want to add colours and the same line types, you can do customizing by using scale_linetype_manual like follows (i did not use fill for the bars this time):
library(ggplot2)
first_q <- mtcars
first_q$Segments <- mtcars$mpg
first_q$val <- seq(1,nrow(mtcars))
first_q$number_of_new_costumers <- mtcars$hp
first_q$type <- "Line"
cols = c("red", "green")
ggplot(first_q) +
geom_bar(aes(x= Segments, y= number_of_new_costumers), stat =
"identity") + theme(axis.text.x = element_blank()) +
scale_y_continuous(expand = c(0, 0), limits = c(0,3000)) +
geom_line(aes(x=Segments,y=val, linetype="solid"), color = "red", alpha = 0.4)+
geom_line(aes(x=Segments,y=disp, linetype="second"), color ="green", alpha = 0.5)+
scale_linetype_manual(values = c("solid","solid"),
guide = guide_legend(override.aes = list(colour = cols)))

Resources