plotting rectangles and lineranges with discrete axis in ggplot2 - r

I'm fairly new to stackoverflow.
I want to plot rectangles instead of lineranges because I want a black border. Actually my professor wants a black border but that is not an issue for stackoverflow.
Load library and create dummy dataset
library(tidyverse)
mydat <- tibble(
mymsmt = rep(c("bio", "bio", "den", "den"), 2),
mylvl = c("NT", "till", "NT", "till", "no", "yes", "no", "yes"),
mytrt = c(rep("tillage", 4), rep("herbicides", 4)),
est = c(-60, -13, -65, -40, -16, -24, -49, -50),
cilow = c(-85, -48, -78, -56, -61, -60, -68, -64),
ciup = c(8, 45, -44, -18, 79, 42, -20, -31)) %>%
# Dummy code mylvls as numeric
mutate(mylvln = rep(c(1, 2), 4))
If I plot with just the linerange, it works (I'm not allowed to embed images yet)
ggplot(mydat, aes(est, mylvl)) +
geom_linerangeh(aes(xmin = cilow, xmax = ciup), color = "blue", size = 5) +
# geom_rect(aes(xmin = cilow, xmax = ciup,
# ymin = mylvln - 0.2, ymax = mylvln + 0.2),
# fill = "red", color = "black") +
geom_point() +
facet_grid(mytrt ~ mymsmt, scales = "free")
Plot with just rectangles, fails, with
Error: Discrete value supplied to continuous scale
ggplot(mydat, aes(est, mylvl)) +
#geom_linerangeh(aes(xmin = cilow, xmax = ciup), color = "blue", size = 5) +
geom_rect(aes(xmin = cilow, xmax = ciup,
ymin = mylvln - 0.2, ymax = mylvln + 0.2),
fill = "red", color = "black") +
geom_point() +
facet_grid(mytrt ~ mymsmt, scales = "free")
Plot with linerange, covered by rectangles, works,
You can see the lineranges in the background
ggplot(mydat, aes(est, mylvl)) +
geom_linerangeh(aes(xmin = cilow, xmax = ciup), color = "blue", size = 5) +
geom_rect(aes(xmin = cilow, xmax = ciup,
ymin = mylvln - 0.2, ymax = mylvln + 0.2),
fill = "red", color = "black", alpha = 0.5) +
geom_point() +
facet_grid(mytrt ~ mymsmt, scales = "free")
Why? It works, I get the figure I want, but I don't know why. Thanks for your help!

You can also use geom_tile in place of geom_rect:
ggplot(mydat, aes(est, mylvl)) +
geom_tile(aes(width = ciup-cilow, height=0.1), fill="red", color="black") +
geom_point() +
facet_grid(mytrt ~ mymsmt, scales = "free")

Related

How to create shaded effects to display confidence interval / error bar on a ggplot2 bar chart?

I would like to reproduce the visual effect from this chart in order to display the confidence interval for an indicator.
This looks a lot more intuitive than the regular error_bar / whisker.
I checked a bit some reference - like https://www.datanovia.com/en/lessons/ggplot-error-bars/ - and tried to play with errorbar and line range..
my_df <-
tibble::tribble(~response, ~estimate, ~lower_ci, ~upper_ci,
"little_bit", 0.353477, 0.255625, 0.451747,
"no", 0.307639, 0.250436, 0.375393,
"very", 0.338883, 0.301007, 0.37572310)
ggplot(my_df, aes(x = reorder(response, -estimate), y = estimate)) +
geom_linerange(aes( ymin = lower_ci, ymax = upper_ci),
width = 0.9,
size = 45,
color = "red",
alpha = 0.7
) +
geom_errorbar(aes(ymin = estimate, ymax = estimate),
width = 0.9,
size = 2 ,
color = "#6EB3FF")
Still not the nice shaded / gradient effects...
Thanks for help!
You'd probably need to do this "manually"
ggplot(my_df, aes(x = reorder(response, -estimate))) +
geom_linerange(data = my_df %>%
group_by(response) %>%
summarize(ymin = lead(seq(lower_ci, upper_ci, length = 100)),
ymax = lag(seq(lower_ci, upper_ci, length = 100)),
alpha = c(seq(0, 0.5, length = 50),
seq(0.5, 0, length = 50)),
estimate = estimate),
aes(ymin = ymin, ymax = ymax, alpha = alpha,
color = response),
size = 45) +
geom_errorbar(aes(ymin = estimate, ymax = estimate),
width = 0.9,
size = 2 ,
color = "#3453A2") +
scale_alpha_identity() +
guides(color = guide_none()) +
xlab("Response") +
theme_minimal(base_size = 20) +
theme(plot.background = element_rect(fill = "#fff2e6", color = NA),
panel.background = element_rect(fill = "#fff9f4", color = NA))

Draw a dashed transparent box around forecast lines

I have 3 forecast plots that are combined together by plotly::subplot. The next step is draw a transparent box (or 3 separate boxes) with red dashed lines around the forecast line of each plot so that they stand out to the reader.
How can I do this ?
Desired Output:
Data (df):
structure(list(year = 1980:2021, AvgTMean = c(24.2700686838937,
23.8852956598276, 25.094446596092, 24.1561175050287, 24.157183605977,
24.3047482638362, 24.7899738481466, 24.5756232655603, 24.5833086228592,
24.7344695534483, 25.3094451071121, 25.2100615173707, 24.3651692293534,
24.5423890611494, 25.2492166633908, 24.7005097837931, 24.2491591827443,
25.0912281781322, 25.0779264303305, 24.403294248319, 24.4983991453592,
24.4292324356466, 24.8179824927011, 24.7243948463075, 24.5086534543966,
24.2818632071983, 24.4567195220259, 24.8402224356034, 24.6574465515086,
24.5440715673563, 23.482670620977, 24.9979594684914, 24.5452453980747,
24.9271462811494, 24.7443215819253, 25.8929839790805, 25.1801908261063,
25.2079308058908, 25.0722425561207, 25.4554644289799, 25.4548979078736,
25.0756772250287), AvgTMin = c(19.6018663372126, 18.9935718486724,
20.8351710187356, 19.7723002680316, 19.8097384811782, 19.7280847671034,
20.2907499842098, 20.1950373662931, 20.1812715311494, 20.1808865070833,
21.0320272801006, 21.1252427976293, 20.1712830368678, 20.407655174727,
21.5430646243391, 20.6760574525862, 20.0822658237356, 21.0735574619397,
21.0871494406322, 20.1311178414224, 20.3191250001149, 20.3474683732557,
20.668169553204, 20.3772270269296, 20.2330157893678, 19.9486551337931,
20.1114496908333, 20.5816350393966, 20.4033879191236, 20.1582514856897,
19.2288879223678, 20.8451063140805, 20.4878865041092, 21.0259712576437,
20.5510100674138, 22.0143793370977, 21.3529094881753, 21.1688506012213,
21.040550304569, 21.4923981385632, 21.6580430460057, 21.2433069288506
), AvgTMax = c(28.9392198638937, 28.778245693046, 29.3549223685201,
28.5411393752011, 28.5058118063649, 28.8825532046983, 29.2903534709195,
28.9574051835776, 28.9865201368247, 29.2891997662069, 29.5881379007328,
29.2960976760201, 28.5602557685057, 28.6782844806753, 28.9566034394684,
28.7262054694971, 28.4171896994397, 29.1100747038649, 29.0698836095546,
28.6766350461063, 28.6788764437787, 28.5122026355891, 28.9690143596839,
29.0727844759914, 28.7854971337931, 28.6163189712069, 28.8032270024138,
29.1000460207471, 28.9127356101149, 28.9310646744109, 27.7376810545833,
29.1520129070402, 28.6037845089512, 28.8295359311638, 28.9388276133764,
29.7726939654598, 29.0086407880029, 29.2482097613937, 29.1050890698132,
29.4187571974569, 29.2519238543247, 28.9081913630029)), class = "data.frame", row.names = c(NA,
-42L))
Code
library(tidyverse)
library(plotly)
AvgTMeanYearFP = ggplot(df, aes(year, AvgTMean)) +
geom_smooth(method = 'lm', fullrange = TRUE) +
annotate('rect', xmin = -Inf, xmax = 2021, ymin = -Inf, ymax = Inf,
fill = 'gray92') +
geom_vline(xintercept = seq(1980, 2020, 5), color = 'white') +
geom_hline(yintercept = seq(23.5, 25.5, 0.5), color = 'white') +
geom_line() +
scale_x_continuous(limits = c(1980, 2030)) +
labs(y = "Avg. Mean T (C)", x = "Year") +
geom_text(aes(x = 2000 , y = 25.5, label = "Historic Trend")) +
geom_text(aes(x = 2025 , y = 25.5, label = "Forecast Trend"))
AvgTMinYearFP = ggplot(df, aes(year, AvgTMin)) +
geom_smooth(method = 'lm', fullrange = TRUE) +
annotate('rect', xmin = -Inf, xmax = 2021, ymin = -Inf, ymax = Inf,
fill = 'gray92') +
geom_vline(xintercept = seq(1980, 2020, 5), color = 'white') +
geom_hline(yintercept = seq(23.5, 25.5, 0.5), color = 'white') +
geom_line() +
scale_x_continuous(limits = c(1980, 2030)) +
ylim(18, 23) +
labs(y = "Avg. Min. T (C)", x = "Year")
AvgTMaxYearFP = ggplot(df, aes(year, AvgTMax)) +
geom_smooth(method = 'lm', fullrange = TRUE) +
annotate('rect', xmin = -Inf, xmax = 2021, ymin = -Inf, ymax = Inf,
fill = 'gray92') +
geom_vline(xintercept = seq(1980, 2020, 5), color = 'white') +
geom_hline(yintercept = seq(23.5, 25.5, 0.5), color = 'white') +
geom_line() +
scale_x_continuous(limits = c(1980, 2030)) +
ylim(27, 30) +
labs(y = "Avg. Max. T (C)", x = "Year")
# Combine plots
subplot(AvgTMeanYearFP, AvgTMinYearFP, AvgTMaxYearFP, titleY = TRUE, shareX = TRUE, nrows = 3) %>%
layout(title ="Historic Average Temperature And Future Temperature Projection")
I actually like one box over all plots more aesthetically. Had a hard time doing this, because there seems to be a known issue with using ggplotly and the layout() function. That's why the shapes are put in p$x$layout$shapes like this.
# Combine plots
p <- subplot(AvgTMeanYearFP, AvgTMinYearFP, AvgTMaxYearFP, titleY = TRUE, shareX = TRUE, nrows = 3) %>%
layout(title ="Historic Average Temperature And Future Temperature Projection")
p$x$layout$shapes <- list(type = "rect",
line = list(color = "red",
dash = 'dash'),
x0 = 2021,
x1 = 2030,
xref = "x",
y0 = 0,
y1 = 1,
yref = "paper")
p
An alternative to a dashed box could be using the opacity.
list(type = "rect",
fillcolor = "red",
opacity = 0.1,
x0 = 2021,
x1 = 2030,
xref = "x",
y0 = 0,
y1 = 1,
yref = "paper")
I can also get you some of the way there - by making a red box in each figure, but putting a single box across the whole plot is going to be more challenging.
library(tidyverse)
library(plotly)
add_box <- function(p, start=2022, stop=NULL, prop_in=.05, ...){
pb <- ggplot_build(p)
rgy <- pb$layout$panel_params[[1]]$y.range
rgx <- pb$layout$panel_params[[1]]$x.range
px1 <- diff(rgx)*prop_in
py1 <- diff(rgy)*prop_in
rgx <- c(1,-1)*px1 + rgx
rgy <- c(1,-1)*py1 + rgy
rgx[1] <- start
if(!is.null(stop)){
rgx[2] <- stop
}
boxdf <- data.frame(x = rgx[c(1,2,2,1,1)],
y=rgy[c(1,1,2,2,1)])
p + geom_path(data=boxdf,
aes(x=x,
y=y),
col="red",
linetype=2)
}
AvgTMeanYearFP = ggplot(df, aes(year, AvgTMean)) +
geom_smooth(method = 'lm', fullrange = TRUE) +
annotate('rect', xmin = -Inf, xmax = 2021, ymin = -Inf, ymax = Inf,
fill = 'gray92') +
geom_vline(xintercept = seq(1980, 2020, 5), color = 'white') +
geom_hline(yintercept = seq(23.5, 25.5, 0.5), color = 'white') +
geom_line() +
scale_x_continuous(limits = c(1980, 2030)) +
labs(y = "Avg. Mean T (C)", x = "Year") +
geom_text(aes(x = 2000 , y = 25.5, label = "Historic Trend")) +
geom_text(aes(x = 2025 , y = 25.5, label = "Forecast Trend"))
AvgTMinYearFP = ggplot(df, aes(year, AvgTMin)) +
geom_smooth(method = 'lm', fullrange = TRUE) +
annotate('rect', xmin = -Inf, xmax = 2021, ymin = -Inf, ymax = Inf,
fill = 'gray92') +
geom_vline(xintercept = seq(1980, 2020, 5), color = 'white') +
geom_hline(yintercept = seq(23.5, 25.5, 0.5), color = 'white') +
geom_line() +
scale_x_continuous(limits = c(1980, 2030)) +
ylim(18, 23) +
labs(y = "Avg. Min. T (C)", x = "Year")
AvgTMaxYearFP = ggplot(df, aes(year, AvgTMax)) +
geom_smooth(method = 'lm', fullrange = TRUE) +
annotate('rect', xmin = -Inf, xmax = 2021, ymin = -Inf, ymax = Inf,
fill = 'gray92') +
geom_vline(xintercept = seq(1980, 2020, 5), color = 'white') +
geom_hline(yintercept = seq(23.5, 25.5, 0.5), color = 'white') +
geom_line() +
scale_x_continuous(limits = c(1980, 2030)) +
ylim(27, 30) +
labs(y = "Avg. Max. T (C)", x = "Year")
# Combine plots
subplot(AvgTMeanYearFP %>% add_box(stop=2030, prop_in=.05),
AvgTMinYearFP %>% add_box(stop=2030, prop_in=.05),
AvgTMaxYearFP %>% add_box(stop=2030, prop_in=.05),
titleY = TRUE, shareX = TRUE, nrows = 3) %>%
layout(title ="Historic Average Temperature And Future Temperature Projection")
The add_box() function does a few different things. First, it builds your plot so I can grab the ranges of the x and y axes. If you try to plot the box all the way to the end of the range, the top, bottom and right side lines don't print. So, I have it pull the those edges prop_in toward the interior of the plot. I found that .05 is about the smallest that worked. Then, I change the rgx and rgy objects accordingly. Then, I replace the first and optionally second value of rgx with the start and stop arguments from the function call. I take the range values and make them into a data frame that will be amenable to plot with geom_path() and then I add the appropriate geom_path() function to your existing plot.

using a grouping variable and geom_rect

I want to show covered ranges (including overlaps) and (after some failures with stacked bar plots) I chose geom_rect. The following code works well for one type.
library(tidyverse)
# create dummy data
foo <- tibble(start = c(1, 150, 140, 75, 300),
end = c(150, 180, 170, 160, 400))
ggplot() +
geom_rect(data = foo, aes(xmin = start, xmax = end, ymin = 0, ymax = 1), fill = "green", linetype = "blank", alpha = 0.3) +
geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1), fill = NA, colour = "black") +
scale_y_continuous(name = "", breaks = NULL, limits = c(0, 1)) +
scale_x_continuous(name = "", breaks = NULL) +
theme_minimal() +
theme(panel.grid = element_blank())
If I add more data (only one more type, but in the original data I do have some more) like below, I can add the data "by hand", i.e. add two lines of code for each type, but I'm looking for a way to do this by grouping, but didn't succeed.
foo <- foo %>%
mutate(type = "A", .before = 1)
bar <- tibble(type = "B",
start = c(1, 30, 40, 100, 150, 200, 310),
end = c(20, 50, 100, 120, 200, 300, 380))
foo <- bind_rows(foo, bar)
ggplot() +
geom_rect(data = foo %>% filter(type == "A"), aes(xmin = start, xmax = end, ymin = 0, ymax = 1), fill = "green", linetype = "blank", alpha = 0.3) +
geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1), fill = NA, colour = "black") +
geom_rect(data = foo %>% filter(type == "B"), aes(xmin = start, xmax = end, ymin = 2, ymax = 3), fill = "green", linetype = "blank", alpha = 0.3) +
geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 2, ymax = 3), fill = NA, colour = "black") +
scale_y_continuous(name = "", breaks = NULL, limits = c(0, 3)) +
scale_x_continuous(name = "", breaks = NULL) +
geom_text(aes(x = c(0, 0), y = c(0.5, 2.5), label = c("A", "B")), size = 4, hjust = 2) +
theme_minimal() +
theme(panel.grid = element_blank())
So, the graph already looks the way I want, but I'd prefer to get here by using grouping (or any other non-manual way).
Maybe there's also a different geom or method to get this kind of graph?
You can write a small helper function that positions a categorical value in continuous space. Example below.
helper <- function(x) {(match(x, sort(unique(x))) - 1) * 2}
ggplot(foo) +
geom_rect(
aes(xmin = start, xmax = end,
ymin = helper(type),
ymax = helper(type) + 1),
fill = "green", linetype = "blank", alpha = 0.3
) +
geom_rect(
aes(xmin = min(start), xmax = max(end),
ymin = helper(type),
ymax = helper(type) + 1),
fill = NA, colour = "black"
) +
scale_y_continuous(name = "", breaks = NULL, limits = c(0, 3)) +
scale_x_continuous(name = "", breaks = NULL) +
annotate(
"text", x = c(0, 0), y = c(0.5, 2.5), label = c("A", "B"),
size = 4, hjust = 2
) +
theme_minimal() +
theme(panel.grid = element_blank())

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:

Building political compass in ggplot, how can I make this code more concise?

So I successfully made a political compass in R with data from my friends which looks like this:
Name Economic Social House Gender
Jimmy -3.75 -2.46 B St Male
Beth -4.75 -7.38 A St Female
The code works fine, but I feel like I wrote too much code to get what I wanted, how can I reduce the amount of code written to get the same result.
Also, it would be nice to have the geom_hline/geom_vline to stop where the squares stop, any Ideas on how to do this??
Code:
df <- data.frame(
stringsAsFactors = FALSE,
Name = c("Jimmy", "Beth"),
Economic = c(-0.04, -4.75),
Social = c(-2.46, -7.38),
House = c("B St", "A St"),
Gender = c("Male", "Female"))
ggplot(df,aes(Economic,Social, color = House))+
xlim(-10,10)+
ylim(-10,10)+
annotate("rect", xmin = 0, xmax = 10, ymin = 0, ymax = 10, fill= "deepskyblue3") +
annotate("rect", xmin = -10, xmax = 0, ymin = -10, ymax = 0 , fill= "palegreen1") +
annotate("rect", xmin = 0, xmax = 10, ymin = -10, ymax = 0, fill= "plum3") +
annotate("rect", xmin = -10, xmax = 0, ymin = 0, ymax = 10, fill= "indianred1") +
geom_hline(yintercept = 0) +
geom_vline(xintercept=0) +
geom_point(size = 4)+
ggtitle("The Gang gets Political")+
theme_minimal()+
geom_text(aes(label=Name),hjust=0, vjust=0, size =4, color = "black")+
theme(axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
axis.title.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.y=element_blank())+
geom_text(
label="Authoritarian",
x=0,
y=10.5,
color = "azure4"
)+
geom_text(
label="Libertatian",
x=0,
y=-10.5,
color = "azure4"
)+
geom_text(
label="Left",
x=-10.5,
y=0,
color = "azure4"
)+
geom_text(
label="Right",
x=10.5,
y=0,
color = "azure4"
)
There are a couple of things you can do that will streamline the plotting
a) Use colour for the annotate elements to add a black border which removes the need for the vline and hlines
b) Use the theme_void to avoid needing to supress so many theme elements from theme_minimal
c) Use a second data set which has the political orientation labels and desired positions so that they can be created using a single geom_text geom
library(ggplot2)
df <- data.frame(
stringsAsFactors = FALSE,
Name = c("Jimmy", "Beth"),
Economic = c(-0.04, -4.75),
Social = c(-2.46, -7.38),
House = c("B St", "A St"),
Gender = c("Male", "Female")
)
labels <- data.frame(label = c("Authoritarian","Libertarian","Left","Right"),
x = c(0, 0, -10.5, 10.5),
y = c(10.5, -10.5, 0, 0)
)
ggplot(df,aes(Economic,Social, color = House))+
annotate("rect", xmin = 0, xmax = 10, ymin = 0, ymax = 10, fill= "deepskyblue3", color = "black") +
annotate("rect", xmin = -10, xmax = 0, ymin = -10, ymax = 0, fill= "palegreen1", color = "black") +
annotate("rect", xmin = 0, xmax = 10, ymin = -10, ymax = 0, fill= "plum3", color = "black") +
annotate("rect", xmin = -10, xmax = 0, ymin = 0, ymax = 10, fill= "indianred1", color = "black") +
geom_point(size = 4)+
ggtitle("The Gang gets Political")+
theme_void()+
geom_text(aes(label=Name),hjust=0, vjust=0, size = 4, color = "black") +
geom_text(data = labels, color = "azure4", aes(x, y, label = label))

Resources