1

For a dataframe df as follows:

  products price_index quantity_index   price_rank quantity_rank
1         a    95.00000       95.00000   high price   high volume
2         b    80.69012       94.53585   high price   high volume
3         c    74.41048       90.95081   high price   high volume
4         d    73.24853       89.82692 medium price medium volume
5         e    70.50442       79.71225 medium price medium volume
6         f    67.11609       75.43498    low price    low volume
7         g    64.14685       58.26419    low price    low volume
8         h    56.76375       56.16531    low price    low volume
9         i    55.76472       56.02838    low price    low volume
10        j    55.70475       50.24873    low price    low volume

Let's assume that the value Top3 of price_index and quantity_index is a high-value area, Top 5 except Top 3 is a medium-value area, and the rest are low-value areas. How to achieve an expected plot with two layers of axis labels as below? Thanks.

ggplot(df, aes(x = price_index, y = quantity_index, label = products)) +
   coord_fixed() +
   geom_point(colour = 'blue', size = 3, alpha=0.9) +
   scale_x_continuous(expand = c(0, 0), limits = c(50, 100),
                      breaks = seq(50, 100, 5)
   ) +
   scale_y_continuous(expand = c(0, 0), limits = c(50, 100),
                      breaks = seq(50, 100, 5)
   ) +
   geom_vline(xintercept = c(68, 74), color='black',
              linetype='dotted', size=1) +
   geom_hline(yintercept = c(77, 90), color='black',
              linetype='dotted', size=1)

The expected result:

enter image description here

Data

df <- structure(list(products = c("a", "b", "c", "d", "e", "f", "g", 
"h", "i", "j"), price_index = c(95, 80.69011538, 74.41047705, 
73.24853055, 70.5044217, 67.1160916, 64.14685495, 56.76375355, 
55.76472446, 55.70475052), quantity_index = c(95, 94.53585227, 
90.95080999, 89.82692004, 79.71224701, 75.43498354, 58.26419203, 
56.16530529, 56.02838119, 50.24873055), price_rank = c("high price", 
"high price", "high price", "medium price", "medium price", "low price", 
"low price", "low price", "low price", "low price"), quantity_rank = c("high volume", 
"high volume", "high volume", "medium volume", "medium volume", 
"low volume", "low volume", "low volume", "low volume", "low volume"
)), class = "data.frame", row.names = c(NA, -10L))

References:

Add secondary X axis labels to ggplot with one X axis

Multi-row x-axis labels in ggplot line chart

ah bon
  • 9,293
  • 12
  • 65
  • 148

1 Answers1

1

Easiest would be to use custom annotation. You can semi-automatically define label positions and then add one or two geom_text layers. I am using two because of the different font orientation, but if you don't want that you can of course make only one data frame and use a single geom_text layer. More relevant comments in the code.

## the following is a semi-automated approach to define the label positions
xlim = c(50,100)
ylim = c(50,100)
xic= c(68, 74)
yic = c(77, 90)
x_lab = sort(c(xlim, xic))
y_lab = sort(c(ylim, yic))
annot_x <- data.frame(x = purrr::map2_dbl(x_lab[1:3], x_lab[2:4], ~(.x + .y)/2), y = 47, labels = paste(c("low", "medium", "high"), "volume"))
annot_y <- data.frame(x = 47, y = purrr::map2_dbl(y_lab[1:3], y_lab[2:4], ~(.x + .y)/2), labels = paste(c("low", "medium", "high"), "price"))
              
ggplot(df, aes(x = price_index, y = quantity_index, label = products)) +
   geom_point(colour = 'blue', size = 3, alpha=0.9) +
## use coord_ instead for your limits!
   scale_x_continuous(expand = c(0, 0), breaks = seq(50, 100, 5)) +
   scale_y_continuous(expand = c(0, 0), breaks = seq(50, 100, 5)) +
   geom_vline(xintercept = xic, color='black',
              linetype='dotted', size=1) +
   geom_hline(yintercept = yic, color='black',
              linetype='dotted', size=1) +
## add geom_text layer
   geom_text(data = annot_y, aes(x, y, label = labels), color = "red", angle = 90) +
   geom_text(data = annot_x, aes(x, y, label = labels), color = "red") +
## use coord_ instead for your limits! And turn off clipping
      coord_fixed(xlim = xlim, ylim = ylim, clip = "off") +
## add a little margin left and bottom
## also give a little margin to both axis titles so they don't overlap with your annotations
   theme(plot.margin = margin(l = 20, b = 20), 
         axis.title.x = element_text(margin = margin(t = 22)),
         axis.title.y = element_text(margin = margin(r = 18)))

enter image description here

tjebo
  • 21,977
  • 7
  • 58
  • 94