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()

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.