I have below dataframe and want to draw a ggplot with stacked bar for lower and upper limits with each data points.
library(ggplot2)
dat = rbind(data.frame('val' = c(3,4), 'val0' = c(1,2), 'val2' = c(5,6), 'name' = c('A', 'A')),
data.frame('val' = c(3,4)+2, 'val0' = c(1,2)+2, 'val2' = c(5,6)+2, 'name' = c('B', 'B')))
dat
> dat
val val0 val2 name
1 3 1 5 A
2 4 2 6 A
3 5 3 7 B
4 6 4 8 B
I am able to obtain below ggplot
ggplot(dat, aes(x = name)) +
geom_point(aes(y = val, color = name), size = 10, alpha = 0.3)
However I want to get below plot (hand-drawn - just for illustration)
Is there any way to draw above plot with my data?
Any pointer will be very helpful
To get the rectangles, the easiest way is probably to use a modified box plot:
ggplot(within(dat, group <- c(1, 2, 1, 2)), aes(name, val, group = group)) +
geom_boxplot(stat = "identity", alpha = 0.5, color = "#00000030",
aes(ymin = val0, lower = val0, fill = name,
group = interaction(name, group),
ymax = val2, upper = val2, middle = val0),
width = 0.5) +
geom_point(position = position_dodge(width = 0.5),
aes(fill = name), shape = 21, size = 4) +
scale_fill_brewer(palette = "Set1") +
theme_minimal(base_size = 20)
Maybe you want to use a dodged geom_pointrange like this:
library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.1.2
dat = rbind(data.frame('val' = c(3,4), 'val0' = c(1,2), 'val2' = c(5,6), 'name' = c('A', 'A')),
data.frame('val' = c(3,4)+2, 'val0' = c(1,2)+2, 'val2' = c(5,6)+2, 'name' = c('B', 'B')))
dat$fac <- factor(unlist(sapply(as.vector(table(dat$name)), seq_len)))
ggplot(dat) +
geom_pointrange(aes(x=name,y=val,colour=name,
ymin=val0,ymax=val2, group = fac), position=position_dodge(width=0.3))
Created on 2022-07-20 by the reprex package (v2.0.1)
Related
I got a bar graph with both alpha and fill. I would like the plot to alternate with alpha and mix colors. Currently, I only get it to mix alpha and order by colors (see MWE below). Any help towards changing the ordering would be very appreciated.
library(tibble)
library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.2.2
set.seed(1)
tibble(
y = runif(20),
x = letters[rep(1:2, times = 10)],
f = factor(LETTERS[rep(1:2, each = 10)], levels = LETTERS[1:2], ordered = TRUE),
a = factor(rep(1:5, times = 4), levels = 1:5, ordered = TRUE)
) %>%
ggplot(aes(x, y, fill = f, alpha = a)) +
geom_col(position = position_dodge2(1)) +
scale_alpha_discrete(range = c(.5, 1))
#> Warning: Using alpha for a discrete variable is not advised.
Created on 2023-01-26 with reprex v2.0.2
I.e., I am trying to get dark yellow mixed with dark purple and light/transparent yellow with light/transparent purple.
Perhaps like this ..., i.e. simply switch the order of fill and alpha to change the grouping:
library(tibble)
library(ggplot2)
set.seed(1)
tibble(
y = runif(20),
x = letters[rep(1:2, times = 10)],
f = factor(LETTERS[rep(1:2, each = 10)], levels = LETTERS[1:2], ordered = TRUE),
a = factor(rep(1:5, times = 4), levels = 1:5, ordered = TRUE)
) %>%
ggplot(aes(x, y, alpha = a, fill = f)) +
geom_col(position = position_dodge2(1)) +
scale_alpha_discrete(range = c(.5, 1))
#> Warning: Using alpha for a discrete variable is not advised.
I have similar data like this :
Dat<-read.table (text=" Class Value
0 8
1 10
0 9
2 12
2 13
1 10
3 15
2 12
3 12
4 17
4 19
1 13
2 12
4 10
", header=TRUE)
A= 0, B=1, C=2, D=3, E=4
I have tried to sketch my plot as follows, but I failed to plot it using geom_line.
The sketch is hypothetical and may not reflect the hypothetical data.
The X-axis shows a range of the values according to the groups, i.e., A, B, C, D, E. For example, in the data, E has a range from 10 and 19.
Another option using geom_linerange like this:
library(dplyr)
library(ggplot2)
Dat %>%
mutate(Class = as.factor(Class)) %>%
group_by(Class) %>%
summarise(x_min = min(Value),
x_max = max(Value)) %>%
ggplot(aes(y = Class, colour = Class, group = Class)) +
geom_linerange(aes(xmin = x_min, xmax = x_max)) +
scale_y_discrete(labels=c("A","B","C","D", "E")) +
theme_classic() +
labs(x = "Value", y = "Class", color = "") +
theme(legend.position = "none")
Created on 2023-01-01 with reprex v2.0.2
Just wanted to throw a base R plot() approach in there for posterity though I see this is tagged with ggplot:
plot(Dat[,2:1], type = "n", xlim = c(5, 20), axes = FALSE, ylab = "")
sapply(unique(Dat$Class), function(x)
segments(x0 = min(Dat$Value[Dat$Class %in% x]),
x1 = max(Dat$Value[Dat$Class == x]),
y0 = Dat$Class[Dat$Class == x][1], col = x+1))
axis(1)
axis(2, at = unique(Dat$Class), labels = LETTERS[unique(Dat$Class)+1], las = 1)
mtext("Class", side = 2, las = 1, adj = 2)
A bit tongue-in-cheek, but this replicates your plot style with minimal code, and uses geom_line, as requested.
library(ggplot2)
ggplot(within(Dat, Class <- LETTERS[Class + 1]),
aes(Value, Class, color = Class)) +
geom_line(linewidth = 4, lineend = "round") +
scale_color_manual(values = c("#ed2028", "#22b14c", "#ffaec9", "#a349a4",
"#c4c4c4"), guide = "none") +
scale_x_continuous(breaks = c(5, 10, 15, 20), limits = c(5, 20)) +
theme_classic(base_size = 30)
library(tidyverse)
library(ggalt)
Dat %>%
group_by(Class) %>%
summarise(min = min(Value),
max = max(Value)) %>%
ggplot() +
geom_dumbbell(aes(
y = Class,
x = min,
xend = max,
color = factor(Class)
), size = 1) +
theme_light() +
xlab("Value")
I have below ggplot:
library(ggplot2)
data = rbind(data.frame('val' = c(10, 30, 15), 'name' = c('A', 'B', 'C'), group = 'gr1'), data.frame('val' = c(30, 40, 12), 'name' = c('A', 'B', 'C'), group = 'gr2'))
ggplot(data, # Draw barplot with grouping & stacking
aes(x = group,
y = val,
fill = name)) +
geom_bar(stat = "identity",
position = "stack", width = .1)
With this, I am getting below plot
However, I want to connect these bars with a curved area where the area would be equal to the value of the corresponding bar-component. A close example could be like,
Is there any way to achieve this with ggplot?
Any pointer will be very helpful.
This is something like an alluvial plot. There are various extension packages that could help you create such a plot, but it is possible to do it in ggplot directly using a bit of data manipulation.
library(tidyverse)
alluvia <- data %>%
group_by(name) %>%
summarize(x = seq(1, 2, 0.01),
val = pnorm(x, 1.5, 0.15) * diff(val) + first(val))
ggplot(data,
aes(x = as.numeric(factor(group)),
y = val,
fill = name)) +
geom_bar(stat = "identity",
position = "stack", width = .1) +
geom_area(data = alluvia, aes(x = x), position = "stack", alpha = 0.5) +
scale_x_continuous(breaks = 1:2, labels = levels(factor(data$group)),
name = "Group", expand = c(0.25, 0.25)) +
scale_fill_brewer(palette = "Set2") +
theme_light(base_size = 20)
EDIT
A more generalized solution for more than 2 groups would be
library(tidyverse)
alluvia <- data %>%
mutate(group = as.numeric(factor(group)),
name = factor(name)) %>%
arrange(group) %>%
group_by(name) %>%
mutate(next_group = lead(group),
next_val = lead(val)) %>%
filter(!is.na(next_val)) %>%
group_by(name, group) %>%
summarise(x = seq(group + 0.01, next_group - 0.01, 0.01),
val = (next_val - val) * pnorm(x, group + 0.5, 0.15) + val)
ggplot(data,
aes(x = as.numeric(factor(group)),
y = val,
fill = name)) +
geom_bar(stat = "identity",
position = "stack", width = .1) +
geom_area(data = alluvia, aes(x = x), position = "stack", alpha = 0.5) +
scale_x_continuous(breaks = seq(length(unique(data$group))),
labels = levels(factor(data$group)),
name = "Group", expand = c(0.25, 0.25)) +
scale_fill_brewer(palette = "Set2") +
theme_light(base_size = 20)
I've searched and tried a bunch of suggestions to be able to display a custom legend instead of the default one in a grouped scatter ggplot. I've tried this and this and following this among others.
For instance, let's say I have a df like this one:
df = data.frame(id = c("A", "A", "B", "C", "C", "C"),
value = c(1,2,1,2,3,4),
ref = c(1.5, 1.5, 1, 2,2,2),
min = c(0.5, 0.5, 1,2,2,2))
and I want to display the values of each id as round dots, but also put the reference values and minimum values for each id as a differently shaped dot, as follows:
p = ggplot(data = df) +
geom_point(aes(x = id, y = value, color = factor(id)), shape = 19, size = 6) +
geom_point(aes(x = id, y = ref, color = factor(id)), shape = 0, size = 8) +
geom_point(aes(x = id, y = min, color = factor(id)), shape = 2, size = 8) +
xlab("") +
ylab("Value")
#print(p)
Now all is fine, but my legend doesn't add anything to the interpretation of the plot, as the X axis and colors are enough to understand it. I know I can remove the legend via theme(legend.position = "none").
Instead, I would like to have a legend of what the actual shapes of each dot represent (e.g., filled round dot = value, triangle = min, square = ref).
Among trying to manually set the scale values via scale_fill_manual and something along those lines
override.shape = shapes$shape
override.linetype = shapes$pch
guides(colour = guide_legend(override.aes = list(shape = override.shape, linetype = override.linetype)))...
....
I've also tried making a secondary plot, but not display it, using something suggested in one of the links pasted above:
shapes = data.frame(shape = c("value", "reference", "minimum"), pch = c(19,0,2), col = c("gray", "gray", "gray"))
p2 = ggplot(shapes, aes(shape, pch)) + geom_point()
#print(p2)
g_legend <- function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)
}
legend <- g_legend(p2)
library(gridExtra)
pp <- arrangeGrob(p1 ,legend,
widths=c(5/4, 1/4),
ncol = 2)
but then I get the error:
> legend <- g_legend(p2)
Error in tmp$grobs[[leg]] :
attempt to select less than one element in get1index
for which I did not find a working solution.. so yeah.. any suggestion on how I could only show a legend related to the different dot shapes would be welcome.
Thank you
You can manually build a shape legend using scale_shape_manual:
library(ggplot2)
ggplot(data = df) +
geom_point(aes(x = id, y = value, color = factor(id), shape = 'value'), size = 6) +
geom_point(aes(x = id, y = ref, color = factor(id), shape = 'ref'), size = 8) +
geom_point(aes(x = id, y = min, color = factor(id), shape = 'min'), size = 8) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
xlab("") +
ylab("Value")
Created on 2020-04-15 by the reprex package (v0.3.0)
But a better way to do this would be to reshape the df to a long format, and map each aes to a variable:
library(dplyr)
library(tidyr)
df %>%
pivot_longer(-id) %>%
ggplot() +
geom_point(aes(x = id, y = value, color = factor(id), shape = name, size = name)) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
scale_size_manual(values = c('value' = 6, 'ref' = 8, 'min' = 8)) +
xlab("") +
ylab("Value")
Created on 2020-04-15 by the reprex package (v0.3.0)
To remove the legend for the color use guide_none():
library(tidyr)
library(ggplot2)
df %>%
pivot_longer(-id) %>%
ggplot() +
geom_point(aes(x = id, y = value, color = factor(id), shape = name, size = name)) +
scale_shape_manual(values = c('value' = 19, 'ref' = 0, 'min' = 2)) +
scale_size_manual(values = c('value' = 6, 'ref' = 8, 'min' = 8)) +
guides(color = guide_none()) +
xlab("") +
ylab("Value")
Created on 2020-04-16 by the reprex package (v0.3.0)
Data:
df = data.frame(id = c("A", "A", "B", "C", "C", "C"),
value = c(1,2,1,2,3,4),
ref = c(1.5, 1.5, 1, 2,2,2),
min = c(0.5, 0.5, 1,2,2,2))
You can tidy your data first using tidyr, and then map the aes shape to the new variable
library(tidyr)
df2 <- pivot_longer(df, -id)
ggplot(data = df2) +
geom_point(aes(x = id, y = value, shape = name), size = 6) +
xlab("") +
ylab("Value")
I want to draw two densities with two vertical lines for the averages.
The legend is once to denote the densities and once the vertical
lines.
I tried the code below. However, only one legend appears and the labeling is wrong.
Can anyone help me?
set.seed(1234)
data <- data.frame(value = rnorm(n = 10000, mean = 50, sd = 20),
type = sample(letters[1:2], size = 10000, replace = TRUE))
data$value[data$type == "b"] <- data$value[data$type == "b"] + 50
mean.a <- mean(data$value[data$type == "a"])
mean.b <- mean(data$value[data$type == "b"])
library(ggplot2)
gp <- ggplot(data = data, aes(x = value))
gp <- gp + geom_density(aes(fill = type), color = "black", alpha=0.3, lwd = 1.0, show.legend = TRUE)
gp <- gp + scale_fill_manual(breaks = 1:2, name = "Density", values = c("a" = "green", "b" = "blue"), labels = c("a" = "Density a", "b" = "Density b") )
gp <- gp + geom_vline(aes(color="mean.a", xintercept=mean.a), linetype="solid", size=1.0, show.legend = NA)
gp <- gp + geom_vline(aes(color="mean.b", xintercept=mean.b), linetype="dashed", size=1.0, show.legend = NA)
gp <- gp + scale_color_manual(name = "", values = c("mean.a" = "red", "mean.b" = "darkblue"), labels = c("mean.a" = "Mean.A", "mean.b" = "Mean.B"))
gp <- gp + theme(legend.position="top")
gp
Here are a couple ways to do it. I'm not sure, but I think some of the difficulty comes from having more than one geom_vline and trying to hard-code values in the aes. You're building three scales here: fill for the density curves, and color and linetype for the vertical lines. But you're aiming (correct me if I'm misreading) for two legends.
The easiest way to deal with getting the proper legends is to make a small data frame for the means, rather than individual values for each mean. You can do this easily with dplyr to calculate means for each type.
library(tidyverse)
set.seed(1234)
data <- data.frame(value = rnorm(n = 10000, mean = 50, sd = 20),
type = sample(letters[1:2], size = 10000, replace = TRUE))
data$value[data$type == "b"] <- data$value[data$type == "b"] + 50
means <- group_by(data, type) %>%
summarise(mean = mean(value))
means
#> # A tibble: 2 x 2
#> type mean
#> <fct> <dbl>
#> 1 a 50.3
#> 2 b 99.9
Then when you plot, you can make a single geom_vline call, assigning the means data frame and allowing the aesthetics you want—color and linetype—to be scaled based on this data. The trick then is reconciling the names and labels: if you don't set the same legend name and labels for both the color and linetype scales, you'll have two legends for the lines. Set them the same, and you get a single legend for the mean lines.
ggplot(data, aes(x = value)) +
geom_density(aes(fill = type), alpha = 0.3) +
geom_vline(aes(xintercept = mean, color = type, linetype = type), data = means) +
scale_color_manual(values = c("red", "darkblue"), labels = c("Mean.A", "Mean.B"), name = NULL) +
scale_linetype_discrete(labels = c("Mean.A", "Mean.B"), name = NULL) +
scale_fill_manual(values = c(a = "green", b = "blue"), name = "Density")
The second way is to just add a step to creating the means data frame where you label the types the way you want later, i.e. "Mean.A" instead of just "a". Then you don't need to adjust labels, and you can skip the linetype scale—unless you want to change linetypes manually—and then just remove the name for that legend for both color and linetype in your labs.
means2 <- group_by(data, type) %>%
summarise(mean = mean(value)) %>%
mutate(type = paste("Mean", str_to_upper(type), sep = "."))
means2
#> # A tibble: 2 x 2
#> type mean
#> <chr> <dbl>
#> 1 Mean.A 50.3
#> 2 Mean.B 99.9
ggplot(data, aes(x = value)) +
geom_density(aes(fill = type), alpha = 0.3) +
geom_vline(aes(xintercept = mean, color = type, linetype = type), data = means2) +
scale_color_manual(values = c(Mean.A = "red", Mean.B = "darkblue")) +
scale_fill_manual(values = c(a = "green", b = "blue"), name = "Density") +
labs(color = NULL, linetype = NULL)
Created on 2018-06-05 by the reprex package (v0.2.0).