augment_trends() and detrend_series()
now accept tsibbles with Date, yearmonth, or
yearquarter indices and return tsibbles with the original
index and key. Index and key supply the default date and grouping
columns; tsibble is optional in Suggests.
Other data-frame functions accept tsibbles with a Date
index and explain how to convert other index classes.
Added stats = "change" to
augment_rolling() and roll_series() for the
change of a level over window periods, as a decimal or in
percent with percent = TRUE (#29).
Added window = "all" to
augment_rolling() and roll_series() for an
expanding window from the first observation, such as a price index
chained from monthly inflation (#30).
index_series() now accepts a Date column name in
base_period to choose a different base date for each group,
and warns when a leading missing value moves the default base to a later
date (#31).Fixed the ucm method never fitting a model. It now
estimates variances by maximum likelihood and returns the smoothed
level. Failed fits raise an error instead of returning a LOWESS trend,
and smoothing no longer applies to
ucm.
ucm_type now defaults to "BSM" for
frequencies 2 to 12 and to "level" otherwise. Explicit BSM
fits above monthly frequency are rejected because they can take several
minutes.
Fixed bn_ar_order being ignored. The
Beveridge-Nelson trend now uses the supplied nonnegative integer order,
selects an order by AIC only when none is supplied, and skips orders
whose arima() fit fails. Restored its progress message for
non-quiet calls.
Fixed the annual STL fallback using the annual HP default of 6.25
instead of lambda = 1600. It now matches
methods = "hp" and raises one warning.
Fixed methods = "ewma" failing on a series with one
observation.
Fixed error hints being dropped from df_to_ts() and
extract_trends() messages. Unrecognised frequencies now
list supported frequencies, and failed conversions explain the accepted
input.
augment_trends() no longer warns about short series
when .quiet = TRUE, matching
extract_trends().
augment_trends(), augment_rolling(),
and decompose_series() now handle date-column names that
overlap generated column names, preserving the date column and applying
the usual numeric suffix to the generated column.
extract_trends() and augment_trends()
now set the default band for bk and
cf from the series frequency, covering cycles of 1.5 to 8
years: c(18, 96) for monthly data, c(6, 32)
for quarterly, and c(2, 8) for annual. It was previously
c(6, 32) at every frequency, so monthly series were
filtered for cycles of 6 to 32 months. Pass band = c(6, 32)
to reproduce earlier monthly results.
extract_trends() and augment_trends()
now set the default HP lambda to
1600 * (frequency / 4)^4, following Ravn and Uhlig (2002):
129600 for monthly data (was 14400) and 6.25 for annual (was 14400).
Quarterly results are unchanged. The old monthly default put the
trend-cycle cutoff near six years instead of the ten implied by 1600 for
quarterly data. Pass smoothing = 14400 to reproduce earlier
monthly results. A smoothing value of 1 or less scales with
the same rule.
The HP filter now warns on weekly and daily data unless
smoothing or hp_lambda is set, even with
.quiet = TRUE. The warning names the lambda used.
extract_trends() and augment_trends()
now apply the first window to methods such as WMA in mixed vector-window
requests, with a warning, instead of silently using the default
window.
extract_trends() and augment_trends()
now honor Kalman smoothing as the measurement-to-process noise ratio and
preserve individually supplied noise variances. Explicitly supplying
both variances takes precedence over the ratio.
extract_trends() and augment_trends()
now report the STL estimator fallback even with
.quiet = TRUE. Quiet augmentation also consolidates
warnings and identifies affected groups.
augment_trends(), augment_rolling(),
decompose_series(), and index_series() now
keep groups distinct when their labels contain periods or combine
missing values with the literal string "NA".
augment_trends() and decompose_series()
now reject interior missing values in daily and weekly series instead of
silently removing those observations before estimation. Leading and
trailing missing values and irregular trading calendars remain
supported.
augment_rolling() now warns about every group whose
year-to-date accumulation starts mid-year.
index_series() now detects frequency independently
within each group, including groups with different dating conventions or
frequencies.
Centered even-window moving averages now return padded
NA values when the series cannot support the N+1 filter
weights.
roll_series() and augment_rolling() now
honor na_rm = TRUE for centered even-window means by
renormalizing the observed weights while retaining boundary
padding.
Fixed augment_trends(),
augment_rolling(), decompose_series(),
deseason_series(), and detrend_series()
returning rows in join or group order rather than preserving the
caller’s input order.
Fixed augment_trends() dropping the warnings raised
by the filter it dispatched to. An STL fallback on a non-seasonal series
now reaches the caller, along with the group it came from. A warning
raised for several groups is reported once.
Fixed augment_trends(),
augment_rolling(), and decompose_series()
dropping rows whose grouping column is NA. Those rows are
now treated as one more series and returned with the rest.
Fixed the ucm method never fitting a model. Every
variance was fixed, so stats::StructTS() failed and every
call fell back to lowess() with a warning. ucm
now estimates the variances by maximum likelihood and returns the
smoothed (two-sided) level instead of the filtered one. Failed fits
raise an error rather than return a LOWESS trend. smoothing
no longer applies to ucm, including in calls that combine
it with other methods.
ucm_type now defaults to "BSM" for
frequencies 2 to 12 and to "level" otherwise. With
maximum-likelihood variances, a level model on seasonal data puts the
seasonality into the level and returns the series itself.
Rejected explicit BSM fits above monthly frequency in
extract_trends() and decompose_series(), where
the state-space fit can take several minutes.
Fixed bn_ar_order being ignored. The
Beveridge-Nelson trend now uses the AR order it sets, requires one
nonnegative integer when supplied, and falls back to AIC selection only
when it is missing. Order selection also skips orders whose
arima() fit fails; before, a failed fit won the
selection.
Restored the Beveridge-Nelson progress message for non-quiet calls.
Fixed the STL fallback for annual series using
lambda = 1600 instead of the annual HP default of 6.25. The
fallback now matches methods = "hp" and raises one warning
instead of a warning and a message.
Fixed methods = "ewma" failing on a series with a
single observation.
Fixed error hints being dropped from the messages of
df_to_ts() and extract_trends(). Unrecognised
frequencies now list the supported ones, and a failed conversion
explains what input is accepted.
augment_trends() no longer warns about short series
when .quiet = TRUE, matching
extract_trends().
Fixed augment_trends(),
augment_rolling(), and decompose_series()
returning an all-NA column for daily and weekly series.
Results were converted back to a data frame through the ts
time index, which advances by 1/252 per observation while a
daily calendar skips weekends and holidays. The regenerated dates
therefore drifted from the real ones, and the join back onto the input
matched nothing. Results now carry the dates the series was built from,
so they rejoin the rows they were computed from.
Fixed augment_trends() and
augment_rolling() duplicating rows for semi-annual data.
The merge key floored dates to the calendar unit, mapping any frequency
other than 12 or 4 to the year, which put both halves of a year on one
key. The key now follows the frequency.
Fixed window = "ytd" resetting off-calendar for
daily and weekly series. The year came from the ts time
index, which advances a year every frequency observations,
so the reset drifted further from January each year. Year-to-date
accumulations now reset on the calendar year. A ts passed
directly to roll_series() carries no dates, so
"ytd" is rejected there for those frequencies.
augment_trends() and augment_rolling()
now reject a repeated date in a daily or weekly series. Two rows cannot
occupy one position, and results are matched back by date.
Rebuilt the trend_ma column of
coffee_arabica and coffee_robusta, which was
NA for every row because the datasets were generated while
the join above was broken. The column now holds the 22-observation
right-aligned moving average its documentation describes.
index_series() rescales one or more data-frame
series to a configurable base value, using either the earliest
observation or the mean over a year or date range, with support for
grouped data and multiple value columns.Fixed the placement of the 2xN moving average used by
extract_trends() and augment_trends() when
methods = "ma" is called with an even window
and align = "center". The filter weights were correct but
sat one period early, so the trend led the series by one month for a
monthly 2x12 and by one quarter for a quarterly 2x4. Composing two
centered RcppRoll passes caused this: for an even window,
align = "center" places n / 2 observations
after the anchor and only n / 2 - 1 before it, and the
offset survives the second pass. The 2xN filter is now applied directly
as the symmetric weights c(1/2, 1, ..., 1, 1/2) / N, which
also pads N / 2 positions at each end instead of
N / 2 - 1 at the start and N / 2 + 1 at the
end. Odd windows and non-centered alignments are unaffected, as are the
other moving average methods.
roll_series() and augment_rolling()
apply the same 2xN filter for stats = "mean" with an even
window and align = "center", so the rolling mean and the
ma trend method agree given the same window and alignment.
The other statistics have no such correction and use a window with one
extra period after the anchor.
Fixed rolling and year-to-date aggregations returning a backend
identity value for a window with no observed values under
na_rm = TRUE. A "sum" returned 0,
a "chain" returned 0, a "min"
returned Inf, a "max" returned
-Inf, a "mean" returned NaN, and
an "sd" returned 0. All six now return
NA, as does "sd" for a window holding a single
value.
roll_series() and augment_rolling() now
warn when a year-to-date accumulation starts mid-year. The first year of
a series beginning in, say, July accumulates from July rather than from
January, so it is not comparable with the years that follow. The values
are unchanged.
Fixed the chain scale warning never reaching a grouped
augment_rolling() call. The warning was gated on
.quiet, which the grouped path sets for every group.
.quiet now suppresses progress messages only. The scale and
calendar checks run once for the whole call rather than once per
group.
window = "ytd" now requires a seasonal frequency.
Annual data previously returned the series unchanged, since each year
holds one observation.
roll_series() and augment_rolling() now
warn about arguments the requested combination ignores:
align under window = "ytd", and
percent = TRUE without
stats = "chain".
A grouped augment_rolling() call with a window
longer than some group now names every group that is too short, with its
number of rows. The error previously came from whichever group failed
first and named none of them.
Documented that augment_rolling() preserves the
caller’s input row order.
Reorganized the pkgdown articles and package vignettes. The Trend Extraction Methods catalogue now lives on the documentation site as a pkgdown article.
Updated vignette and article plots to use the
ekioplot visual identity. The package is listed in
Suggests and is used only when building the
vignettes.
The method catalogue in the Trend Extraction Methods
article is now generated from the package’s method registry and shows
which of window, smoothing, band,
and align each method accepts, and which methods can run
one-sided.
Corrected the method documentation: window sets the
seasonal window for stl, smoothing is read on
a different scale by each method, bk and cf
return the series minus the chosen band, and triangular and
gaussian do not accept
align = "left".
New augment_rolling() and roll_series()
add rolling and year-to-date aggregations, mirroring the
augment_trends() / extract_trends() pair.
augment_rolling() takes a data frame and adds
roll_{stat}_{window} columns
(e.g. roll_sum_12); roll_series() takes a
ts, xts, or zoo object and
returns ts results. Six statistics are available:
"sum", "chain", "mean",
"sd", "min", and "max".
stats = "chain" calculates
prod(1 + r) - 1 which assumes the series is a rate, e.g.,
monthly inflation rate. Use percent = TRUE when rates are
in percentage points; a warning is issued when the values look
mis-scaled for the declared setting.
window = "ytd" computes an expanding year-to-date
accumulation that resets each January, for any of the six
statistics.
align defaults to "right", the
convention for accumulated economic indicators, rather than the centered
default used for trends.
Grouped series are supported via group_cols, and
multiple value_col entries are suffixed with the column
name.
Rolling statistics are kept in a registry separate from the trend
methods. A rolling sum is not in the units of the series, so it is not a
trend and cannot be passed to detrend_series().
Series with gaps were previously handled differently by each entry point, and the disagreements were silent. Missing value handling is now one policy, applied everywhere: a gap inside the observed span is rejected, and missing values at the edges are excluded from estimation rather than rejected.
augment_trends(), decompose_series(),
deseason_series(), and detrend_series() no
longer return silently misdated results for series with interior gaps.
These functions assumed the series had no gaps. Because observations are
positioned by period, a missing period shifted every later value one
slot earlier, so results merged back onto the wrong dates and the series
lost its final period. Interior gaps and duplicated periods are now
rejected, whether the gap comes from a row with a missing value or from
a period absent from the data.
extract_trends() now rejects missing values inside
the observed span of a ts, xts, or
zoo input. Previously they were passed straight to the
filters, where the outcome depended on the method and was usually
silent: stl, spline, and hamilton
raised an error, hp, bk, and cf
returned an all-NA series, and the recursive methods
(ewma, bn) propagated the gap to every later
observation. Impute the gaps before extracting a trend.
Leading and trailing missing values continue to work.
extract_trends() excludes them from estimation and returns
the result on the time base of the input, with NA for the
periods that were never observed.
augment_rolling() and roll_series() are
the exception, by design. A rolling window has well-defined local
semantics for a gap, so rows with missing values keep their calendar
position and na_rm controls whether an affected window
yields NA or is computed from the observations
present.
df_to_ts() was the last entry point still building a
misdated series from a gapped input, and it now applies the same check.
A missing or duplicated period is rejected instead of shifting every
later observation one slot earlier. Rows are also sorted before
conversion, and a row with no date is dropped with a warning rather than
left in place to occupy a period it cannot be positioned in. Missing
values are kept, which is what leaves the series correctly
dated.
df_to_ts() now counts the starting period in units
of the frequency. It previously used the calendar month whatever the
frequency, so a quarterly series beginning in April started at Q4, an
annual series dated March started two years late, and a semiannual
series beginning in July started at H1. Monthly series were
unaffected.
Series whose frequency has no exact calendar period (weekly, daily) are not grid-checked. Their starting period is now placed proportionally within the year rather than defaulting to the first period.
Fixed the error raised when a data frame has no rows.
augment_trends(), augment_rolling(),
decompose_series(), deseason_series(), and
detrend_series() now say the input has no rows. The message
previously came from frequency detection or from a complete-cases check
further downstream, and named neither the argument nor the
problem.
Fixed augment_trends() and
decompose_series() returning NULL for a
grouped call on a data frame with no rows.
Fixed augment_trends() and
decompose_series() failing on a grouped call when the
grouping column is a factor with unused levels. split()
turns an unused level into an empty group, which was sent through the
conversion path and rejected for having no complete cases. Empty groups
are now dropped, as augment_rolling() already did.
Function documentation has been tightened, and the missing value policy is now stated on the arguments it applies to.
Reworded the README, the vignettes, and the help pages: cut the fixed method counts that go stale on each release, replaced promises that the components sum back exactly with what the functions do, and removed the duplicated sections in Getting Started.
Added "henderson" to the documented
methods options of augment_trends() and
extract_trends(). The method has always been accepted, but
the help pages listed the other nineteen.
This release combines the 1.3.0 development series, which was never published on CRAN, with the 1.4.0 changes.
decompose_series() is now exported and available for
use. This pipe-friendly function decomposes a time series into trend,
seasonal, and remainder components, adding trend_*,
seasonal_*, and remainder_* columns to the
input data frame. Five methods are available: "stl"
(default), "regression", "classic" (classical
decomposition via centred moving averages,
stats::decompose()), "bsm" (Basic Structural
state-space Model estimated by the Kalman smoother,
stats::StructTS()), and "seats"
(X-13ARIMA-SEATS via the optional seasonal
package, a Suggested dependency only required for this method). It
supports grouped decomposition via group_cols, and the
components add back up to the original series. See the new
Decomposing Series vignette. Additional conveniences:
methods accepts a vector
(e.g. c("stl", "classic")), adding each method’s components
as separate columns for side-by-side comparison.transform = "log" provides a uniform multiplicative
decomposition across every method (decompose on the log scale,
exponentiate back).seasadj = TRUE adds a seasadj_{method}
column with the seasonally adjusted series.deseason_series() is a new convenience wrapper
around decompose_series() focused on seasonal adjustment.
It adds a seasadj_{method} column with the deseasoned
series (methods "stl" or "seats"), and
optionally the full trend/seasonal/remainder decomposition via
components = TRUE.
detrend_series() is a new convenience wrapper around
augment_trends() that returns the detrended series,
i.e. the deviation from the trend (the cycle in economics).
transform = "log" returns the log deviation from trend
(approximately the percentage deviation, the output-gap convention), and
components = TRUE also keeps the fitted
trend_{method} columns.
h = 8, p = 4) regardless of frequency, so
monthly series were filtered with a two-quarter horizon instead of the
recommended two-year one. Monthly data now defaults to
h = 24, p = 12 (Hamilton 2018); quarterly
behaviour is unchanged. Because the monthly defaults are larger, monthly
series now require at least 37 observations (h + p + 1) and
the first 35 trend values are NA (previously 13 and 11).
Pass params = list(hamilton_h = , hamilton_p = ) to
reproduce old results.Removed the glue dependency. The
two remaining glue::glue() calls were replaced by the
interpolation cli already provides.
Removed dead internal code left over from earlier refactors: the
unused .ensure_odd_window() and
.check_deprecated_params() helpers, leftover
zlema references, and stale
HoltWinters/roll_median namespace
imports.
The list of valid methods is now defined in a single internal
registry, which augment_trends() and
extract_trends() both read from. The valid decomposition
methods for decompose_series() are defined there as
well.
The unified parameter validation (window,
smoothing, band, align,
params) shared by augment_trends() and
extract_trends() now lives in a single internal helper,
shared by both functions.
Added a Trend Extraction Methods vignette cataloguing all 20 trend methods by family.
Added a Detrending Series vignette covering
detrend_series(): the deseason-then-detrend workflow for
seasonal data, percentage deviations from trend via
transform = "log", method comparison (HP vs Hamilton), and
grouped detrending.
Removed outdated references to the
TTR package from the
augment_trends() and extract_trends()
documentation. The EWMA window parameter is now documented
by what it does: it sets alpha = 2 / (window + 1).
group_vars argument in
augment_trends() is deprecated in favour of
group_cols, and calls with group_vars now
issue a deprecation warning. Replace group_vars = ... with
group_cols = ...; group_vars will be removed
in a future release.augment_trends() accepts multiple value columns
through a character vector in value_col. Trends are
extracted for each column and named trend_{method}_{col}
(e.g. trend_stl_consumption).
The UCM trend estimator now uses fixed variance components with
signal-to-noise ratios derived from Hodrick-Prescott filter lambdas,
producing smoother trends by default. The smoothing
parameter overrides the default.
Added two Transport for London datasets:
transit_london_monthly, monthly totals of reported bus and
Tube journeys, and transit_london_avgs, monthly averages of
the reported daily journey counts.
group_cols in place of the
deprecated group_vars.Release Date: November 2025
Removed Butterworth and Savitzky-Golay filters:
The Butterworth low-pass filter and the Savitzky-Golay polynomial
smoothing have been removed to focus the package on core econometric
methods. The signal package dependency has been
removed.
Removed exponential smoothing methods: Simple
and double exponential smoothing (exp_simple,
exp_double) have been removed. Users can continue using
EWMA for exponential smoothing. The forecast package
dependency has been removed.
Release Date: January 2025
window=12, align="center" now correctly
applies a 2x12 MA instead of naive centeringglue package to Imports for message
formatting.ma_2x() internal function implementing proper
double-smoothing.ensure_odd_window() utility function for future
useThis is an important correctness fix for users doing seasonal adjustment or business cycle analysis with monthly/quarterly data. The new implementation ensures that centered moving averages with even windows produce econometrically sound results.
The first production release of trendseries, an R package for extracting trends from economic time series.
Two functions cover the main workflows. augment_trends()
takes a data frame and adds one trend_{method} column per
requested method, with grouped operations through dplyr.
extract_trends() takes ts, xts,
or zoo objects and returns ts results.
Four families cover the methods in this release.
Both functions share one parameter system across every method, with
window, smoothing, band,
align, and params. Defaults track the series
frequency, so the HP filter sets lambda to 1600 for quarterly and 14400
for monthly data, moving averages default to four-quarter or
twelve-month windows, and bandpass filters use the 6-to-32-quarter
business cycle range. Monthly and quarterly series are the main target;
STL and the moving average methods also run on daily and other
frequencies.
Ten economic datasets ship with the package.
gdp_construction,
ibcbr, vehicles, oil_derivatives,
and electric.retail_households and
retail_autofuel.coffee_arabica and
coffee_robusta, both daily.series_metadata.# install.packages("devtools")
devtools::install_github("viniciusoike/trendseries")library(trendseries)
gdp_construction |>
augment_trends(value_col = "index", methods = c("hp", "bk", "ma"))Built on mFilter, hpfilter, RcppRoll, forecast, dlm, signal, and tsbox. MIT license; requires R 4.1.0 or later.