I want to create a ggplot figure with six panels in R. The first five facets should represent five different subsets of data in bar charts, and the final facet should represent the whole data. I further want to have a fixed y-axis scale across the first five facets, but a different scale in the final facet. I am aware that it is currently not possible to specify individual ylims for each facet within the ggplot functionality (https://github.com/hadley/ggplot2/issues/187), but am wondering if I can do something similar using grid and possibly gtable packages, neither of which I'm very familiar with at the moment.
The following is my attempt. I replace the final facet with a facet in another figure.
library("ggplot2")
library("dplyr")
library("grid")
# create data
set.seed(1)
d1 <- data_frame(
value = rnorm(3 * 5, mean = 30, sd = 10),
f = rep(LETTERS[1:3], 5),
p = rep(paste("Panel", 1:5), each = 3)
)
d2 <- d1 %>%
mutate(p = "Total") %>%
rbind(d1)
# make initial figures
plot1 <- ggplot(d2, aes(f, value)) +
geom_bar(stat = "identity") +
facet_wrap(~ p) +
coord_cartesian(ylim = c(0, 50))
plot2 <- ggplot(d2, aes(f, value)) +
geom_bar(stat = "identity") +
facet_wrap(~ p, scales = "free_y")
# extract their grobs
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)
# replace the final facet of plot1 with the final facet of plot2
g1[["grobs"]][[7]] <- g2[["grobs"]][[7]]
g1[["grobs"]][[19]] <- g2[["grobs"]][[19]]
g1[["grobs"]][[25]] <- g2[["grobs"]][[25]]
# draw the figure
grid.newpage()
grid.draw(g1)
And here's what I get.
As can be seen, however, the y-axis label of the final facet overlaps with the preceding facet. Does anyone know a way to avoid the overlap e.g., by making the final facet smaller?
One approach is to extract the "Total" plot from "g2", then insert it into "g1", but first remove the "Total" plot from "g1". But you will notice that the x-axis tick mark labels do not align across the facets.
# Load packages
library(ggplot2)
library(dplyr)
library(gtable)
library(grid)
# create data
set.seed(1)
d1 <- data.frame(
value = rnorm(3 * 5, mean = 30, sd = 10),
f = rep(LETTERS[1:3], 5),
p = rep(paste("Panel", 1:5), each = 3)
)
d2 <- d1 %>%
mutate(p = "Total") %>%
rbind(d1)
# make initial figures
plot1 <- ggplot(d2, aes(f, value)) +
geom_bar(stat = "identity") +
facet_wrap(~ p) +
coord_cartesian(ylim = c(0, 50))
plot2 <- ggplot(d2, aes(f, value)) +
geom_bar(stat = "identity") +
facet_wrap(~ p, scales = "free_y")
# Get the ggplot grobs
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)
# Extract "Total" plot from g2
keep = g2$layout$name %in% c("panel-3-2", "axis-b-3-2", "axis-l-2-3", "strip-t-3-2")
pos = subset(g2$layout, keep, c(t,l,b,r))
g2 = g2[c(min(pos$t):max(pos$b)), c(min(pos$l):max(pos$r))]
# Remove "Total" plot from g1
keep = !g1$layout$name %in% c("panel-3-2", "axis-b-3-2", "strip-t-3-2")
pos = subset(g1$layout, !keep, c(t,l,b,r))
g1$grobs <- g1$grobs[keep]
g1$layout <- g1$layout[keep, ]
# Insert g2 into g1
g1 = gtable_add_grob(g1, g2, t=min(pos$t), b=max(pos$b), l=min(pos$l), r=max(pos$r))
# Draw it
grid.newpage()
grid.draw(g1)
Another approach is to extract the "Total" plot from "g2" as before, but to move its y-axis to the right side of the plot (using code borrowed from here. (I tweaked your "plot2" so that the tick mark labels are better aligned in the final plot.) In this way, the "Total" panel takes as much space as the other panels, and thus the x-axis tick mark labels align, but the y-axis for the "Total" panel sticks out to the right.
# Make initial figures
plot1 <- ggplot(d2, aes(f, value)) +
geom_bar(stat = "identity") +
facet_wrap(~ p) +
coord_cartesian(ylim = c(0, 50))
plot2 <- ggplot(d2, aes(f, value)) +
geom_bar(stat = "identity") +
facet_wrap(~ p, scales = "free_y") +
theme(axis.text.y = element_text(hjust = 0)) ## For better formatting of labels
# extract their grobs
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)
# Extract "Total" plot from g2
keep = g2$layout$name %in% c("panel-3-2", "axis-b-3-2", "axis-l-2-3", "strip-t-3-2")
pos = subset(g2$layout, keep, c(t,l,b,r))
g2 = g2[c(min(pos$t):max(pos$b)), c(min(pos$l):max(pos$r))]
# Get the position of the panel in the layout
panel <- c(subset(g2$layout, grepl("panel", g2$layout$name), se = t:r))
# Get the row number of the y-axis in the layout
rn <- which(grepl("axis-l", g2$layout$name))
# Extract the axis (tick marks and axis text from the gtable)
axis.grob <- g2$grobs[[rn]]
axisl <- axis.grob$children[[2]] # Two children - get the second
axisl # Note: two grobs - tick marks and text
# Reverse the grobs and the widths
axisl$widths <- rev(axisl$widths)
axisl$grobs <- rev(axisl$grobs)
axisl$grobs[[1]]$x <- axisl$grobs[[1]]$x - unit(1, "npc") + unit(2.75, "pt")
axisl$grobs[[2]]$children[[1]]$x = unit(.15, "npc")
# Remove the column containing the left axis
g2 <- g2[, -(panel$r-1)]
## remove empty panels
keep = !g1$layout$name %in% c("panel-3-2", "axis-b-3-2", "strip-t-3-2")
pos = subset(g1$layout, !keep, c(t,l,b,r))
g1$grobs <- g1$grobs[keep]
g1$layout <- g1$layout[keep, ]
# Insert g2 into g1
g1 = gtable_add_grob(g1, g2, t = min(pos$t), b = max(pos$b), l = min(pos$l), r = max(pos$r))
# Add a new column to g1, and add the revised axisl grob to the new column.
pos = subset(g1$layout, grepl("panel", g1$layout$name), c(t,l,b,r)) # position of bottom right panel
g1 <- gtable_add_cols(g1, axisl$widths, max(pos$r))
g1 <- gtable_add_grob(g1, axisl, t = max(pos$b), l = max(pos$r)+1, r = max(pos$r)+2)
# Draw it
grid.newpage()
grid.draw(g1)
Related
I need to plot some simple line plots along with an image. However, the image's left and right side need to align to both the left and right end of the line plot.
My code produces the following graph:
However, I need the graph to look like this (dash lines on the edge only for reference):
As such, I need a way to both move and scale the image along the x-axis to accomplish this.
The code I am currently using is:
library(tidyverse)
library(grid)
library(gridExtra)
library(cowplot)
library(magick)
df <- tibble(Long_Name_For_X= -10:10,Long_Name_For_Y = x^2,Long_Name_For_Z= x)
testImage <- image_read(file.path("C:/index.jpg"))
p1 <- df %>%
gather(variable,value,-Long_Name_For_X) %>%
ggplot(aes(x=Long_Name_For_X,y=value,color=variable)) +
geom_line()
p2 <- ggdraw() + draw_image(testImage)
plot_grid(p1,p2,ncol=1,align = "v", axis = "l")
We need to separate the legend from the plot to make it easier for alignment. The procedure is similar to this answer
library(tidyverse)
library(magick)
library(cowplot)
library(patchwork)
x <- -10:10
df <- tibble(Long_Name_For_X = -10:10, Long_Name_For_Y = x^2, Long_Name_For_Z = x)
testImage <- image_read(file.path("./img/index.png"))
p1 <- df %>%
gather(variable, value, -Long_Name_For_X) %>%
ggplot(aes(x = Long_Name_For_X, y = value, color = variable)) +
geom_line()
p2 <- ggdraw() + draw_image(testImage)
# get legend
leg <- get_legend(p1)
# create a blank plot for legend alignment
blank_p <- plot_spacer() + theme_void()
# align legend with the blank plot
p3 <- plot_grid(leg,
blank_p,
nrow = 2)
# align p1 & p2 without any legend
p12 <- plot_grid(p1 + theme(legend.position = 'none'),
p2,
nrow = 2)
# put everything together
final_plot <- plot_grid(p12,
p3,
ncol = 2,
rel_widths = c(2, 1))
final_plot
Let say I did four measurements/experiments (M1..M4) producing intensity values which vary across 5 locations (loc_1...5). I observed various classes of elements (n=7). Now I would like to summarize the results into a single diagram using facet_grid and geom_raster from ggplot2. I end up with the diagram below (see image).
Now the question is whether there is a simple solution to force rows to fill the space in each facet (i.e to drop unused rows in each panel).
Thank you
rm(list=ls())
library(ggplot2)
library(reshape2)
set.seed(123)
# let's create a fake dataset
nb.mesure <- 4
n.row <- 200
n.col <- 5
nb.class <- 7
d <- matrix(round(runif(n.row * n.col),2), nc=n.col)
colnames(d) <- sprintf("Loc_%02d", 1:5)
# These strings will be the row names of each heatmap
# in the subsequent facet plot
elements <- sample(replicate(n.row/2, 1:100))
# let's create a data.frame d
d <- data.frame(d,
mesure = sort(rep(c("M1","M2","M3", "M4"), n.row/4)),
elements= elements,
class=sample(nb.class,
length(elements),
rep=T,
prob = seq(0.01, 0.25, length.out=7))
)
# Data are melt
dm <- melt(d, id.var=c( "mesure", "elements", "class"))
colnames(dm) <- c("mesure","elements", "class", "pos", "intensity")
# Plotting
p <- ggplot(dm, aes(x = pos, y = elements, fill = intensity))
p <- p + geom_raster()
p <- p + facet_grid(mesure~class , scales = "free", space="free_y")
p <- p + theme_bw()
p <- p + theme(text = element_text(size=8))
p <- p + theme(text = element_text(family = "mono", face = "bold"))
p <- p + theme(axis.text.y = element_blank(),
axis.ticks.y=element_blank(),
axis.text.x = element_text(colour="grey20",
size=6,angle=45,
vjust = 0.3))
print(p)
If your goal is to introduce NA values (or something else) for all missing combinations of mesure, elements, class and pos you can use the complete function from the tidyr package like so:
library(tidyr)
dm <- complete(dm, mesure, elements, class, pos, fill = list(intensity = NA))
UPDATE
In case you want to have the non-NA values expand to fill each facet you have to move away from facet_grid and switch to facet_wrap.
p <- p + facet_wrap(mesure~class , scales = "free_y", nrow = 4)
Is it possible to split the fill legend of a ggplot barplot following the values on the x-axis of the plot?
For example using this data:
library(ggplot2)
data <- data.frame(val=c(2,4,5,6,7,8,9),var1=c("A","A","A","B","B","C","C"),
var2=sample(LETTERS[1:7]))
ggplot(data,aes(x=factor(var1),y=val,fill=var2))+geom_bar(stat="identity")
I get the following plot:
I would like to have something like this to make it easier to find what each fill color corresponds to:
An alternative to the solutions in the links in the comments. The solution assumes that the data is available in an aggregated form, and that each category of var2 appear in one and only one category of var1. That is, the number of keys (and their order) in the legend is correct. All that need happen is for space to be inserted between appropriate keys and text dropped into those spaces. It gets the information it needs to construct the plot from the initial plot or its build data.
library(ggplot2)
library(gtable)
library(grid)
set.seed(1234)
data <- data.frame(val = c(2,4,5,6,7,8,9),
var1 = c("A","A","A","B","B","C","C"),
var2 = sample(LETTERS[1:7]))
# Sort levels of var2
data$var2 = factor(data$var2, labels = data$var2, levels = data$var2)
p = ggplot(data, aes(x = factor(var1), y = val, fill = var2)) +
geom_bar(stat = "identity")
# Get the ggplot grob
g = ggplotGrob(p)
# Get the legend
leg = g$grobs[[which(g$layout$name == "guide-box")]]$grobs[[1]]
# Get the labels from the ggplot build data
gt = ggplot_build(p)
labels = rev(gt$layout$panel_params[[1]]$x.labels)
## Positions of the labels
# Get the number of keys within each label from the ggplot build data
gt$data[[1]]$x
N = as.vector(table(gt$data[[1]]$x))
N = N[-length(N)]
# Get the positions of the labels in the legend gtable
pos = rev(cumsum(N)) + 3
pos = c(pos, 3)
# Add rows to the legend gtable, and add the labels to the new rows
for(i in seq_along(pos)){
leg = gtable_add_rows(leg, unit(1.5, "lines"), pos = pos[i])
leg = gtable_add_grob(leg, textGrob(labels[i], y = 0.1, just = "bottom"),
t = pos[i] + 1, l = 2)
}
# Put the legend back into the plot
g$grobs[[which(g$layout$name == "guide-box")]]$grobs[[1]] = leg
# Draw it
grid.newpage()
grid.draw(g)
I have two pieces of data that I want to overlay onto the same plot. I've looked at several ggplot articles and I don't think it's possible within ggplot. So I have been using barplot. I have 5 tiers and I'm plotting total dollars by tier as a solid bar.
Then I have another piece of data that represents the number of tasks within those tiers by two different types of workers. I have this as a stacked bar plot. But I want to show them on the same graph with the total dollar amount as one bar and then the corresponding stacked bar next to it.
Here are the plots:
The data for the first graph looks like this (it's a table):
1 2 3 4 5
0 9 340 97 812 4271
1 1 417 156 3163 11314
The data for the second graph looks like this (this is a dataset):
Tier variable value
1 1 Opp_Amt 16200.00
2 2 Opp_Amt 116067.50
3 3 Opp_Amt 35284.12
4 4 Opp_Amt 278107.10
5 5 Opp_Amt 694820.29
I want to put the graphs on top of each other but the bars keep overlapping and I want them to appear side by side by tier.
Code for what I have so far.
par(mar=c(2.5, 4, 4, 4)+2)
## Plot first set of data and draw its axis
barplot(data1$value, axes=FALSE,ylim=c(0,700000), xlab="", ylab="",
col="black",space=-10,main="Work Score")
axis(2, ylim=c(0,700000),col="black",las=1) ## las=1 makes horizontal labels
mtext("Total Opportunity Amount",side=2,line=3.5)
box()
## Allow a second plot on the same graph
par(new=TRUE)
## Plot the second plot and put axis scale on right
m <- barplot(counts, xlab="", ylab="", ylim=c(0,16000),axes=FALSE, col=c("red","darkblue"),space=3,width=0.5,density=20)
## a little farther out (line=4) to make room for labels
mtext("Task Ratio: Outbound to AE",side=4,col="red",line=3.5)
axis(4, ylim=c(0,16000), col="red",col.axis="black",las=1)
And it gives me this
Using ggplot, I would do something like one of these. They plot the two sets of data separately. The first arranges the data into one dataframe, then uses facet_wrap() to position the plots side-by-side. The second generates the two plot objects separately, then combines the two plots and the legend into a combined plot.
But if you really need the "dual y-axis" approach, then with some fiddling, and using the plots' layouts and gtable functions, it can be done (using code borrowed from here).
Like this:
library(ggplot2)
library(gtable)
library(plyr)
df1 <- data.frame(Tier = rep(1:5, each = 2),
y = c(9, 1, 340, 417, 97, 156, 812, 3063, 4271, 11314),
gp = rep(0:1, 5))
df2 <- read.table(text = "
Tier variable value
1 Opp_Amt 16200.00
2 Opp_Amt 116067.50
3 Opp_Amt 35284.12
4 Opp_Amt 278107.10
5 Opp_Amt 694820.29", header = TRUE)
dfA = df1
dfB = df2
names(dfA) = c("Tier", "Value", "gp")
dfA$var = "Task Ratio"
dfB = dfB[,c(1,3)]
dfB$gp = 3
dfB$var = "Total Opportunity Amount"
names(dfB) = names(dfA)
df = rbind(dfA, dfB)
df$var = factor(df$var)
df$var = factor(df$var, levels = rev(levels(df$var)))
ggplot(df, aes(Tier, Value, fill = factor(gp))) +
geom_bar(position = "stack", stat = "identity") +
facet_wrap( ~ var, scale = "free_y") +
scale_fill_manual("Group", breaks = c("0","1"), values = c("#F8766D", "#00BFC4", "black")) +
theme_bw() +
theme(panel.spacing = unit(2, "lines"),
panel.grid = element_blank())
Or this:
p1 <- ggplot(df1, aes(factor(Tier), y, fill = factor(gp))) +
geom_bar(position = "stack", stat = "identity") +
#guides(fill = FALSE) +
scale_y_continuous("Task Ratio",
limit = c(0, 1.1*max(ddply(df1, .(Tier), summarise, sum = sum(y)))),
expand = c(0,0)) +
scale_x_discrete("Tier") +
theme_bw() +
theme(panel.grid = element_blank())
p2 <- ggplot(df2, aes(factor(Tier), value)) +
geom_bar(stat = "identity") +
scale_y_continuous("Total Opportunity Amount", limit = c(0, 1.1*max(df2$value)), expand = c(0,0)) +
scale_x_discrete("Tier") +
theme_bw() +
theme(panel.grid = element_blank())
# Get the ggplot grobs,
# And get the legend from p1
g1 <- ggplotGrob(p1)
leg = gtable_filter(g1, "guide-box")
legColumn = g1$layout[which(g1$layout$name == "guide-box"), "l"]
g1 = g1[,-legColumn]
g2 <- ggplotGrob(p2)
# Make sure the width are the same in g1 and g2
library(grid)
maxWidth = unit.pmax(g1$widths, g2$widths)
g1$widths = as.list(maxWidth)
g2$widths = as.list(maxWidth)
# Combine g1, g2 and the legend
library(gridExtra)
grid.arrange(arrangeGrob(g2, g1, nrow = 1), leg,
widths = unit.c(unit(1, "npc") - leg$width, leg$width), nrow=1)
Or the dual y-axis approach (But not recommended for reasons given in #Phil's post):
width1 = 0.6 # width of bars in p1
width2 = 0.2 # width of bars in p2
pos = .5*width1 + .5*width2 # positioning bars in p2
p1 <- ggplot(df1, aes(factor(Tier), y, fill = factor(gp))) +
geom_bar(position = "stack", stat = "identity", width = width1) +
guides(fill = FALSE) +
scale_y_continuous("",
limit = c(0, 1.1*max(ddply(df1, .(Tier), summarise, sum = sum(y)))),
expand = c(0,0)) +
scale_x_discrete("Tier") +
theme_bw() +
theme(panel.grid = element_blank(),
axis.text.y = element_text(colour = "red", hjust = 0, margin = margin(l = 2, unit = "pt")),
axis.ticks.y = element_line(colour = "red"))
p2 <- ggplot(df2, aes(factor(Tier), value)) +
geom_blank() +
geom_bar(aes(x = Tier - pos), stat = "identity", width = width2) +
scale_y_continuous("", limit = c(0, 1.1*max(df2$value)), expand = c(0,0)) +
theme_bw() +
theme(panel.grid = element_blank())
# Get ggplot grobs
g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(p2)
# Get locations of the panels in g1
pp1 <- c(subset(g1$layout, name == "panel", se = t:r))
## Get bars from g2 and insert them into the panel in g1
g <- gtable_add_grob(g1, g2$grobs[[which(g2$layout$name == "panel")]][[4]][[4]], pp1$t, pp1$l)
# Grab axis from g1, reverse elements, and put it on the right
index <- which(g1$layout$name == "axis-l")
grob <- g1$grobs[[index]]
axis <- grob$children[[2]]
axis$widths <- rev(axis$widths)
axis$grobs <- rev(axis$grobs)
axis$grobs[[1]]$x <- axis$grobs[[1]]$x - unit(1, "npc") + unit(3, "pt")
g <- gtable_add_cols(g, g1$widths[g1$layout[index, ]$l], pp1$r)
g <- gtable_add_grob(g, axis, pp1$t, pp1$l+1)
# Grab axis from g2, and put it on the left
index <- which(g2$layout$name == "axis-l")
grob <- g2$grobs[[index]]
axis <- grob$children[[2]]
g <- gtable_add_grob(g, rectGrob(gp = gpar(col = NA, fill = "white")), pp1$t-1, pp1$l-1, pp1$b+1)
g <- gtable_add_grob(g, axis, pp1$t, pp1$l-1)
# Add axis titles
# right axis title
RightAxisText = textGrob("Task Ratio", rot = 90, gp = gpar(col = "red"))
g <- gtable_add_cols(g, unit.c(unit(1, "grobwidth", RightAxisText) + unit(1, "line")), 5)
g <- gtable_add_grob(g, RightAxisText, pp1$t, pp1$r+2)
# left axis title
LeftAxisText = textGrob("Total Opportunity Amount", rot = 90)
g <- gtable_add_grob(g, LeftAxisText, pp1$t, pp1$l-2)
g$widths[2] <- unit.c(unit(1, "grobwidth", LeftAxisText) + unit(1, "line"))
# Draw it
grid.newpage()
grid.draw(g)
It appears you are trying to plot two variables on two different y scales on to one chart. I recommend against this, and this is considered bad practice. See, for example, #hadley 's (the author of ggplot2) answer here about a similar issue: https://stackoverflow.com/a/3101876/3022126
It is possible to plot two variables on one y axis if they have comparable scales, but the range of your two datasets do not greatly overlap.
Consider other visualisations, perhaps using two separate charts.
Try looking at the add parameter for barplot.
## Function to create alpha colors for illustration.
col2alpha <- function(col, alpha = 0.5) {
tmp <- col2rgb(col)
rgb(tmp[1]/255, tmp[2]/255, tmp[3]/255, alpha)
}
## Some fake data
dat1 <- data.frame(id = 1:4, val = c(10, 8, 6, 4))
dat2 <- data.frame(id = 1:4, val = c(4, 6, 8, 10))
barplot(dat1$val, col = col2alpha("blue"))
barplot(dat2$val, col = col2alpha("red"), add = TRUE)
I am trying to label points in a scatterplot in R (ggplot2) using numbers (1, 2, 3, ...) and then match the numbers to names in a legend (1 - Alpha, 2 - Bravo, 3 - Charlie... ), as a way of dealing with too many, too long labels on the plot.
Let's assume this is a.df:
Name X Attribute Y Attribute Size Attribute Color Attribute
Alpha 1 2.5 10 A
Bravo 3 3.5 5 B
Charlie 2 1.5 10 C
Delta 5 1 15 D
And this is a standard scatterplot:
ggplot(a.df, aes(x=X.Attribute, y=Y.Attribute, size=Size.Attribute, fill=Colour.Attribute, label=Name)) +
geom_point(shape=21) +
geom_text(size=5, hjust=-0.2,vjust=0.2)
Is there a way to change it as follows?
have scatterplot points labeled with numbers (1,2,3...)
have a legend next to the plot assigning the plot labels (1,2,3...) to a.df$Name
In the next step I would like to assign other attributes to the point size and color, which may rule out some 'hacks'.
Here's an alternative solution, which draws the labels as geom_text. I've borrowed from
ggplot2 - annotate outside of plot.
library(MASS) # for Cars93 data
library(grid)
library(ggplot2)
d <- Cars93[1:30,]
d$row_num <- 1:nrow(d)
d$legend_entry <- paste(" ", d$row_num, d$Manufacturer, d$Model)
ymin <- min(d$Price)
ymax <- max(d$Price)
y_values <- ymax-(ymax-ymin)*(1:nrow(d))/nrow(d)
p <- ggplot(d, aes(x=Min.Price, y=Price)) +
geom_text(aes(label=row_num)) +
geom_text(aes(label=legend_entry, x=Inf, y=y_values, hjust=0)) +
theme(plot.margin = unit(c(1,15,1,1), "lines"))
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)
This is pretty hacky, but might help. The plot labels are simply added by geom_text, and to produce a legend, I've mapped colour to a label in the data. Then to stop the points being coloured, I override it with scale_colour_manual, where you can set the colour of the points, as well as the labels on the legend. Finally, I made the points in the legend invisible by setting alpha = 0, and the squares that are usually behind the dots in theme().
dat <- data.frame(id = 1:10, x = rnorm(10), y = rnorm(10), label = letters[1:10])
ggplot(dat, aes(x, y)) + geom_point(aes(colour = label)) +
geom_text(aes(x = x + 0.1, label = id)) +
scale_colour_manual(values = rep("black", nrow(dat)),
labels = paste(dat$id, "=", dat$label)) +
guides(colour = guide_legend(override.aes = list(alpha = 0))) +
theme(legend.key = element_blank())