0

I am working with data that I am turning into a tornado diagram that in the end I want to look something like this:

sampleimage

Currently my data looks like this:

sampledata

Using facet_wrap I get a result like this but want it all on one graph to look like the sample at the start:

ggplot(data = df, aes(x = Person)) + 
  geom_crossbar(aes(y=Mean, ymin= Min, ymax = Max)) + 
  coord_flip() + 
  facet_wrap(~Letter, strip.position = "left", scales = "free_x") +
  theme(panel.spacing = unit(0, "lines"), strip.background = element_blank(), strip.placement = "outside")

failedattempt

Is there any way to do this in ggplot? Sorry I had to include the images as links!

stefan
  • 90,330
  • 6
  • 25
  • 51
  • Please provide a [minimal dataset](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example/5963610#5963610) that runs. – Alexlok Aug 27 '20 at 21:04

1 Answers1

2

How about this? See the code and result below with some explanations after.

# reorder levels in order of appearance
df$Person <- factor(df$Person, levels=unique(df$Person))

ggplot(df, aes(x=Letter)) +
  geom_crossbar(
    aes(y=Mean, ymin=Min, ymax=Max),
    fill='dodgerblue2', width=0.8
  ) +
  facet_grid(Person~., scales='free', space='free', switch = 'y') +
  scale_y_continuous(expand=expansion(mult=c(0.3,0.3))) +
  labs(x=NULL, y='Axis Label Here') +
  theme_classic() +
  theme(
    strip.placement = 'outside',
    strip.background = element_rect(color=NA, fill=NA),
    strip.text.y=element_text(angle=90, size=12),
    panel.spacing = unit(0,'pt'),
    panel.background = element_rect(color='black')
  ) +
  coord_flip()

enter image description here

Now, for some explanation, where I'll step through the code from top to bottom, calling out specific changes/adjustments as they come up.

  • Re-leveling Person Factor. The purpose here is to ensure that the order we see the listing of "Persons" matches the order in which they are listed in the dataset. You can list them in any order, of course, but the default for characters/strings is: If it is a factor, then the order = the ordering of the levels. If it is not a factor, the order = alphabetical.

  • Overall Adjustment for facets. Given that each df$Person has one or more df$Letters associated, and given your example plot, it seems that you actually want to have facets be df$Person, with each having an x aesthetic for df$Letter.

  • Facet_grid. I use facet_grid() instead of facet_wrap(), since it offers more control. If you use the . ~ facet or facet ~ . notation, it acts just like facet_wrap(), except it will not "wrap" around. There are three critical arguments that are only all available via facet_grid():

    • scales=. This will remove the extra space in each facet that is not used. Since not every df$Person has the same amount of df$Letter associated, this is very important.

    • space=. By default, the space occupied by each facet is kept constant. This means that if one name has 3 letters and another facet has only 1, the width of the bars in each facet will be smaller in the one with 3 vs the one bar in the facet with only 2. Setting space="free" allows for all widths to be constant: it's the facet size that is "free" to adjust to the bar - not the other way around.

    • switch=. This allows for the strip placement (facet label) to be on the opposite side. It doesn't place it outside though...

  • Expanding the y scale. This is purely aesthetic. I'm trying to match what you show, which has extra space around the bars.

  • Theme Elements. There's a decent amount going on here, but basically I'm putting the facet label outside (strip.placement) and removing the box that goes around it usually (strip.background). I also smoosh the facets together (panel.spacing) and decided it was easier to view when you drew some lines between the facets (panel.background).

Some other things would be purely aesthetic, but I think this gets you close to your desired result. If you want to include the information / text... that's a different matter.

chemdork123
  • 12,369
  • 2
  • 16
  • 32
  • This works so well thanks! I'm wondering if you would know within this how to evenly space out the (now y) axis labels because the labels I want to put are really long and I'm trying to wrap my text but it doesn't expand the spacing. It tried scale_x_discrete expansion but it only expands the distance between the "groups" and not uniformly across – Mary Regan Aug 28 '20 at 14:44
  • The best way to do that is likely by saving your plot with a different aspect ratio and size. Graphics in R are kind of funny - text does not scale the same as the plot stuff. This means that you have drastically different results if you, for example save with `ggsave("myfile.png", width=5, height=8)` and `ggsave("myfile.png", width=8, height=10)` - or something like that. You can see this in action if you take your plot window and resize - the plot will look different. My guess is that saving with a larger `height=` will get the plot and labels to work. – chemdork123 Aug 29 '20 at 13:29
  • Oh, and for wrapping text on the axis, my favorite method is listed [here](https://stackoverflow.com/a/45493489/9664796) using `wrap_format()` from the `scales` package. – chemdork123 Aug 29 '20 at 13:30