Adding geom_segment to faceted plot that have free x axis - r

I am trying to make a faceted plot with a free x axis (scales = "free_x"). The x axis is a categorical variable and I am having problems aligning a geom_segment. Is there an easy way to avoid the issue presented in the following example:
library(tidyverse)
library(HistData)
library(datasets)
data(Cholera)
Cholera
stats <- Cholera %>% group_by(region, water) %>% summarise(mean = mean(cholera_drate), max = max(cholera_drate))
# This works but I want to get rid of empty categories
ggplot() +
geom_boxplot(data = Cholera, aes(x = region, y = cholera_drate)) +
geom_point(data = stats, aes(x = region, y = mean), color = "red") +
geom_segment(data = stats, aes(x = as.numeric(region) - 0.25, xend = as.numeric(region) + 0.25, y = mean, yend = mean), color = "blue") +
facet_wrap(~ water)
# If I use free_x then geom does not align anymore
ggplot() +
geom_boxplot(data = Cholera, aes(x = region, y = cholera_drate)) +
geom_point(data = stats, aes(x = region, y = mean), color = "red") +
geom_segment(data = stats, aes(x = as.numeric(region) - 0.25, xend = as.numeric(region) + 0.25, y = mean, yend = mean), color = "blue") +
facet_wrap(~ water, scales = "free_x")

The first thing to do is to use facet_grid with scales = free_x and then space = free_x to get:
ggplot() +
geom_boxplot(data = Cholera, aes(x = region, y = cholera_drate)) +
geom_point(data = stats, aes(x = region, y = mean), color = "red") +
facet_grid(cols = vars(water), scales = "free_x", space = "free_x") +
geom_segment(data = stats,
aes(x = as.numeric(region) - 0.25, xend = as.numeric(region) + 0.25,
y = mean, yend = mean), color = "blue")
Then you'll see that the real problem is the fact that you're positioning the segment based on the numeric factor of region which changes when you use space = free_x & scales = free_x thereby leaving the segment behind in the first facet as seen below:
The only thing I can think of to solve this is a sort of a hack. We'll create a separate data for the segments from stats and then change the 'Kent' value to 'North' which is the desired position (of the segment left behind) after we do space = free_x & scales = free_x as seen below:
data_lab <- stats
data_lab$region[8] <- "North" ## change Kent to North
ggplot() +
geom_boxplot(data = Cholera, aes(x = region, y = cholera_drate)) +
geom_point(data = stats, aes(x = region, y = mean), color = "red") +
facet_grid(cols = vars(water), scales = "free_x", space = "free_x") +
geom_segment(data = data_lab,
aes(x = as.numeric(region) - 0.25, xend = as.numeric(region) + 0.25,
y = mean, yend = mean), color = "blue")
to get:
I hope it helps.

Related

Why my codes is unable to add legend to figure in ggplot2

I want to add some legend to this figure.
There are one set of scatter points and two lines.
Below is my codes.
rm(list = ls())
n=500
set.seed(100)
x1=seq(from=-3,to=3,length.out = n)
a=rnorm(n,mean=0,sd=0.2)
z=1+2*x1+a
p=1/(1+exp(-z))
y=(p>=(runif(n,min=0.4,max=0.6)))*1
X=data.frame(x1=x1,y=y)
K=glm(formula=y~x1,family=binomial(link="logit"),data=X)
p_fit=1/(1+exp(-(K$coefficients[1]+K$coefficients[2]*x1)))
ggplot()+ xlab("x1")+ ylab("y")+facet_grid()+
geom_point(data=data.frame(x1,y), aes(x=x1, y=y),size=4)+
geom_line(data=data.frame(x1,p), aes(x=x1, y=p),size=1.2,col="blue")+
geom_line(data=data.frame(x1,p_fit), aes(x=x1, y=p_fit),size=1.5,col="red")+
theme(legend.position =c(0.8,0.5))
In ggplot legends appear when you map a variable or a constant to an aesthetic. In your case, try as follow.
ggplot() +
geom_point(data = data.frame(x1, y), aes(x = x1, y = y), size = 3, alpha = 0.5)+
geom_line(data = data.frame(x1, p),
aes(x = x1, y = p, color = 'p'),
size = 1.2) +
geom_line(data = data.frame(x1, p_fit),
aes(x = x1, y = p_fit, color = "p_fit"), size = 1.5) +
xlab("x1") +
ylab("y")+
theme(legend.position = c(0.8, 0.5)) +
scale_color_manual(values = c("blue", "red"))

How to draw a line between two points when holding one axis fixed (time series)

I am trying to draw a straight line between each points of two variables, that are observed at the same time, using ggplot2.
I looked at geom_segment but I struggle to make it work in my case.
Here is my minimum working example and a drawing of what I would like to achieve (the part I am missing is in blue color).
I would appreciate any help!
set.seed(1234)
y <- rnorm(10,0,0.01)
Date <- seq(as.Date("2000/1/1"), by = "day", length.out = 10)
example_df <- tibble(Date,y) %>% mutate(avg = mean(y))
ggplot(example_df, mapping = aes(x = Date)) + geom_point(mapping = aes(y = y)) +
geom_line(aes(y = y)) +
geom_line(aes(y = avg), col = "red")
geom_segment will work:
ggplot(example_df, aes(x = Date)) +
geom_point(aes(y = y)) +
geom_line(aes(y = y)) +
geom_line(aes(y = avg), col = "red")+
geom_segment(aes(xend = Date, y = y, yend = avg), col = 'blue')

Adding boxplot below density plot

I'm new to ggplot and I'm trying to create this graph:
But actually, I'm just stuck here:
This is my code :
ggplot(diamonds) +
aes(x = carat, group = cut) +
geom_line(stat = "density", size = 1) +
theme_grey() +
facet_wrap(~cut, nrow = 5, strip.position = "right") +
geom_boxplot(aes())
Does someone know what I can do next?
Edit: As of ggplot2 3.3.0, this can be done in ggplot2 without any extension package.
Under the package's news, under new features:
All geoms and stats that had a direction (i.e. where the x and y axes
had different interpretation), can now freely choose their direction,
instead of relying on coord_flip(). The direction is deduced from
the aesthetic mapping, but can also be specified directly with the new
orientation argument (#thomasp85, #3506).
The following will now work directly (replacing all references to geom_boxploth / stat_boxploth in the original answer with geom_boxplot / stat_boxplot:
library(ggplot2)
ggplot(diamonds, aes(x = carat, y = -0.5)) +
# horizontal boxplots & density plots
geom_boxplot(aes(fill = cut)) +
geom_density(aes(x = carat), inherit.aes = FALSE) +
# vertical lines at Q1 / Q2 / Q3
stat_boxplot(geom = "vline", aes(xintercept = ..xlower..)) +
stat_boxplot(geom = "vline", aes(xintercept = ..xmiddle..)) +
stat_boxplot(geom = "vline", aes(xintercept = ..xupper..)) +
facet_grid(cut ~ .) +
scale_fill_discrete()
Original answer
This can be done easily with a horizontal boxplot geom_boxploth() / stat_boxploth(), found in the ggstance package:
library(ggstance)
ggplot(diamonds, aes(x = carat, y = -0.5)) +
# horizontal box plot
geom_boxploth(aes(fill = cut)) +
# normal density plot
geom_density(aes(x = carat), inherit.aes = FALSE) +
# vertical lines at Q1 / Q2 / Q3
stat_boxploth(geom = "vline", aes(xintercept = ..xlower..)) +
stat_boxploth(geom = "vline", aes(xintercept = ..xmiddle..)) +
stat_boxploth(geom = "vline", aes(xintercept = ..xupper..)) +
facet_grid(cut ~ .) +
# reproduce original chart's color scale (o/w ordered factors will result
# in viridis scale by default, using the current version of ggplot2)
scale_fill_discrete()
If you are limited to the ggplot2 package for one reason or another, it can still be done, but it would be less straightforward, since geom_boxplot() and geom_density() go in different directions.
Alternative 1: calculate the box plot's coordinates, & flip them manually before passing the results to ggplot(). Add a density layer in the normal way:
library(dplyr)
library(tidyr)
p.box <- ggplot(diamonds, aes(x = cut, y = carat)) + geom_boxplot()
p.box.data <- layer_data(p.box) %>%
select(x, ymin, lower, middle, upper, ymax, outliers) %>%
mutate(cut = factor(x, labels = levels(diamonds$cut), ordered = TRUE)) %>%
select(-x)
ggplot(p.box.data) +
# manually plot flipped boxplot
geom_segment(aes(x = ymin, xend = ymax, y = -0.5, yend = -0.5)) +
geom_rect(aes(xmin = lower, xmax = upper, ymin = -0.75, ymax = -0.25, fill = cut),
color = "black") +
geom_point(data = . %>% unnest(outliers),
aes(x = outliers, y = -0.5)) +
# vertical lines at Q1 / Q2 / Q3
geom_vline(data = . %>% select(cut, lower, middle, upper) %>% gather(key, value, -cut),
aes(xintercept = value)) +
# density plot
geom_density(data = diamonds, aes(x = carat)) +
facet_grid(cut ~ .) +
labs(x = "carat") +
scale_fill_discrete()
Alternative 2: calculate the density plot's coordinates, & flip them manually before passing the results to ggplot(). Add a box plot layer in the normal way. Flip the whole chart:
p.density <- ggplot(diamonds, aes(x = carat, group = cut)) + geom_density()
p.density.data <- layer_data(p.density) %>%
select(x, y, group) %>%
mutate(cut = factor(group, labels = levels(diamonds$cut), ordered = TRUE)) %>%
select(-group)
p.density.data <- p.density.data %>%
rbind(p.density.data %>%
group_by(cut) %>%
filter(x == min(x)) %>%
mutate(y = 0) %>%
ungroup())
ggplot(diamonds, aes(x = -0.5, y = carat)) +
# manually flipped density plot
geom_polygon(data = p.density.data, aes(x = y, y = x),
fill = NA, color = "black") +
# box plot
geom_boxplot(aes(fill = cut, group = cut)) +
# vertical lines at Q1 / Q2 / Q3
stat_boxplot(geom = "hline", aes(yintercept = ..lower..)) +
stat_boxplot(geom = "hline", aes(yintercept = ..middle..)) +
stat_boxplot(geom = "hline", aes(yintercept = ..upper..)) +
facet_grid(cut ~ .) +
scale_fill_discrete() +
coord_flip()
Maybe this will help. Although need little upgrade :)
library(tidyverse)
library(magrittr)
library(wrapr)
subplots <-
diamonds$cut %>%
unique() %>%
tibble(Cut = .) %>%
mutate(rn = row_number() - 1) %$%
map2(
.x = Cut,
.y = rn,
~annotation_custom(ggplotGrob(
diamonds %>%
filter(cut == .x) %.>%
ggplot(data = .) +
aes(x = carat, fill = cut) +
annotation_custom(ggplotGrob(
ggplot(data = .) +
geom_boxplot(
aes(x = -1, y = carat),
fill = .y + 1
) +
coord_flip() +
theme_void() +
theme(plot.margin = margin(t = 20))
)) +
geom_line(stat = 'density', size = 1) +
theme_void() +
theme(plot.margin = margin(t = .y * 100 + 10, b = (4 - .y) * 100 + 40))
))
)
ggplot() + subplots

Creating two geom_tile layers on ggmap

I'm trying to represent two variables (crime and rent prices) on a ggmap using geom_tile. Individually, both maps look great.
heat_crime = ggmap(cmap) +
geom_tile(data = LatLonCounts_crime, aes(x = Longitude, y = Latitude, alpha = Frequency), fill="red") +
scale_alpha(range = c(0, 0.8))
heat_price = ggmap(cmap) +
geom_tile(data = mean_price_per_bedroom_per_tile, aes(x = Longitude, y = Latitude, alpha = mean_price), fill="blue") +
scale_alpha(range = c(0, 0.8))
I would like to add both geom_tiles on the same map. I've tried this:
heat_all = ggmap(cmap) +
geom_tile(data = LatLonCounts_crime, aes(x = Longitude, y = Latitude, alpha = Frequency), fill="red") +
scale_alpha(range = c(0, 0.8)) +
geom_tile(data = mean_price_per_bedroom_per_tile, aes(x = Longitude, y = Latitude, alpha = mean_price), fill="blue") +
scale_alpha(range = c(0, 0.8))
But the geom_tile overlay then combines the price and crime variable into one (as becomes clear when you look at the legend).
How can I keep both variables fully separated and show them both on the same ggmap?

r (ggplot2 line graph): changing linetype for errorbars changes them in legend

I have a line graph like this one:
df <- data.frame(x = c(1,1,2,2,1,1,2,2),
y = c(1.5,1.9,2.1,1.6,1.4,1.8,2.0,1.7),
error = c(0.2),
group = c("g1","g2","g1","g2","g3","g4","g3","g4"))
ggplot(df, aes(x = x, y = y, color = group, linetype = group)) +
geom_point() + geom_line() +
geom_errorbar(aes(ymin = y - error, ymax = y + error),
linetype = 1, width = 0.5,
position = position_dodge(width = 0.2)) +
scale_color_manual(values = c("g1"="Black", "g2"="Grey", "g3"="Black", "g4"="Grey")) +
scale_linetype_manual(values=c("g1"=1,"g2"=1,"g3"=2,"g4"=2))
I need to make it black and white, so with several groups, I used both color and linetype. When I change line type, I want to have error bars solid although the lines are dotted, so I overrode the linetype for error bars. For some reason, this also changes the legend, so it is no longer clear which line is which.
I know this somehow depends on the color = group in aes, because when I just had the linetype, the legend was fine. For some reason I just can't find a way to do linetype, color, and solid errorbars at the same time. Anybody know why this is?
Try this:
ggplot(df, aes(x = x, y = y, colour = group, group = group)) +
geom_line(aes(y=y,linetype = group)) +
geom_point()+
geom_errorbar(aes(ymin = y - error, ymax = y + error),
colour = rep(c("black","grey"),4),
width = 0.1,
position = "dodge") +
scale_color_manual(values = c("g1"="Black", "g2"="Grey", "g3"="Black", "g4"="Grey")) +
scale_linetype_manual(values=c("g1"=1,"g2"=1,"g3"=2,"g4"=2))
You don't need the linetype = group inside ggplot, as the error bars will never use this info. You just make it more complex. linetype will be used only by the lines and the legend. error bars need to know the color and the grouping variable, that's why you include them inside ggplot.
Setting the colours by creating a column:
df <- data.frame(x = c(1,1,2,2,1,1,2,2),
y = c(1.5,1.9,2.1,1.6,1.4,1.8,2.0,1.7),
error = c(0.2),
group = c("g1","g2","g1","g2","g3","g4","g3","g4"))
df$group_cols = "black"
df$group_cols[df$group %in% c("g2","g4")] = "grey"
ggplot(df, aes(x = x, y = y, colour = group, group = group)) +
geom_line(aes(y=y,linetype = group)) +
geom_point()+
geom_errorbar(aes(ymin = y - error, ymax = y + error),
colour = df$group_cols,
width = 0.1,
position = "dodge") +
scale_color_manual(values = c("g1"="Black", "g2"="Grey", "g3"="Black", "g4"="Grey")) +
scale_linetype_manual(values=c("g1"=1,"g2"=1,"g3"=2,"g4"=2))

Resources