4

I want to make a "timeline" using a stacked column chart. What I want to do is to remove all background so I just have a bar and nothing else. I am stuck with the white background of the chart.

Here is a minimal example:

mytheme <-  theme(
        axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank(),
        axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        axis.ticks.y=element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        #panel.background = element_blank()
)

Name <- c("One", "Two", "Three", "Four")
value <- c(2, 5, 7, 8)

df <-  data.frame(Name, value)

ggplot(df)+
  aes(x= "Name", y = value,  fill = Name) +
  geom_col(width = .1, show.legend = FALSE) + 
  xlab("") + ylab("") +
  coord_flip() +
  mytheme

giving me a bar but on a big background shown here.

(I have commented out the panel.background just to show the extent of the background. I want to "crop" that white background so I have only the bar or just a little space around the bar. Help pls.

Update

After the suggestion by @Gregor, I modified the code ggplot as follows:

ggplot(df)+
  aes(x= "Name", y = value,  fill = Name) +
  geom_col(width = .1, show.legend = FALSE) + 
  xlab("") + ylab("") +
  coord_flip(expand = FALSE) +
  mytheme

The result is a very wide bar (tall on page because I have used coord_flip):

enter image description here

All I need to do now is to reduce the height (as it is seen in the picture) to make it more like my original bar. How can I change that?

Solution

I finally found a simple solution. As I am using R Markdown, I used:

```{r, ..., fig.width=4, fig.height=.3}

in the preamble to the embedded figure. Not the best solution, but it works. Thanks to all.

Marcus Campbell
  • 2,746
  • 4
  • 22
  • 36
Kaveh1000
  • 153
  • 1
  • 11
  • 5
    Couple ideas (but not a full answer): (a) Set `aes(x = 1)` instead of `x = "Name"`, a numeric x will fill up the space and automatically resize as you adjust. (b) Use `expand = FALSE` inside your `coord` call. (c) Use `coord_fixed` instead of `coord_flip` - this will mean you need to use, say, `geom_tile` instead of `geom_col` for a horizontal result. – Gregor Thomas Sep 11 '18 at 19:27
  • Thanks @Gregor. Good hints. `expand = FALSE` is interesting. I am trying hard to understand the basics of ggplot2, so not on solid ground. Looking at `geom_tile` too. – Kaveh1000 Sep 11 '18 at 20:19
  • I also have some ideas but really not good answer. If you use other device like png, pdf or whatever else you can try with `plot.margin=unit(c(-100,-2,-100,-2), "cm")` in theme and then scale it down in output device like `ggsave("plot.png", width = 5, height = 1)` – Darek Bienkowski Sep 11 '18 at 20:21
  • @Gregor I think your 'ideas' perfectly solve the question. Very interesting ! Thanks – tjebo Sep 11 '18 at 22:06
  • I have used `coord_flip(expand = FALSE)` and `aes(x = 1)`. Now I have a "fat" bar with no surround. So in above diagram the height has increase. I just need to know how to reduce that height. Thanks. – Kaveh1000 Sep 12 '18 at 07:27
  • @Kaveh1000 Are you able to resize your graphics device? (e.g. change the height / width in `png()`, `jpeg()`, etc. if you are saving the result to an image file) – Z.Lin Sep 12 '18 at 09:53
  • Hi @Z.Lin. That is an option I have not looked at yet. But ideally I want to have a bar with no surround. This is so I can put one after another in `R Markdown`, and have no gaps. – Kaveh1000 Sep 12 '18 at 20:40

1 Answers1

4

This is a very interesting question. Here's one way to approach this: instead of using a vertically stacked bar plot and then flipping it horizontally, we can simply create a "regular" bar plot where the bars are placed adjacent to one another and have equal heights, but each bar has a variable width which is equal to the duration of your desired time intervals. We can "crop" the background using coord_fixed(expand = FALSE).

It is probably best if we directly specify the x-axis positions to ggplot(), so that we can use them to place our labels (more detail about that later) - here's how to position the intervals for your timeline:

Let Xi denote the distance (along the x-axis) from the origin to the center of each time interval i. For each interval i, Xi is the sum of the lengths of the previous time intervals X0 : Xi - 1 , plus half the length of Xi. We can have R automatically generate these values for us by adding a column that uses a lagged cumulative sum, as you will see below.

I have left panel.background = element_blank() commented out, so that you can confirm that we do indeed obtain "just the timeline", with no background.

Code:

library("tidyverse")

mytheme <-  theme(
  axis.title.x=element_blank(),
  axis.text.x=element_blank(),
  axis.ticks.x=element_blank(),
  axis.title.y=element_blank(),
  axis.text.y=element_blank(),
  axis.ticks.y=element_blank(),
  panel.grid.major = element_blank(),
  panel.grid.minor = element_blank(),
  #panel.background = element_blank()
)

Name <- c("One", "Two", "Three", "Four")
Value <- c(2, 5, 7, 8)

df <-  data.frame(Name, Value) %>%
  mutate(Name = factor(Name, levels = Name),
         xPos = cumsum(lag(Value, default = 0)) + Value/2) # calculate interval position

ggplot(data = df, aes(x = xPos, y = 1, width = Value, fill = Name)) +
  geom_col(position = "identity",
           show.legend = FALSE) +
  geom_text(aes(label = Name),
            position = position_fill(vjust = 0.5)) +
  coord_fixed(expand = FALSE) + # crop background around geom_col()
  mytheme

Output:

Timeline Plot

Why It Works:

As of ggplot2 version 3.1.0, we are initially caught between a rock and hard place: if we choose to use coord_flip() we can get a horizontal timeline, but we cannot (easily) resize our plotting area to have the dimensions we want. If we instead use coord_fixed(expand = FALSE), we have the desired plotting margins, but we cannot easily make a horizontal "stack" of bars, as coord_fixed() is incompatible with coord_flip().

There is an interesting argument called position_dodge2() that you can use with geom_col() to place bars of variable widths adjacent to one another. If you set padding = 0, you can obtain a plot which appears very similar to what you see above, but the actual "x-coordinates" of each interval will begin at the left edge of each bar, as opposed to the center.

This makes centering the text labels very difficult, as we would need to adjust their positioning depending on how wide each interval is, but ggplot2 options like position_nudge() only allow us to adjust all of the labels by some constant amount. Consequently, we specify the center of each time interval directly. Hopefully, as ggplot2 continues to improve, we will see options like position_nudge() gain additional functionality to deal with this problem more easily!

Marcus Campbell
  • 2,746
  • 4
  • 22
  • 36