I want to mark some part of a plot by filling the complete area from some x1 to x2 with (transparent) color in ggplot2.
With base R I would do something like:
plot(1:100)
polygon(x = c(0, 25, 25, 0), y = c(-1000, -1000, 1000, 1000), col = "#FF000050")
When doing the same with ggplot2 I'm stuck with the problem that the polygon either does not go to the upper and lower edge of the plot or isn't plotted at all if I limit the y-axis with ylim.
ggplot(data = data.frame(x = 1:100, y = 1:100), aes(x = x, y = y)) +
geom_point() +
#ylim(0, 100) +
geom_polygon(data = data.frame(x = c(0, 25, 25, 0), y = c(-1000, -1000, 1000, 1000)), aes(x = x, y = y), color = "red", fill = "red", alpha = 0.1)
I don't want to limit the solution to geom_polygon, maybe there is a better way to mark this part of the plot. In my real world data plot, I am using geom_bar for a stacked barplot, but I don't think the solution depends on that.
You can use -Inf and +Inf to define the limits of the polygon (or better in this case, a rect).
ggplot2 will ignore them for building the plot limits:
ggplot() +
geom_point(data = data.frame(x = 1:100, y = 1:100), aes(x = x, y = y)) +
geom_polygon(data = data.frame(x = c(0, 25, 25, 0), y = c(-Inf, -Inf, Inf, Inf)), aes(x = x, y = y), color = "red", fill = "red", alpha = 0.1) +
geom_rect(aes(xmin = 30, xmax = 35, ymin = -Inf, ymax = Inf), color = 'green', fill = "green", alpha = .1)
Note that I moved the data assignment from the ggplot call to the geom_point. The motive for this is better explained in this question.
Try this:
ggplot(data = data.frame(x = 1:100, y = 1:100), aes(x = x, y = y)) +
geom_rect(aes(xmin = 0, xmax = 25, ymin = 0, ymax = 100), fill = "red", alpha = 0.01)+
geom_point()+
scale_y_continuous(limits = c(0, 100), expand = c(0, 0))
Related
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)
I am using the ggpattern package in R for the first time and I can't get the image to work in my plot. A simplified version of my code is here:
x = seq(-1.5, 3.5, 0.1)
y = c( rep(1.0, 22), rep(0.2, 12), rep(0.7, 7), rep(1,10))
ref = data.frame(x = x, y = y)
library(dplyr)
library(ggplot2)
library(ggpattern)
coral = system.file("Coral", "coral.jpg", package="ggpattern")
p = ggplot(ref, aes(x = x, y = y))+
scale_y_reverse(lim = c(1, 0))+
theme_classic()+
geom_ribbon_pattern(aes(x = x, ymin = 1, ymax = y),
color = "darkblue",
fill = NA,
size = 1.5,
pattern = 'image',
pattern_type = 'squish',
pattern_filename = coral) +
geom_ribbon(aes(x = x, ymin = 0, ymax = y), fill = "lightblue")
When I run this code, all I get is this:
Plot with missing image fill
But with this code, I think the white area under the curve should be filled in with the coral image. Does anyone know what I'm doing wrong here? I have searched StackOverflow and GitHub and cannot find an answer.
I think the problem here is that there is no "coral.jpg" file in the img folder of ggpattern.
When i edit your code with one of the images present in the folder, it works fine.
x = seq(-1.5, 3.5, 0.1)
y = c( rep(1.0, 22), rep(0.2, 12), rep(0.7, 7), rep(1,10))
ref = data.frame(x = x, y = y)
library(dplyr)
library(ggplot2)
library(ggpattern)
coral = system.file("img", "magpie.jpg", package="ggpattern")
p = ggplot(ref, aes(x = x, y = y))+
scale_y_reverse(lim = c(1, 0))+
theme_classic()+
geom_ribbon_pattern(aes(x = x, ymin = 1, ymax = y),
color = "darkblue",
fill = NA,
size = 1.5,
pattern = 'image',
pattern_type = 'squish',
pattern_filename = coral) +
geom_ribbon(aes(x = x, ymin = 0, ymax = y), fill = "lightblue")
p
I tried lately to annotate a graph with boxes above a ggplot.
Here is what I want:
I found a way using grid, but I find it too complicated, and I am quite sure there is a better way to do it, more ggplot2 friendly. Here is the example and my solution:
the data:
y2 <- 350
mesure_pol <- data.frame(x1 = c(1,4,7),
x2 = c(4,7,10),
politiquecat = c(1:3),
politique = c("Phase 1\n","Phase 2\n","Phase 3\n"),
y = c(y2,y2,y2)
)
mesure_pol$x_median <- (mesure_pol$x1 + mesure_pol$x2)/2
colorpal <- viridis::inferno(n=3,direction = -1)
plot
the main plot
p <- ggplot(data = mesure_pol) +
geom_rect(aes(xmin = x1,
xmax = x2,
ymin = 0,
ymax = 300,
fill = as.factor(politiquecat)),
fill = colorpal,
color = "black",
size = 0.3,
alpha = 0.2)+
theme(plot.margin=unit(c(60, 5.5, 5.5, 5.5), "points"))+
coord_cartesian(clip = 'off')
the annotation part
Here is the part I am not happy with:
for (i in 1:dim(mesure_pol)[1]) {
text <- textGrob(label = mesure_pol[i,"politique"], gp = gpar(fontsize=7,fontface="bold"),hjust = 0.5)
rg <- rectGrob(x = text$x, y = text$y, width = stringWidth(text$label) - unit(3,"mm") ,
height = stringHeight(text$label) ,gp = gpar(fill=colorpal[i],alpha = 0.3))
p <- p + annotation_custom(
grob = rg,
ymin = mesure_pol[i,"y"], # Vertical position of the textGrob
ymax = mesure_pol[i,"y"],
xmin = mesure_pol[i,"x_median"], # Note: The grobs are positioned outside the plot area
xmax = mesure_pol[i,"x_median"]) +
annotation_custom(
grob = text,
ymin = mesure_pol[i,"y"], # Vertical position of the textGrob
ymax = mesure_pol[i,"y"],
xmin = mesure_pol[i,"x_median"], # Note: The grobs are positioned outside the plot area
xmax = mesure_pol[i,"x_median"])
}
Is there a simplier/nicer way to obtain similar result ? I tried with annotate, label but without any luck.
An alternative approach to achieve the desired result would be to make the annotations via a second ggplot which could be glued to the main plot via e.g. patchwork.
For the annotation plot I basically used your code for the main plot, added a geom_text layer, get rid of the axix, etc. via theme_void and set the limits in line with main plot. Main difference is that I restrict the y-axis to a 0 to 1 scale. Besides that I shifted the xmin, xmax, ymin and ymax values to add some space around the rectangels (therefore it is important to set the limits).
library(ggplot2)
library(patchwork)
y2 <- 350
mesure_pol <- data.frame(x1 = c(1,4,7),
x2 = c(4,7,10),
politiquecat = c(1:3),
politique = c("Phase 1\n","Phase 2\n","Phase 3\n"),
y = c(y2,y2,y2)
)
mesure_pol$x_median <- (mesure_pol$x1 + mesure_pol$x2)/2
colorpal <- viridis::inferno(n=3,direction = -1)
p <- ggplot(data = mesure_pol) +
geom_rect(aes(xmin = x1,
xmax = x2,
ymin = 0,
ymax = 300,
fill = as.factor(politiquecat)),
fill = colorpal,
color = "black",
size = 0.3,
alpha = 0.2)
ann <- ggplot(data = mesure_pol) +
geom_rect(aes(xmin = x1 + 1,
xmax = x2 - 1,
ymin = 0.2,
ymax = 0.8,
fill = as.factor(politiquecat)),
fill = colorpal,
color = "black",
size = 0.3,
alpha = 0.2) +
geom_text(aes(x = x_median, y = .5, label = politique), vjust = .8, fontface = "bold", color = "black") +
coord_cartesian(xlim = c(1, 10), ylim = c(0, 1)) +
theme_void()
ann / p +
plot_layout(heights = c(1, 4))
By setting a second x-axis and filling the background of the new axis labels with element_markdown from the ggtext package. You may achieve this:
Here is the code:
library(ggtext)
y2 <- 350
mesure_pol <- data.frame(x1 = c(1,4,7),
x2 = c(4,7,10),
politiquecat = c(1:3),
politique = c("Phase 1\n","Phase 2\n","Phase 3\n"),
y = c(y2,y2,y2)
)
mesure_pol$x_median <- (mesure_pol$x1 + mesure_pol$x2)/2
p <- ggplot(data = mesure_pol) +
geom_rect(aes(xmin = x1,
xmax = x2,
ymin = 0,
ymax = 300,
fill = as.factor(politiquecat)),
fill = c("yellow", "red", "black"),
color = "black",
size = 0.3,
alpha = 0.2) +
scale_x_continuous(sec.axis = dup_axis(name = "",
breaks = c(2.5, 5.5, 8.5),
labels = c("Phase 1", "Phase 2", "Phase 3"))) +
theme(plot.margin=unit(c(60, 5.5, 5.5, 5.5), "points"),
axis.ticks.x.top = element_blank(),
axis.text.x.top = element_markdown(face = "bold",
size = 12,
fill = adjustcolor(c("yellow", "red", "black"),
alpha.f = .2)))+
coord_cartesian(clip = 'off')
I have a kind of "time series", with different measures taken at regular points on the same individuals.
I want to graphically represent 2 of these time series on the same graph (no problem with that), and add a background which depends on a third factor.
Here a reproducible example of what I've done:
df <- data.frame(
x = seq(1, 20),
y = sample(c(1:10), 20, replace = TRUE),
z = sample(c(1:10), 20, replace = TRUE),
w = sample(c("yes", "no"), 20, replace = TRUE)
)
ggplot(df) +
geom_line(aes(x = x, y = y), color = 'darkorange') +
geom_line(aes(x = x, y = z), color = 'royalblue') +
geom_raster(aes(x = x, y = 5, fill = w, alpha = w)) +
scale_alpha_ordinal(range = c(0, 0.8)) +
scale_fill_manual(values = c("gray32", "gray32"))
Which give me almost what I want excepted that I would like my raster to cover my whole y-axis window.
Any idea?
Thank you!
I think it's simplest to use geom_rect here:
ggplot(df) +
geom_line(aes(x = x, y = y), color = 'darkorange') +
geom_line(aes(x = x, y = z), color = 'royalblue') +
geom_rect(aes(xmin = x - 0.5, xmax = x + 0.5,
ymin = -Inf, ymax = Inf, fill = w, alpha = w)) +
scale_alpha_ordinal(range = c(0, 0.8)) +
scale_fill_manual(values = c("gray32", "gray32"))
It's probably also possible with geom_tile and geom_raster, but I couldn't get the range to cover the whole vertical space without also fiddling with coord_cartesian.
I have the following data structure:
y <- rep(1:10, 2)
group <- rep(c('a', 'b'), each = 10)
dens <- c(c(seq(from = 0, to = 0.8, by = 0.1), 0),
c(seq(from = -0, to = -0.8, by = -0.1), 0))
my_dat <- data.frame(group, dens, y, stringsAsFactors = FALSE )
These are calculated density disributions, in order to make a grouped violin plot, such as in
Split violin plot with ggplot2
# Plot 1:
require(ggplot2)
ggplot(my_dat, aes(x = dens, y = y, fill = group)) +
geom_polygon(color = 'black', show.legend = FALSE)
Now this is simplified, because my data contains hundreds of rows for a smooth outline. (However, there is the central vertical line in my case.) I would now like to remove exactly this vertical central line.
(I guess the problem is removing any specified part of the polygon.)
An idea in my example was to overplot this with a vertical line:
#Plot 2
ggplot(my_dat, aes(x = dens, y = y, fill = group)) +
geom_polygon(color = 'black', show.legend = FALSE) +
geom_segment(x = 0,
xend = 0,
y = min(y) + 0.2,
yend = max(y) - 0.2,
color = '#00BFC4')
But to get the end of the over plotting segment line correct is tricky. (I have purposefully left the line a bit too short for demonstration)
edit
the groups are not distributed in a symmetrical fashion, although my example strongly suggests so.
You can always just plot another polygon on top
x <- with(my_dat, chull(dens, y))
my_dat2 <- my_dat[c(x, x[1L]), ]
ggplot(my_dat, aes(x = dens, y = y, fill = group)) +
geom_polygon(show.legend = FALSE) +
geom_polygon(data = my_dat2, aes(group = 1), size = 1,
fill = 'transparent',
# fill = NA, ## or this
color = 'black')
I think the simpler solution is to first draw all the outlines and then all the filled areas. This should work for any arbitrary polygon shapes.
y <- rep(1:10, 2)
group <- rep(c('a', 'b'), each = 10)
dens <- c(c(seq(from = 0, to = 0.8, by = 0.1), 0),
c(seq(from = -0, to = -0.8, by = -0.1), 0))
my_dat <- data.frame(group, dens, y, stringsAsFactors = FALSE )
require(ggplot2)
ggplot(my_dat, aes(x = dens, y = y)) +
geom_polygon(color = 'black', fill = NA, size = 2) +
geom_polygon(aes(fill = group), color = NA)