Chop up bsplines and color them - r

I am intrigued by this plot of Albert Cairo.
I can smooth my curve sufficiently with ggforce::bspline
However, now that I don't have a date axis I am unsure as to how to change the color of a spline midway.
Let's assume that the three points represent the years 1990, 1991 and 1992. And someone got elected on July 1, 1990. I would like to change the color of the spline at this point. So the curved line would be red from origin until aprox (12, 5.6) then blue from (12, 5.6) to (17,4)
I am not sure how to accomplish this.
library(ggforce)
library(ggplot2)
data <- tibble (
x = c(10, 15, 17),
y = c(5, 7, 4)
)
ggplot(data) +
stat_bspline2(aes(x = x, y = y), n = 300, geom = "bspline0", color = "red") +
stat_bspline2(aes(x = x, y = y), n = 3, geom = "point", color = "red") +
geom_point(aes(x = x, y = y), color = "grey")
Thinking about what M.A. told me about groups I now have code that can:
Change the color of straight line segments:
# Works for straight lines
ggplot(data, aes(x=x, y=y, colour = g, group = 1)) +
geom_line(size = 3) +
geom_point() +
scale_color_manual(values = c("A" = "red", "B" = "pink", "C" = "green", "D" = "white"))
And the continuous colour of a bspline. But I would like this to be discrete colors only as in the plot above.
# Works with continuous color
ggplot(data, aes(x=x, y=y, colour = g, group = 1)) +
geom_bspline2(size = 4, n = 300) +
scale_color_manual(values = c("A" = "red", "B" = "pink", "C" = "green", "D" = "white"))
Or this error, "Error: Continuous value supplied to discrete scale" with:
ggplot(data) +
stat_bspline2(aes(x = x, y = y, color = ..group.., group = 1), n = 300, geom = "bspline0") +
scale_color_manual(values = c("A" = "red", "B" = "pink", "C" = "green", "D" = "white"))
So I'm wondering how to manually control the color of discrete segments with bspline.

You can do this by grouping:
data <- tibble (
x = c(10, 15, 17, 17, 20, 22),
y = c(5, 7, 4, 4, 0, 5),
g = c("A", "A", "A", "B", "B", "B")
)
ggplot(data) +
stat_bspline2(
aes(x = x, y = y, color = ..group.., group = g),
n = 300, geom = "bspline0") +
scale_colour_gradient(low = "blue", high = "red", guide=FALSE)
Edit:
The error Continuous value supplied to discrete scale is is somewhat confusing here. I don't know if there is an easier way to get what you want but it can be achieved using scale_colour_gradientn(). This function allows to map the group g to a gradient between n colours so you want n to be the number of groups.
For example, consider a larger data set with four groups:
# example data
data <- tibble (
x = c(10, 15, 17, 17, 20, 22, 22, 23, 25, 25, 27, 29),
y = c(5, 7, 4, 4, 0, 5, 5, 6, 5, 5, 4, 5.5),
g = c("A", "A", "A", "B", "B", "B", "C", "C", "C", "D","D","D")
)
You can use a palette like rainbow() and specify the number of colours for the gradient to be 4 since there are four groups A, B, C and D.
# use a colour palette:
ggplot(data) +
stat_bspline2(
aes(x = x, y = y, color = ..group.., group = g),
n = 300, size = 1, geom = "bspline0") +
scale_color_gradientn(colours = rainbow(4),
guide = F
)
For custom colours, you may do the following:
# use custom colors:
ggplot(data, aes(x=x, y=y, color = ..group.., group = g)) +
geom_bspline2(size = 1, n = 300) +
scale_color_gradientn(
colours = c("red", "pink", "green", "white"),
guide = F
)
This uses a gradient between the colours red, pink, green and white. Note that the order of the colours matters as a different order leads to a different gradient and thus a different mapping of the groups.

Related

Separate legend by facet for geom_segment and geom_point

I am interested in creating a figure akin to the below, which has a separate legend entry for each of four components: two geom_segment and two geom_point, which are grouped according to some facet (corresponding to the color).
Here is a sample dataset, and some initial code.
x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
y <- c(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
z <- c(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)
s <- c(5, 5, 5, 5, 5, 5, 5, 5, 5, 5)
t <- c(10, 10, 10, 10, 10, 10, 10, 10, 10, 10)
n <- c("A", "A", "A", "A", "A", "A", "A", "A", "A", "A",
"B", "B", "B", "B", "B", "B", "B", "B", "B", "B")
df <- cbind(rbind(data.frame(x = x, y = y, s = s), data.frame(x = x, y = z, s = t)), n)
ggplot(data = df, mapping = aes(x = x, y = y, color = n)) +
geom_point() +
scale_color_manual(values = c("blue", "gray64")) +
geom_segment(mapping = aes(x = 0, xend = 10, y = 10, yend = 10), color = "blue", inherit.aes = FALSE) +
geom_segment(mapping = aes(x = 0, xend = 10, y = 20, yend = 20), color = "gray64", inherit.aes = FALSE)
Give this a shot:
library(ggplot2)
ggplot() +
geom_point(data = dplyr::filter(df, n == "A"), mapping = aes(x = x, y = y, shape = "Label A.1"), color = "gray") +
geom_point(data = dplyr::filter(df, n == "B"), mapping = aes(x = x, y = y, shape = "Label B.1"), color = "blue") +
geom_line(data = dplyr::filter(df, n == "A"), mapping = aes(x = x, y = s, color = "Label A.2")) +
geom_line(data = dplyr::filter(df, n == "B"), mapping = aes(x = x, y = s, color = "Label B.2")) +
scale_shape_manual(name = NULL, values = c(19, 19)) +
scale_color_manual(name = NULL, values = c("Label A.2" = "gray", "Label B.2" = "blue")) +
guides(shape = guide_legend(override.aes = list(color = c("Label A.1" = "gray", "Label B.1" = "blue"))))
ggplot2 doesn't seem to like separating legends (I suppose it does make the plot more complicated). In this solution, we add each layer separately, controlling the legend using shape (which we later set to 19, the default filled-in circle) and color aesthetics. In the last line, we make sure the colors of the shape layer are correct (try the code without the last line to see what it does!).
I also don't think "facet" is used correctly in your question. Generally, facets are like subplots.

Allowing for different colors for boxplot and overlying points in ggplot2

Here is the data:
set.seed(1234)
df = tribble(
~group,~value,
"a", rnorm(1000, mean = 5),
"b", rnorm(1000, mean = 7, sd = 1.5),
"c", rnorm(1000, mean = 8),
"c", rnorm(1000, mean = 9),
"c", rnorm(1000, mean = 7)
) %>%
unnest(value)
I use the code below to create box plot and overlying dots:
library(ggplot2)
ggplot(df, aes(x = group, y = value)) +
geom_boxplot(aes(color = group), fill = NA, outlier.shape = NA)+
geom_point(aes(color = group), shape = 21, position = position_jitter(
seed = 1, width = .05
), alpha = 0.1)
Here is the figure:
The border color of the overlying points and the border color of the boxes are identical. For example, when the boxplot is orange, the border of points is also orange. I wish to know how could I allow for different colors for the boxplot and overlying points' border. For example, I wish to make colors of the boxplot to be black, blue, brown for groups a, b, and c, respectively. I also want the color of the border of overlying points to be red, green, yellow.
Another option to achieve your desired result would be to use the ggnewscale package which allows for multiple scales and legends for the same aesthetic:
library(ggplot2)
library(ggnewscale)
ggplot(df, aes(x = group, y = value)) +
geom_boxplot(aes(color = group), fill = NA, outlier.shape = NA) +
scale_color_manual(values = c("black", "blue", "brown")) +
new_scale_color() +
geom_point(aes(color = group),
shape = 21,
position = position_jitter(seed = 1, width = .05), alpha = 0.1
) +
scale_color_manual(values = c("red", "green", "yellow"))
Maybe you look for something like this:
Transform group to factor and then recode with recode_factor from dplyr package with colors you desire.
Then within ggplot assign your new colors:
library(tidyverse)
df %>%
mutate(group = factor(group),
color_group = recode_factor(group, "a"="gold", "b"="purple", "c"="black")) %>%
ggplot(aes(x = group, y = value)) +
geom_boxplot(aes(color = color_group), fill = NA, outlier.shape = NA)+
geom_point(aes(color = group), shape = 21, position = position_jitter(
seed = 1, width = .05
), alpha = 0.1)

Overlay Each Bar of Stacked ggplot2 Barchart with Line

I have a stacked ggplot2 barchart, which looks as follows:
# Example data
data <- data.frame(level = rep(1:3, 3),
values = c(20, 30, 25, 15, 10, 5, 18, 20, 30),
group = as.factor(rep(LETTERS[1:3], each = 3)))
# Draw plot without lines
library("ggplot2")
my_plot <- ggplot(data, aes(x = level, y = values, fill = group)) +
geom_bar(stat = "identity") +
scale_fill_manual(breaks = c("A", "B", "C"),
values = c("forestgreen", "darkgoldenrod1", "brown2"))
my_plot
Now, I want to overlay each bar of this barchart with a blue line of a certain height. The blue lines should also be represented in the legend of the plot.
The data for these lines looks as follows:
# Data for lines
data_line <- data.frame(level = 1:3,
values = c(25, 40, 10),
group = as.factor("D"))
The output should look as follows (image drawn in paint):
Question: How could I add these data as overlaying lines?
One option using geom_segment
my_plot +
geom_segment(data = data_line,
aes(x = level - 0.45,
xend = level + 0.45,
y = values,
yend = values,
col = "D"), # 'fake' a legend
size = 2,
inherit.aes = FALSE) +
scale_color_manual(name = NULL,
values = c(D = "#007fff")) +
guides(fill = guide_legend(order = 1),
color = guide_legend(order = 2)) +
theme(legend.margin = margin(t = -1, b = -15)) # trial and error

Add select points to colored bspline

Continuing on from my previous bspline question
If this is my curve:
data <- tibble (
x = c(10, 15, 17, 17, 20, 22, 22, 23, 25, 25, 27, 29),
y = c(5, 7, 4, 4, 0, 5, 5, 6, 5, 5, 4, 5.5),
g = c("A", "A", "A", "B", "B", "B", "C", "C", "C", "D","D","D"),
pt = c(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1)
)
ggplot(data) +
stat_bspline2(aes(x=x, y=y, color = ..group.., group = g), size = 4, n = 300, geom = "bspline0") +
scale_color_gradientn(colours = c("red", "pink", "green", "white"), guide = F)
How do I add dots to selected points on the curve?
Here's how not to do it:
ggplot(data) +
stat_bspline2(aes(x=x, y=y, color = ..group.., group = g), size = 4, n = 300, geom = "bspline0") +
scale_color_gradientn(colours = c("red", "pink", "green", "white"), guide = F) +
stat_bspline2(data = pt, aes(x = x, y = x, color = ..group.., group = pt), n = 12, geom = "point", size = 9)
)
It isn't perfect, but it works. Add some columns with the positions of the points you want (I'm assuming that if pt = 1, you want the point plotted)
data <- data %>%
mutate(pt_x = ifelse(pt == 1, x, NA),
pt_y = ifelse(pt == 1, y, NA))
ggplot(data) +
stat_bspline2(aes(x=x, y=y, color = ..group.., group = g), size = 4, n = 300, geom = "bspline0") +
scale_color_gradientn(colours = c("red", "pink", "green", "white"), guide = F) +
geom_point(aes(pt_x, pt_y))

geom_point with different legend for fill and shape

Hmmm, maybe it's the temprature or I'm once again do not see the obvious ...
Here is my code:
library(ggplot2)
p <- ggplot()
p <- p + geom_point(aes(x = 1, y=1,bg = "I", group = "B"),pch = 21, size = 20, color=NA)
p <- p + geom_point(aes(x = 1, y=1.125,bg = "I", group = "B" ),pch = 22, size = 20, color=NA)
p <- p + geom_point(aes(x = 0.75, y=1.125,bg = "II", group = "A" ),pch = 22, size = 20, color=NA)
p <- p + geom_point(aes(x = 0.85, y=1.125,bg = "III", group = "A" ),pch = 22, size = 20, color=NA)
p <- p + scale_fill_manual(values= c("darkred", "darkblue", "darkgreen"), guide=guide_legend(override.aes = list(shape = 23)))
#p <- p + scale_fill_manual(values= c("darkred", "darkblue", "darkgreen"), guide=guide_legend(inherit.aes = FALSE))
p <- p + scale_shape_manual(labels = c("circle", "rectangle"),values = c(21, 22))
p
What I'm trying to achieve are basically two legends, one that reflects the color, in this example there are three different colors ("I", "II", and "III"), and two different types of shapes "rectangle" and "circle", there will never be more than these two different shapes.
Unfortunately there are some additional constraints ... I can't use the aesthetic color due to the fact that I'm also using geom_segment to somehow connect the shapes, and that is the second constraint, I have to use ggplot2.
But I'not able to produce these two legends, any help is appreciated ...
Why don't you store all your points in a data frame? It suits perfectly:
df <- data.frame(x = c(1, 1, 0.75, 0.85),
y = c(1, 1.125, 1.125, 1.125),
nr = c("I", "I", "II", "III"),
sh = c("B", "B", "A", "A"))
And now you can easily see the required mapping:
ggplot(df, aes(x, y, fill = nr, shape = sh)) +
geom_point(size = 20, color = NA) +
scale_shape_manual(labels = c("circle", "rectangle"), values = c(21, 22),
guide = guide_legend(override.aes = list(colour = 1))) +
scale_fill_manual(values = c("darkred", "darkblue", "darkgreen"),
guide = guide_legend(override.aes = list(shape = 23)))

Resources