so I've had this question for quite a while now and I have not found a solution to this. Whenever I want to label a parameter of interest, such as central tendencies (mean, median etc.), for example, I create a vertical and/or horizontal line and use annotate for the labeling. However, I have not been able to use LaTeX and non-hardcoded values concurrently. A MRE is shown below:
data <- data.frame(stat = rnorm(1000))
plot <-
ggplot(data, aes(x = stat)) +
geom_histogram(aes(y=..density..), fill="aquamarine", color="black") +
geom_density(color="black", size=1) +
geom_vline(xintercept = 0, color = "blue", linetype="dashed", size = 1) +
annotate("text", x=-0.2, y=0.2,
label=paste0("Mean = ", round(mean(data$stat), 4)),
angle=90, size=7, color="black")
plot
See figure here: https://i.stack.imgur.com/QzojQ.png
In the example above, I generate some random normal data, save it to a dataframe and plot it. I add a dashed vertical line corresponding to the mean of the data and then label it via annotate. Note, however, that I explicitly write Mean in the label since the LaTeX operators don't work and random letters are not ideal for me.
A solution I found is by using expression instead of paste0 when labeling as shown below:
data <- data.frame(stat = rnorm(1000))
plot <-
ggplot(data, aes(x = stat)) +
geom_histogram(aes(y=..density..), fill="aquamarine", color="black") +
geom_density(color="black", size=1) +
geom_vline(xintercept = 0, color = "blue", linetype="dashed", size = 1) +
annotate("text", x=-0.25, y=0.2, label = expression(mu ~ "= -0.0403"),
angle=90, size = 7, color = "black")
plot
See figure here: https://i.stack.imgur.com/rnW5i.png
This allows us to get the LaTeX characters, such as $\mu$ in our case, to show in the plot. But now the downside is that I have to hardcode the mean value in the expression field. This is not ideal and I don't want to do this. I want to be able to do something as follows but this doesn't work:
annotate("text", x=-0.25, y=0.2,
label = expression(mu ~ round(mean(data$stat), 4)),
angle=90, size = 7, color = "black")
I have not been able to find a solution to this so any help is greatly appreciated.
One option to fix that would be to create your expression as a character string instead of using expression and add parse=TRUE to annotate:
library(ggplot2)
set.seed(123)
data <- data.frame(stat = rnorm(1000))
ggplot(data, aes(x = stat)) +
geom_histogram(aes(y = after_stat(density)), fill = "aquamarine", color = "black") +
geom_density(color = "black", size = 1) +
geom_vline(xintercept = 0, color = "blue", linetype = "dashed", size = 1) +
annotate("text",
x = -0.2, y = 0.2,
label = paste0("mu ==", round(mean(data$stat), 4)),
angle = 90, size = 7, color = "black", parse = TRUE
)
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> ℹ Please use `linewidth` instead.
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Related
Does anyone know how to make the the value of the grid line appear on the grid line in ggplot2?
I have a very large plot that I want to display the grid line value on intermittently throughout the plot, so when you are zoomed in you can easily tell what the value is. The only way I can currently think to do that is to add the labels with another data frame or something with the label and the x, y position. I was wondering if there was a more efficient way or an actual built in mechanism to do this.
Thanks!
There is no option built-in to do this specifically, but it is pretty straightforward using expand.grid to create the grid points and labels, then simply draw the result on with geom_text:
library(ggplot2)
gridlines <- expand.grid(Petal.Width = seq(0, 2, 0.5),
Sepal.Length = seq(4, 8),
Species = "setosa")
ggplot(iris, aes(Sepal.Length, Petal.Width, color = Species)) +
geom_point() +
geom_text(data = gridlines, color = "gray50", aes(label = Sepal.Length),
angle = 90, vjust = -0.5, nudge_y = 0.25) +
geom_text(data = gridlines, color = "gray50", aes(label = Petal.Width),
vjust = -0.5, nudge_x = 0.5) +
theme_bw(base_size = 20) +
theme(panel.grid.minor = element_blank())
Created on 2022-09-05 with reprex v2.0.2
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)))
I have a chart that shows mobile usage by operating system. I'd like to add vertical lines to identify when those operating systems were released. I'll go through the chart and then the code.
The chart -
The code -
dev %>%
group_by(os) %>%
mutate(monthly_change = prop - lag(prop)) %>%
ggplot(aes(month, monthly_change, color = os)) +
geom_line() +
geom_vline(xintercept = as.numeric(ymd("2013-10-01"))) +
geom_text(label = "KitKat", x = as.numeric(ymd("2013-10-01")) + 80, y = -.5)
Instead of adding the text in the plot, I'd like to create a legend to identify each of the lines. I'd like to give each of them its own color and then have a legend to identify each. Something like this -
Can I make my own custom legend like that?
1) Define a data frame that contains the line data and then use geom_vline with it. Note that BOD is a data frame that comes with R.
line.data <- data.frame(xintercept = c(2, 4), Lines = c("lower", "upper"),
color = c("red", "blue"), stringsAsFactors = FALSE)
ggplot(BOD, aes( Time, demand ) ) +
geom_point() +
geom_vline(aes(xintercept = xintercept, color = Lines), line.data, size = 1) +
scale_colour_manual(values = line.data$color)
2) Alternately put the labels right on the plot itself to avoid an extra legend. Using the line.data frame above. This also has the advantage of avoiding possible multiple legends with the same aesthetic.
ggplot(BOD, aes( Time, demand ) ) +
geom_point() +
annotate("text", line.data$xintercept, max(BOD$demand), hjust = -.25,
label = line.data$Lines) +
geom_vline(aes(xintercept = xintercept), line.data, size = 1)
3) If the real problem is that you want two color legends then there are two packages that can help.
3a) ggnewscale Any color geom that appears after invoking new_scale_color will get its own scale.
library(ggnewscale)
BOD$g <- gl(2, 3, labels = c("group1", "group2"))
line.data <- data.frame(xintercept = c(2, 4), Lines = c("lower", "upper"),
color = c("red", "blue"), stringsAsFactors = FALSE)
ggplot(BOD, aes( Time, demand ) ) +
geom_point(aes(colour = g)) +
scale_colour_manual(values = c("red", "orange")) +
new_scale_color() +
geom_vline(aes(xintercept = xintercept, colour = line.data$color), line.data,
size = 1) +
scale_colour_manual(values = line.data$color)
3b) relayer The experimental relayer package (only on github) allows one to define two color aethetics, color and color2, say, and then have separate scales for each one.
library(dplyr)
library(relayer)
BOD$g <- gl(2, 3, labels = c("group1", "group2"))
ggplot(BOD, aes( Time, demand ) ) +
geom_point(aes(colour = g)) +
geom_vline(aes(xintercept = xintercept, colour2 = line.data$color), line.data,
size = 1) %>% rename_geom_aes(new_aes = c("colour" = "colour2")) +
scale_colour_manual(aesthetics = "colour", values = c("red", "orange")) +
scale_colour_manual(aesthetics = "colour2", values = line.data$color)
You can definitely make your own custom legend, but it is a bit complicated, so I'll take you through it step-by-step with some fake data.
The fake data contained 100 samples from a normal distribution (monthly_change for your data), 5 groupings (similar to the os variable in your data) and a sequence of dates from a random starting point.
library(tidyverse)
library(lubridate)
y <- rnorm(100)
df <- tibble(y) %>%
mutate(os = factor(rep_len(1:5, 100)),
date = seq(from = ymd('2013-01-01'), by = 1, length.out = 100))
You already use the colour aes for your call to geom_line, so you will need to choose a different aes to map onto the calls to geom_vline. Here, I use linetype and a call to scale_linetype_manual to manually edit the linetype legend to how I want it.
ggplot(df, aes(x = date, y = y, colour = os)) +
geom_line() +
# set `xintercept` to your date and `linetype` to the name of the os which starts
# at that date in your `aes` call; set colour outside of the `aes`
geom_vline(aes(xintercept = min(date),
linetype = 'os 1'), colour = 'red') +
geom_vline(aes(xintercept = median(date),
linetype = 'os 2'), colour = 'blue') +
# in the call to `scale_linetype_manual`, `name` will be the legend title;
# set `values` to 1 for each os to force a solid vertical line;
# use `guide_legend` and `override.aes` to change the colour of the lines in the
# legend to match the colours in the calls to `geom_vline`
scale_linetype_manual(name = 'lines',
values = c('os 1' = 1,
'os 2' = 1),
guide = guide_legend(override.aes = list(colour = c('red',
'blue'))))
And there you go, a nice custom legend. Please do remember next time that if you can provide your data, or a minimally reproducible example, we can better answer your question without having to generate fake data.
I would like to draw multiple verical lines on a ggplot, which positions come from a separate vector.
library(ggplot2)
carats <- c(2.5, 0.1)
ggplot(diamonds, aes(carat)) +
geom_histogram() +
geom_vline(aes(xintercept = carats[1]), col = "black", linetype = "dotted", size = 0.5) +
geom_vline(aes(xintercept = carats[2]), col = "black", linetype = "dotted", size = 0.5)
Adding them one by one works, but I would like to avoid such approach add use draw_vline instead:
hist <- ggplot(diamonds, aes(carat)) + geom_histogram()
draw_vline <- function(histogram, line_value){
hist + geom_vline(aes(xintercept = line_value), col = "black", linetype = "dotted", size = 0.5)
}
draw_vline(hist, carats[1])
This gives me error:
Error in eval(expr, envir, enclos) : object 'line_value' not found
How can I specify my function to work with external vector which isn't in ggplot env?
aes() is for mapping data from columns in the data frame. You don't have a data frame and the _vline/_hline//_abline even show that the default use of
xintercept, yintercept, slope & intercept is outside aes(). This works fine in aes(), too, provided you supplied the call with data set, but you didn't.
library(ggplot2)
carats <- c(2.5, 0.1)
ggplot(diamonds, aes(carat)) +
geom_histogram() +
geom_vline(xintercept = carats, col = "black", linetype = "dotted", size = 0.5)
I'm trying to create a scatterplot where the points are jittered (geom_jitter), but I also want to create a black outline around each point. Currently I'm doing it by adding 2 geom_jitters, one for the fill and one for the outline:
beta <- paste("beta == ", "0.15")
ggplot(aes(x=xVar, y = yVar), data = data) +
geom_jitter(size=3, alpha=0.6, colour=my.cols[2]) +
theme_bw() +
geom_abline(intercept = 0.0, slope = 0.145950, size=1) +
geom_vline(xintercept = 0, linetype = "dashed") +
annotate("text", x = 2.5, y = 0.2, label=beta, parse=TRUE, size=5)+
xlim(-1.5,4) +
ylim(-2,2)+
geom_jitter(shape = 1,size = 3,colour = "black")
However, that results in something like this:
Because jitter randomly offsets the data, the 2 geom_jitters are not in line with each other. How do I ensure the outlines are in the same place as the fill points?
I've see threads about this (e.g. Is it possible to jitter two ggplot geoms in the same way?), but they're pretty old and not sure if anything new has been added to ggplot that would solve this issue
The code above works if, instead of using geom_jitter, I use the regular geom_point, but I have too many overlapping points for that to be useful
EDIT:
The solution in the posted answer works. However, it doesn't quite cooperate for some of my other graphs where I'm binning by some other variable and using that to plot different colours:
ggplot(aes(x=xVar, y = yVar, color=group), data = data) +
geom_jitter(size=3, alpha=0.6, shape=21, fill="skyblue") +
theme_bw() +
geom_vline(xintercept = 0, linetype = "dashed") +
scale_colour_brewer(name = "Title", direction = -1, palette = "Set1") +
xlim(-1.5,4) +
ylim(-2,2)
My group variable has 3 levels, and I want to colour each group level by a different colour in the brewer Set1 palette. The current solution just colours everything skyblue. What should I fill by to ensure I'm using the correct colour palette?
You don't actually have to use two layers; you can just use the fill aesthetic of a plotting character with a hole in it:
# some random data
set.seed(47)
df <- data.frame(x = rnorm(100), y = runif(100))
ggplot(aes(x = x, y = y), data = df) + geom_jitter(shape = 21, fill = 'skyblue')
The colour, size, and stroke aesthetics let you customize the exact look.
Edit:
For grouped data, set the fill aesthetic to the grouping variable, and use scale_fill_* functions to set color scales:
# more random data
set.seed(47)
df <- data.frame(x = runif(100), y = rnorm(100), group = sample(letters[1:3], 100, replace = TRUE))
ggplot(aes(x=x, y = y, fill=group), data = df) +
geom_jitter(size=3, alpha=0.6, shape=21) +
theme_bw() +
geom_vline(xintercept = 0, linetype = "dashed") +
scale_fill_brewer(name = "Title", direction = -1, palette = "Set1")