Étiqueter la dernière valeur de chaque courbe dans ggplot2

Placez la valeur finale de chaque série au bout de sa courbe — fini la chasse à la légende

Data Visualization

Un guide pratique pour étiqueter la dernière valeur de chaque courbe dans un graphique ggplot2 multi-courbes ou de séries temporelles. Extrayez le dernier point de chaque groupe en R base, posez une étiquette geom_text()/geom_label() au bout de la courbe, empêchez son rognage au bord du panneau, placez les valeurs sur un axe secondaire, et écartez les étiquettes de fin qui se chevauchent avec ggrepel.

Auteur·rice
Date de publication

14 juillet 2026

Modifié

14 juillet 2026

AstuceCe que vous trouverez ici
  • Obtenez le dernier point de chaque courbe en R base pur — sans dplyr.
  • Écrivez la valeur (et le nom de la série) au bout de la courbe avec geom_text().
  • Empêchez le rognage de l’étiquette au bord du panneau — expansion d’échelle ou coord_cartesian(clip = "off").
  • Placez les valeurs sur un axe secondaire pour une échelle de droite sans légende.
  • Écartez les étiquettes de fin qui se chevauchent avec ggrepel quand deux courbes se terminent proches l’une de l’autre.

Votre graphique en courbes se termine, et le lecteur doit remonter chaque courbe jusqu’à une légende pour savoir quelle courbe correspond à quelle série — et la légende ne lui donne jamais la valeur. Placez le nombre là où l’œil se trouve déjà : au bout de la courbe. C’est la touche finale la plus efficace sur un graphique multi-courbes ou de séries temporelles, et une fois le principe compris, c’est trois lignes de code.

L’astuce a deux moitiés : trouver le dernier point de chaque courbe, puis y dessiner une étiquette. Tout le reste — empêcher l’étiquette d’être coupée, gérer deux courbes qui se terminent l’une sur l’autre — n’est que fignolage. Ce guide fait tout cela, avec du code ggplot2 réellement exécuté que vous pouvez copier et exécuter.

A wide ggplot2 time-series chart of the DAX, SMI, CAC and FTSE stock indices from 1991 to 1998, four coloured lines each labelled at its right end with the index name and final value, repelled apart where DAX and FTSE finish close together, on a clean theme_minimal grid.

Les données : quelques courbes qui partagent un axe des x

Tout graphique de « dernière valeur » part de données au format long — une ligne par observation, avec une colonne indiquant à quelle série appartient la ligne. (Le format long est la forme que ggplot2 attend pour des courbes groupées ; voyez tracer plusieurs courbes pour le pourquoi.) Nous utilisons le jeu de données intégré EuStockMarkets, les cours de clôture quotidiens de quatre indices européens — le DAX allemand, le SMI suisse, le CAC français et le FTSE britannique — de 1991 à 1998. Il se présente comme une large matrice de séries temporelles, nous l’empilons donc dans un data.frame long en R base :

library(ggplot2)
eu <- as.data.frame(EuStockMarkets)
eu$time <- as.numeric(time(EuStockMarkets))   # decimal year for the x-axis

# wide -> long, base R: repeat time, stack the four price columns
stocks <- data.frame(
  time  = rep(eu$time, 4),
  index = rep(c("DAX", "SMI", "CAC", "FTSE"), each = nrow(eu)),
  price = c(eu$DAX, eu$SMI, eu$CAC, eu$FTSE)
)
head(stocks)
      time index   price
1 1991.496   DAX 1628.75
2 1991.500   DAX 1613.63
3 1991.504   DAX 1606.51
4 1991.508   DAX 1621.04
5 1991.512   DAX 1618.16
6 1991.515   DAX 1610.61

Voici le graphique brut — quatre courbes, et une légende que vous devez sans cesse consulter :

ggplot(stocks, aes(x = time, y = price, colour = index)) +
  geom_line(linewidth = 0.7) +
  scale_colour_viridis_d(end = 0.9) +
  labs(x = NULL, y = "Index level", colour = "Index") +
  theme_minimal()

ggplot2 time-series chart of four European stock indices as coloured lines, with a legend on the right identifying DAX, SMI, CAC and FTSE.

Étape 1 — obtenir le dernier point de chaque courbe

L’étiquette doit se placer à l’observation finale de chaque courbe, extrayez donc d’abord une ligne par groupe : la ligne dont le x est le plus grand (ici, le time le plus récent). En R base, scindez les données selon la colonne de groupe et prenez la ligne au x maximal de chaque morceau avec which.max() :

library(ggplot2)
eu <- as.data.frame(EuStockMarkets)
eu$time <- as.numeric(time(EuStockMarkets))
stocks <- data.frame(
  time  = rep(eu$time, 4),
  index = rep(c("DAX", "SMI", "CAC", "FTSE"), each = nrow(eu)),
  price = c(eu$DAX, eu$SMI, eu$CAC, eu$FTSE)
)

# one row per index: its last (latest-time) observation
ends <- do.call(rbind, lapply(split(stocks, stocks$index),
                              function(d) d[which.max(d$time), ]))
ends
         time index   price
CAC  1998.646   CAC 3995.00
DAX  1998.646   DAX 5473.72
FTSE 1998.646  FTSE 5455.00
SMI  1998.646   SMI 7676.30

split() découpe le tableau en une liste, un élément par indice ; which.max(d$time) trouve la ligne la plus récente de chaque série ; do.call(rbind, ...) empile les quatre gagnantes dans un petit data frame. Cette table ends — quatre lignes, une par courbe — est ce que nous étiquetons. Le principe est tout à fait général : il trouve le vrai dernier point même lorsque les séries ont des longueurs différentes (une courbe qui s’arrête tôt est tout de même étiquetée à sa propre fin). Si vous préférez le tidyverse, le même résultat s’obtient avec dplyr::slice_max(order_by = time, n = 1, by = index) — mais R base garde la recette sans dépendances.

Étape 2 — dessiner l’étiquette avec geom_text()

Ajoutez maintenant une deuxième couche — un geom_text() alimenté par le petit tableau ends plutôt que par les données complètes. Faites correspondre son label à ce que vous voulez au bout de la courbe ; ici le nom de l’indice et sa valeur finale. Définissez hjust = 0 pour que le texte soit aligné à gauche et s’étende vers la droite, à l’écart de la courbe, et retirez la légende — les étiquettes l’ont remplacée :

ggplot(stocks, aes(x = time, y = price, colour = index)) +
  geom_line(linewidth = 0.7) +
  geom_text(
    data = ends,
    aes(label = paste0(index, " ", round(price))),
    hjust = 0, nudge_x = 0.1, fontface = "bold"
  ) +
  scale_colour_viridis_d(end = 0.9) +
  labs(x = NULL, y = "Index level") +
  theme_minimal() +
  theme(legend.position = "none")

ggplot2 time-series chart of four stock indices with each line labelled at its right end by name and final value, no legend; the DAX and FTSE labels sit close together.

C’est toute l’idée. Comme l’étiquette hérite de colour = index depuis l’aes() du graphique, chaque étiquette s’accorde à sa courbe gratuitement. Deux choses restent grossières, et les deux étapes suivantes les corrigent : les étiquettes débordent du bord droit (elles sont rognées), et le DAX et le FTSE se terminent si près que leurs étiquettes se chevauchent.

Étape 3 — empêcher le rognage des étiquettes au bord

Une étiquette placée au dernier point dépasse la zone de tracé et se retrouve coupée. La correction propre consiste à donner plus de place au panneau sur la droite en élargissant l’échelle des xexpansion(mult = c(0.02, 0.18)) ajoute 2 % de marge à gauche et 18 % à droite, à l’intérieur du panneau, pour que le texte ait où se loger :

ggplot(stocks, aes(x = time, y = price, colour = index)) +
  geom_line(linewidth = 0.7) +
  geom_text(
    data = ends,
    aes(label = paste0(index, " ", round(price))),
    hjust = 0, nudge_x = 0.1, fontface = "bold"
  ) +
  scale_x_continuous(expand = expansion(mult = c(0.02, 0.18))) +
  scale_colour_viridis_d(end = 0.9) +
  labs(x = NULL, y = "Index level") +
  theme_minimal() +
  theme(legend.position = "none")

The same labelled stock-index chart with extra space added on the right so the end labels sit fully inside the panel and are no longer cut off.

L’alternative — quand vous voulez que les étiquettes vivent dans la marge en dehors du panneau plutôt que de grignoter l’espace du graphique — est de désactiver le rognage et d’élargir plutôt la marge du graphique :

ggplot(stocks, aes(x = time, y = price, colour = index)) +
  geom_line(linewidth = 0.7) +
  geom_text(
    data = ends,
    aes(label = paste0(index, " ", round(price))),
    hjust = 0, nudge_x = 0.1, fontface = "bold"
  ) +
  coord_cartesian(clip = "off") +
  scale_colour_viridis_d(end = 0.9) +
  labs(x = NULL, y = "Index level") +
  theme_minimal() +
  theme(legend.position = "none",
        plot.margin = margin(5.5, 60, 5.5, 5.5))   # extra right margin (pt)

The labelled stock-index chart with clipping turned off, so the end labels sit in a widened right-hand plot margin outside the panel.

coord_cartesian(clip = "off") autorise les géométries à dessiner en dehors du panneau ; la marge droite plot.margin agrandie (en points, ordre : haut, droite, bas, gauche) leur donne un endroit où se poser. Optez pour la version à expansion d’échelle par défaut — elle garde l’étiquette à l’intérieur de la zone des données — et pour clip = "off" quand vous voulez précisément les étiquettes dans la marge. Le rognage est corrigé maintenant, mais le DAX et le FTSE se terminent si près que leurs étiquettes se chevauchent encore ici — l’étape 5 (ggrepel) les écarte.

Étape 4 — les valeurs sur un axe secondaire

Une voie différente et très compacte évite complètement la géométrie de texte : afficher la valeur finale de chaque courbe comme une graduation sur un axe des y secondaire. sec_axis() reflète l’échelle principale (~ . = « mêmes valeurs »), et vous définissez ses graduations sur les derniers cours et ses étiquettes sur les noms des indices :

ggplot(stocks, aes(x = time, y = price, colour = index)) +
  geom_line(linewidth = 0.7) +
  scale_y_continuous(
    sec.axis = sec_axis(~ ., breaks = ends$price, labels = ends$index)
  ) +
  scale_colour_viridis_d(end = 0.9) +
  labs(x = NULL, y = "Index level") +
  theme_minimal() +
  theme(legend.position = "none")

ggplot2 stock-index chart whose right-hand secondary axis carries a tick for each line's final value, labelled with the index name aligned to where the line ends.

L’axe de droite nomme désormais chaque courbe là où elle aboutit — une échelle nette, sans légende, sans rognage à gérer. Sa seule limite est le revers d’être un axe : les graduations qui tombent trop près les unes des autres (le DAX et le FTSE, encore) se tassent, et il affiche le nom de la série, pas une étiquette libre. Quand deux fins coïncident presque, écartez-les — c’est exactement ce que fait l’étape suivante.

Étape 5 — écarter les extrémités qui se chevauchent avec ggrepel

Le DAX et le FTSE se terminent à un cheveu l’un de l’autre, si bien que leurs étiquettes geom_text() se chevauchent. Le package ggrepel (install.packages("ggrepel")) résout cela : geom_text_repel() écarte les étiquettes pour qu’aucune ne se heurte. Pour des étiquettes de fin, contraignez la répulsion à la direction verticale et poussez tout vers la droite, de sorte que les étiquettes restent dans une colonne nette à côté des extrémités des courbes :

library(ggrepel)
ggplot(stocks, aes(x = time, y = price, colour = index)) +
  geom_line(linewidth = 0.7) +
  geom_text_repel(
    data = ends,
    aes(label = paste0(index, " ", round(price))),
    hjust = 0, direction = "y", nudge_x = 0.35,
    segment.color = NA, fontface = "bold"
  ) +
  scale_x_continuous(expand = expansion(mult = c(0.02, 0.18))) +
  scale_colour_viridis_d(end = 0.9) +
  labs(x = NULL, y = "Index level") +
  theme_minimal() +
  theme(legend.position = "none")

The stock-index chart with ggrepel end-labels: the DAX and FTSE labels are pushed apart vertically so all four names and values are readable in a clean column at the right.

direction = "y" laisse les étiquettes se déplacer uniquement de haut en bas ; nudge_x = 0.35 les décale des extrémités des courbes ; segment.color = NA masque les petites lignes de rappel (retirez-le pour les garder). C’est la version vers laquelle se tourner pour tout graphique réel — elle se dégrade en douceur de « les étiquettes ne se chevauchent pas » à « les étiquettes ont à peine bougé » selon l’encombrement des extrémités. Utilisez plutôt geom_label_repel() pour des étiquettes encadrées sur un fond chargé.

Autres approches

Approche Idéale quand Remarque
geom_text() + expansion d’échelle Quelques courbes bien séparées La plus simple ; le choix par défaut au quotidien.
Axe de droite sec_axis() Vous voulez une échelle sans légende Compact ; se tasse quand les extrémités sont proches.
ggrepel Deux courbes ou plus se terminent proches La réponse générale robuste.
Package directlabels Vous voulez une seule fonction pour placer toutes les étiquettes Package externe ; voyez ci-dessous.

Si vous préférez ne pas gérer du tout le tableau ends, le package directlabels automatise l’étiquetage des courbes — geom_dl(aes(label = index), method = "last.points") trouve et place chaque étiquette de fin pour vous :

# illustrative — install.packages("directlabels")
library(directlabels)
ggplot(stocks, aes(time, price, colour = index)) +
  geom_line() +
  geom_dl(aes(label = index), method = "last.points") +
  theme_minimal() + theme(legend.position = "none")

C’est pratique pour un passage rapide « nommer chaque courbe », même si vous renoncez au contrôle fin (texte d’étiquette personnalisé, décalage précis) que la recette ends + ggrepel ci-dessus vous offre.

Foire aux questions

Construisez un petit data frame contenant uniquement le dernier point de chaque courbe, puis ajoutez une couche geom_text() alimentée par ce tableau. En R base, do.call(rbind, lapply(split(df, df$group), function(d) d[which.max(d$x), ])) extrait la ligne finale de chaque groupe ; ensuite geom_text(data = ends, aes(label = value), hjust = 0) l’écrit au bout de la courbe. Comme la couche de texte hérite de la correspondance colour, chaque étiquette s’accorde automatiquement à sa courbe.

Scindez les données selon la colonne de regroupement et choisissez la ligne au x maximal dans chaque morceau : do.call(rbind, lapply(split(df, df$group), function(d) d[which.max(d$x), ])). Cela gère les séries de longueurs différentes — chaque groupe est étiqueté à sa propre dernière observation. L’équivalent tidyverse est dplyr::slice_max(order_by = x, n = 1, by = group).

L’étiquette dépasse le panneau de tracé, qui la rogne. Soit vous ajoutez de la place à l’intérieur du panneau en élargissant l’échelle des x — scale_x_continuous(expand = expansion(mult = c(0.02, 0.18))) —, soit vous laissez les géométries dessiner en dehors du panneau avec coord_cartesian(clip = "off") et élargissez la marge droite plot.margin. La voie de l’expansion d’échelle est le choix par défaut le plus propre car elle garde l’étiquette dans la zone des données.

Étiquetez chaque courbe à sa fin et retirez la légende avec theme(legend.position = "none"). Les étiquettes de fin portent la même information — quelle couleur correspond à quelle série — là où le lecteur regarde déjà, de sorte que la légende devient superflue. Un axe secondaire (sec_axis(~ ., breaks = ..., labels = ...)) est une autre option sans légende qui nomme chaque courbe sur l’échelle de droite.

Utilisez ggrepel (geom_text_repel(direction = "y")) quand vous voulez un contrôle total sur le texte de l’étiquette et un évitement fiable des chevauchements — c’est la réponse générale robuste. Tournez-vous vers directlabels (geom_dl(method = "last.points")) quand vous voulez simplement nommer chaque courbe rapidement avec une seule fonction, sans texte personnalisé ni décalage. Les deux reposent sur la même idée : placer l’étiquette au dernier point de chaque courbe.

Pour aller plus loin

🟢 Avec un agent IA

Demandez à Prova « étiquette la dernière valeur de chaque courbe dans mon propre graphique ggplot2, et écarte celles qui se chevauchent » — collez vos données et elle répond avec du code que vous pouvez exécuter. The runtime is the judge. Demander à Prova →

Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Citation

BibTeX
@online{kassambara2026,
  author = {Kassambara, Alboukadel},
  title = {Étiqueter la dernière valeur de chaque courbe dans ggplot2},
  date = {2026-07-14},
  url = {https://www.datanovia.com/blog/ggplot-line-last-value-label},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
Kassambara, Alboukadel. 2026. “Étiqueter la dernière valeur de chaque courbe dans ggplot2.” July 14. https://www.datanovia.com/blog/ggplot-line-last-value-label.