library(tidyverse)
mQ <- quantile(mtcars$wt, c(0.025, 0.975))
mtcarsQ <- data.frame(x = c(min(as.numeric(diamonds$cut)),
max(as.numeric(diamonds$cut))),
ymin = rep(mQ[1], 2),
ymax = rep(mQ[2], 2))
ggplot() +
geom_blank(data = diamonds, aes(x = cut, y = y)) +
geom_ribbon(data = mtcarsQ, aes(x = x, ymin = ymin, ymax = ymax), alpha=0.2) +
geom_boxplot(data = diamonds, aes(x = cut, y = y, fill = cut, group = cut)) +
coord_cartesian(ylim = c(0, 12)) +
theme_bw()
I'd like to extend my geom_ribbon() from code chunk above in either direction on the x-axis. Something resembling the image below. My preferred geom_ribbon() would fully entend into the blue dotted line box.
How do I do this?
You could subtract/add 0.5 from mtcarsQ$x
mtcarsQ <- data.frame(x = c(min(as.numeric(diamonds$cut)) - 0.5,
max(as.numeric(diamonds$cut)) + 0.5),
ymin = rep(mQ[1], 2),
ymax = rep(mQ[2], 2))
ggplot() +
geom_blank(data = diamonds, aes(x = cut, y = y)) +
geom_ribbon(data = mtcarsQ, aes(x = x, ymin = ymin, ymax = ymax), alpha=0.2) +
geom_boxplot(data = diamonds, aes(x = cut, y = y, fill = cut, group = cut)) +
coord_cartesian(ylim = c(0, 12)) +
theme_bw()
Update
In response to your comment, here is an example
mtcars %>%
mutate(cyl = factor(cyl)) %>%
ggplot() +
geom_col(aes(cyl, mpg)) +
geom_rect(
data = data.frame(
xmin = min(as.integer(as.factor(mtcars$cyl))) - 0.5,
xmax = max(as.integer(as.factor(mtcars$cyl))) + 0.5,
ymin = 20,
ymax = 120),
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
alpha = 0.2, fill = "green")
Related
I have been trying to build a bar chart for GDP growth in UK and overlay it with a recession bands. I can do what is necessary with the bar plot but the moment I overlay with the recession bands, i get an error that a variable cannot be found.
uk.recessions.df <- read.table(textConnection(
"Peak, Trough
1857-06-01, 1858-12-01
1867-06-01, 1869-12-01
1873-10-01, 1879-03-01
1882-03-01, 1885-05-01
1887-03-01, 1888-04-01
1890-07-01, 1891-05-01
1893-01-01, 1894-06-01
1895-12-01, 1897-06-01
1919-03-01, 1921-07-01
1930-01-01, 1931-12-01
1956-04-01, 1956-08-01
1961-07-01, 1962-01-01
1973-09-01, 1974-04-01
1975-04-01, 1975-10-01
1980-01-01, 1981-04-01
1990-07-01, 1991-09-01
2008-04-01, 2009-07-01
2020-01-01, 2020-07-01"), sep=',',
colClasses=c('Date', 'Date'), header=TRUE)
uk.recessions.trim.df <- subset(uk.recessions.df, Peak >= min(tbl.QQGDP$Date))
tbl.data <- tbl.QQGDP %>%
mutate(Value = GDPGrowth < 0)
p <- ggplot(data = tbl.data, aes(x = Date, y = GDPGrowth, fill = Value)) +
geom_col(position = "identity", colour = "black", size = 0.25) +
scale_fill_manual(values = c("#85225f","#dbab01"), guide = FALSE) +
theme_tq()
p <- p +
geom_rect(data = uk.recessions.trim.df,
aes(xmin = Peak, xmax = Trough, ymin = -Inf, ymax = Inf),
fill = "grey", alpha = 0.5)
p
The error i get is
Error in FUN(X[[i]], ...) : object 'GDPGrowth' not found
I am cannot figure out what i am doing wrong. Any help (even if to tell me off for a silly mistake!!) will be greatly appreciated.
By default, geom_*() elements inherit the aesthetic mappings from the top level of the plot (ggplot()). In your case, the geom_rect() call is inheriting aes(x = Date, y = GDPGrowth, fill = Value) but can't find those objects as you have a different data source (uk.recessions.trim.df instead of tbl.data).
If you add the option inherit.aes = FALSE to geom_rect() you'll get the desired plot.
p <- ggplot(data = tbl.data, aes(x = Date, y = GDPGrowth, fill = Value)) +
geom_col(position = "identity", colour = "black", size = 0.25) +
scale_fill_manual(values = c("#85225f","#dbab01"), guide = FALSE)
p <- p +
geom_rect(data = uk.recessions.trim.df,
aes(xmin = Peak, xmax = Trough, ymin = -Inf, ymax = Inf),
fill = "grey", alpha = 0.5,
inherit.aes = FALSE)
p
An alternative (and probably better method) is to define data and aes in each geom separately, instead of in the initial ggplot() call. Eg:
p <- ggplot() +
geom_col(data = tbl.data,
aes(x = Date, y = GDPGrowth, fill = Value),
position = "identity", colour = "black", size = 0.25) +
scale_fill_manual(values = c("#85225f","#dbab01"), guide = FALSE)
p <- p +
geom_rect(data = uk.recessions.trim.df,
aes(xmin = Peak, xmax = Trough, ymin = -Inf, ymax = Inf),
fill = "grey", alpha = 0.5)
p
I'm trying to plot a geom_rect(). Why do I receive an Error in FUN(X[[i]], ...) : object 'Month' not found? If I run df$Month in my console the object is there:
df$Month
#> [1] 2019-01 2019-02 2019-03
#> Levels: 2019-01 2019-02 2019-03
Here's my code block:
library(tidyverse)
df <- tibble(Month = factor(c("2019-01", "2019-02", "2019-03")),
Value = c(4, 9, 7))
ggplot(df, aes(Month, Value, group = 1)) +
geom_line() +
theme_minimal() +
geom_rect(data =
data.frame(xmin = min(as.integer(df$Month)) - 0.5,
xmax = max(as.integer(df$Month)) + 0.5,
ymin = min(df$Value),
ymax = max(df$Value)),
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
alpha = 0.2, fill = "green")
#> Error in FUN(X[[i]], ...) : object 'Month' not found
This works:
ggplot(df, aes(Month, Value, group = 1)) +
geom_line() +
theme_minimal() +
geom_rect(data =
data.frame(xmin = min(as.integer(df$Month)) - 0.5,
xmax = max(as.integer(df$Month)) + 0.5,
ymin = min(df$Value),
ymax = max(df$Value)),
aes(x = NULL,y = NULL,xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
alpha = 0.2, fill = "green")
by unmapping the inherited x/y aesthetics from the top ggplot call. It's understandable that this might be confusing, though, since the description in ?geom_rect sorta kinda implies that geom_rect isn't looking for those aesthetics at all.
You just have an extra step of setting up a dataframe in geom_rect which coincide with data in ggplot. Simply provide your max and min values to geom_rect and it works:
ggplot(df, aes(Month, Value, group = 1)) +
geom_line() +
theme_minimal() +
geom_rect(aes(xmin = min(as.integer(Month)) - 0.5,
xmax = max(as.integer(Month)) + 0.5,
ymin = min(Value),
ymax = max(Value)),
alpha = 0.2/nrow(df), fill = "green")
I was able to return your desired result by calling df in geom_line() after gemo_rect(). However leaving the Month field as is returned the error: Error: Discrete value supplied to continuous scale.
I worked around this by wrapping as.integer() around Month.
ggplot() +
theme_minimal() +
geom_rect(data =
data.frame(xmin = min(as.integer(df$Month)) - 0.5,
xmax = max(as.integer(df$Month)) + 0.5,
ymin = min(df$Value),
ymax = max(df$Value)),
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
alpha = 0.2, fill = "green") +
geom_line(data = df, aes(as.integer(Month), Value, group = 1))
You might have to clean up your x-axis label but it achieves desired outcome!
Problem
I want to draw a number of arrows on a map. With some googling, I've learned that annotation_custom with rasterGrob can do the thing:
library(ggplot2)
library(png)
library(grid)
img = readPNG('floorplan.png')
g = rasterGrob(img, interpolate = TRUE)
data = read.csv('est_err_structured.csv', header = FALSE)
x1 = data$V1
y1 = data$V2
x2 = data$V3
y2 = data$V4
p = ggplot() +
geom_segment(data = data, mapping = aes(x = x1, y = y1, xend = x2, yend = y2),
arrow = arrow(length = unit(0.2, 'cm'))) +
annotation_custom(g, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf) +
xlab('x (m)') +
ylab('y (m)') +
theme_bw()
pdf('err_on_map.pdf')
p
dev.off()
However, when I run this script, I only got arrows but not background image:
Attachment
est_err_structured.csv
floorplan.png
References
https://stackoverflow.com/a/16418186
https://stackoverflow.com/a/10769839
You need to study ggplot2 a bit more. E.g., the variables mapped within aes are taken from the data.frame defined as data and in this example you must pass it to ggplot. I think annotation_custom somehow needs it to get the proper coordinates or dimensions.
p = ggplot(data) +
annotation_custom(g, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf) +
geom_segment(aes(x = V1, y = V2, xend = V3, yend = V4),
arrow = arrow(length = unit(0.2, 'cm'))) +
xlab('x (m)') +
ylab('y (m)') +
theme_bw()
You'll need to pass the plot width and height to pdf in order to align the image correctly with the plot.
Edit:
#baptiste recommends annotation_raster, which makes the positioning easier:
ggplot(data) +
annotation_raster(img, xmin = 50, xmax = 600, ymin = 20, ymax = 400) +
geom_segment(aes(x = V1, y = V2, xend = V3, yend = V4),
arrow = arrow(length = unit(0.2, 'cm'))) +
coord_cartesian(xlim = c(50, 600), ylim = c(20, 400)) +
xlab('x (m)') +
ylab('y (m)') +
theme_bw()
I am trying to plot a stacked barplot using ggplot2::geom_bar with backgroud shading (using ggplot2::geom_rect()) according to a categorical variable as follows:
shading <- data.frame(min = seq(from = 0.5, to = max(as.numeric(as.factor(diamonds$clarity))), by = 1),
max = seq(from = 1.5, to = max(as.numeric(as.factor(diamonds$clarity))) + 0.5, by = 1),
col = c(0,1))
ggplot() +
theme(panel.background = element_rect(fill = "transparent")) +
geom_bar(data = diamonds, mapping = aes(clarity, fill=cut)) +
geom_rect(data = shading,
aes(xmin = min, xmax = max, ymin = -Inf, ymax = Inf,
fill = factor(col), alpha = 0.1)) +
geom_bar(data = diamonds, mapping = aes(clarity, fill=cut)) +
guides(alpha = FALSE)
How to change the colours of the shading?
I have tried scale_fill_manual(values = c("white", "gray53")), but it seems that multiple scale aesthetics are not possible in ggplot2 (https://github.com/hadley/ggplot2/issues/578). Is there another way to get the desired result ?
Yes, instead of putting your colours in the aes(), put them outside (so they are used as-is). Since it's outside the aes() call you will have to use an explicit fill=shading$col rather than fill=col, and shading$col should have the colour name you are after (rather than a variable interpreted as a factor).
shading$col <- ifelse(shading$col, 'white', 'gray53')
ggplot() +
theme(panel.background = element_rect(fill = "transparent")) +
geom_bar(data = diamonds, mapping = aes(clarity, fill=cut)) +
geom_rect(data = shading,
aes(xmin = min, xmax = max, ymin = -Inf, ymax = Inf, alpha = 0.1),
fill=shading$col) + # <-- here
geom_bar(data = diamonds, mapping = aes(clarity, fill=cut)) +
guides(alpha = FALSE)
I have to create a figure with ggplot2 that has roughly this structure:
p <- ggplot() +
geom_rect(data = regions,aes(xmin = xmin, xmax = xmax, ymin = -Inf, ymax = Inf),
fill = "yellow",alpha = 0.1) +
geom_line(data = data, aes(x=dt, y = y, color = case)) +
geom_point(data = data, aes(x=dt, y = y, color = case)) +
facet_grid(groups ~ ., scale="free_y")
geom_vline(x=as.numeric(dates_start), color = "orange3",linetype="dashed") +
geom_vline(x=as.numeric(dates_end), color = "orange3",linetype="dashed")
p
Is there anyway I can avoid having to pass all the details in geom_point? since they are the same as the one used in geom_line?