--- title: "Why not geom_text() or geom_label()?" vignette: > %\VignetteIndexEntry{Why not geom_text() or geom_label()?} %\VignetteEngine{quarto::html} %\VignetteEncoding{UTF-8} knitr: opts_chunk: collapse: true comment: "#>" fig-width: 7 fig-height: 4 --- A campus map numbers its buildings and lists them in an index next to the map, so a reader can find the library or the sports centre at a glance. This Stack Overflow question [Adding index to points in ggplot](https://stackoverflow.com/questions/48869642/adding-index-to-points-in-ggplot) shows such a map and asks how to draw the same kind of index in ggplot2, noting that there is no built-in way. The accepted answer is a workaround. The same problem comes up in other questions, all quite old: [numbered point labels plus a legend in a scatterplot](https://stackoverflow.com/questions/24801987/numbered-point-labels-plus-a-legend-in-a-scatterplot) and [ggplot2: numbered points on plot and in legend](https://stackoverflow.com/questions/57024684/ggplot2-numbered-points-on-plot-and-in-legend). ggindex tries to solve this problem. ggplot2 draws the numbers easily with `geom_text()` or `geom_label()`. The issue is to be able to draw the legend. The examples below use the data from the question: five points, each with a number and a description. ```{r} #| label: setup library(ggplot2) library(ggindex) ``` ```{r} #| label: data df <- data.frame(x = 6:10, y = 6:10, id = 1:5) df$label <- paste0("Label", df$id) ``` ## Numbers without a legend `geom_label()` numbers the points, but nothing explains the numbers. ```{r} #| label: label ggplot(df, aes(x, y)) + geom_point() + geom_label(aes(label = id), nudge_x = 0.2) + theme_minimal() ``` Mapping the description to another aesthetic, such as `fill`, adds a legend. Its keys show the point symbol, not the numbers, so the reader still can't tell which point is which. ```{r} #| label: fill ggplot(df, aes(x, y)) + geom_point(aes(fill = label)) + geom_label(aes(label = id), nudge_x = 0.2) + theme_minimal() ``` ## The workaround: numbers in the legend text The accepted answer pastes the number into the description, maps the result to `fill` on an invisible layer, and hides the legend keys. ```{r} #| label: workaround df$legend <- paste0(df$id, ": ", df$label) ggplot(df, aes(x, y)) + geom_point(aes(fill = legend), alpha = 0) + geom_point() + geom_label(aes(label = id), nudge_x = 0.2) + scale_fill_discrete(name = "Index") + theme_minimal() + theme(legend.key.width = unit(0, "cm")) ``` The result reads right, but the link between numbers and descriptions is held together by hand: - The legend is text only. The markers on the plot and in the legend don't match. - The entries are sorted as text, so with ten or more points "10: ..." comes before "2: ...". Keeping the number order needs a factor with explicit levels. - `fill` is used up by an invisible layer and can't show another variable. - The numbers appear in two places, `id` and `legend`, which must be kept in sync. ## What ggindex adds ggindex adds an `info` aesthetic with its own scale. Its legend keys draw the marker with the `label` of each entry, so the numbers on the plot and in the legend come from the same mapping. ```{r} #| label: focus ggplot(df, aes(x, y, label = id, info = label)) + geom_focus() + scale_info_discrete("Index") + theme_minimal() ``` - Legend entries are sorted by `label`, as numbers. - `colour` and `fill` style the markers and stay free for other variables. - Without `label`, the `info` entries are numbered for you. - `focus` numbers a subset of the points and leaves the rest to other layers. - Maps work the same way with `geom_sf_focus()`. See [Get started](ggindex.html) for these features, [Styling numbered markers](styling.html) for colours, fonts, themes and facets, and [Numbered markers on other plots](other-plots.html) for line charts, bar charts and distributions.