Why geom_rect colours only first row of facet_wrap? - r

I am trying to get shaded rectangles on every even-numbered panel of my facet_wrap plot. However, when I use geom_rect, it produces the rectangles only on the second panel. I tried using annotate and geom_tile but with no success. I presume I am missing some simple detail here, probably related to the fact that my X variable is categorical and not numeric, but I am fighting this for a few hours already...
Here is my code:
even_numbers <- seq(2,nrow(df.plt),2)
ggplot(df.plt) +
geom_rect(data = df.plt[even_numbers, ],
xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
facet_wrap(vars(Grp), ncol=1)
And the resulting plot:
resulting plot with geom_rect and facet_wrap not working as expected
Edit:
I have created a dummy dataset example which replicates my issue:
set.seed(002) # just to make it reproducible
df.tmp = data.frame(nst = rnorm(100*2), Srs = sample(rep(c("S3","S4"),100)), Cnd = sample(rep(c("DN","DA","DV","DAV"),50)), Grp = sample(rep(c("close","far"),100)))
even_numbers <- seq(2,nrow(df.tmp),2)
ggplot(df.tmp) +
geom_rect(data = df.tmp[even_numbers, ],
xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
facet_wrap(vars(Grp), ncol=1)

While your idea was right IMHO you could achieve your desired result more easily by putting the xmin and xmax values in a dataframe and by mapping on aesthetics. First note that we only need a vector of even numbers of length(unique(df.tmp$Cnd)), i.e. the number of categories of Cnd. Second, as we are mixing discrete and continuous x variables I added an scale_x_discrete before geom_rect as otherwise we will get an error.
library(ggplot2)
even_numbers <- seq(2, length(unique(df.tmp$Cnd)), 2)
rects <- data.frame(
xmin = even_numbers - 0.5,
xmax = even_numbers + 0.5
)
ggplot(df.tmp) +
scale_x_discrete() +
geom_rect(
data = rects, aes(xmin = xmin, xmax = xmax),
ymin = -Inf, ymax = Inf, alpha = 0.3, fill = "grey"
) +
geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
facet_wrap(vars(Grp), ncol = 1)
EDIT Just in case. The reason why your approach did not work is that the relevant part of the data used for the rects contains only the far group. The issue is that basically only rects corresponding to even numbers in the range 1 to 4 (the number of Cnd categories) are displayed. As can be seen from the following code snippet which replicates the data which under the hood is used for the rects in your approach only the far grp is present (after filtering for even numbers in the range 1 to 4):
even_numbers <- seq(2,nrow(df.tmp),2)
dplyr::bind_cols(df.tmp[even_numbers, ], data.frame(even_number = even_numbers)) |>
dplyr::filter(even_number <= 4)
#> nst Srs Cnd Grp even_number
#> 1 0.1848492 S3 DV far 2
#> 2 -1.1303757 S3 DA far 4

I have been trying to understand why only the second segment gets shaded. Eventually I prepared a second example, in which I have found another possible solution. However, I am not fully satisfied as I did not understand the issue with geom_rect / facet_wrap; I only found some workaround.
Here's the example:
# constructing the dataframe so all the combinations are present for both even and odd rows
df.tmp = data.frame(nst = rnorm(16*6),
Srs = rep(c("S3", "S4"), each=8, 6),
Cnd = rep(c("DN", "DA", "DV", "DAV"), each=2, 12),
Grp = rep(c(rep(c("close","far"), 8), rev(rep(c("close","far"), 8))),3) )
even_numbers <- seq(2,nrow(df.tmp),2) # so the df.tmp[even_numbers, ] contains all the combinations
ggplot(df.tmp) +
geom_rect(data = df.tmp[even_numbers, ],
xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
facet_wrap(vars(Grp), ncol=1)
As you can see here, the plot has shaded rectangles only in the second row, despite ensuring the df.tmp[even_numbers, ] includes close datapoints as well:
Here I change the ggplot so it contains geom_rect separately for close and far segments:
even_numbers <- seq(2,length(unique(df.tmp$Cnd)),2) # here the df.tmp[even_numbers, ] doesn't need to have all the combinations
ggplot(df.tmp) +
geom_rect(data = df.tmp[df.tmp$Grp=="close", ][even_numbers, ],
xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
geom_rect(data = df.tmp[df.tmp$Grp=="far", ][even_numbers, ],
xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
facet_wrap(vars(Grp), ncol=1)
As you can see below, it works now:
As I mentioned earlier, I am still not sure why geom_rect did not work in the first place. In my solution, a separate geom_rect needs to be prepared for each segment, so it's definitely not a solution for a plot with many of them. I was trying to find a more elegant way, so one wouldn't have to bother how many segments or other groupings are declared.

Related

How can I re-arrange my multi-plot to reverse alphabetical order?

Fig1
I've made my figure(Fig1 above) and annotated it to my liking, but I want the assorted figures to be displayed in reverse alphabetical order according to the top label names. I can reverse the values of the x-axis, but "order()" and "title()" don't seem to produce the effect I'm aiming for here. Is there any way to tell R I want the multi plot ordered differently? Or, is this just something I'll have to edit myself in another program like Adobe?
Code might be sloppy, but I'm just starting to work in R.
library(tidypaleo)
data<-read.table('multiplot_r.txt',sep="\t",header=TRUE)
as_tibble(data)
zone_data <- tibble(ymin = 0, ymax = 9649, xmin = -Inf, xmax = Inf)
multi_plot +
geom_rect(mapping = aes(ymin = ymin, ymax = ymax, xmin = xmin, xmax =
xmax), data = zone_data, alpha = 0.2, inherit.aes = FALSE)
multi_plot <- ggplot(data, aes(x = value, y = Age)) +
geom_lineh() +
scale_y_reverse() +
facet_geochem_gridh(vars(param)) +
geom_hline(yintercept = c(2200,2800,5000,5500), linetype = "dashed",
colour = "red")+
labs(x = "d18O", y = "Years BP")
multi_plot
Set param as a factor and reverse the order.
library (tidyverse)
data <- data %>% mutate(param = fct_rev(param))

trouble combining geom_rect and facet_grid in ggplot

I want to shade part of the background in each facet of a simple plot. If I omit faceting and run geom_rect + geom_point, the expected results appear as shown in the MRE below. If I omit the rectangle and run geom_point + facet_grid, the expected 4 panels have each point in the correct facet. But when I combine geom_rect + geom_point + and facet_grid, the points in the first category and only those get plotted in every facet. What is going on please???
library(ggplot2)
set.seed(42)
syn.dat <- data.frame(
category.1 = as.factor(rep(c("1A", "1B"), each = 8)),
category.2 = as.factor(rep(rep(c("2A", "2B"), times = 2), each = 4)),
x = rep(-1:2, each = 4) + runif(8, max = .4),
y = rep(-1:2, each = 4) + runif(8, max = .4))
ggplot() +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = .5,
ymax = Inf), fill = "lightyellow") +
geom_point(data = syn.dat, aes(x = x, y = y)) +
facet_grid(cols = vars(category.1),
rows = vars(category.2))
I'm not totally sure about this, but it may be that you need to explicitly provide the data argument to ggplot itself, in order for facet_grid to correctly pick up all the values?
ggplot(syn.dat) +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 0.5, ymax = Inf), fill = "lightyellow") +
geom_point(aes(x = x, y = y)) +
facet_grid(rows = vars(category.2), vars(cols = category.1))

Shade background of a ggplot chart using geom_rect with categorical variables

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)

alpha not working on facetted line graph with x-varying geom_rect()

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.

Ggplot2 different alpha behaviour [duplicate]

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")

Resources