I am trying to shade a certain section of a time series plot (a bit like recession shading - similarly to the graph at the bottom of this article on recession shading in excel). I have put a little, possibly clumsy, sample together to illustrate.
I first create a time series, plot it with ggplot2 and then want to use geom_rect to provide the shading. But I must get something wrong in the arguments.
a<-rnorm(300)
a_ts<-ts(a, start=c(1910, 1), frequency=12)
a_time<-time(a_ts)
a_series<-ts.union(big=a_ts, month=a_time)
a_series_df<-as.data.frame(a_series)
ggplot(a_series)+
geom_line(mapping=aes_string(x="month", y="big"))+
geom_rect(
fill="red",alpha=0.5,
mapping=aes_string(x="month", y="big"),
xmin=as.numeric(as.Date(c("1924-01-01"))),
xmax=as.numeric(as.Date(c("1928-12-31"))),
ymin=0,
ymax=2
)
Note that I have also tried which also did not work.
geom_rect(
fill="red",alpha=0.5,
mapping=aes_string(x="month", y="big"),
aes(
xmin=as.numeric(as.Date(c("1924-01-01"))),
xmax=as.numeric(as.Date(c("1928-12-31"))),
ymin=0,
ymax=2)
)
Its a bit easier using annotate and also note that the bounds for the rectange can be specified as shown:
ggplot(a_series_df, aes(month, big)) +
geom_line() +
annotate("rect", fill = "red", alpha = 0.5,
xmin = 1924, xmax = 1928 + 11/12,
ymin = -Inf, ymax = Inf) +
xlab("time")
This would also work:
library(zoo)
z <- read.zoo(a_series_df, index = 2)
autoplot(z) +
annotate("rect", fill = "red", alpha = 0.5,
xmin = 1924, xmax = 1928 + 11/12,
ymin = -Inf, ymax = Inf) +
xlab("time") +
ylab("big")
Either one gives this:
Code works fine, conversion to decimal date is needed for xmin and xmax, see below, requires lubridate package.
library("lubridate")
library("ggplot2")
ggplot(a_series_df)+
geom_line(mapping = aes_string(x = "month", y = "big")) +
geom_rect(
fill = "red", alpha = 0.5,
mapping = aes_string(x = "month", y = "big"),
xmin = decimal_date(as.Date(c("1924-01-01"))),
xmax = decimal_date(as.Date(c("1928-12-31"))),
ymin = 0,
ymax = 2
)
Cleaner version, shading plotted first so the line colour doesn't change.
ggplot() +
geom_rect(data = data.frame(xmin = decimal_date(as.Date(c("1924-01-01"))),
xmax = decimal_date(as.Date(c("1928-12-31"))),
ymin = -Inf,
ymax = Inf),
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
fill = "grey", alpha = 0.5) +
geom_line(data = a_series_df,aes(month, big), colour = "blue") +
theme_classic()
To use geom_rect you need to define your rectangle coordinate through a data.frame:
shade = data.frame(x1=c(1918,1930), x2=c(1921,1932), y1=c(-3,-3), y2=c(4,4))
# x1 x2 y1 y2
#1 1918 1921 -3 4
#2 1930 1932 -3 4
Then you give ggplot your data and the shade data.frame:
ggplot() +
geom_line(aes(x=month, y=big), color='red',data=a_series_df)+
geom_rect(data=shade,
mapping=aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2), color='grey', alpha=0.2)
library(xts)
library(zoo)
library(ggts)
Creating an xts object
data<-as.xts(x=runif(228,20,40),order.by = seq(as.Date("2000/01/01"), by = "month", length.out = 228))
Creating data frame of dates for which you want to crate shades
date<-data.frame(as.Date("2008-01-01"),as.Date("2009-01-01"))
Now create plot with shaded area
plot_data<-ggts(data)+geom_cycle(date)
Related
I have a data frame with a titration curve of pH, function of volume. I would like to put two colored/shadowed horizontal rectangles to show the range of pH of two indicators, methyl orange (3.2
Here is a reproducible exemple :
Volume <- c(1:5)
pH <- c(3,4,9,10,12)
df <- data.frame(Volume,pH)
ggplot(df,aes(x=Volume,y=pH))+geom_line(color="purple")+
geom_rect(aes(ymin=3.2,ymax=4.4,xmin=-Inf,xmax=Inf))+
geom_rect(aes(ymin=8.2,ymax=10,xmin=-Inf,xmax=Inf))+
scale_fill_manual(values = alpha(c("orange", "pink"),alpha = .3))
Which gives me this result :
How can theses rectangles be orange, pink and shadowed ?
Try this:
Volume <- c(1:5)
pH <- c(3,4,9,10,12)
df <- data.frame(Volume,pH)
ggplot(df,aes(x=Volume,y=pH))+geom_line(color="purple")+
geom_rect(aes(ymin=3.2,ymax=4.4,xmin=-Inf,xmax=Inf), fill="orange", alpha=.3)+
geom_rect(aes(ymin=8.2,ymax=10,xmin=-Inf,xmax=Inf), fill="pink", alpha=.3)
When something is not working, it is often a problem of referencing the wrong data or aesthetic in your geom_... calls.
Try to be very explicit when calling your geom.
Below I am creating an explicit data frame for your rectangles. You should explicitly call this data and also add inherit.aes = FALSE, so that it won't try to read the aesthetic from the ggpolt main call.
library(tidyverse)
mydf <- data.frame(Volume = c(1:5), pH = c(3, 4, 9, 10, 12))
ann_rect <- bind_rows(
data.frame(ymin = 3.2, ymax = 4.4, xmin = -Inf, xmax = Inf, fill = "orange"),
data.frame(ymin = 8.2, ymax = 10, xmin = -Inf, xmax = Inf, fill = "pink")
)
ggplot(mydf, aes(x = Volume, y = pH)) +
geom_rect(
inherit.aes = FALSE,
data = ann_rect,
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill)
) +
geom_line(color = "purple") +
scale_fill_identity()
Alternatively, leave the main call empty and explicitly reference the data in each geom call.
ggplot() +
geom_rect(data = ann_rect, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill)) +
geom_line(data = mydf, aes(x = Volume, y = pH), color = "purple") +
scale_fill_identity()
Created on 2020-03-19 by the reprex package (v0.3.0)
This question already has answers here:
Conditionally change panel background with facet_grid?
(2 answers)
Closed 2 years ago.
I am attempting to shade multiple regions of interest on a scatter plot. Based on this answer, I believe I am forced to leave the intial ggplot() call empty, and supplie the geom_rect() calls before the geom_point() call. I have gotten all of this to work. I can not however apply a facet based on data passed to geom_point().
The following correctly plots my data (scatter plot over red and blue regions of different sizes):
ggplot() +
geom_rect(aes(xmin=885, xmax=1544, ymin=-Inf, ymax=Inf), alpha=.2, fill = "red") +
geom_rect(aes(xmin=1858, xmax=2580, ymin=-Inf, ymax=Inf), alpha=.2, fill = "blue") +
geom_point(data=df, aes(x=Position, y=Max_Freq))
However all of the following produce errors as follows:
+ facet_wrap(~Replicate)
#Error in if (empty(data)) { : missing value where TRUE/FALSE needed
+ facet_wrap(data~Replicate)
#Error in combine_vars(data, params$plot_env, rows, drop = params$drop):
# At least one layer must contain all variables used for facetting
+ facet_wrap(data$Replicate)
#Error in data$Replicate : object of type 'closure' is not subsettable
In other graphs when df is supplied in the ggplot() call (ie ggplot(df, aes(x=Position, y=Max_Freq)) the first option correctly facets the data. Admittedly, I am not well versed in R, but it seems like this should have a simple solution.
One approach is to create a data frame with the rect coordinates that will also have the facet variable. Example:
some data:
df <- data.frame(x = rnorm(20),
y = runif(20),
facet = sample(c("A", "B"),
20,
replace = TRUE))
create a data frame with geom_rect coordinates:
rect1 <- data.frame(xmin = -1,
xmax = 1,
ymin = -Inf,
ymax = Inf,
facet = c("A", "B"))
plot it:
ggplot() +
geom_rect(data = rect1 , aes(xmin = xmin,
xmax = xmax,
ymin = ymin,
ymax = ymax),
alpha = 0.2, fill = "blue") +
geom_point(data = df, aes(x = x, y = y))+
facet_wrap(~facet)
This allows for per facet customization of rectangles. Example:
rect2 <- data.frame(xmin = c(-1, 0),
xmax = c(0, 2),
ymin = c(-Inf, 0.25),
ymax = Inf,
facet = c("A", "B"))
ggplot() +
geom_rect(data = rect2 , aes(xmin = xmin,
xmax = xmax,
ymin = ymin,
ymax = ymax,
fill = facet),
alpha = 0.2) +
geom_point(data = df, aes(x = x, y = y))+
facet_wrap(~facet)
or just to plot rectangles in some facets:
rect3 <- data.frame(xmin = -1,
xmax = 0,
ymin = 0.25,
ymax = Inf,
facet = "A")
This is my dataset example:
df <- data.frame(group = rep(c("group1","group2","group3", "group4", "group5", "group6"), each=3),
X = paste(letters[1:18]),
Y = c(1:18))
As you can see, there are three variables, two of them categorical (group and X). I have constructed a line chart using ggplot2 where the X axis is X and Y axis is Y.
I want to shade the background using the group variable, so that 6 different colors must appear.
I tried this code:
ggplot(df, aes(x = X, y = Y)) +
geom_rect(xmin = 0, xmax = 3, ymin = -0.5, ymax = Inf,
fill = 'blue', alpha = 0.05) +
geom_point(size = 2.5)
But geom_rect() only colorize the area between 0 and 3, in the X axis.
I guess I can do it manually by replicating the the geom_rect() so many times as groups I have. But I am sure there must be a more beautiful code using the variable itself. Any idea?
To get shading for the entire graph, geom_rect needs the xmin and xmax locations for all the rectangles, so these need to be provided by mapping xmin and xmax to columns in the data, rather than hard-coding them.
ggplot(df, aes(x = X, y = Y)) +
geom_rect(aes(xmin = X, xmax = dplyr::lead(X), ymin = -0.5, ymax = Inf, fill = group),
alpha = 0.5) +
geom_point(size = 2.5) +
theme_classic()
Here is one way:
df2 <- df %>% mutate(Xn=as.numeric(X))
ggplot(df2) +
geom_rect(aes(xmin=Xn-.5, xmax=Xn+.5, ymin=-Inf, ymax=Inf, fill = group), alpha=0.5, stat="identity") +
geom_point(aes(x = Xn, y = Y), size = 2.5) + scale_x_continuous(breaks=df2$Xn, labels=df2$X)
This will get you close - need to add a couple columns to your data frame. Using dplyr here.
df <- df %>%
group_by(group) %>%
mutate(xmin = sort(X)[1],
xmax = sort(X, decreasing = T)[1])
ggplot(df, aes(x = X, y = Y)) +
geom_point(size = 2.5) +
geom_rect(aes(xmin=xmin, xmax = xmax, fill = group), ymin = -0.5, ymax = Inf,
alpha = 0.05)
I have a data frame in this kind of format:
df <- data.frame(
time = rep(seq(from = as.POSIXct("2016-08-10 11:00:00"),
to = as.POSIXct("2016-08-10 12:00:00"), by="sec"), 2),
value = c(diffinv(rnorm(3601)), diff(rnorm(3601))),
facets = c(rep("A",3601), rep("B", 3601)),
shading = rep(c(rep("x", 1500), rep("y", 750), rep("z", 1351)), 2),
stringsAsFactors = FALSE
)
I can plot the value time series on separate graphs sharing the x-axis using ggplot2's facet_grid function. I also want to include another dimension in my plot - the variable shading to shade the background.
I know I can do this by specifying the ranges of the x-axis the shaded regions will cover:
xRange1 <- range(df$time[df$shading=="x"])
xRange2 <- range(df$time[df$shading=="y"])
xRange3 <- range(df$time[df$shading=="z"])
yRange <- range(df$value)
When I first set this up I include alpha in each of my geom_rect
ggplot(df, aes(x = time, y = value)) +
geom_line() +
facet_grid(facets ~ ., scales = "free_y") +
geom_rect(aes(xmin = xRange1[1], xmax = xRange1[2]),
ymin = yRange[1], ymax = yRange[2],
alpha = 0.3, fill = "#EEF2BF") +
geom_rect(aes(xmin = xRange2[1], xmax = xRange2[2]),
ymin = yRange[1], ymax = yRange[2],
alpha = 0.3, fill = "#A3BAB6",) +
geom_rect(aes(xmin = xRange3[1], xmax = xRange3[2]),
ymin = yRange[1], ymax = yRange[2],
alpha = 0.3, fill = "#BFA67E")
Obviously the alpha didn't work.
One way to get around this is to put geom_line() at the end:
ggplot(df, aes(x = time, y = value)) +
facet_grid(facets ~ ., scales = "free_y") +
geom_rect(aes(xmin = xRange1[1], xmax = xRange1[2]),
ymin = yRange[1], ymax = yRange[2],
alpha = 0.3, fill = "#EEF2BF") +
geom_rect(aes(xmin = xRange2[1], xmax = xRange2[2]),
ymin = yRange[1], ymax = yRange[2],
alpha = 0.3, fill = "#A3BAB6",) +
geom_rect(aes(xmin = xRange3[1], xmax = xRange3[2]),
ymin = yRange[1], ymax = yRange[2],
alpha = 0.3, fill = "#BFA67E") +
geom_line()
But that hides the grid and doesn't solve the underlying problem.
I have looked at several posts and none of them address this directly. I have looked at using other functions in my plot including scale_fill_manual
(last example on page) and scale_alpha
Edit: I suspect the best solution also involves setting up the geom_rect in a less manual way. My actual data frame has more than 3 character values I want to shade with.
This question already has answers here:
geom_rect and alpha - does this work with hard coded values?
(4 answers)
Closed 2 years ago.
I recently upgraded to R version 3.2.3 and also to ggplot version 2.0.0.
Trying to upgrade some old code to the newer versions I encountered a weird behaviour with ggplot2 and its transparency settings.
Now my question is, is this a bug or a feature (if so, can someone enlighten me as to why its good to have it this way)? The result I want to have is (obviously) plot 2.
Say I plot a line and lay a rectangle with transparency over it like this:
library(ggplot2)
plot_data <- data.frame(x = 1:100, y = rnorm(100))
# Plot 1
ggplot(data = plot_data, aes(x = x, y = y)) +
geom_line() +
geom_rect(aes(xmin = 20, xmax = 50, ymin = -Inf, ymax = Inf), fill = "red",
alpha = 0.1) + ggtitle("Plot 1")
# Plot 2
ggplot() +
geom_line(data = plot_data, aes(x = x, y = y)) +
geom_rect(aes(xmin = 20, xmax = 50, ymin = -Inf, ymax = Inf), fill = "red",
alpha = 0.1) + ggtitle("Plot 2")
To my understanding plot 1 and 2 should be identical. However, I get the following plots:
Plot 1:
and plot 2:
Additionally, if I play around with the alpha-values (for example setting them to 0.01, I get the two following plots:
and
I believe that calling geom_rect without a data parameter will effectively draw an individual rectangle for each row of the data.frame which is why the alpha is "working", but not quite as expected. I have not been able to replicate and get to parity/agreement between the methods, but as you noted, I think it is doing something along the lines of drawing either 100 individual rectangles, or 30 (the width of the rectangles; from 20 to 50) which is why alpha = 0.1 / 100 and alpha = 0.1 / 30 gets you closer, but not quite matching.
Regardless, I would probably use annotate, as that better describes the behavior/result you are trying to achieve without issues and works, as expected, in both cases -- annotations will draw a single instance per geom:
ggplot(data = plot_data, aes(x = x, y = y)) +
# geom_rect(aes(xmin = 20, xmax = 50, ymin = -Inf, ymax = Inf, alpha = 0.1, fill = "red")) +
annotate("rect", xmin = 20, xmax = 50, ymin = -Inf, ymax = Inf, alpha = 0.1, fill = "red") +
geom_line() +
ggtitle("Plot 1")
ggplot() +
geom_line(data = plot_data, aes(x = x, y = y)) +
# geom_rect(aes(xmin = 20, xmax = 50, ymin = -Inf, ymax = Inf), fill = "red", alpha = 0.1) +
annotate("rect", xmin = 20, xmax = 50, ymin = -Inf, ymax = Inf, fill = "red", alpha = 0.1) +
ggtitle("Plot 2")