Plot labels at ends of lines
To use Baptiste's idea, you need to turn off clipping. But when you do, you get garbage. In addition, you need to suppress the legend, and, for geom_text
, select Capex for 2014, and increase the margin to give room for the labels. (Or you can adjust the hjust
parameter to move the labels inside the plot panel.) Something like this:
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)
But, this is the sort of plot that is perfect for 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)
Edit To increase the space between the end point and the labels:
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))
Plot labels at end of lines
Here is an example that should clarify how you can put the label at the end of each line.
library(ggplot2)
library(ggrepel)
library(dplyr)
# Create a dataset similar to your obs1
n <- 10
obs1 <- data.frame(Profundidad=rep(seq(0, 20000, length.out=n), n),
value = rep(sqrt(0:9),n)*rep(1:10, each=n),
variable=factor(rep(1:10, each=n)))
# Find the x and y position of the last point for each line
xy_labs <- obs1 %>%
group_by(variable) %>%
summarize(pos = which.max(Profundidad),
x = Profundidad[pos],
y = value[pos])
ggplot(obs1, aes(Profundidad, value, group=variable, col = variable)) +
geom_line() +
geom_point(alpha = 0.5)+
theme_linedraw() +
theme(axis.text = element_text(size = 12),
axis.title.x = element_text(size = 16),
axis.title.y = element_text(size = 16),
legend.position = "none") +
labs(x =" Profundidad de secuenciación", y = "ASVs observados")+
geom_label_repel(data=xy_labs, aes(x=x, y=y, label = y),
nudge_x = 0.1, inherit.aes=F,
na.rm = FALSE)
label end of lines outside of plot area
An alternative to ggrepel is to use geom_text and turn "clipping" off (similar to this question/answer), e.g.
covid %>%
ggplot(aes(x = date, y = deaths_roll7_100k, color = Province_State)) +
geom_line() +
scale_y_continuous(breaks = seq(0, 2.4, .2)) +
scale_x_date(breaks = seq.Date(from=as.Date('2020-09-01'),
to=as.Date('2021-07-12'),
by="month"),
date_labels = "%b\n%Y",
limits = as.Date(c("2020-09-01", "2021-07-01"))) +
geom_text(data = . %>% filter(date == max(date)),
aes(color = Province_State, x = as.Date(Inf),
y = deaths_roll7_100k),
hjust = 0, size = 4, vjust = 0.7,
label = c("Arizona\n", "North Carolina")) +
coord_cartesian(expand = FALSE, clip = "off")
--
With some more tweaks and the Financial-Times/ftplottools R theme you can get the plot looking pretty similar to the Financial Times figure, e.g.
library(tidyverse)
#remotes::install_github("Financial-Times/ftplottools")
library(ftplottools)
library(extrafont)
#font_import()
#fonts()
covid %>%
ggplot() +
geom_line(aes(x = date, y = deaths_roll7_100k,
group = Province_State, color = Province_State)) +
geom_text(data = . %>% filter(date == max(date)),
aes(color = Province_State, x = as.Date(Inf),
y = deaths_roll7_100k),
hjust = 0, size = 4, vjust = 0.7,
label = c("Arizona\n", "North Carolina")) +
coord_cartesian(expand = FALSE, clip = "off") +
ft_theme(base_family = "Arimo for Powerline") +
theme(plot.margin = unit(c(1,6,1,1), "lines"),
legend.position = "none",
plot.background = element_rect(fill = "#FFF1E6"),
axis.title = element_blank(),
panel.grid.major.x = element_line(colour = "gray75"),
plot.caption = element_text(size = 8, color = "gray50")) +
scale_color_manual(values = c("#E85D8C", "#0D5696")) +
scale_x_date(breaks = seq.Date(from = as.Date('2020-09-01'),
to = as.Date('2021-07-01'),
by = "1 month"),
limits = as.Date(c("2020-09-01", "2021-07-01")),
date_labels = "%b\n%Y") +
scale_y_continuous(breaks = seq(from = 0, to = 2.4, by = 0.2)) +
labs(title = "New deaths attributed to Covid-19 in North Carolina and Arizona",
subtitle = "Seven-day rolling average of new deaths (per 100k)\n",
caption = "Source: Analysis of data from John Hopkins SSE\nUpdated: 12th July 2021 | CCBY4.0")
Plot labels at ends of lines on left-hand side
A simple solution is to modify the layout of the gtable associated to the p
plot.
# Generate a toy dataset
set.seed(123)
temp = data.frame(year=rep(2001:2015,2), value=cumsum(rnorm(30)), variable=rep(c("A","B"),each=15))
library(scales)
temp$value <- rescale(temp$value, to=c(95*10^6,105*10^6))
library(ggplot2)
p <- ggplot(temp, aes(x=year, y=value/10^6, group=variable)) + geom_point(shape=1, size=2) + geom_line(size=1) +
geom_text(data=temp[temp$year==min(temp$year),], aes(label=c("MSA 2012", "MSA en cours"), x=year-.25, y=value/10^6),
hjust = 1, size=4.6) +
scale_x_continuous(breaks=seq(2001,2015,2)) + scale_y_continuous(breaks=seq(95,105,5), position="right") +
labs(x="Année", y="Emploi (millions)") +
theme(panel.grid.major=element_blank(), panel.grid.minor=element_blank(), panel.background=element_blank(),
axis.line = element_line(colour = "black"), legend.position="top", legend.direction="horizontal",
axis.text = element_text(color="black", size=13), axis.title = element_text(color="black", size=13),
aspect.ratio=.25)
library(grid)
gt <- ggplotGrob(p)
### Modify the layout of the gtable
gt$widths[[2]] <- unit(2.5, "cm")
###
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)
How to label end of lines of plot area (Seaborn or Matplotlib)
Not perfect but here is the idea using ax.text(...)
:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv("https://github.com/owid/energy-data/raw/master/owid-energy-data.csv")
year_start, year_end = 2000, 2020
countries = ("Afghanistan", "Bhutan", "Cambodia", "Denmark", "Egypt", "Finland")
column = "energy_cons_change_pct"
df_ = df.copy()
df_ = df_[df_["country"].isin(countries)]
df_ = df_[df_["year"] >= year_start]
df_ = df_[df_["year"] <= year_end]
df_ = df_.fillna(method="ffill").fillna(method="bfill")
fig, ax = plt.subplots(figsize=(20, 20))
sns.lineplot(
data=df_,
x="year",
y=column,
hue="country",
style="country",
legend=False,
ax=ax,
)
last_values = df_[["country", column]][df_["year"] == year_end]
for country, value in last_values.itertuples(index=False):
ax.text(x=year_end + 0.2, y=value, s=country, va="center")
ax.grid()
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.set_xticks(np.arange(year_start, year_end + 1))
ax.set_xlim([None, year_end + 0.2])
plt.show()
Plot labels at ends of lines in stacked area chart
The question code is plotting the text labels in the value
's of the last time
, when in fact the areas are cumulative. And in reverse order.
Also, the following graph plots data created with the same code but with
set.seed(1234)
Then the data creation code is the same as in the question.
# stacked area chart
ggplot(data, aes(x=time, y=value, fill=group)) +
geom_area()+
geom_text(data = data %>%
filter(time == last(time)) %>%
mutate(value = cumsum(rev(value))),
aes(label = rev(group),
x = time + 0.5,
y = value,
color = rev(group))) +
guides(color = FALSE) + theme_bw() +
scale_x_continuous(breaks = scales::pretty_breaks(10))
Edit.
Following the discussion in the comments to this answer, I have decided to post code based on the comment by user Jake Kaupp.
ggplot(data, aes(x = time, y = value, fill = group)) +
geom_area()+
geom_text(data = data %>% filter(time == last(time)),
aes(x = time + 0.5, y = value,
label = rev(group), color = rev(group)),
position = position_stack(vjust = 0.5)) +
guides(color = FALSE) +
theme_bw() +
scale_x_continuous(breaks = scales::pretty_breaks(10))
How to add horizontal line labels at end of line when using multiple plots in Roassal3
Following a tip from Alexandre Bergel, I now have a reasonable way to draw labels at the end of plot lines. For interest, I've included, in the playground code below, three ways to identify the plot lines, using the end of plot labels, using right side decorations and using a more standard legend. It's messy, in places but can be cleaned up a lot by moving code to more appropriate objects.
| chart plot dates values firstDate labels legend offset plotLabel renderedLabel canvasCopy maxOffset |
chart := RSChart new.
canvasCopy := RSCanvas new.
dates := (10 to: 1 by: -1) collect: [ :i | Date today subtractDays: i ].
firstDate := dates first.
offset := 20 @ -50.
maxOffset := 0.
values := #(
#(3.72 4.27 3.99 4.91 5.09 4.91 5.09 4.91 4.44 4.91)
#(4.29 4.01 3.82 3.91 4.01 3.73 4.47 4.28 4.18 4.00)
#(1.98 2.04 2.12 2.12 2.21 2.27 2.27 2.10 2.19 1.95)
#(2.5 2.3 2.7 2.73 2.15 2.6 2.63 2.57 2.4 2.8)
#(2.0 1.98 1.98 1.98 1.99 1.96 2.07 1.96 1.90 1.95)
).
labels := #('first series' 'second series' 'series number 4' 'the third series' 'a fifth series').
values with: labels do: [ :series :label |
plot := RSLinePlot new.
plot
x: (dates collect: [ :date | date julianDayNumber - firstDate julianDayNumber ])
y: series.
chart addPlot: plot.
"If adding a legend to the right, RSYLabel decoration is needed but with modified offsets to get them to lay out vertically."
plotLabel := RSYLabelDecoration new right;
title: '~' , label;
fontSize: 12;
rotationAngle: 90;
color: (chart colorFor: plot);
offset: offset;
yourself.
chart addDecoration: plotLabel.
renderedLabel := (plotLabel renderIn: canvasCopy) label.
maxOffset := maxOffset max: renderedLabel textWidth.
offset := (0 - maxOffset) @ (offset y + renderedLabel textHeight + 4).
].
canvasCopy := nil.
chart addDecoration: (RSHorizontalTick new labelConversion: [ :value |
Date julianDayNumber: firstDate julianDayNumber + value ]; useDiagonalLabel; yourself).
chart addDecoration: RSVerticalTick new.
"When adding labels at the end of the plot lines, the right hand line of the chart box will overlay the labels so alter the
spine to a polygon which only draws the x and y axes. "
spine := chart decorations
detect: [ :d | d class == RSChartSpineDecoration ].
spine shape: (RSPolygon new points: {(0 @ 0). (0 @ 0). (0 @ (chart extent y)). (chart extent x @ (chart extent y)). (chart extent x @ (chart extent y)). (0 @ (chart extent y)) }; noPaint; withBorder; yourself).
chart ylabel: 'The values'.
chart build.
"To use standard legend under the chart"
legend := RSLegend new defaultLabel: (RSLabel new fontSize: 8; yourself).
legend container: chart canvas.
labels with: chart plots do: [ :c : p |
legend text: c withShape: (RSPolygon new points: { 0 @ -2. 10 @ -2. 10 @ 2. 0 @ 2 }; color: (chart colorFor: p))
].
legend layout grid.
legend build.
rsLabels := Dictionary new: chart plots size.
chart plots with: labels do: [ :plot :label | rsLabels at: plot put: ((RSLabel text: label) fontSize: 5) ].
lastys := chart plots collect: [ :pl |
Association key: pl value: (pl yScale scale: pl yValues last) ].
lastys sort: [ :el1 :el2 |
el1 value = el2 value
"If the last y coordinate is the same for both plots, order by the second last y point (reverse ordering)"
ifTrue: [ (el1 key yValues at: el1 key yValues size - 1) > (el2 key yValues at: el2 key yValues size - 1) ]
ifFalse: [ el1 value < el2 value ] ].
lastys withIndexDo: [ :lasty :index |
numPlots := lastys size.
plot := lasty key.
label := rsLabels at: plot.
label color: plot color.
chart canvas add: label.
yPoint := lasty value.
textHeight := label textHeight.
index < numPlots ifTrue: [
"Reset the y point to be about textHeight away from the next one, if they are close"
(diff := (yPoint - (lastys at: index + 1) value) abs) < textHeight
ifTrue: [ yPoint := yPoint - (textHeight / 2) + (diff / 2) ] ].
index > 1 ifTrue: [
"Reset the y point to be about textHeight away from the last one, if they are close"
(diff := ((lastys at: index - 1) value - yPoint ) abs) < textHeight
ifTrue: [ yPoint := yPoint + (textHeight / 2) - (diff / 2) ] ].
label translateTo: ((plot xScale scale: plot xValues last) + (label textWidth / 2) + 1) @ yPoint.
].
^chart canvas
How to display labels at the end of added geom_lines?
The first step of making this a lot easier for yourself is by changing the shape of your data.
Try the "reshape2" package, made by Hadley Wickham, the maker of ggplot.
If you would apply the "melt" function on your data.frame, you would end up with a data.frame with two columns: one for the values (the numbers in you data.frame) and one for the type off the values (the column names of your data.frame).
As an example:
emp.data <- data.frame("emp_dayNumber" = 1:100,
"emp_monthly" = rnorm(100),
"emp_yearly" = rnorm(100),
"emp_WorkedDays" = sample(c(TRUE,FALSE), 100, replace = TRUE))
library(reshape2)
## Select the colums you want to plot:
select.data <- emp.data[ , 1:3]
## Change the data.frame to a long format, and state that you want to keep "emp_dayNumber" variable
## as a separate column (as you use it for the x-axis)
plot.data <- melt(emp.data, id.vars = "emp_dayNumber")
Your data should now look like this:
emp_dayNumber variable value
1 1 emp_monthly 0.4231487
2 2 emp_monthly -1.0966351
3 3 emp_monthly 0.2761555
4 4 emp_monthly 0.8575178
5 5 emp_monthly -0.8528019
6 6 emp_monthly 0.4341048
Now plot your data, where "emp_dayNumber" should be your x, "value" your y and "variable" your color
ggplot(toplot.data, aes(x = "emp_dayNumber", y = "value", color = "variable")) +
geom_line()
Try to always apply this on all your plotting functions. This will in the end save you a lot of time.
For more explanations on long and wide format see: http://www.cookbook-r.com/Manipulating_data/Converting_data_between_wide_and_long_format/
Using this, you could now apply the solution stated in the post linked in the commment by "mnm", or using "ggrepel", as you now only use one y variable!
Related Topics
Create and Assign Multiple New Dataframe Columns in Ifelse Statement
Ggplot2: Setting Geom_Bar Baseline to 1 Instead of Zero
How to Make a Great R Reproducible Example
Reshaping Data.Frame from Wide to Long Format
How to Make a List of Data Frames
Changing from Upper to Lower Case in Several Data Frames
How to Fix Spaces in Column Names of a Data.Frame (Remove Spaces, Inject Dots)
Subtracting Two Columns to Give a New Column in R
How to Remove the Negative Values from a Data Frame in R
Creating a for Loop to Subset Data on R
Remove Total Value for One Column in Powerbi
How to Add a Row to Data Frame Based on a Condition
Converting Year and Month ("Yyyy-Mm" Format) to a Date
R Collapse Multiple Rows into 1 Row - Same Columns
Creating a Boxplot for Each Column in R