The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
library(trendseries)
library(dplyr)
library(ggplot2)
theme_series <- theme_minimal(paper = "#fefefe") +
theme(
legend.position = "bottom",
panel.grid.minor = element_blank(),
# Use colors
palette.colour.discrete = c(
"#024873FF",
"#BF4F26FF",
"#D98825FF",
"#D9AA1EFF",
"#A2A637FF"
)
)The trendseries package helps you extract trends from
economic time series data. Think of trends as the underlying direction
of your data, stripped of short-term fluctuations and noise.
This vignette will walk you through the basics, starting with the simplest possible example and gradually building up to more complex analyses.
The main purpose of trendseries is to simplify the
workflow of trend extraction, especially when working with data frames
and grouped data. It provides two main functions:
augment_trends(): Takes a data frame with a date column
and a value column, and adds new columns with the extracted trends using
specified methods.extract_trends(): Takes a single time series object
(ts) and returns the extracted trends.Tabular data frames are the most common format for time series data
in R, but most trend extraction methods are designed for ts
objects. trendseries bridges this gap, allowing you to work
directly with data frames without cumbersome conversions.
trendseries was designed to work fluidly with
tidyverse tools like dplyr and
ggplot2.
This package was designed with economic time series in mind, in this sense, it includes methods commonly used in economics (e.g., Hodrick-Prescott filter) as well as general-purpose smoothing methods (e.g., LOESS, moving averages).
Let’s start with the simplest possible example: extracting a trend from Brazilian GDP construction data.
# Load the data
data("gdp_construction", package = "trendseries")
# Take a quick look
head(gdp_construction)
#> # A tibble: 6 × 2
#> date index
#> <date> <dbl>
#> 1 1995-01-01 100
#> 2 1995-04-01 100
#> 3 1995-07-01 100
#> 4 1995-10-01 100
#> 5 1996-01-01 97.8
#> 6 1996-04-01 101.This dataset contains quarterly observations of Brazil’s construction sector GDP. Let’s extract a trend using the Hodrick-Prescott (HP) filter, one of the most common methods in economics.
# Extract trend using HP filter
gdp_with_trend <- augment_trends(
gdp_construction,
value_col = "index",
methods = "hp"
)
#> Auto-detected quarterly (4 obs/year)
#> Computing HP filter (two-sided) with lambda = 1600
# View the result
head(gdp_with_trend)
#> # A tibble: 6 × 3
#> date index trend_hp
#> <date> <dbl> <dbl>
#> 1 1995-01-01 100 101.
#> 2 1995-04-01 100 101.
#> 3 1995-07-01 100 102.
#> 4 1995-10-01 100 103.
#> 5 1996-01-01 97.8 103.
#> 6 1996-04-01 101. 104.That’s it! The augment_trends() function added a new
column called trend_hp to your data. The original data
stays intact, and you get a new column with the trend.
Let’s see what this trend looks like.
# Prepare data for plotting
plot_data <- gdp_with_trend |>
select(date, index, trend_hp) |>
tidyr::pivot_longer(
cols = c(index, trend_hp),
names_to = "series",
values_to = "value"
) |>
mutate(
series = case_when(
series == "index" ~ "Data (original)",
series == "trend_hp" ~ "HP Filter Trend"
)
)
# Create the plot
ggplot(plot_data, aes(x = date, y = value, color = series)) +
geom_line(linewidth = 0.8) +
labs(
title = "Brazil GDP Construction: Original vs Trend",
x = "Date",
y = "Construction Index",
color = NULL
) +
theme_seriesNotice how the trend smooths out the short-term fluctuations in the original data. This makes it easier to see the long-term direction of the construction sector.
If your data is already a ts object, you can use the
extract_trends() function directly. This is useful for
quick analyses or when working with single series.
The HP filter is just one way to extract trends. Let’s compare it with two other popular methods.
# Extract multiple trends at once
gdp_comparison <- gdp_construction |>
augment_trends(
value_col = "index",
methods = c("hp", "loess", "ma")
)
#> Auto-detected quarterly (4 obs/year)
#> Computing HP filter (two-sided) with lambda = 1600
#> Computing loess trend with span = 0.75
#> Computing 2x4-period MA (auto-adjusted for even-window centering)
# View the first few rows
gdp_comparison |>
select(date, index, starts_with("trend_")) |>
head()
#> # A tibble: 6 × 5
#> date index trend_hp trend_loess trend_ma
#> <date> <dbl> <dbl> <dbl> <dbl>
#> 1 1995-01-01 100 101. 109. NA
#> 2 1995-04-01 100 101. 109. 99.7
#> 3 1995-07-01 100 102. 108. 99.6
#> 4 1995-10-01 100 103. 107. 101.
#> 5 1996-01-01 97.8 103. 107. 102.
#> 6 1996-04-01 101. 104. 106. 103.Now let’s visualize all three methods:
# Prepare data for plotting
comparison_plot <- gdp_comparison |>
select(date, index, starts_with("trend_")) |>
tidyr::pivot_longer(
cols = c(index, starts_with("trend_")),
names_to = "method",
values_to = "value"
) |>
mutate(
method = case_when(
method == "index" ~ "Data (original)",
method == "trend_hp" ~ "HP Filter",
method == "trend_loess" ~ "LOESS",
method == "trend_ma" ~ "Moving Average"
)
)
# Plot
ggplot(comparison_plot, aes(x = date, y = value, color = method)) +
geom_line(linewidth = 0.8) +
labs(
title = "Comparing Different Trend Extraction Methods",
subtitle = "Same data, different methods",
x = "Date",
y = "Construction Index",
color = "Method"
) +
theme_series
#> Warning: Removed 4 rows containing missing values or values outside the scale range
#> (`geom_line()`).Each method produces a slightly different trend. The HP filter and LOESS are quite similar, while the moving average is slightly more responsive to changes in the data.
So far we’ve used quarterly data (4 observations per year). Let’s try monthly data, which is more common in economic analysis.
# Load monthly vehicle production data
data("vehicles", package = "trendseries")
# Look at recent data (last 4 years)
recent_vehicles <- vehicles |>
slice_tail(n = 48)
head(recent_vehicles)
#> # A tibble: 6 × 2
#> date production
#> <date> <dbl>
#> 1 2021-08-01 178900
#> 2 2021-09-01 156803
#> 3 2021-10-01 170178
#> 4 2021-11-01 176322
#> 5 2021-12-01 219446
#> 6 2022-01-01 136105The process is exactly the same - augment_trends()
automatically detects the frequency.
# Extract trend from monthly data
vehicles_with_trend <- vehicles |>
augment_trends(
value_col = "production",
methods = "hp"
)
#> Auto-detected monthly (12 obs/year)
#> Computing HP filter (two-sided) with lambda = 14400
vehicles_with_trend <- vehicles_with_trend |>
tidyr::pivot_longer(
cols = c(production, trend_hp),
names_to = "series",
values_to = "value"
) |>
mutate(
series = ifelse(series == "production", "Original", "HP Trend"),
# To make sure the trend is plotted on top of the original series
# configure levels accordingly
series = factor(series, levels = c("Original", "HP Trend"))
)
ggplot(vehicles_with_trend, aes(x = date, y = value, color = series)) +
geom_line(linewidth = 0.8) +
labs(
title = "Brazil Vehicle Production: Monthly Data",
subtitle = "Last 4 years of data",
x = "Date",
y = "Production (thousands of units)",
color = NULL
) +
theme_seriesDifferent trend methods accept different parameters. The
trendseries package simplifies this with two main
parameters:
window: Controls the period for moving
averages (e.g., 12 months)smoothing: Controls how smooth the
trend should beLet’s experiment with the window parameter for a moving
average.
# Try different window sizes
vehicles_windows <- recent_vehicles |>
augment_trends(
value_col = "production",
methods = "ma",
window = 6
) |>
rename(trend_ma_6m = trend_ma)
#> Auto-detected monthly (12 obs/year)
#> Computing 2x6-period MA (auto-adjusted for even-window centering)
# Add 12-month window
vehicles_windows <- vehicles_windows |>
augment_trends(
value_col = "production",
methods = "ma",
window = 12
)
#> Auto-detected monthly (12 obs/year)
#> Computing 2x12-period MA (auto-adjusted for even-window centering)
# Visualize
vehicles_windows <- vehicles_windows |>
select(date, production, trend_ma_6m, trend_ma) |>
tidyr::pivot_longer(
cols = c(production, trend_ma_6m, trend_ma),
names_to = "method",
values_to = "value"
) |>
mutate(
method = case_when(
method == "production" ~ "Data (original)",
method == "trend_ma_6m" ~ "MA (6-month)",
method == "trend_ma" ~ "MA (12-month)"
)
)
ggplot(vehicles_windows, aes(x = date, y = value, color = method)) +
geom_line(linewidth = 0.8) +
labs(
title = "Window Size Comparison",
subtitle = "Larger windows = smoother trends",
x = "Date",
y = "Production (thousands)",
color = NULL
) +
theme_series
#> Warning: Removed 18 rows containing missing values or values outside the scale range
#> (`geom_line()`).Key insight: Larger windows produce smoother trends but are slower to react to changes. Smaller windows track the data more closely but may still include some noise.
For monthly data: - Short-term trend: window = 3 to
6 - Medium-term trend: window = 12 (one year)
- Long-term trend: window = 24 or more
A common task is comparing trends across different time-series. Let’s
look at economic activity and vehicle production together.
augment_trends() can handle this easily with the
group_vars argument.
data("ibcbr", package = "trendseries")
series <- full_join(ibcbr, vehicles, by = "date")
series <- series |>
filter(date >= as.Date("2010-01-01")) |>
tidyr::pivot_longer(
cols = c(index, production),
names_to = "indicator",
values_to = "value"
) |>
# Normalize to Jan 2010 = 100 for comparison
mutate(
norm_index = value / first(value) * 100,
.by = indicator
)
series <- augment_trends(
series,
value_col = "norm_index",
methods = "hp",
group_vars = "indicator"
)
#> Auto-detected monthly (12 obs/year)
#> Computing HP filter (two-sided) with lambda = 14400
#> Auto-detected monthly (12 obs/year)
#> Computing HP filter (two-sided) with lambda = 14400# Plot trends only
series |>
ggplot(aes(x = date, color = indicator)) +
geom_line(aes(y = norm_index), alpha = 0.4) +
geom_line(aes(y = trend_hp), linewidth = 1) +
labs(
title = "Economic Indicators: HP Filter Trends",
subtitle = "Normalized to first observation = 100",
x = "Date",
y = "Index (normalized)",
color = "Indicator"
) +
theme_seriesThis shows both series moved together during the pandemic period but diverged afterwards, with economic activity recovering more strongly than vehicle production.
The trendseries package supports many trend extraction
methods. Here are the most commonly used:
hp: Hodrick-Prescott filter (default,
widely used in macroeconomics)bk: Baxter-King bandpass filter
(isolates business cycles)cf: Christiano-Fitzgerald filter
(asymmetric bandpass)hamilton: Hamilton regression filter
(recent alternative to HP)ma: Simple moving average (easiest to
understand)ewma: Exponentially weighted moving
average (more weight on recent data)wma: Weighted moving average
(emphasizes recent observations)zlema: Zero-lag exponential moving
average (reduced lag)triangular: Triangular moving average
(double-smoothed)loess: Local polynomial regression
(flexible, data-adaptive)spline: Smoothing splines (very
smooth)stl: Seasonal-trend decomposition
(handles seasonality)sg: Savitzky-Golay filter (preserves
features like peaks)kalman: Kalman smoother (optimal under
certain assumptions)kernel: Kernel regression smoother
(nonparametric)Recommendation for beginners: Start with
hp for general use, ma when you want something
simple and interpretable, and stl when your data has clear
seasonal patterns.
Here are some ready-to-use patterns for common scenarios:
Now that you understand the basics, you can:
?augment_trends and ?extract_trends.The package includes several economic datasets for practice.
All series are monthly except for gdp_construction. -
ibcbr: Central Bank Economic Activity
Index - smooth, good for learning -
vehicles: Vehicle production - cyclical,
interesting patterns - electric:
Electricity consumption - seasonal patterns -
oil_derivatives: Oil derivatives
production - gdp_construction: GDP
construction index - smooth, great for first examples
retail_households: UK household goods
retail (monthly)retail_autofuel: UK automotive fuel
retail (monthly)coffee_arabica: Coffee prices
(daily)coffee_robusta: Coffee prices
(daily)trendseries easier than the traditional
workflow?Time series have a specific structure in R (ts) and most
filtering methods are designed for ts objects. However,
datasets come as data frames with date columns, which can make applying
filters cumbersome.
The usual workflow involves (1) converting individual columns to
ts, (2) applying the filter, (3) and then converting back
to a data.frame. This can be cumbersome, especially when
working with multiple series or grouped data. Merging back the results
with the original data can also be error-prone due to misalignment of
dates and additional NA values introduced by some
filters.
For instance, to replicate the first example in this vignette,
without trendseries, you would have to first convert the
data frame to a ts object, mannually inputing both
frequency and start parameters:
gdp_cons <- ts(
gdp_construction$index,
frequency = 4,
start = c(1996, 1)
)
# Or, using lubridate to extract year and month
gdp_cons <- ts(
gdp_construction$index,
frequency = 4,
start = c(lubridate::year(min(gdp_construction$date)),
lubridate::quarter(min(gdp_construction$date)))
)Then apply the HP filter using the mFilter package.
And finally, convert it back to a data.frame and merge
with the original data.
trendseries?The closest alternative to trendseries is the
tsibble/fable ecosystem, which provides a
model() function for applying models — including some trend
extraction methods — to grouped time series. Like
trendseries, these packages integrate well with
tidyverse tools and pipes.
However, fable was designed primarily for forecasting,
which means its trend extraction capabilities are more limited. They
also lack some popular methods commonly used by economists, such as the
HP filter and the Hamilton filter.
Additionally, these packages require using the tsibble
data structure, which pulls users away from the familiar
data.frame/tibble format. For users working
with just a few time series and relying on R’s built-in ts
functionality, the tsibble structure can feel unnecessarily
complex.
This package was inspired by the need for a simpler workflow for trend extraction in R. It builds upon many existing packages, including:
mFilter for economic filters.hpfilter for Hodrick-Prescott filtering.signal for Savitzky-Golay filtering.TTR and zoo for efficient moving
averages.tsbox for time series conversions.If you run into issues:
?augment_trendsexample(augment_trends)vignette(package = "trendseries")These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.