I am facing a difficulty for a plot: I want to remove a part of a fill legend in a ggplot plot, while keeping the automated coloring. here is an example:
library(ggplot2)
df1 <- data.frame(x = 1:20,y1 = rnorm(20,2,0.2),y2 = sqrt(1:20))
df2 <- data.frame(x1 = c(1,5,10),x2 = c(5,10,20),color2 = as.factor(1:3))
ggplot(data=df1) +
geom_rect(data = df2,
aes(xmin = x1,
xmax = x2,
ymin = 0,
ymax = Inf,
fill = color2),
color = "black",
size = 0.3,
alpha = 0.2)+
geom_bar(aes(x = x,
y= y1,
fill = "daily"),
stat='identity',
width = 0.75,
size = 0.1,
alpha = 0.5) +
geom_line(aes(x = x,
y =y2,
color = "somthing"),
size = 1.5)
I would like to:
keep only the daily entry of the fill legend
keep the automated filling based on the color2 for the geom_rect
ideally, merge the two legends (color and fill) into one
I have been playing around with scale_fill_manual and guide, but I did not come with something working. I feel that the solution could be making two independent layer and add them, but I don't know how to do that.
Does anyone know how to do ?
Remember you can set the breaks on any scale, so just set a single break at "daily" on your fill scale. To merge it with the color scale (if I understand your meaning) you can just give the color guide and its single break the same names as the fill guide and fill break:
ggplot(data=df1) +
geom_rect(data = df2,
aes(xmin = x1,
xmax = x2,
ymin = 0,
ymax = Inf,
fill = color2),
color = "black",
size = 0.3,
alpha = 0.2)+
geom_bar(aes(x = x,
y= y1,
fill = "daily"),
stat='identity',
width = 0.75,
size = 0.1,
alpha = 0.5) +
geom_line(aes(x = x,
y =y2,
color = "somthing"),
size = 1.5) +
scale_fill_discrete(breaks = "daily", name = NULL) +
scale_color_discrete(name = "labels") +
theme(legend.margin = margin(0, 0, -10, 0))
Related
First we prepare some toy data that sufficiently resembles the one I am working with.
rawdata <- data.frame(Score = rnorm(1000, seq(1, 0, length.out = 10), sd = 1),
Group = rep(LETTERS[1:3], 10000))
stdev <- c(10.78,10.51,9.42)
Now we plot the estimated densities via geom_density_ridges. I also add a grey highlight around zero via geom_rect. I also flip the chart with coord_flip.
p <- ggplot(rawdata, aes(x = Score, y = Group)) +
scale_y_discrete() +
geom_rect(inherit.aes = FALSE, mapping = aes(ymin = 0, ymax = Inf, xmin = -0.1 * min(stdev), xmax = 0.1 * max(stdev)),
fill = "grey", alpha = 0.5) +
geom_density_ridges(aes(fill = Group), scale = 0.5, size = 1, alpha=0.5) +
scale_color_manual(values = col) +
scale_fill_manual(values = col) +
labs(title="Toy Graph", y="Group", x="Value") +
coord_flip(xlim = c(-8, 8), ylim = NULL, expand = TRUE, clip = "on")
p
And this is the solution I get, which is close to what I was expecting, despite the detail of this enormous gap between the y axis an the start of the first factor in the x axis A. I tried using expand=c(0,0) inside scale_y_discrete() following some suggestions from other posts, but it does not make the gap smaller at all. If possible I would still like to have a certain gap, although minimal. I've been also trying to flip the densities in the y axis so the gap is filled by first factor density plot but I have been unsuccessful as it does not seem as trivial as one could expect.
Sorry, I know this might be technically two different questions, "How to reduce the gap from the y axis to the first density plot?" and "How to flip the densities from y axis to reduce the gap?" But I would really be happy with the first one as I understand the second question seems to be apparently less straightforward.
Thanks in advance! Any help is appreciated.
Flipping the densities also effectively reduces the space, so this might be all you need to do. You can achieve it with a negative scale parameter:
ggplot(rawdata, aes(x = Score, y = Group)) +
scale_y_discrete() +
geom_rect(inherit.aes = FALSE,
mapping = aes(ymin = 0, ymax = Inf,
xmin = -0.1 * min(stdev),
xmax = 0.1 * max(stdev)),
fill = "grey", alpha = 0.5) +
geom_density_ridges(aes(fill = Group), scale = -0.5, size = 1, alpha = 0.5) +
scale_color_manual(values = col) +
scale_fill_manual(values = col) +
labs(title = "Toy Graph", y = "Group", x = "Value") +
coord_flip(xlim = c(-8, 8), ylim = NULL, expand = TRUE, clip = "on")
If you want to keep the densities pointing the same way but just reduce space on the left side, simply set hard limits in your coord_flip, with no expansion:
ggplot(rawdata, aes(x = Score, y = Group)) +
geom_rect(inherit.aes = FALSE,
mapping = aes(ymin = 0, ymax = Inf,
xmin = -0.1 * min(stdev),
xmax = 0.1 * max(stdev)),
fill = "grey", alpha = 0.5) +
geom_density_ridges(aes(fill = Group), scale = 0.5, size = 1, alpha = 0.5) +
scale_color_manual(values = col) +
scale_fill_manual(values = col) +
scale_y_discrete() +
labs(title = "Toy Graph", y = "Group", x = "Value") +
coord_flip(xlim = c(-8, 8), ylim = c(0.8, 4), expand = FALSE)
Context: I would like to use viridis color palette for a color scale and a fill scale within the same plot. The geoms I use do not all have a color or a fill argument so it is not possible to "blend" everything into one scale. In other words, I want to use the same palette but make sure that each color from this palette is used only once across scales, while keeping the properties of the viridis palette (printing and colorblind friendly).
Problem: when using scale_color_viridis_d() and scale_fill_viridis_d() I end up with colors being shared across the fill and the color scale which make the final plot difficult to read.
Question: is it possible to tell ggplot to pick different colors within the viridis palette across the fill and the color scales?
library(ggplot2)
# example dataset
zones <- data.frame(starts = c(1, 3, 4),
ends = c(2, 3.75, 5),
item = rep("A", 3))
# current plot, note that the same color from viridis scale is picked
ggplot(data = zones) +
geom_rect(aes(xmin = starts, xmax = ends, ymin = -Inf, ymax = Inf, fill = "areas")) +
geom_segment(aes(x = starts, xend = ends, y = item, yend = item, color = "segments")) +
scale_color_viridis_d() +
scale_fill_viridis_d(alpha = 0.5)
Expected output: different colors from the same palette being used for the fill and the color scales. Here I used begin = and end = arguments from the scale_*_viridis_d() functions but I feel this is not optimal as many colors are "lost" by spliting the palette like this.
# look of the expected output
ggplot(data = zones) +
geom_rect(aes(xmin = starts, xmax = ends, ymin = -Inf, ymax = Inf, fill = "areas")) +
geom_segment(aes(x = starts, xend = ends, y = item, yend = item, color = "segments")) +
scale_color_viridis_d(begin = 0.5, end = 1) +
scale_fill_viridis_d(alpha = 0.5, begin = 0, end = 0.49)
[EDIT]
I got a step closer using aesthetics and guide arguments of scale_fill_viridis_d() but it messes the legend:
ggplot(data = zones) +
geom_rect(aes(xmin = starts, xmax = ends, ymin = -Inf, ymax = Inf, fill = "areas")) +
geom_segment(aes(x = starts, xend = ends, y = item, yend = item, color = "segments")) +
# scale_color_viridis_d() +
scale_fill_viridis_d(alpha = 0.5,
aesthetics = c("color", "fill"),
guide = guide_legend("Highlights"))
Created on 2022-04-12 by the reprex package (v2.0.1)
I guess the simplest way would be to get the viridis color in reverse order. Normally it's from deep purple to yellow, with the direction = -1 argument, we can have it from yellow to deep purple.
library(ggplot2)
zones <- data.frame(starts = c(1, 3, 4),
ends = c(2, 3.75, 5),
item = rep("A", 3))
ggplot(data = zones) +
geom_rect(aes(xmin = starts, xmax = ends, ymin = -Inf, ymax = Inf, fill = "areas")) +
geom_segment(aes(x = starts, xend = ends, y = item, yend = item, color = "segments")) +
scale_color_viridis_d(direction = -1) +
scale_fill_viridis_d(alpha = 0.5)
Created on 2022-04-12 by the reprex package (v2.0.1)
I am trying to create 3 layers of rectangles each with different color on top of each other to get something like below image:
Data:
library(tidyverse)
df_vaccination <- data.frame(type = c('Population', 'First.Dose.Administered', 'Second.Dose.Administered'),
count = c(1366400000, 952457943, 734608556))
Code tried:
df_vaccination %>%
ggplot()+
geom_rect(aes(xmin = 0, ymin = 0, xmax = count, ymax = 0,
size = 10, lineend = 'round',
alpha = 0.5, fill = type)) +
scale_fill_manual(values = c("#d8b365", "orange", "#5ab4ac")) +
theme_clean() +
scale_x_continuous(labels = unit_format(scale = 1e-7, unit = "Cr")) +
guides(color = guide_legend(order = 1),
size = FALSE,
alpha = FALSE)
Result I am getting is blank plot when I am using geom_rect() & scale_fill_manual(). I am not sure why am I getting blank rectangle:
Convert type column to ordered factor so that largest number plots first, then use geom_col with x = 1. This will make the bars to plot on top of each other, lastly flip the coordinates:
df_vaccination$type <- factor(df_vaccination$type, levels = df_vaccination$type)
ggplot(df_vaccination, aes(x = 1, y = count, fill = type))+
geom_col() +
scale_fill_manual(values = c("#d8b365", "orange", "#5ab4ac")) +
coord_flip() +
theme_void()
I'd like to insert median lines for factor levels into a violin plot in ggplot2. Here's some reproducible data:
set.seed(12)
FactorVar <- sample(LETTERS[1:5], 500, replace = T)
NumericVar <- abs(rnorm(500))
df <- data.frame(FactorVar, NumericVar)
To get the grouped medians I use tapply:
medians <- tapply(df$NumericVar, df$FactorVar, FUN = median)
And this is the code for the plot. As can be seen, I'm inserting each median line individually. That's cumbersome and uneconomical:
library(ggplot2)
g <-
ggplot(data = df,
aes(x = FactorVar, y = NumericVar, fill = FactorVar)) +
geom_violin(scale = "count", trim = F, adjust = 0.75) +
geom_point(aes(y = NumericVar),
position = position_jitter(width = .15), size = 0.9, alpha = 0.8) +
geom_hline(yintercept = mean(NumericVar), color = "blue", size = 0.8, linetype = 4) +
geom_segment(x = 0.5, xend = 1.5, y= medians[1], yend = medians[1], color = "red", linetype = 2) +
geom_segment(x = 1.5, xend = 2.5, y = medians[2], yend = medians[2], color = "red", linetype = 2) +
geom_segment(x = 2.5, xend = 3.5, y = medians[3], yend = medians[3], color = "red", linetype = 2) +
geom_segment(x = 3.5, xend = 4.5, y = medians[4], yend = medians[4], color = "red", linetype = 2) +
geom_segment(x = 4.5, xend = 5.5, y = medians[5], yend = medians[5], color = "red", linetype = 2) +
guides(fill = FALSE) +
guides(color = FALSE) +
coord_flip() +
theme_gray(); g
How can the median segments be inserted in a single command? Also, observe how the median line for factor A is thinner than the others? Why's that?
One method (that simplifies the +/- axis) would be to facet it. Before, though, we'll need to put the medians into a frame, preferably with the same grouping factors as the original.
mediansdf <- data.frame(FactorVar=names(medians), NumericVar=medians)
g <-
ggplot(data = df,
aes(x = FactorVar, y = NumericVar, fill = FactorVar)) +
geom_violin(scale = "count", trim = F, adjust = 0.75) +
geom_point(aes(y = NumericVar),
position = position_jitter(width = .15), size = 0.9, alpha = 0.8) +
geom_hline(yintercept = mean(NumericVar), color = "blue", size = 0.8, linetype = 4) +
guides(fill = FALSE) +
guides(color = FALSE) +
coord_flip() +
theme_gray() +
facet_grid(FactorVar~., scales="free") +
geom_segment(aes(x = 0.5, xend = 1.5, yend = NumericVar), color = "red", linetype = 2, data = mediansdf)
g
This example reused the y aesthetic, but since we have a different frame, we could easily use different names (and specify them within aes(...). One advantage to using the same variable names is (in my opinion) clearer declarative code.
Since the facet_grid adds the factor label on the right side, you likely could remove it from the axis. Note, if you do not use scales="free", then you'll see all factors in each facet, which is distracting and unnecessary.
The reason I am suggesting facets is that it makes the x and xend simple and relative to a single violin, so 0.5 to 1.5; otherwise, as you saw, there is some assumption on which is going with which integer placement.
Last, the appearance of thinner red lines for me was while looking at the raster plot window. If you save to vector-based format (e.g., PDF), the lines appear to be the same thickness.
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.