tabtimm
tabtimm

Reputation: 431

How to plot columns above and below data points in ggplot2

Using ggplot2, I am plotting percentage values for 15 species across three sites (each species occurs in each site). The data points associated with site 'C' are my reference points.

Now, instead of plotting sites 'A' and 'B' as points, I would like to display them using vertical lines or column-like structures. As such, these data points should be extended as vertical lines to the top or bottom side of the site 'C' points (green colour), i.e. to the top where values are larger than the reference value and bottom for smaller values.

Specifically, I would hope for a red line from a red point to the green point and a blue line from the blue point to the green point. The red line should ideally have the same width as the red point (and same for blue). The line should also be offset as are the red and blue points (relative to the green point), so that lines do not overlap. Finally, the line should not go to the center but the edge of a point.

For this purpose I have offset points for 'A' and 'B' and also reduced their size to half of the reference point size.

library(ggplot2) 
MyData$species <- as.character(MyData$species)
MyData$species <- factor(MyData$species, levels=unique(MyData$species))

pos <- position_dodge(width=0.21)
cols <- c("C" = "darkgreen", "B" = "blue", "A" = "red")

tiff(file = "MyData.tiff", height=10, width=10, units="in", res=300, compression="lzw")
ggplot(data = MyData, aes(x=species, y=value, group=site, colour=site)) +
  geom_point(data=subset(MyData, site=="C"), size = 4, shape=15, alpha=1, position=pos) + 

  geom_line(data=subset(MyData, site=="C"), size = 2, lwd=2, alpha=0.4, show_guide=FALSE) +

  geom_point(data=subset(MyData, site!="C"), size = 1.8, shape=15, alpha=1, position = pos) + 
  scale_colour_manual(values = cols) +
  xlab("Species") +
  ylab("Value (%)") + 
  scale_y_continuous(expand=c(0.01,0.01),
                     limits=c(0.0,100),   
                     breaks=c(0,20,40,60,80,100),
                     labels=c("0","20","40","60","80","100")) + 
  theme_bw() +
  theme(legend.position="none") +
  theme(axis.title.x = element_text(vjust=0.1,face="bold", size=16),
        axis.text.x = element_text(vjust=0.4, size=14, angle=90, hjust=1.0)) +
  theme(axis.title.y = element_text(vjust=0.1,face="bold", size=16),
        axis.text.y = element_text(face="bold", size=14, angle=0)) +
  theme(panel.grid.minor=element_blank(), panel.grid.major=element_blank()) +
  theme(panel.border = element_rect(size=1, color = "black")) +
  theme(plot.margin = unit(c(0.3,0.4,0.3,0.3),"lines"))
dev.off()

This is my current plot. So basically, I would like to replace the red and blue points with lines that extend to the green points (without overlapping them).

enter image description here

Many thanks in advance for any advice on an elegant solution for this.

This is a dput() of my dataset.

structure(list(site = structure(c(3L, 3L, 3L, 3L, 3L, 3L, 3L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L), .Label = c("A", "B", "C"), class = "factor"), 
    species = structure(c(13L, 11L, 2L, 14L, 1L, 9L, 12L, 10L, 
    6L, 8L, 15L, 7L, 3L, 4L, 5L, 13L, 11L, 2L, 14L, 1L, 9L, 12L, 
    10L, 6L, 8L, 15L, 7L, 3L, 4L, 5L, 13L, 11L, 2L, 14L, 1L, 
    9L, 12L, 10L, 6L, 8L, 15L, 7L, 3L, 4L, 5L), .Label = c("Species 1", 
    "Species 10", "Species 11", "Species 12", "Species 13", "Species 14", 
    "Species 15", "Species 2", "Species 3", "Species 4", "Species 5", 
    "Species 6", "Species 7", "Species 8", "Species 9"), class = "factor"), 
    value = c(2, 3.25, 3.53, 4.31, 4.59, 5.26, 6.02, 6.42, 6.6, 
    7.26, 8.89, 12.45, 35.62, 72.42, 73.55, 1.36, 2.36, 2.17, 
    10.34, 6.84, 1.88, 5.09, 7.35, 3.87, 10.55, 6.6, 14.64, 39.57, 
    88.06, 64.54, 5.03, 12.34, 5.42, 3.63, 5.16, 6.04, 3, 8.94, 
    3.28, 7.64, 6.25, 21.96, 39.35, 78.55, 47.35)), .Names = c("site", 
"species", "value"), class = "data.frame", row.names = c(NA, 
-45L))

Upvotes: 1

Views: 484

Answers (2)

Z.Lin
Z.Lin

Reputation: 29095

You can try geom_linerange() for the lines from points A/B to point C.

Define the ymin/ymax values for each site/species, & reorder site such that A / B lines drop down to each side of point C:

library(dplyr)

MyData <- MyData %>%
  group_by(species) %>%
  mutate(value.C = value[site == "C"]) %>%
  rowwise() %>%
  mutate(value.min = min(value, value.C),
         value.max = max(value, value.C)) %>%
  ungroup() %>%
  mutate(site = factor(site, levels = c("A", "C", "B")))

Plot:

# vary dodge width such that the lines drop to the edge of point C
# for your chosen dimensions (for mine 0.5 was about right)
pos <- position_dodge(width = 0.5) 

ggplot(data = MyData,
       aes(x = species, y = value, 
           ymin = value.min, ymax = value.max, 
           group = site, colour = site, size = site)) +

  geom_linerange(size = 1.8, alpha = 0.4, position = pos) +

  geom_line(data = subset(MyData, site == "C"),
            size = 2, lwd = 2, alpha = 0.4) +
  geom_point(data = subset(MyData, site == "C"),
             size = 4, shape = 15, position = pos) +
  scale_color_manual(values = cols) +
  theme_classic() +
  theme(legend.position = "none")
  # + other theme-related settings...

plot

Upvotes: 2

Hardik Gupta
Hardik Gupta

Reputation: 4790

You can add geom_line to draw the vertical lines

library(ggplot2) 
MyData$species <- as.character(MyData$species)
MyData$species <- factor(MyData$species, levels=unique(MyData$species))

pos <- position_dodge(width=0.21)
cols <- c("C" = "darkgreen", "B" = "blue", "A" = "red")

windows()
ggplot(data = MyData, aes(x=species, y=value, group=site, colour=site)) +
  geom_point(data=subset(MyData, site=="C"), size = 4, shape=15, alpha=1, position=pos) + 
  geom_line(data=subset(MyData, site=="C"), size = 2, lwd=2, alpha=0.4, show_guide=FALSE) +
  geom_point(data=subset(MyData, site!="C"), size = 1.8, shape=15, alpha=1, position = pos) + 
  geom_line(aes(group = species)) + #New code Added
  scale_colour_manual(values = cols) +
  xlab("Species") +
  ylab("Value (%)") + 
  scale_y_continuous(expand=c(0.01,0.01),
                     limits=c(0.0,100),   
                     breaks=c(0,20,40,60,80,100),
                     labels=c("0","20","40","60","80","100")) + 
  theme_bw() +
  theme(legend.position="none") +
  theme(axis.title.x = element_text(vjust=0.1,face="bold", size=16),
        axis.text.x = element_text(vjust=0.4, size=14, angle=90, hjust=1.0)) +
  theme(axis.title.y = element_text(vjust=0.1,face="bold", size=16),
        axis.text.y = element_text(face="bold", size=14, angle=0)) +
  theme(panel.grid.minor=element_blank(), panel.grid.major=element_blank()) +
  theme(panel.border = element_rect(size=1, color = "black")) +
  theme(plot.margin = unit(c(0.3,0.4,0.3,0.3),"lines"))

Upvotes: 1

Related Questions