236

The data I'm playing with comes from the internet source listed below

nba <- read.csv("http://datasets.flowingdata.com/ppg2008.csv", sep=",")

What I want to do, is create a 2D points graph comparing two metrics from this table, with each player representing a dot on the graph. I have the following code:

nbaplot <- ggplot(nba, aes(x= MIN, y= PTS, colour="green", label=Name)) + 
                  geom_point() 

This gives me the following:

NBA Plot

What I want is a label of player's name right next to the dots. I thought the label function in ggplot's aesthetics would do this for me, but it didn't.

I also tried text() function and the textxy() function from library(calibrate), neither of which appears to work with ggplot.

How can I add name labels to these points?

Tung
  • 26,371
  • 7
  • 91
  • 115
Green Demon
  • 4,078
  • 6
  • 24
  • 32

3 Answers3

361

Use geom_text , with aes label. You can play with hjust, vjust to adjust text position.

ggplot(nba, aes(x= MIN, y= PTS, colour="green", label=Name))+
  geom_point() +geom_text(hjust=0, vjust=0)

enter image description here

EDIT: Label only values above a certain threshold:

  ggplot(nba, aes(x= MIN, y= PTS, colour="green", label=Name))+
  geom_point() +
  geom_text(aes(label=ifelse(PTS>24,as.character(Name),'')),hjust=0,vjust=0)

chart with conditional labels

agstudy
  • 119,832
  • 17
  • 199
  • 261
  • 5
    Is there any way to shift the labels around (dodge them ever so slightly), so that they do not overlap? – Thomas Browne May 17 '14 at 16:16
  • 2
    I don't think there is any easy solution within `ggplot2`. Maybe [this](http://stackoverflow.com/questions/7611169/intelligent-point-label-placement-in-r) can help you. – agstudy May 18 '14 at 08:42
  • 1
    Is there any way to only label points above a certain value, for example PTS greater than 24 on the above plot? – ONeillMB1 Mar 23 '15 at 20:41
  • short of properly 'dodging', consider this ``hjust = -0.1`` to get the printed labels ever so slightly away from the data point. – PatrickT Mar 03 '16 at 11:28
  • 3
    To shift labels around, consider [ggrepel](https://cran.r-project.org/web/packages/ggrepel/index.html). – Homer White Aug 11 '17 at 23:21
  • If we want to add another ifelse lets say "PTS =10 " how we should edit the `geom_text(aes(label=ifelse(PTS>24,as.character(Name),''))`??? I tried wit **&&** and with **&** but I doesn't work at all. – KGee Jan 30 '20 at 11:02
  • For the first example, `label` doesn't need to be specified twice in `aes` – qwr Nov 23 '21 at 04:45
155

The ggrepel package works great for repelling overlapping text labels away from each other. You can use either geom_label_repel() (draws rectangles around the text) or geom_text_repel() functions.

library(ggplot2)
library(ggrepel)

nba <- read.csv("http://datasets.flowingdata.com/ppg2008.csv", sep = ",")

nbaplot <- ggplot(nba, aes(x= MIN, y = PTS)) + 
  geom_point(color = "blue", size = 3)

### geom_label_repel
nbaplot + 
  geom_label_repel(aes(label = Name),
                  box.padding   = 0.35, 
                  point.padding = 0.5,
                  segment.color = 'grey50') +
  theme_classic()

enter image description here

### geom_text_repel
# only label players with PTS > 25 or < 18
# align text vertically with nudge_y and allow the labels to 
# move horizontally with direction = "x"
ggplot(nba, aes(x= MIN, y = PTS, label = Name)) + 
  geom_point(color = dplyr::case_when(nba$PTS > 25 ~ "#1b9e77", 
                                      nba$PTS < 18 ~ "#d95f02",
                                      TRUE ~ "#7570b3"), 
             size = 3, alpha = 0.8) +
  geom_text_repel(data          = subset(nba, PTS > 25),
                  nudge_y       = 32 - subset(nba, PTS > 25)$PTS,
                  size          = 4,
                  box.padding   = 1.5,
                  point.padding = 0.5,
                  force         = 100,
                  segment.size  = 0.2,
                  segment.color = "grey50",
                  direction     = "x") +
  geom_label_repel(data         = subset(nba, PTS < 18),
                  nudge_y       = 16 - subset(nba, PTS < 18)$PTS,
                  size          = 4,
                  box.padding   = 0.5,
                  point.padding = 0.5,
                  force         = 100,
                  segment.size  = 0.2,
                  segment.color = "grey50",
                  direction     = "x") +
  scale_x_continuous(expand = expand_scale(mult = c(0.2, .2))) +
  scale_y_continuous(expand = expand_scale(mult = c(0.1, .1))) +
  theme_classic(base_size = 16)

Edit: To use ggrepel with lines, see this and this.

Created on 2019-05-01 by the reprex package (v0.2.0).

Tung
  • 26,371
  • 7
  • 91
  • 115
  • 1
    Neat! I really like the first plot. I tried this with my data and I am happy with the result except that the legend shows "a"s instead of the shapes shown in the plot. (I use an aesthetic shape to distinguish points according to a factor) – bee guy May 31 '18 at 06:52
  • 2
    I solved this issue now by (a) extracting the legend of a plot without the geom_label_repel https://stackoverflow.com/questions/12041042/how-to-plot-just-the-legends-in-ggplot2?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa and (b) adding it then with gridExtra::grid.arrange to a plot with the labels. If you know a simpler solution, I would still appreciate that! – bee guy May 31 '18 at 07:12
  • 1
    @beeguy: not sure I get what you're asking but I recently saw a commit on `ggplot2` dev that mentioned similar thing https://github.com/tidyverse/ggplot2/commit/1d537ce78bd58772d64a554a9b105159470799c6. You can try to install both dev version of `ggplot2` & `ggrepel` to see if your problem is fixed – Tung May 31 '18 at 12:36
  • 1
    @beeguy: fyi there's also [`lemon`](https://cran.r-project.org/web/packages/lemon/vignettes/legends.html) package which is very good at manipulating plot legend. – Tung May 31 '18 at 12:41
  • 1
    Where is the Greek freak though? – Abel Callejo May 08 '19 at 08:52
  • 1
    Related: [Remove 'a' from legend when using aesthetics and geom_text](https://stackoverflow.com/questions/18337653/remove-a-from-legend-when-using-aesthetics-and-geom-text) – Tung Dec 10 '19 at 06:11
  • Is there a way to add chars inside the boxes? E.g. I add numeric values to the datapoints but would like to add the corresponding parameter so people know what the values represent. Means, instead of only "17" there should be "rH = 17". Is there a way to do that? – Ben May 05 '23 at 09:25
  • 1
    @Ben: I would just create another variable e.g., `dat <- dat %>% mutate(Label = paste0(" rH = ", value))` – Tung May 05 '23 at 14:01
11

Instead of using the ifelse as in the above example, one can also prefilter the data prior to labeling based on some threshold values, this saves a lot of work for the plotting device:

xlimit <- 36
ylimit <- 24
ggplot(myData)+geom_point(aes(myX,myY))+
    geom_label(data=myData[myData$myX > xlimit & myData$myY> ylimit,], aes(myX,myY,myLabel))
Patrick Dolan
  • 111
  • 1
  • 2