Zeichnen Sie Beschriftungen an den Zeilenenden

75

Ich habe die folgenden Daten ( temp.datvollständige Daten siehe Endnote)

   Year State     Capex
1  2003   VIC  5.356415
2  2004   VIC  5.765232
3  2005   VIC  5.247276
4  2006   VIC  5.579882
5  2007   VIC  5.142464
...

und ich kann die folgende Tabelle erstellen:

ggplot(temp.dat) + 
  geom_line(aes(x = Year, y = Capex, group = State, colour = State))

Geben Sie hier die Bildbeschreibung ein

Anstelle der Legende möchte ich die Etiketten haben

  1. farbig wie die Serie
  2. rechts vom letzten Datenpunkt für jede Serie

Ich habe die Kommentare von baptiste in der Antwort im folgenden Link bemerkt, aber wenn ich versuche, seinen Code ( geom_text(aes(label = State, colour = State, x = Inf, y = Capex), hjust = -1)) anzupassen, wird der Text nicht angezeigt.

ggplot2 - außerhalb des Plots mit Anmerkungen versehen

temp.dat <- structure(list(Year = c("2003", "2004", "2005", "2006", "2007", 
"2008", "2009", "2010", "2011", "2012", "2013", "2014", "2003", 
"2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", 
"2012", "2013", "2014", "2003", "2004", "2005", "2006", "2007", 
"2008", "2009", "2010", "2011", "2012", "2013", "2014", "2003", 
"2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", 
"2012", "2013", "2014"), State = structure(c(1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 
4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L), .Label = c("VIC", 
"NSW", "QLD", "WA"), class = "factor"), Capex = c(5.35641472365348, 
5.76523240652641, 5.24727577535625, 5.57988239709746, 5.14246402568366, 
4.96786288162828, 5.493190785287, 6.08500616799372, 6.5092228474591, 
7.03813541623157, 8.34736513875897, 9.04992300432169, 7.15830329914056, 
7.21247045701994, 7.81373928617117, 7.76610217197542, 7.9744994967006, 
7.93734452080786, 8.29289899132255, 7.85222269563982, 8.12683746325074, 
8.61903784301649, 9.7904327253813, 9.75021175267288, 8.2950673974226, 
6.6272705639724, 6.50170524635367, 6.15609626379471, 6.43799637295979, 
6.9869551384028, 8.36305663640294, 8.31382617231745, 8.65409824343971, 
9.70529678167458, 11.3102788081848, 11.8696420977237, 6.77937303542605, 
5.51242844820827, 5.35789621712839, 4.38699327451101, 4.4925792218211, 
4.29934654081527, 4.54639175257732, 4.70040615159951, 5.04056109514957, 
5.49921208937735, 5.96590909090909, 6.18700407463007)), class = "data.frame", row.names = c(NA, 
-48L), .Names = c("Year", "State", "Capex"))
Hugh
quelle
Ich würde nur einen separaten Datenrahmen mit nur den Daten erstellen, die Sie so zeichnen möchten, geom_text(data = temp.dat[cumsum(table(temp.dat$State)), ], aes(label = State, colour = State, x = Year, y = Capex))aber es gibt möglicherweise eine bessere Möglichkeit, Dinge zu tun
rawr

Antworten:

88

Um Baptistes Idee zu verwenden, müssen Sie das Abschneiden deaktivieren. Aber wenn Sie das tun, bekommen Sie Müll. Darüber hinaus müssen Sie die Legende unterdrücken und für geom_textCapex für 2014 auswählen und den Rand erhöhen, um Platz für die Beschriftungen zu schaffen. (Oder Sie können den hjustParameter anpassen , um die Beschriftungen im Plotfenster zu verschieben.) So etwas:

library(ggplot2)
library(grid)

p = ggplot(temp.dat) + 
  geom_line(aes(x = Year, y = Capex, group = State, colour = State)) + 
  geom_text(data = subset(temp.dat, Year == "2014"), aes(label = State, colour = State, x = Inf, y = Capex), hjust = -.1) +
  scale_colour_discrete(guide = 'none')  +    
  theme(plot.margin = unit(c(1,3,1,1), "lines")) 

# Code to turn off clipping
gt <- ggplotGrob(p)
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)

Geben Sie hier die Bildbeschreibung ein

Aber das ist die Art von Handlung, für die es perfekt ist directlabels.

library(ggplot2)
library(directlabels)

ggplot(temp.dat, aes(x = Year, y = Capex, group = State, colour = State)) + 
  geom_line() +
  scale_colour_discrete(guide = 'none') +
  scale_x_discrete(expand=c(0, 1)) +
  geom_dl(aes(label = State), method = list(dl.combine("first.points", "last.points")), cex = 0.8) 

Geben Sie hier die Bildbeschreibung ein

Bearbeiten So vergrößern Sie den Abstand zwischen dem Endpunkt und den Beschriftungen:

ggplot(temp.dat, aes(x = Year, y = Capex, group = State, colour = State)) + 
  geom_line() +
  scale_colour_discrete(guide = 'none') +
  scale_x_discrete(expand=c(0, 1)) +
  geom_dl(aes(label = State), method = list(dl.trans(x = x + 0.2), "last.points", cex = 0.8)) +
  geom_dl(aes(label = State), method = list(dl.trans(x = x - 0.2), "first.points", cex = 0.8)) 
Sandy Muspratt
quelle
4
Wusste nichts über das directlabelsPaket. In der Dokumentation konnte ich keine Möglichkeit finden, den horizontalen Abstand zwischen den Endpunkten und der Textbeschriftung manuell zu vergrößern. Was ist der beste Weg, dies zu tun?
Hugh
Ich habe eine Bearbeitung hinzugefügt. Siehe FAQs (Nummer 5) unter http://directlabels.r-forge.r-project.org/
Sandy Muspratt
Versuch, das Paket zu installieren : package ‘directlabels’ is not available (for R version 3.3.2). Ich kann die FAQ-Seite für das Paket ebenfalls nicht finden. Lebt es noch?
MERose
@MERose Hmm. Ich bin mir nicht sicher, was passiert. Der Link ist noch aktiv. "Häufig gestellte Fragen" befindet sich auf der ersten Seite. Und ich habe gerade mit cran nachgefragt - directlabels ist verfügbar.
Sandy Muspratt
@slhck, Sieht aus wie es nicht installiert wurde. Haben Sie versucht zu installieren quadprog?
Sandy Muspratt
71

Eine neuere Lösung ist zu verwenden ggrepel:

library(ggplot2)
library(ggrepel)
library(dplyr)

temp.dat %>%
  mutate(label = if_else(Year == max(Year), as.character(State), NA_character_)) %>%
  ggplot(aes(x = Year, y = Capex, group = State, colour = State)) + 
  geom_line() + 
  geom_label_repel(aes(label = label),
                  nudge_x = 1,
                  na.rm = TRUE)

Geben Sie hier die Bildbeschreibung ein

Hugh
quelle
7
Perfekt - aber ich habe "scale_color_discrete (guide = FALSE)" hinzugefügt, um die jetzt unnötigen Legenden von der Außenseite des Diagramms zu entfernen (um einige wichtige Bildschirmflächen zu sparen)
juhariis
Hallo, können Sie es auf diesen Fall erweitern: stackoverflow.com/questions/48487713/… ?
Hercules Apergis
25

Diese Frage ist alt, aber goldfarben, und ich gebe eine andere Antwort für müde Leute.

Das Prinzip dieser Lösung kann ganz allgemein angewendet werden.

Plot_df <- 
  temp.dat %>% mutate_if(is.factor, as.character) %>%  # Who has time for factors..
  mutate(Year = as.numeric(Year))

Und jetzt können wir unsere Daten unterteilen

ggplot() + 
geom_line(data = Plot_df, aes(Year, Capex, color = State)) +
geom_text(data = Plot_df %>% filter(Year == last(Year)), aes(label = State, 
                                                           x = Year + 0.5, 
                                                           y = Capex, 
                                                           color = State)) + 
          guides(color = FALSE) + theme_bw() + 
          scale_x_continuous(breaks = scales::pretty_breaks(10))

Der letzte Teil von pretty_breaks dient nur dazu, die Achse darunter zu fixieren.

Geben Sie hier die Bildbeschreibung ein

Nick
quelle
8

Sie sind sich nicht sicher, ob dies der beste Weg ist, aber Sie können Folgendes versuchen (spielen Sie ein wenig xlimdamit, um die Grenzwerte richtig einzustellen):

library(dplyr)
lab <- tapply(temp.dat$Capex, temp.dat$State, last)
ggplot(temp.dat) + 
    geom_line(aes(x = Year, y = Capex, group = State, colour = State)) +
    scale_color_discrete(guide = FALSE) +
    geom_text(aes(label = names(lab), x = 12, colour = names(lab), y = c(lab), hjust = -.02))

Geben Sie hier die Bildbeschreibung ein

Datengräber
quelle
2
Dies erzeugt eine Fehlermeldung: "Fehler: Die Ästhetik muss entweder die Länge 1 haben oder mit den Daten (48) übereinstimmen: x, y, label, hjust"
invictus
3

Sie haben die Lösung von @ Baptiste nicht zu 100% emuliert. Sie müssen annotation_customalle Ihre verwenden und durchlaufen Capex:

library(ggplot2)
library(dplyr)
library(grid)

temp.dat <- structure(list(Year = c("2003", "2004", "2005", "2006", "2007", 
"2008", "2009", "2010", "2011", "2012", "2013", "2014", "2003", 
"2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", 
"2012", "2013", "2014", "2003", "2004", "2005", "2006", "2007", 
"2008", "2009", "2010", "2011", "2012", "2013", "2014", "2003", 
"2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", 
"2012", "2013", "2014"), State = structure(c(1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 
4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L), .Label = c("VIC", 
"NSW", "QLD", "WA"), class = "factor"), Capex = c(5.35641472365348, 
5.76523240652641, 5.24727577535625, 5.57988239709746, 5.14246402568366, 
4.96786288162828, 5.493190785287, 6.08500616799372, 6.5092228474591, 
7.03813541623157, 8.34736513875897, 9.04992300432169, 7.15830329914056, 
7.21247045701994, 7.81373928617117, 7.76610217197542, 7.9744994967006, 
7.93734452080786, 8.29289899132255, 7.85222269563982, 8.12683746325074, 
8.61903784301649, 9.7904327253813, 9.75021175267288, 8.2950673974226, 
6.6272705639724, 6.50170524635367, 6.15609626379471, 6.43799637295979, 
6.9869551384028, 8.36305663640294, 8.31382617231745, 8.65409824343971, 
9.70529678167458, 11.3102788081848, 11.8696420977237, 6.77937303542605, 
5.51242844820827, 5.35789621712839, 4.38699327451101, 4.4925792218211, 
4.29934654081527, 4.54639175257732, 4.70040615159951, 5.04056109514957, 
5.49921208937735, 5.96590909090909, 6.18700407463007)), class = "data.frame", row.names = c(NA, 
-48L), .Names = c("Year", "State", "Capex"))

temp.dat$Year <- factor(temp.dat$Year)

color <- c("#8DD3C7", "#FFFFB3", "#BEBADA", "#FB8072")

gg <- ggplot(temp.dat) 
gg <- gg + geom_line(aes(x=Year, y=Capex, group=State, colour=State))
gg <- gg + scale_color_manual(values=color)
gg <- gg + labs(x=NULL)
gg <- gg + theme_bw()
gg <- gg + theme(legend.position="none")

states <- temp.dat %>% filter(Year==2014)

for (i in 1:nrow(states))  {
  print(states$Capex[i])
  print(states$Year[i])
  gg <- gg + annotation_custom(
    grob=textGrob(label=states$State[i], 
                    hjust=0, gp=gpar(cex=0.75, col=color[i])),
    ymin=states$Capex[i],
    ymax=states$Capex[i],
    xmin=states$Year[i],
    xmax=states$Year[i])
}    

gt <- ggplot_gtable(ggplot_build(gg))
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.newpage()
grid.draw(gt)

(Sie möchten das Gelb ändern, wenn Sie den weißen Hintergrund beibehalten.)

Geben Sie hier die Bildbeschreibung ein

hrbrmstr
quelle
2

Ich möchte eine Lösung für Fälle hinzufügen, in denen Sie längere Markennamen haben. In allen bereitgestellten Lösungen befinden sich die Beschriftungen im Plotbereich. Wenn Sie jedoch längere Namen haben, werden sie abgeschnitten. So habe ich dieses Problem gelöst:

library(tidyverse)

# Make the "State" variable have longer levels
temp.dat <- temp.dat %>% 
    mutate(State = paste0(State, '-a-long-string'))

ggplot(temp.dat, aes(x = Year, y = Capex, color = State, group = State)) + 
    geom_line() +
    # Add labels at the end of the line
    geom_text(data = filter(temp.dat, Year == max(Year)),
              aes(label = State),
              hjust = 0, nudge_x = 0.1) +
    # Allow labels to bleed past the canvas boundaries
    coord_cartesian(clip = 'off') +
    # Remove legend & adjust margins to give more space for labels
    # Remember, the margins are t-r-b-l
    theme(legend.position = 'none',
          plot.margin = margin(0.1, 2.6, 0.1, 0.1, "cm")) 

Geben Sie hier die Bildbeschreibung ein

jhelvy
quelle
Ich würde mich freuen, wenn Sie mir helfen, wenn ich eine Markierung oder ein X oder Koordinaten nur an einem Punkt setzen möchte, der niedriger ist (in diesem Fall, wie kann ich eine Beschriftung bei (2008, 5) im WA-a-long- setzen? Ich
freue
In meiner Lösung filtere ich die Daten, um die genauen x- und y-Koordinaten auszuwählen, die ich für meine Beschriftungen möchte. Da ich sie am Ende der Zeilen haben wollte, habe ich sie data = filter(temp.dat, Year == max(Year))innerhalb des geom_text()Anrufs verwendet. In Ihrem Fall könnten Sie den Filter auf ändern data = filter(temp.dat, Year == 2008, State = "WA"), wodurch Sie nur die Bezeichnung "WA" an der x-Position von 2008 erhalten, und Sie könnten die y-Position anpassen, indem Sie den nudge_yParameter ingeom_text()
jhelvy
Ich sehe dies nicht als Verbesserung an, da es nicht praktikabel ist, Margen fest einzustellen. Nach meiner Lösung unten: temp.dat <- temp.dat%>% mutate (State = paste0 (State, '-a-long-string')) Plot_df <- temp.dat%>% mutate_if (is.factor, as .character)%>% mutate (Year = as.numeric (Year)) ggplot () + geom_line (data = Plot_df, aes (Year, Capex, color = State)) + geom_text (data = Plot_df%>% filter (Year) == last (Year)), aes (label = State, x = Year + 3, y = Capex, color = State), hjust = 1) + Guides (color = FALSE) + theme_bw () + scale_x_continuous (break = scale :: pretty_breaks (10))
Nick
Ich bin mir nicht sicher, was das Festlegen von Rändern weniger praktisch macht als das Festlegen von Skalengrenzen. Die Lösung mit dem höchsten Rang ändert die Plotränder. Der größere Unterschied, den ich zwischen meiner und Ihrer Lösung sehe, besteht darin, dass in meiner Lösung die x-Achse am letzten Datenpunkt stoppt, während sie in Ihrer Lösung so weit wie nötig fortgesetzt wird, sodass der Markenname in die Plotgrenze passt, in der keine Daten vorhanden sind Punkte.
Jhelvy
@jhelvy Zwei Dinge. Erstens ist das Festlegen von Rändern nicht einfacher als das Erhöhen des x-Randes (eine Eingabe - 3 Jahre, was intuitiv und einfach ist. Ränder sind 4 Eingaben und nicht intuitiv). Bis zu Ihrem letzten Punkt möchten Sie, dass sich die x-Achse der Figur erweitert - andernfalls gehen Ihre Namen wie in Ihrer Lösung außerhalb Ihrer Themen (genau das, was Sie nicht wollen). In meiner Lösung - der Name ist immer noch in Ihrem Thema, Ihr Name geht nach draußen. Das ist natürlich nicht ideal. Die am besten bewertete Lösung ist veraltet (viel lästiger als andere Lösungen hier) - und hat auch Namen, die außerhalb der Themenauswahl liegen.
Nick
1

Ich bin auf diese Frage gekommen, um eine angepasste Linie (z. B. loess()) am letzten angepassten Punkt und nicht am letzten Datenpunkt direkt zu kennzeichnen . Ich habe schließlich einen Ansatz ausgearbeitet, der größtenteils auf Tidyverse basiert. Es sollte auch für die lineare Regression mit ein paar Mods funktionieren, also lasse ich ihn hier für die Nachwelt.

library(tidyverse)

temp.dat$Year <- as.numeric(temp.dat$Year)
temp.dat$State <- as.character(temp.dat$State)

#example of loess for multiple models
#https://stackoverflow.com/a/55127487/4927395

models <- temp.dat %>%
  tidyr::nest(-State) %>%
  dplyr::mutate(
    # Perform loess calculation on each CpG group
    m = purrr::map(data, loess,
                   formula = Capex ~ Year, span = .75),
    # Retrieve the fitted values from each model
    fitted = purrr::map(m, `[[`, "fitted")
  )

# Apply fitted y's as a new column
results <- models %>%
  dplyr::select(-m) %>%
  tidyr::unnest()

#find final x values for each group
my_last_points <- results %>% group_by(State) %>% summarise(Year = max(Year, na.rm=TRUE))

#Join dataframe of predictions to group labels
my_last_points$pred_y <- left_join(my_last_points, results)

# Plot with loess line for each group
ggplot(results, aes(x = Year, y = Capex, group = State, colour = State)) +
  geom_line(alpha = I(7/10), color="grey", show.legend=F) +
  #stat_smooth(size=2, span=0.3, se=F, show_guide=F)
  geom_point(size=1) +
  geom_smooth(se=FALSE)+
  geom_text(data = my_last_points, aes(x=Year+0.5, y=pred_y$fitted, label = State))

direct_label

Mark Neal
quelle