The forecast package version of autolayer has this nice series argument for adding a legend to one's forecast plots.
library(feasts)
library(forecast)
ETS <- forecast(ets(AirPassengers), h=5)
forecast::autoplot(AirPassengers) +
forecast::autolayer(ETS, series="ETS", PI=FALSE)
I'm working with Fable now, and would like to replicate this legend.
Here's some test data I'm working with. (Their dput() will be available at the bottom of this question)
# > test
# A tsibble: 3 x 2 [1D]
create_date HPTS.East
<date> <dbl>
1 2025-05-16 33
2 2025-05-17 50
3 2025-05-18 31
# > fcast
# A fable: 4 x 4 [1D]
# Key: .model [2]
.model create_date HPTS.East .mean
<chr> <date> <dist> <dbl>
1 ets 2025-05-19 N(5.3, 23) 5.35
2 ets 2025-05-20 N(5.1, 24) 5.05
3 stlf 2025-05-19 t(N(1.7, 0.42)) 3.38
4 stlf 2025-05-20 t(N(2.2, 0.44)) 5.12
When multiple fable forecast models are selected, autolayer will automatically add a legend.
library(fpp3)
autoplot(test) +
autolayer(fcast |> filter(.model %in% c("stlf", "ets")), level = NULL)
But not when only one model is selected...
autoplot(test) +
autolayer(fcast |> filter(.model %in% c("stlf")), level = NULL)
How can I get autolayer to display the legend when there is only one fable model selected?
I'm sure it's possible to manually manipulate the ggplot object and add a legend, but I feel like I'm just missing a simple argument call like: show_legend = TRUE or something similar.
# dput(test)
test =
structure(list(create_date = structure(c(20224, 20225, 20226), class = "Date"),
HPTS.East = c(33, 50, 31)), class = c("tbl_ts", "tbl_df",
"tbl", "data.frame"), row.names = c(NA, -3L), key = structure(list(
.rows = structure(list(1:3), ptype = integer(0), class = c("vctrs_list_of",
"vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -1L)), index = structure("create_date", ordered = TRUE), index2 = "create_date", interval = structure(list(
year = 0, quarter = 0, month = 0, week = 0, day = 1, hour = 0,
minute = 0, second = 0, millisecond = 0, microsecond = 0,
nanosecond = 0, unit = 0), .regular = TRUE, class = c("interval",
"vctrs_rcrd", "vctrs_vctr")))
# dput(fcast)
fcast =
structure(list(.model = c("ets", "ets", "stlf", "stlf"), create_date = structure(c(20227,
20228, 20227, 20228), class = "Date"), HPTS.East = structure(list(
structure(list(mu = 5.34517049658451, sigma = 4.81792422458686), class = c("dist_normal",
"dist_default")), structure(list(mu = 5.050629714793, sigma = 4.89510333541553), class = c("dist_normal",
"dist_default")), structure(list(dist = structure(list(mu = 1.72153910975751,
sigma = 0.645715613426544), class = c("dist_normal",
"dist_default")), transform = function (HPTS.East)
HPTS.East^2, inverse = function (HPTS.East)
sqrt(HPTS.East)), class = c("dist_transformed", "dist_default"
)), structure(list(dist = structure(list(mu = 2.16255346189791,
sigma = 0.665457675943415), class = c("dist_normal",
"dist_default")), transform = function (HPTS.East)
HPTS.East^2, inverse = function (HPTS.East)
sqrt(HPTS.East)), class = c("dist_transformed", "dist_default"
))), vars = "HPTS.East", class = c("distribution", "vctrs_vctr",
"list")), .mean = c(5.34517049658451, 5.050629714793, 3.38064555985075,
5.11947139402257)), class = c("fbl_ts", "tbl_ts", "tbl_df", "tbl",
"data.frame"), row.names = c(NA, -4L), key = structure(list(.model = c("ets",
"stlf"), .rows = structure(list(1:2, 3:4), ptype = integer(0), class = c("vctrs_list_of",
"vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -2L), .drop = TRUE), index = structure("create_date", ordered = TRUE), index2 = "create_date", interval = structure(list(
year = 0, quarter = 0, month = 0, week = 0, day = 1, hour = 0,
minute = 0, second = 0, millisecond = 0, microsecond = 0,
nanosecond = 0, unit = 0), .regular = TRUE, class = c("interval",
"vctrs_rcrd", "vctrs_vctr")), response = "HPTS.East", dist = "HPTS.East", model_cn = ".model")
I looked through https://otexts.com/fpp3/ets-forecasting.html for its autoplot examples, but wasn't able to find a way to add the legend like the fpp2 textbook (forecast package).
The autoplot() and autolayer() methods for fable objects are designed to only produce colours when there are multiple .model values, and there is no option to force colours.
These plot helpers are designed as quick analytical tools, to produce publication ready graphics I recommend constructing the plot using the grammar with ggplot2 and ggdist.
# Import `dput()` data as above, removed for brevity
library(fpp3)
library(ggdist)
fcast |>
ggplot(aes(ydist = HPTS.East, x = create_date)) +
stat_lineribbon(
aes(colour = .model, fill = .model, fill_ramp = after_stat(.width)),
alpha = 0.3, .width = c(0.8, 0.95)
) +
geom_line(aes(y = HPTS.East), data = test)

Filtering forecasts from a single model…
fcast |>
filter(.model == "ets") |>
ggplot(aes(ydist = HPTS.East, x = create_date)) +
stat_lineribbon(
aes(colour = .model, fill = .model, fill_ramp = after_stat(.width)),
alpha = 0.3, .width = c(0.8, 0.95)
) +
geom_line(aes(y = HPTS.East), data = test)

Created on 2025-05-28 with reprex v2.1.1