R - Ggplot Dodging Geom_Lines

R - ggplot dodging geom_lines

You have actually two issues here:

  1. If the two lines are plotted using two layers of geom_line() (because you have two data frames), then each line "does not know" about the other. Therefore, they can not dodge each other.

  2. position_dodge() is used to dodge in horizontal direction. The standard example is a bar plot, where you place various bars next to each other (instead of on top of each other). However, you want to dodge in vertical direction.

Issue 1 is solved by combining the data frames into one as follows:

library(dplyr)
df_all <- bind_rows(Group1 = df1, Group2 = df2, .id = "group")
df_all
## Source: local data frame [4 x 4]
##
## group id var id_num
## (chr) (fctr) (dbl) (dbl)
## 1 Group1 A 1 1.0
## 2 Group1 A 10 1.0
## 3 Group2 A 1 0.9
## 4 Group2 A 15 0.9

Note how setting .id = "Group" lets bind_rows() create a column group with the labels taken from the names that were used together with df1 and df2.

You can then plot both lines with a single geom_line():

library(ggplot2)
ggplot(data = df_all, aes(x=var, y=id, colour = group)) +
geom_line(position = position_dodge(width = 0.5)) +
scale_color_manual("",values=c("salmon","skyblue2"))

Sample Image

I also used position_dodge() to show you issue 2 explicitly. If you look closely, you can see the red line stick out a little on the left side. This is the consequence of the two lines dodging each other (not very successfully) in vertical direction.

You can solve issue 2 by exchanging x and y coordinates. In that situation, dodging horizontally is the right thing to do:

ggplot(data = df_all, aes(y=var, x=id, colour = group))  +
geom_line(position = position_dodge(width = 0.5)) +
scale_color_manual("",values=c("salmon","skyblue2"))

Sample Image

The last step is then to use coord_flip() to get the desired plot:

ggplot(data = df_all, aes(y=var, x=id, colour = group))  +
geom_line(position = position_dodge(width = 0.5)) +
scale_color_manual("",values=c("salmon","skyblue2")) +
coord_flip()

Sample Image

R ggplot2 geom_line position dodge

I don't think you can dodge vertically, but another option is just to add a little dodging yourself. For example:

eps = 0.05

ggplot(sa, aes(x=value, y=ifelse(type=="typ1", id + eps, id - eps),
group=grp, color=type)) +
geom_line(size=6) +
scale_y_continuous(breaks=1:2, limits=c(min(sa$id-eps), max(sa$id+eps)))

You'll have to play around with the amount of dodging, axis limits, etc., to get the look you want for a given aspect ratio.

Sample Image

using position_dodge with geom_line

Just move group = Behaviour to ggplot(..., aes(..., group = Behaviour)).

ggplot(rep, aes(x = stage, y = Repeatability, shape = Behaviour, colour=Age, group = Behaviour)) +
geom_point(
position = position_dodge(width = 0.3),
size = 3) +
geom_line(
position = position_dodge(width = 0.3),
data = function(x) inner_join(x, my_colors %>% filter(color == 'black')))+
scale_colour_manual(
values = c("orange", "black", "red", "black", "black", "pink", "black"),
name = "Study",
breaks=c("A","A1","A2", "CY", "PE","PW", "PW2"))+
geom_errorbar(
aes(ymin=LCI, ymax=UCI),
position=pd,
width=0.1,
size=0.5)

Plot


Ok, here is another option. The idea is to pre-compute the dodged positions using jitter. This will turn the categorial variable stage into a continuous variable stage.jitter that requires manually specifying x-axis labels through scale_x_continuous.

rep %>%
mutate(stage.jitter = jitter(as.numeric(stage), 0.5)) %>%
ggplot(aes(x = stage.jitter, y = Repeatability, shape = Behaviour, colour=Age, group = Behaviour)) +
geom_point(size = 3) +
geom_line(
data = function(x) inner_join(x, my_colors %>% filter(color == 'black')))+
scale_colour_manual(
values = c("orange", "black", "red", "black", "black", "pink", "black"),
name = "Study",
breaks=c("A","A1","A2", "CY", "PE","PW", "PW2")) +
scale_x_continuous(
"stage",
labels = function(x) rep %>% pull(stage) %>% levels() %>% .[x]) +
geom_errorbar(
aes(ymin = LCI, ymax = UCI),
width = 0.1,
size = 0.5)

Second plot

You may have to play around with the amount of jitter by changing the factor value inside jitter.

How to combine position_dodge() and geom_line() with overlapping groupings?

One option to achieve your desired result would be to convert your x axis variable to a numeric and adjust the positions manually instead of making use of position_dodge:

library(tidyverse)

tbl = tibble(name = rep(paste("name", 1:12), each = 2),
grouping1 = rep(c("A", "B"), 12),
grouping2 = c(rep("X", 8), rep("Y",8), rep("Z",8)),
value = 1:24)

width <- .125

tbl1 <- tbl %>%
mutate(grouping2 = factor(grouping2),
x = as.numeric(grouping2) + width * if_else(grouping1 == "A", -1, 1))

breaks <- unique(as.numeric(tbl1$grouping2))
labels <- unique(tbl1$grouping2)

ggplot(tbl1, aes(x = x, y = value, color = grouping1)) +
geom_line(aes(group = name), color = "grey44") +
geom_point() +
scale_x_continuous(breaks = breaks, labels = labels)

Sample Image

Displaying multiple lines on ggplot with grouped (dodged) bars

Try this

ggplot(line_data,aes(x=month, y=water_val)) +  
geom_col(data=bar_data, aes(fill = water_type), position="dodge") +
geom_line(aes(color=water_type)) +
ggtitle("V1 and V2") +
scale_y_continuous(breaks = seq(0,25,10)) +
xlab("Month") +
ylab("(cm)") +
facet_wrap(~year,nrow=2)

output

R - ggplot2 geom_line dodge

This is much easier to accomplish if you reshape your summary data:

dt <- mtcars %>% 
group_by(am, cyl) %>%
summarise(m = mean(disp)) %>%
spread(am, m)

cyl 0 1
* <dbl> <dbl> <dbl>
1 4 135.8667 93.6125
2 6 204.5500 155.0000
3 8 357.6167 326.0000

While "0" and "1" are poor column names, they can still be used in aes() if you quote them in backticks. The calls to position_dodge() also become unnecessary:

dt %>% ggplot(aes(x = factor(cyl), y = `0`, fill = factor(cyl))) + 
geom_bar(stat = 'identity') +
geom_point(aes(x = factor(cyl), y = `1`), colour = 'black') +
geom_segment(aes(x = factor(cyl), xend = factor(cyl), y = `0`, yend = `1`))

Sample Image

Cannot change width of position_dodge using geom_line

As I already said in my comment to achieve your final goal is IMHO not possible using position="dodge", i.e. you dodge all error bars by the same amount or none. Instead I would suggest to do the dodging manually via the x positions. To this end first determine the dates where the error bars overlap. For these dates you could then add or subtract the amount by which you want the errors to be dodged. In my code below I dodge by one day:

library(ggplot2)
library(dplyr)
library(tidyr)

dodge_width <- 1 # days
yield <- yield |>
mutate(lo = Yield - se, hi = Yield + se) |>
select(-Yield, -se) |>
pivot_wider(names_from = Treatment, values_from = c(lo, hi)) |>
mutate(is_dodge = (lo_Ash >= lo_Control & lo_Ash <= hi_Control) |
(hi_Ash >= lo_Control & hi_Ash <= hi_Control) |
(lo_Control >= lo_Ash & lo_Control <= hi_Ash) |
(hi_Control >= lo_Ash & hi_Control <= hi_Ash)) |>
select(Year, Date, is_dodge) |>
right_join(yield) |>
mutate(dodge = Date + case_when(
is_dodge & Treatment == "Ash" ~ -lubridate::days(dodge_width),
is_dodge & !Treatment == "Ash" ~ lubridate::days(dodge_width),
TRUE ~ lubridate::days(0)
))
#> Joining, by = c("Year", "Date")

ggplot(yield, aes(x = Date, y = Yield, color = Treatment, group = Treatment)) +
geom_line(size = 1) +
geom_point(size = 2) +
geom_errorbar(aes(x = dodge, ymin = Yield - se, ymax = Yield + se), color = "black") +
theme(
panel.background = element_blank(),
panel.border = element_rect(colour = "black", fill = NA)
) +
theme(text = element_text(size = 13)) +
facet_grid(. ~ Year, scales = "free_x") +
labs(y = "Average Yield (L)")

Sample Image



Related Topics



Leave a reply



Submit