Bulat
Bulat

Reputation: 6969

Plotting change as bar between the values with ggplot

I would like to plot change between values as a single bar between values as Economist does:

enter image description here

This plot is called dumbbell, so question is likely to be a duplicate if you know what to search for.

Here is a code example that makes a bar plot for two categories. It would be great to be able to plot it per example above.

library(ggplot2)
library(ggthemes)
Animals <- read.table(
  header=TRUE, text='Year        Reason Species
1   2018       Genuine      24
2   2019      Genuine      16
3   2019 Misclassified      85
4   2018 Misclassified      41
5   2018     Taxonomic       2
6   2019     Taxonomic       7
7   2018       Unclear      41
8   2019       Unclear     117')

Animals$Year <- as.character(Animals$Year)

ggplot(Animals, aes(factor(Reason), Species, fill = Year)) + 
  geom_bar(stat="identity", position = "dodge") + 
  scale_fill_brewer(palette = "Set1") + 
  theme_economist() + 
  coord_flip()

Ability to colour decrease as pink (for example) would be a bonus.

enter image description here

Upvotes: 3

Views: 655

Answers (2)

Bulat
Bulat

Reputation: 6969

I have found geom function in ggalt package that does something like this:

library(ggalt)
library(data.table)
Animals <- data.table(Animals)
Animals.wide <- dcast(Animals, Reason ~ Year, value.var = "Species")
colnames(Animals.wide) <- c("Reason", "species.2018", "species.2019")
p <- ggplot(
  Animals.wide,
  aes(
    y = Reason,
    x = species.2018,
    xend = species.2019
  )
)

# > Animals.wide
#          Reason species.2018 species.2019
# 1:       Genuine           24           16
# 2: Misclassified           41           85
# 3:     Taxonomic            2            7
# 4:       Unclear           41          117

p <- p + geom_dumbbell(
    size = 10,
    color = "#C1D9E4",
    colour_x = "#39C1D1",
    colour_xend = "#953D4D",
    dot_guide = TRUE, 
    dot_guide_size = 0.25,
    position = position_dodgev(height = 0.4),
    show.legend = TRUE
)
p <- p + scale_x_continuous(position = "top") 
p <- p + theme_economist()
p <- p + xlab("Species")
p <- p + ylab(NA)
p <- p + theme(
  legend.position = "top",
  axis.text.y = element_text(size = 20),
  axis.title.y = element_blank(),
  axis.title.x = element_blank(),
  axis.line.x = element_blank(),
  axis.ticks.x = element_blank(),
  axis.text.x = element_text(size = 20, margin = margin(-0.4, unit = "cm"))
)
p

It is not as flexible as solution from @bdemarest, but almost does the job.

enter image description here

Upvotes: 2

bdemarest
bdemarest

Reputation: 14667

Here is a possible solution where I use a second, auxiliary data.frame along with geom_segment() to draw a connecting line between the two timepoints. I've also included some visual customizations like colors, point type, transparency, and arrows. These can be removed or modified as needed.

# Auxiliary data.frame to draw segments.
adat = reshape2::dcast(data=Animals, 
                       formula=Reason ~ Year, 
                       value.var="Species")
adat
#          Reason 2018 2019
# 1       Genuine   24   16
# 2 Misclassified   41   85
# 3     Taxonomic    2    7
# 4       Unclear   41  117

# Colors selected from 
# https://yutannihilation.github.io/allYourFigureAreBelongToUs/ggthemes/economist_pal/
year_colors = c("2018"="#D7D29E", "2019"="#82C0E9")

p1 = ggplot() +
    theme_economist() +
    geom_point(data=Animals, 
               aes(y=Reason, x=Species, fill=Year),
               size=6, shape=21, color="grey30") +
    geom_segment(data=adat, 
                 aes(y=Reason, yend=Reason, x=`2018`, xend=`2019`),
                 size=1.8, color="grey30",
                 lineend="butt", linejoin="mitre",
                 arrow=arrow(length = unit(0.02, "npc"), type="closed")) +
    scale_fill_manual(values=year_colors)

p2 = ggplot() +
     theme_economist() +
     geom_segment(data=adat, 
                  aes(y=Reason, yend=Reason, x=`2018`, xend=`2019`),
                  size=6, color="#82C0E9", alpha=0.6, lineend="butt") +
     geom_point(data=Animals, 
                aes(y=Reason, x=Species, color=Year),
                size=6) +
     xlab("Species") +
     scale_color_manual(values=c("2018"="#C10534", "2019"="#008BBC"))

ggsave("segment_plot_1.png", plot=p1, width=8, height=2.5, dpi=150)
ggsave("segment_plot_2.png", plot=p2, width=8, height=2.5, dpi=150)

# Basic version.
p3 = ggplot() +
     geom_point(data=Animals, aes(y=Reason, x=Species, color=Year)) +
     geom_segment(data=adat, aes(y=Reason, yend=Reason, x=`2018`, xend=`2019`))

ggsave("segment_plot_3.png", plot=p3, width=8, height=2.5, dpi=150)

enter image description here

enter image description here

enter image description here

Upvotes: 2

Related Questions