0

I have the following dataframe

unit <- c("a", "b", "c", "d")
top1990 <- c(100, 80, 70, 90)
base1990 <- c(40, 60, 20, 30)
top2000 <- c(120, 85, 65, 80)
base2000 <- c(40, 65, 25, 15)
df <- data.frame(unit, top1990, base1990, top2000, base2000)

unit top1990 base1990 top2000 base2000
a     100       40     120       40
b      80       60      85       65
c      70       20      65       25
d      90       30      80       15

I need to plot on the same graph, for each unit, two bars, one for the year 1990 and the other one for the year 2000. Each bar should start from the value "base***" and should end at the value "top****". I also need to order units by "top1990" (decreasing order).

I used the function geom_segment of the ggplot2 library, as follows

df$unit <- factor(df$unit, levels = df$unit[order(df$top1990, decreasing = T)])    

ggplot(data = df) + geom_segment(aes(x=df$unit, xend=df$unit, y=df$top1990, yend=df$base1990), size = 7, color = "blue") +
geom_segment(aes(x=df$unit, xend=df$unit, y=df$top2000, yend=df$base2000), size = 7, color = "red")

However, I obtained this, in which bars are overlapping, wheareas I would need them side-by-side.

enter image description here

What I am missing?

d.b
  • 32,245
  • 6
  • 36
  • 77
M.S.
  • 37
  • 1
  • 5
  • 1
    I'm no quite sure exactly how you want the plot to look, but I think you need to convert to long format from wide and use `geom_bar` to produce a `bar plot`. You don't need to call your data with `df$`, this is supplied in the `ggplot` call at the start, so all you need to do is for e.g. `aes( x=unit,..)` – bob1 Mar 25 '19 at 20:33
  • For each unit, I would like to plot the two bars (1990 and 2000) side by side. According to what I researched on internet, with geom_bar, all bars start from the same value (and this is not my case,e.g. I need to specify that the 1990 bar for the "a" unit starts from 40, while the 1990 bar for the "b" unit from 60 and so on). – M.S. Mar 25 '19 at 20:50
  • So you want the red and blue bars side by side, or a,b,c,d side by side? – bob1 Mar 25 '19 at 21:33
  • 3
    Don't use `$` inside `aes`! You'' want to reshape your data, so you no longer have the year variable in the column headers, then have a single `geom_segment` and use the `position` argument. – Axeman Mar 25 '19 at 22:13

1 Answers1

4

I think it is necessary to transform your data first. I suggest {cdata} for your transformation needs. I think what you want is geom_linerange() with a thicker size, such that they look like boxes.

unit <- c("a", "b", "c", "d")
top1990 <- c(100, 80, 70, 90)
base1990 <- c(40, 60, 20, 30)
top2000 <- c(120, 85, 65, 80)
base2000 <- c(40, 65, 25, 15)

df <- data.frame(unit, top1990, base1990, top2000, base2000)

library(cdata)
library(ggplot2)

control_table <- 
  qchar_frame(
    year, base      , top       |
    1990, "base1990", "top1990" |
    2000, "base2000", "top2000" 
  )

df_2 <- 
  rowrecs_to_blocks(
    df, controlTable = control_table,
    controlTableKeys = "year",
    columnsToCopy = "unit"
  )

df_2 %>% 
  ggplot(aes(unit, ymin = base, ymax = top, color = year)) +
  geom_linerange(position = position_dodge(width = 1), size = 20)

With {cdata}, I transformed your data into this form

  unit year base top
1    a 1990   40 100
2    a 2000   40 120
3    b 1990   60  80
4    b 2000   65  85
5    c 1990   20  70
6    c 2000   25  65
7    d 1990   30  90
8    d 2000   15  80

We can now use base as ymin and top as ymax as aesthetics to geom_linerange().

Here is the output graph (I exaggerated on the line size a bit and dodge width can be smaller to make the lines closer to each other).

Base to Top with a Thick LineRange

I hope this helps.

bdemarest
  • 14,397
  • 3
  • 53
  • 56
Recle Vibal
  • 109
  • 3