How to use ggplot in a loop with computed parameters? - r

I am trying to produce a variable number of rectangles (layers) in a ggplot of a zoo object. I would like to do this in a loop since I do not know ahead of time how many rectangles I will need. Here is a toy example.
library("zoo")
library("ggplot2")
set.seed(1)
y <- runif(50, min = 1, max = 2)
start <- as.numeric(as.Date("2018-01-01"))
x <- as.Date(start:(start + 49))
x.zoo <- zoo(y, order.by = x)
## Fill areas
bars <- data.frame(start = c(x[5], x[20], x[35]),
end = c(x[10], x[25], x[40]))
I can plot these manually with this code:
## Plot manually
print(autoplot.zoo(x.zoo, facets = NULL) +
geom_rect(aes(xmin = bars[1,1],
xmax = bars[1,2], ymin = -Inf, ymax = Inf),
fill = "pink", alpha = 0.01) +
geom_rect(aes(xmin = bars[2,1],
xmax = bars[2,2], ymin = -Inf, ymax = Inf),
fill = "pink", alpha = 0.01) +
geom_rect(aes(xmin = bars[3,1],
xmax = bars[3,2], ymin = -Inf, ymax = Inf),
fill = "pink", alpha = 0.01))
This gives me this desired image:
I tried using the loop below but it only plots the last bar. How do I do this??
## This didn't work but illustrates what I am trying to do
p = autoplot.zoo(x.zoo, facets = NULL)
for(i in 1:3) {
p = p + geom_rect(aes(xmin = bars[i,1],
xmax = bars[i,2], ymin = -Inf, ymax = Inf),
fill = "pink", alpha = 0.01)
}
print(p)

You don't need a loop. geom_rect is vectorised
autoplot.zoo(x.zoo, facets = NULL) +
geom_rect(aes(xmin = start, xmax = end, ymin = -Inf, ymax = Inf), data = bars, fill = "pink", alpha = 0.4, inherit.aes = FALSE)

One way to avoid the for loop is to convert x.zoo into a data.frame and map the data to geom_line. This way, you can map the bars data to geom_rect separately.
dat <- data.frame(index = index(x.zoo), data.frame(x.zoo))
ggplot() +
geom_rect(data = bars, aes(xmin = start, xmax = end, ymin =-Inf, ymax = Inf), fill = 'pink', alpha = .5) +
geom_line(data=dat, aes(x = index, y = x.zoo))

Related

Distinguish theme (background) color for negative and positive values in geom_boxplot

For data of this type:
set.seed(123)
df <- data.frame(
Q = c(rep("q_pol",10), rep("q_wh",10)),
slope = c(rnorm(10,-0.5), rnorm(10, 0.5)),
Recipient = rep(c("A", "B"),10)
)
how can I color the theme (or background) of these boxplots in two different colors: the upper half for values > 0, say, "lightblue" and the lower half for values < 0, say, "darkblue":
library(ggplot2)
ggplot(df,
aes(x = Q, y = slope, color = Recipient)) +
geom_boxplot(notch = TRUE)
One option would be to add different filled backgrounds using geom_rect:
library(ggplot2)
ggplot(df,
aes(x = Q, y = slope, color = Recipient)) +
geom_rect(data = data.frame(
xmin = c(-Inf, -Inf),
xmax = c(Inf, Inf),
ymin = c(-Inf, 0),
ymax = c(0, Inf),
fill = c("darkblue", "lightblue")
), aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill), inherit.aes = FALSE, alpha = .5) +
scale_fill_manual(values = c("darkblue" = "darkblue", "lightblue" = "lightblue"), guide = "none") +
geom_boxplot(notch = TRUE)

How to remove border from quadrant lines in geom_point plot (ggplot2) after adding "size"

How to remove border from quadrant lines in geom_point plot (ggplot2) after adding "size"?
ggplot(
DurablesSIZE,
aes(
x = DurablesSIZE$`GDP LQ`,
y = DurablesSIZE$Slope,
color = DurablesSIZE$Sector,
size = DurablesSIZE$`2019 GDP`
)
) +
geom_point() +
geom_hline(yintercept = 0) +
geom_vline(xintercept = 1) +
xlim(0, 5.5) +
ylim(-0.26, 0.26) +
geom_rect(aes(
xmin = 1,
xmax = Inf,
ymin = 0,
ymax = Inf
),
fill = "green",
alpha = 0.03) +
geom_rect(aes(
xmin = -Inf,
xmax = 1,
ymin = -Inf,
ymax = 0
),
fill = "red",
alpha = 0.03) +
geom_rect(aes(
xmin = -Inf,
xmax = 1,
ymin = 0,
ymax = Inf
),
fill = "yellow",
alpha = 0.03) +
geom_rect(aes(
xmin = 1,
xmax = Inf,
ymin = -Inf,
ymax = 0
),
fill = "yellow",
alpha = 0.03) +
labs(y = "Slope of GDP LQ (5Y)",
x = "2019 GDP LQ",
color = "Sector",
size = "2019 GDP") +
ggtitle("Oregon Durable Manufacturing \nTargeting Potential (GDP)") +
geom_text(
aes(label = ifelse(Slope > 0 & LQ > 1, as.character(Sector), '')),
hjust = 0,
vjust = 0,
size = 2.5,
nudge_x = -0.07,
nudge_y = 0.013
) +
theme(legend.key = element_rect(colour = NA, fill = NA),
legend.box.background = element_blank())
After adding size to my points, there is a weird border around the quadrant line weird border.
The size aesthetic is being applied globally, so it is creating a thick border around each geom_rect -- it controls border width for that geom.
To remove it, take size out of the global aes mapping and use geom_point(aes(size = '2019 GDP')) + to apply it to that layer alone.
Another note: if you use geom_rect for annotation purposes, it will be plotted once for each relevant line of your data, leading to massive overplotting and minimal control of alpha. It will be better to use annotate("rect" ...) for those, or to create a separate summary table which those layers can refer to so they only plot once.
Here's some fake data I made up so that I could run your code. Please include something like this in your questions.
DurablesSIZE <- tibble(
`GDP LQ` = 0.5*(1:10),
LQ = 10:1,
Slope = 0.05*(-4:5),
Sector = rep(LETTERS[1:5], 2),
`2019 GDP` = 1:10
)
Result with original code:
Revision with size aesthetic only applied locally:

geom_rect gives empty plot with no rectangle

I would like to get a rectangle box between every point of dawn and dusk. I don't understand why the code below is not giving the desired result
dawn = seq(200, 210, by = 0.5)
dusk = seq(200.5, 210.5, by = 0.5)
night = data.frame("dusk" = dusk, "dawn" = dawn)
ggplot()+
geom_rect(data = night, aes(xmin = dawn , xmax = dusk ,
ymin = -Inf, ymax = Inf),
fill = "blue", alpha = 0.5, colour = NA)
I couldnt see the filled rectangle.
As an alternative, you could define the coordinate system with limits...
ggplot()+
geom_rect(data = night, aes(xmin = dawn , xmax = dusk ,
ymin = -Inf, ymax = Inf),
fill = "blue", alpha = 0.5, colour = NA) +
coord_cartesian(ylim=c(0.2,0.8))
In this way it works with Inf as well.
You need a y aesthetic. What do you want to plot anyways??
library(ggplot2)
dawn = seq(200, 210, by = 0.5)
dusk = seq(200.5, 210.5, by = 0.5)
night = data.frame("dusk" = dusk, "dawn" = dawn, y = 1)
ggplot(night, aes(dawn, y))+
geom_rect(data = night, aes(xmin = dawn , xmax = dusk ,
ymin = -Inf, ymax = Inf),
fill = "blue", alpha = 0.5, colour = NA)
Created on 2021-02-09 by the reprex package (v0.3.0)

How to add areas under a geom_line?

I want to display on the same graph a geom_line and the state (which is in a vector).
The data for example:
Timestamp;Value;State
20190618;1.2;UP
20190619;1.0;DOWN
20190620;1.1;UP
...
This is an example of what i'd like to obtain:
I know how to geom_line, i've already try to use geom_area but none of these try succeed.
Any help ? :-)
library(tidyverse)
ggplot(df) +
geom_rect(aes(xmin = Timestamp, xmax = lead(Timestamp),
ymin = 0, ymax = Inf,
fill = State), alpha = 0.2) +
geom_step(aes(Timestamp, Value))
# based on your data, after converting into table with Timestamp as a date
df <- structure(list(Timestamp = structure(c(18065, 18066, 18067), class = "Date"),
Value = c(1.2, 1, 1.1), State = c("UP", "DOWN", "UP")),
class = "data.frame", row.names = c(NA, -3L))
Is this what you are looking for (alternative to geom_rect)?
Prepare example data
x <- 1:5
y <- c(4,1,6,2,2)
plot.df <- data.frame(Timestamp=x, Value=y)
Code for the plot:
library(ggplot2)
ggplot(plot.df, aes(x=Timestamp,y=Value)) +
annotate("rect", xmin = 1, xmax = 2, ymin = -Inf, ymax = Inf,
alpha = .2, fill = "green") +
annotate("rect", xmin = 2, xmax = 3, ymin = -Inf, ymax = Inf,
alpha = .2, fill = "red") +
annotate("rect", xmin = 3, xmax = 4, ymin = -Inf, ymax = Inf,
alpha = .2, fill = "green") +
geom_step(direction = "h") +
theme_classic()

Horizontal gradient with rasterGrob R for ggplot2 background

I am trying to add a background to a plot to show the light conditions over a 24-hour period (i.e. nighttime, sunrise, daytime, sunset). I would like to use a gradient to denote the light transition periods (8AM-9AM for sunrise and 8PM-9PM for sunset) and solid colours for the day and night.
I am very close, however the gradients are in a vertical orientation and I need it horizontal.
Any help would be much appreciated!
Current working code
library(ggplot2)
library(scales)
## date, start and stop time
datestart <- as.POSIXct(strptime('2017-06-20 00:00:00', format = "%Y-%m-%d %H:%M:%S"))
datestop <- as.POSIXct(strptime('2017-06-20 23:59:59', format = "%Y-%m-%d %H:%M:%S"))
## sunrise
risestart <- as.POSIXct(strptime('2017-06-20 08:00:00', format = "%Y-%m-%d %H:%M:%S"))
risestop <- as.POSIXct(strptime('2017-06-20 09:00:00', format = "%Y-%m-%d %H:%M:%S"))
## sunset
setstart <- as.POSIXct(strptime('2017-06-20 20:00:00', format = "%Y-%m-%d %H:%M:%S"))
setstop <- as.POSIXct(strptime('2017-06-20 21:00:00', format = "%Y-%m-%d %H:%M:%S"))
## data limits
lims <- c(datestart, datestop)
## generate some random data
timelist <- seq(datestart, datestop, by = '15 mins')
act <- runif(length(timelist), min = 0, max = 50)
data <- data.frame(timelist, act)
## colours
nightColour <- c("#9ea5ff")
sunriseColour <- c("#9ea5ff", "#fcffbd")
testColour <- c(c("#9ea5ff"), c("#fcffbd"))
dayColour <- c("#fcffbd")
sunsetColour <- c("#fcffbd","#9ea5ff")
## add background
nightGrob <- rasterGrob(nightColour, width = unit(1,"npc"), height = unit(1,"npc"),
interpolate = TRUE)
sunriseGrob <- rasterGrob(sunriseColour, width = unit(1,"npc"), height = unit(1,"npc"),
interpolate = TRUE)
dayGrob <- rasterGrob(dayColour, width = unit(1,"npc"), height = unit(1,"npc"),
interpolate = TRUE)
sunsetGrob <- rasterGrob(sunsetColour, width = unit(1,"npc"), height = unit(1,"npc"),
interpolate = TRUE)
## plot
ggplot(data = data, aes(x = timelist, y = act)) +
annotation_custom(nightGrob, xmin = as.numeric(datestart), xmax = as.numeric(risestart) + 100, ymin = -Inf, ymax = Inf) +
annotation_custom(sunriseGrob, xmin = as.numeric(risestart), xmax = as.numeric(risestop), ymin = -Inf, ymax = Inf) +
annotation_custom(dayGrob, xmin = as.numeric(risestop), xmax = as.numeric(setstart), ymin = -Inf, ymax = Inf) +
annotation_custom(sunsetGrob, xmin = as.numeric(setstart), xmax = as.numeric(setstop), ymin = -Inf, ymax = Inf) +
annotation_custom(nightGrob, xmin = as.numeric(setstop), xmax = as.numeric(datestop), ymin = -Inf, ymax = Inf) +
geom_bar(stat = "identity", colour = "black", fill = "white") +
scale_x_datetime(limits = lims, expand = c(0,0), breaks = date_breaks('1 hour'), labels = date_format(format = "%H", tz = "Europe/London")) +
scale_y_continuous(expand = c(0,0))
Current progress
You can also make a gradient with lots of geom_rects instead of rasterGrob if you like.
Here's a function that returns a data.frame of plot data for gradient backgrounds that you can plot with geom_rect.
GenerateGradientData <- function(start_hour,
stop_hour,
start_colour,
stop_colour,
x_resolution = 100) {
# define the colour palette
colour_function <- colorRampPalette(
c(start_colour, stop_colour),
alpha = TRUE)
# set up the rect coordinates
x_range <- seq(start_hour,
stop_hour,
length.out = x_resolution + 1)
grad_xmin <- x_range[-length(x_range)]
grad_xmax <- x_range[c(1:x_resolution + 1)]
# define colours
grad_colours <- colour_function(x_resolution)
# return data.frame
data.frame(
xmin = grad_xmin,
xmax = grad_xmax,
ymin = -Inf,
ymax = Inf,
grad_colours = grad_colours
)
}
Here's an example using a numerical x-axis:
# dummy data
set.seed(1)
plot_data <- data.frame(
hours = c(1:24),
value = rnorm(24, 100, 30)
)
# day/night colours
night_colour <- c("#9ea5ff")
day_colour <- c("#fcffbd")
# generate data for a one-hour sunrise gradient
sunrise_pd <- GenerateGradientData(start_hour = 8,
stop_hour = 9,
start_colour = night_colour,
stop_colour = day_colour,
x_resolution = 1000)
# generate data for a one-hour sunset gradient
sunset_pd <- GenerateGradientData(start_hour = 20,
stop_hour = 21,
start_colour = day_colour,
stop_colour = night_colour,
x_resolution = 1000)
# setup plot
ggplot(plot_data, aes(x = hours, y = value)) +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
# day background
geom_rect(xmin = 9,
xmax = 20,
ymin = -Inf,
ymax = Inf,
fill = day_colour) +
# night background
geom_rect(xmin = -Inf,
xmax = 8,
ymin = -Inf,
ymax = Inf,
fill = night_colour) +
geom_rect(xmin = 21,
xmax = Inf,
ymin = -Inf,
ymax = Inf,
fill = night_colour) +
# gradient backgrounds for sunrise and sunset
geom_rect(data = sunrise_pd,
mapping = aes(xmax = xmax,
xmin = xmin,
ymax = ymax,
ymin = ymin),
fill = sunrise_pd$grad_colours,
inherit.aes = FALSE) +
geom_rect(data = sunset_pd,
mapping = aes(xmax = xmax,
xmin = xmin,
ymax = ymax,
ymin = ymin),
fill = sunset_pd$grad_colours,
inherit.aes = FALSE) +
# finally, plot your data on top
geom_col(fill = NA, colour = "black")
Here's the output:
This could look a bit blocky depending on x_resolution, the graphics device you save with and the image viewer.

Resources