Construct a manual legend for a complicated plot
You need to map attributes to aesthetics (colours within the aes statement) to produce a legend.
cols <- c("LINE1"="#f04546","LINE2"="#3591d1","BAR"="#62c76b")
ggplot(data=data,aes(x=a)) +
geom_bar(stat="identity", aes(y=h, fill = "BAR"),colour="#333333")+ #green
geom_line(aes(y=b,group=1, colour="LINE1"),size=1.0) + #red
geom_point(aes(y=b, colour="LINE1"),size=3) + #red
geom_errorbar(aes(ymin=d, ymax=e, colour="LINE1"), width=0.1, size=.8) +
geom_line(aes(y=c,group=1,colour="LINE2"),size=1.0) + #blue
geom_point(aes(y=c,colour="LINE2"),size=3) + #blue
geom_errorbar(aes(ymin=f, ymax=g,colour="LINE2"), width=0.1, size=.8) +
scale_colour_manual(name="Error Bars",values=cols) + scale_fill_manual(name="Bar",values=cols) +
ylab("Symptom severity") + xlab("PHQ-9 symptoms") +
ylim(0,1.6) +
theme_bw() +
theme(axis.title.x = element_text(size = 15, vjust=-.2)) +
theme(axis.title.y = element_text(size = 15, vjust=0.3))
I understand where Roland is coming from, but since this is only 3 attributes, and complications arise from superimposing bars and error bars this may be reasonable to leave the data in wide format like it is. It could be slightly reduced in complexity by using geom_pointrange.
To change the background color for the error bars legend in the original, add + theme(legend.key = element_rect(fill = "white",colour = "white"))
to the plot specification. To merge different legends, you typically need to have a consistent mapping for all elements, but it is currently producing an artifact of a black background for me. I thought guide = guide_legend(fill = NULL,colour = NULL)
would set the background to null for the legend, but it did not. Perhaps worth another question.
ggplot(data=data,aes(x=a)) +
geom_bar(stat="identity", aes(y=h,fill = "BAR", colour="BAR"))+ #green
geom_line(aes(y=b,group=1, colour="LINE1"),size=1.0) + #red
geom_point(aes(y=b, colour="LINE1", fill="LINE1"),size=3) + #red
geom_errorbar(aes(ymin=d, ymax=e, colour="LINE1"), width=0.1, size=.8) +
geom_line(aes(y=c,group=1,colour="LINE2"),size=1.0) + #blue
geom_point(aes(y=c,colour="LINE2", fill="LINE2"),size=3) + #blue
geom_errorbar(aes(ymin=f, ymax=g,colour="LINE2"), width=0.1, size=.8) +
scale_colour_manual(name="Error Bars",values=cols, guide = guide_legend(fill = NULL,colour = NULL)) +
scale_fill_manual(name="Bar",values=cols, guide="none") +
ylab("Symptom severity") + xlab("PHQ-9 symptoms") +
ylim(0,1.6) +
theme_bw() +
theme(axis.title.x = element_text(size = 15, vjust=-.2)) +
theme(axis.title.y = element_text(size = 15, vjust=0.3))
To get rid of the black background in the legend, you need to use the override.aes
argument to the guide_legend
. The purpose of this is to let you specify a particular aspect of the legend which may not be being assigned correctly.
ggplot(data=data,aes(x=a)) +
geom_bar(stat="identity", aes(y=h,fill = "BAR", colour="BAR"))+ #green
geom_line(aes(y=b,group=1, colour="LINE1"),size=1.0) + #red
geom_point(aes(y=b, colour="LINE1", fill="LINE1"),size=3) + #red
geom_errorbar(aes(ymin=d, ymax=e, colour="LINE1"), width=0.1, size=.8) +
geom_line(aes(y=c,group=1,colour="LINE2"),size=1.0) + #blue
geom_point(aes(y=c,colour="LINE2", fill="LINE2"),size=3) + #blue
geom_errorbar(aes(ymin=f, ymax=g,colour="LINE2"), width=0.1, size=.8) +
scale_colour_manual(name="Error Bars",values=cols,
guide = guide_legend(override.aes=aes(fill=NA))) +
scale_fill_manual(name="Bar",values=cols, guide="none") +
ylab("Symptom severity") + xlab("PHQ-9 symptoms") +
ylim(0,1.6) +
theme_bw() +
theme(axis.title.x = element_text(size = 15, vjust=-.2)) +
theme(axis.title.y = element_text(size = 15, vjust=0.3))
Manually add legend entry to Seaborn Legend
Edit: when you use fill, you don't get lines. My bad. Changed the code slightly.
To do this, you need to specify the handles and labels to the legend argument. The way I found to get the PolyCollection
artists is with ax.get_children()
. Then, you call plt.legend(handles, labels)
. Here's a toy example:
sns.displot([0, 1, 2, 2, 3, 3, 4, 4, 5], legend=True, kind='kde', label='test', fill=True)
children = plt.gca().get_children()
l = plt.axvline(3.5, c='r')
plt.legend([children[0], l], ['curve', 'line'] )
How to add a legend manually for line chart
The neatest way to do it I think is to add colour = "[label]"
into the aes()
section of geom_line()
then put the manual assigning of a colour into scale_colour_manual()
here's an example from mtcars
(apologies that it uses stat_summary
instead of geom_line
but does the same trick):
library(tidyverse)
mtcars %>%
ggplot(aes(gear, mpg, fill = factor(cyl))) +
stat_summary(geom = "bar", fun = mean, position = "dodge") +
stat_summary(geom = "line",
fun = mean,
size = 3,
aes(colour = "Overall mean", group = 1)) +
scale_fill_discrete("") +
scale_colour_manual("", values = "black")
Created on 2020-12-08 by the reprex package (v0.3.0)
The limitation here is that the colour and fill legends are necessarily separate. Removing labels (blank titles in both scale_
calls) doesn't them split them up by legend title.
In your code you would probably want then:
...
ggplot(data = impact_end_Current_yr_m_actual, aes(x = month, y = gender_value)) +
geom_col(aes(fill = gender))+
geom_line(data = impact_end_Current_yr_m_plan,
aes(x=month, y= gender_value, group=1, color="Plan"),
size=1.2)+
scale_color_manual(values = "#288D55") +
...
(but I cant test on your data so not sure if it works)
Manually defined legend in Plotly on Python
- this creates exactly the graph you requested
- start by putting your sample data into a dataframe to open up Plotly Express
- start by updating traces to use colors columns
- adding legend is done. Really is not a functional legend as it cannot be used for filtering the figure, will just show unique colors used in figure. This is achieved by adding additional small traces
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
import numpy as np
df = pd.DataFrame(
{
"day": ["day" + str(i) for i in range(1, 8)],
"starts": [10, 50, 70, 75, 20, 50, 90],
"ends": [95, 5, 80, 20, 50, 10, 75],
"starts_colors": ["green", "orange", "red", "red", "green", "orange", "red"],
"ends_colors": ["red", "green", "red", "green", "orange", "green", "red"],
}
)
# build figure, hover_data / customdata is used to hold colors
fig = px.bar(
df,
x="day",
y=["starts", "ends"],
barmode="group",
hover_data={"starts_colors":False, "ends_colors":False},
)
# update colors of bars
fig.plotly_update(
data=[
t.update(marker_color=[c[i] for c in t.customdata])
for i, t in enumerate(fig.data)
]
)
# just for display purpose, create traces so that legend contains colors. does not connect with
# bars
fig.update_traces(showlegend=False).add_traces(
[
go.Bar(name=c, x=[fig.data[0].x[0]], marker_color=c, showlegend=True)
for c in np.unique(df.loc[:,["starts_colors","ends_colors"]].values.ravel())
]
)
How to manually add legend
You should explicitly provide a column in aes
if you want it on the legend. In this example, you just put a constant column
test1$col <- "Task 1"
ggplot(test1, aes(x=Seconds, y=Freq, fill=col)) +
geom_histogram(stat="identity", alpha=0.5, width=1, color="black")+
ylim(0,180) +
labs(title="Task 1", x="Number of Seconds Inside Island", y = "Count", fill = "Task")
how to add manually a legend to ggplot
The hardest part here was recreating your data set for demonstration purposes. It's always better to add a reproducible example. Anyway, the following should be close:
library(ggplot2)
set.seed(123)
ID1.4.5.6.7 <- data.frame(Time = c(rep(1, 3),
rep(c(2, 3, 4, 5), each = 17)),
mRNA = c(rnorm(3, 0.1, 0.25),
rnorm(17, 0, 0.25),
rnorm(17, -0.04, 0.25),
rnorm(17, -0.08, 0.25),
rnorm(17, -0.12, 0.25)))
lm_mRNATime <- lm(mRNA ~ Time, data = ID1.4.5.6.7)
Now we run your code with the addition of a custom colour guide:
predict_ID1.4.5.6.7 <- predict(lm_mRNATime, ID1.4.5.6.7)
ID1.4.5.6.7$predicted_mRNA <- predict_ID1.4.5.6.7
colors <- c("data" = "Blue", "predicted_mRNA" = "red", "fit" = "Blue")
p <- ggplot( data = ID1.4.5.6.7, aes(x = Time, y = mRNA, color = "data")) +
geom_point() +
geom_line(aes(x = Time, y = predicted_mRNA, color = "predicted_mRNA"),
lwd = 1.3) +
geom_smooth(method = "lm", aes(color = "fit", lty = 2),
se = TRUE, lty = 2) +
scale_x_discrete(limits = c('0', '20', '40', '60', '120')) +
scale_color_manual(values = colors) +
labs(title = "ID-1, ID-4, ID-5, ID-6, ID-7",
y = "mRNA", x = "Time [min]", color = "Legend") +
guides(color = guide_legend(
override.aes = list(shape = c(16, NA, NA),
linetype = c(NA, 2, 1)))) +
theme(plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
legend.key.width = unit(30, "points"))
Related Topics
Replacing Few Values in a Pandas Dataframe Column with Another Value
How to Print to Stderr in Python
How to Add Percentages on Top of Bars in Seaborn
How to Merge a Transparent Png Image with Another Image Using Pil
Pandas Dataframe Stored List as String: How to Convert Back to List
Sqlite/Sqlalchemy: How to Enforce Foreign Keys
When to Use "While" or "For" in Python
Differencebetween 'Same' and 'Valid' Padding in Tf.Nn.Max_Pool of Tensorflow
How to Shift a Column in Pandas Dataframe
Why Does '.Sort()' Cause the List to Be 'None' in Python