Time-series features in feasts

A guide to feature extraction and interpretation

Author

Dianne Cook

0.1 Overview

The feasts package provides feature extraction and statistical tools for tidy time-series analysis. It is designed to work with tsibble data and with fabletools::features(), making it possible to reduce each time series to a vector of numerical features that describe characteristics such as trend, seasonality, autocorrelation, intermittency, stability, and spectral structure.

This report focuses on features of individual time series rather than the plotting and decomposition facilities of feasts.

The current CRAN release used as the reference here is feasts 0.5.0.

0.2 What is a time-series feature?

A time-series feature is a numerical summary intended to capture some characteristic of a time series.

For example:

  • a measure of trend strength describes how much of the variation can be associated with the trend;
  • a measure of seasonal strength describes how strongly a repeating seasonal component contributes to the series;
  • an autocorrelation feature describes dependence between observations separated by a particular lag;
  • spectral entropy describes how concentrated or dispersed the series’ spectral energy is across frequencies;
  • lumpiness describes how much the local variance changes over time.

Features are therefore a form of dimension reduction for time-series data. Instead of comparing two long series point-by-point, we can compare their feature vectors.

This is particularly useful in feature-based time-series analysis, where many time series are represented in a common feature space and then explored with graphics or multivariate methods.

0.3 Extracting features with features()

The usual workflow is to supply a list of feature functions to fabletools::features().

For example:

The result contains the identifying variables from the original tsibble together with one or more numerical features for each series.

A useful way to think about the workflow is:

many time series
      |
      v
feature extraction
      |
      v
feature matrix
      |
      +--> visualisation
      +--> clustering
      +--> dimension reduction
      +--> classification
      +--> similarity search

Feature extraction does not replace looking at the original series. It provides a compact representation that can be useful for comparing many series.

1 Feature families

The main feature families available in feasts can be grouped as follows.

Family Main functions What they describe
STL decomposition feat_stl() trend, seasonality, curvature, spikes and residual dependence
Autocorrelation feat_acf() serial dependence in the original and differenced series
Partial autocorrelation feat_pacf() direct dependence at successive lags
Distribution/structure n_crossing_points(), longest_flat_spot() crossings of the median and runs in value intervals
Long-range dependence coef_hurst() persistence / fractional differencing
Stability var_tiled_mean(), var_tiled_var() change in local level and variance
Change points shift_level_max(), shift_var_max(), shift_kl_max() largest local distributional changes
Frequency domain feat_spectral() concentration of spectral density
Intermittency feat_intermittent() zero occurrence and spacing in intermittent demand
Conditional heteroskedasticity stat_arch_lm() ARCH-type dependence in squared observations
Stationarity unitroot_kpss(), unitroot_pp() evidence relevant to unit roots
Differencing unitroot_ndiffs(), unitroot_nsdiffs() number of ordinary/seasonal differences indicated
Residual whiteness box_pierce(), ljung_box() remaining autocorrelation
Transformation guerrero() choice of Box–Cox transformation parameter

Some of these are more naturally thought of as statistical tests or modelling diagnostics than descriptive features. They are nevertheless available through the feature-extraction framework.

2 STL features: feat_stl()

feat_stl() extracts features from an STL decomposition:

[ y_t = T_t + S_t + R_t, ]

where (T_t) is the trend, (S_t) is the seasonal component and (R_t) is the remainder.

The function requires a seasonal period, .period.

      trend_strength seasonal_strength_12     seasonal_peak_12 
           0.9909550            0.9406725            7.0000000 
  seasonal_trough_12            spikiness            linearity 
          11.0000000            3.0277699         1325.3317266 
           curvature           stl_e_acf1          stl_e_acf10 
         131.0646804            0.5091751            0.9303664 

2.1 Trend strength

trend_strength measures the strength of the trend relative to the variance remaining after removing the seasonal component.

It is scaled between 0 and 1:

  • near 0: little evidence of a systematic trend;
  • near 1: most of the remaining variation is associated with the trend.

It is important to remember that this is a feature of the particular decomposition, not a hypothesis test for whether a trend exists.

2.2 Seasonal strength

seasonal_strength_* measures the strength of each seasonal component.

The value is between 0 and 1:

  • near 0: weak seasonality;
  • near 1: strong seasonality.

For a monthly series with annual seasonality, for example, the feature is based on the seasonal component with period 12.

2.3 Seasonal peak and trough

seasonal_peak_* and seasonal_trough_* identify the position in the seasonal cycle at which the seasonal component reaches its maximum and minimum.

These are useful when two series have similarly strong seasonality but their peaks occur at different times of year.

The interpretation of the numerical position depends on the seasonal period. For example, with .period = 12, the values identify positions in a 12-month cycle.

2.4 Spikiness

spikiness measures variation in the remainder variance. It is based on leave-one-out estimates of the remainder variance.

A large value indicates that the series contains observations that have a large influence on the estimated remainder variance.

Thus, spikiness is better interpreted as instability of the residual variance associated with unusual observations, rather than simply “the number of spikes”.

2.5 Linearity

linearity is obtained from a quadratic approximation to the estimated trend.

It measures the linear component of the trend. A positive value indicates an increasing linear component and a negative value a decreasing component.

Because it is based on the scale of the series and the index, it should generally not be compared directly across series with very different scales without appropriate standardisation.

2.6 Curvature

curvature is the quadratic coefficient from the same approximation.

It captures whether the trend bends rather than following an approximately straight line.

2.7 Residual autocorrelation

feat_stl() also returns:

  • stl_e_acf1 — the lag-1 autocorrelation of the STL remainder;
  • stl_e_acf10 — the sum of squared autocorrelations over the first ten lags.

These are useful for identifying series whose STL decomposition has not removed all serial dependence.

3 Autocorrelation features: feat_acf()

Autocorrelation measures the similarity between observations separated by a given lag.

The autocorrelation at lag (k) is

[ k = (y_t,y{t-k}). ]

feat_acf() summarises autocorrelation in the original series and after first and second differencing.

       acf1       acf10  diff1_acf1 diff1_acf10  diff2_acf1 diff2_acf10 
  0.9480473   5.6700871   0.3028553   0.4088376  -0.1910059   0.2507803 
season_acf1 
  0.7603950 

The features include:

  • acf1 — lag-1 autocorrelation of the original series;
  • acf10 — sum of squared autocorrelations over the first ten lags;
  • diff1_acf1 — lag-1 autocorrelation after first differencing;
  • diff1_acf10 — summed squared autocorrelations after first differencing;
  • diff2_acf1 — lag-1 autocorrelation after second differencing;
  • diff2_acf10 — summed squared autocorrelations after second differencing.

For seasonal data, an additional feature is returned:

  • season_acf — autocorrelation at the first seasonal lag.

3.0.1 Interpretation

A large positive acf1 indicates persistence: adjacent observations tend to be similar.

A large acf10 indicates substantial serial dependence across the first several lags.

The differenced features help distinguish persistence in levels from persistence that remains after removing a trend-like component.

4 Partial autocorrelation: feat_pacf()

The partial autocorrelation function (PACF) measures the association between (y_t) and (y_{t-k}) after accounting for the intermediate lags.

feat_pacf() returns summaries of the first five partial autocorrelations for:

  • the original series;
  • the first-differenced series;
  • the second-differenced series.

For seasonal series it also returns:

  • season_pacf — the partial autocorrelation at the first seasonal lag.

The squared coefficients are summed, so larger values indicate stronger overall partial autocorrelation over the specified early lags.

      pacf5 diff1_pacf5 diff2_pacf5 season_pacf 
  0.9670971   1.1764357   1.1541896  -0.1354311 

PACF features can therefore help distinguish series whose dependence structure is concentrated in a few direct lags from series whose dependence is more diffuse.

5 Crossing points: n_crossing_points()

n_crossing_points() counts how often the series crosses its median.

Conceptually, it asks:

How frequently does the series move from one side of its median to the other?

n_crossing_points 
                7 

A high value indicates frequent oscillation around the centre of the series. A low value can occur when the series spends long periods predominantly above or below its median.

This feature can be useful for distinguishing smooth, slowly changing series from rapidly fluctuating series.

6 Flat spots: longest_flat_spot()

longest_flat_spot() divides the observed value range into ten equal-width intervals and finds the longest consecutive run within any one interval.

longest_flat_spot 
               18 

A large value indicates that the series spends a long time within a relatively narrow portion of its value range.

The feature is therefore related to plateaus or periods of little movement, but it should not be interpreted as a direct estimate of the number of flat periods.

Older versions of feasts used the name n_flat_spots(). This was renamed to longest_flat_spot() because the feature is a maximum run length rather than a count of flat spots.

7 Hurst coefficient: coef_hurst()

The Hurst coefficient is intended to describe long-range dependence or persistence.

coef_hurst 
 0.9992466 

The implementation is based on the fractional differencing parameter (d), with

[ H = d + . ]

Roughly:

  • (H ): behaviour consistent with weak dependence;
  • (H > 0.5): persistent behaviour;
  • (H < 0.5): anti-persistent behaviour.

The Hurst coefficient is not a general-purpose “trend score”. In particular, apparent long-range dependence can be confounded with nonstationarity and other forms of temporal structure.

8 Stability and lumpiness

feasts measures changes in local means and local variances using non-overlapping tiled windows.

8.1 Stability: var_tiled_mean()

var_tiled_mean() calculates the variance of the means of successive windows.

var_tiled_mean 
     0.9330704 

A large value means that the local level changes substantially across the series.

This is called stability in the feature-based time-series literature: a stable series has similar local means over time.

8.2 Lumpiness: var_tiled_var()

var_tiled_var() calculates the variance of the local variances.

var_tiled_var 
   0.01924803 

A large value means that the variability of the series changes substantially from one part of the series to another.

Thus:

  • stability concerns changes in local level;
  • lumpiness concerns changes in local variance.

The window size is important. When .size is not supplied, it is selected using .period.

9 Local distributional shifts

Three features use sliding windows to identify the largest change between neighbouring portions of the series.

9.1 Level shift: shift_level_max()

shift_level_max() finds the largest difference between local means.

It returns both the magnitude of the largest shift and its location.

  shift_level_max shift_level_index 
             54.5             128.0 

A large value indicates a substantial local change in level.

9.2 Variance shift: shift_var_max()

shift_var_max() finds the largest difference between local variances.

  shift_var_max shift_var_index 
       2342.152         118.000 

It can identify changes from a relatively stable regime to a highly variable regime, or vice versa.

9.3 Distributional shift: shift_kl_max()

shift_kl_max() compares neighbouring windows using a Kullback–Leibler divergence based on estimated distributions.

  shift_kl_max shift_kl_index 
     0.1210444    122.0000000 

A large value indicates a strong change in the estimated local distribution.

Unlike shift_level_max() and shift_var_max(), this is not restricted to changes in a single moment such as the mean or variance.

10 Spectral entropy: feat_spectral()

feat_spectral() measures the entropy of the spectral density.

The spectral density describes how variation in a series is distributed over frequencies.

spectral_entropy 
       0.2961049 

The spectral entropy is based on Shannon entropy:

[ H_s = -_j p_j (p_j), ]

where (p_j) represents the normalised spectral density.

The interpretation is:

  • low spectral entropy: spectral energy is concentrated in a relatively small number of frequencies;
  • high spectral entropy: spectral energy is spread across many frequencies.

A strongly periodic signal therefore tends to have lower spectral entropy than a series whose variation is distributed broadly across frequencies.

The current implementation estimates the spectral density using stats::spec.ar() with the Burg method.

11 Intermittency: feat_intermittent()

Intermittent time series contain many zero observations, as is common with demand data for products that are ordered only occasionally.

feat_intermittent() returns four features.

     zero_run_mean nonzero_squared_cv    zero_start_prop      zero_end_prop 
          2.333333           0.280000           0.200000           0.000000 

11.1 zero_run_mean

The average interval between non-zero observations.

Large values indicate that demand occurs relatively infrequently.

11.2 nonzero_squared_cv

The squared coefficient of variation of the non-zero observations:

[ CV^2 = ( {(x_{})})^2. ]

It describes the variability of demand when demand occurs, rather than the variability caused by zeros.

11.3 zero_start_prop

The proportion of the series occupied by the initial run of zeros.

11.4 zero_end_prop

The proportion of the series occupied by the final run of zeros.

These features are useful for characterising different kinds of intermittent demand.

12 ARCH statistic: stat_arch_lm()

stat_arch_lm() measures dependence in the squared observations using an ARCH-LM-style statistic.

ARCH effects occur when the conditional variance changes over time in a way related to previous squared observations.

stat_arch_lm 
   0.9171945 

A larger value indicates stronger evidence of dependence in the squared series.

This feature is especially relevant when the interest is in time-varying volatility rather than only changes in the mean.

13 Stationarity and differencing

Some feasts functions provide statistical diagnostics rather than purely descriptive features.

13.1 KPSS test: unitroot_kpss()

The KPSS test is used to assess whether a time series is stationary around a specified deterministic component.

  kpss_stat kpss_pvalue 
   2.739474    0.010000 

The function returns a test statistic and an associated p-value.

The interpretation is test-based, so it should not be treated as a continuously scaled descriptive feature in the same way as trend_strength or spectral_entropy.

13.2 Phillips–Perron test: unitroot_pp()

unitroot_pp() provides the Phillips–Perron unit-root test.

  pp_stat pp_pvalue 
-1.844341  0.100000 

It returns the test statistic and p-value.

The KPSS and Phillips–Perron tests have different null hypotheses, so their p-values should not be interpreted as interchangeable measures of stationarity.

13.3 Number of ordinary differences: unitroot_ndiffs()

unitroot_ndiffs() determines the minimum number of ordinary differences required to obtain a series judged stationary according to the supplied unit-root function.

ndiffs 
     1 

The default procedure uses the KPSS p-value.

The result is usually a small integer such as 0, 1, or 2.

13.4 Number of seasonal differences: unitroot_nsdiffs()

unitroot_nsdiffs() similarly estimates the number of seasonal differences required.

nsdiffs 
      1 

The default approach uses seasonal strength from an STL decomposition rather than a formal seasonal unit-root test.

14 Residual autocorrelation tests

14.1 Box–Pierce: box_pierce()

The Box–Pierce test assesses whether a group of autocorrelations is jointly consistent with zero.

It is commonly used as a diagnostic for model residuals.

  bp_stat bp_pvalue 
 129.4263    0.0000 

14.2 Ljung–Box: ljung_box()

The Ljung–Box test is a modified version of the portmanteau test that generally has better small-sample behaviour than the original Box–Pierce statistic.

  lb_stat lb_pvalue 
 132.1415    0.0000 

These are better regarded as diagnostic tests than as intrinsic descriptive characteristics of a time series.

15 Guerrero’s method: guerrero()

guerrero() is used to choose a Box–Cox transformation parameter.

A Box–Cox transformation is

[ w_t = \begin{cases} \dfrac{y_t^\lambda-1}{\lambda}, & \lambda \ne 0,\\ \log(y_t), & \lambda = 0. \end{cases}

]

Guerrero’s method chooses () by seeking a transformation that makes the variability relatively stable across seasonal subseries.

lambda_guerrero 
      0.1107572 

This is therefore a feature-like numerical summary used to guide preprocessing rather than a direct description of the dynamics of the untransformed series.

16 Putting the features together

A broad feature set can be extracted as follows:

The result is a feature matrix, with one row for each time series.

This representation can then be explored using ordinary multivariate methods.

For example:

Or the feature matrix can be standardised before applying dimension reduction or clustering:

17 How to interpret the feature space

A feature vector should not be treated as an objective replacement for the original series.

Two important considerations are:

  1. Features encode particular notions of similarity.
    Two series may have very different shapes but similar feature values, or look similar while differing on a feature that captures a property not obvious visually.

  2. Many features are dependent on scale, decomposition choices, or tuning parameters.
    For example, STL features depend on the seasonal period; tiled features depend on window size; shift features depend on the sliding-window size.

For exploratory analysis, it is therefore useful to combine:

  • feature-space visualisation;
  • plots of representative original time series;
  • knowledge of the data-generating context;
  • sensitivity to the feature definitions and tuning parameters.

18 A compact feature dictionary

Feature Interpretation
trend_strength Strength of the STL trend, from 0 to 1
seasonal_strength_* Strength of an STL seasonal component, from 0 to 1
seasonal_peak_* Position of the seasonal peak
seasonal_trough_* Position of the seasonal trough
spikiness Variation in the estimated remainder variance
linearity Linear component of the estimated trend
curvature Quadratic component of the estimated trend
stl_e_acf1 Lag-1 autocorrelation of the STL remainder
stl_e_acf10 Squared autocorrelation summary of the STL remainder
acf1 Lag-1 autocorrelation
acf10 Squared autocorrelation summary over early lags
diff1_acf1 Lag-1 autocorrelation after first differencing
diff1_acf10 Autocorrelation summary after first differencing
diff2_acf1 Lag-1 autocorrelation after second differencing
diff2_acf10 Autocorrelation summary after second differencing
season_acf Autocorrelation at the first seasonal lag
pacf5 Squared PACF summary over the first five lags
diff1_pacf5 PACF summary after first differencing
diff2_pacf5 PACF summary after second differencing
season_pacf PACF at the first seasonal lag
n_crossing_points Number of crossings of the median
longest_flat_spot Longest run within one of ten value intervals
coef_hurst Hurst coefficient / persistence measure
stability Variance of tiled local means
lumpiness Variance of tiled local variances
shift_level_max Largest local mean shift
shift_level_index Location of the largest level shift
shift_var_max Largest local variance shift
shift_var_index Location of the largest variance shift
shift_kl_max Largest local KL-divergence shift
shift_kl_index Location of the largest distributional shift
spectral_entropy Dispersion of spectral energy across frequencies
zero_run_mean Average spacing between non-zero observations
nonzero_squared_cv Squared CV of non-zero observations
zero_start_prop Proportion in the initial zero run
zero_end_prop Proportion in the final zero run
arch_lm ARCH-type dependence in squared observations
kpss_stat / kpss_pvalue KPSS stationarity diagnostic
pp_stat / pp_pvalue Phillips–Perron unit-root diagnostic
ndiffs Number of ordinary differences indicated
nsdiffs Number of seasonal differences indicated
bp_stat / bp_pvalue Box–Pierce residual autocorrelation diagnostic
lb_stat / lb_pvalue Ljung–Box residual autocorrelation diagnostic
lambda_guerrero Box–Cox parameter selected by Guerrero’s method

19 Practical cautions

19.1 Features are not automatically comparable

Some features are bounded and naturally comparable, while others depend on the scale of the original series.

For example, trend_strength is bounded between 0 and 1, whereas linearity is not a scale-free quantity.

Before clustering or computing distances between feature vectors, it is usually sensible to inspect the feature distributions and standardise appropriate variables.

19.2 The seasonal period matters

Several features depend explicitly on .period.

For example:

      trend_strength seasonal_strength_12     seasonal_peak_12 
           0.9909550            0.9406725            7.0000000 
  seasonal_trough_12            spikiness            linearity 
          11.0000000            3.0277699         1325.3317266 
           curvature           stl_e_acf1          stl_e_acf10 
         131.0646804            0.5091751            0.9303664 

and

     trend_strength seasonal_strength_6     seasonal_peak_6   seasonal_trough_6 
          0.9724289           0.5264522           1.0000000           5.0000000 
          spikiness           linearity           curvature          stl_e_acf1 
         30.2808961        1327.3029759         126.0163956           0.5386102 
        stl_e_acf10 
          1.7831103 

describe different decompositions.

The seasonal period should therefore be based on the temporal structure of the data rather than selected simply to make a feature extraction call run.

19.3 Missing values matter

Some features require contiguous observations or use specific handling of missing values. Before extracting a large feature set, it is worth checking missingness and the effective length of each series.

19.4 Feature extraction is exploratory

A feature such as trend_strength = 0.8 does not mean that an 80% probability of a trend has been established.

Likewise, a high spectral_entropy is not a hypothesis test.

The numerical values describe properties induced by particular algorithms and definitions.

20 Suggested workflow for teaching or exploratory analysis

A useful teaching sequence is:

  1. Plot the original series.
  2. Identify plausible temporal structure: trend, seasonality, changing variance, intermittency and dependence.
  3. Extract a small set of interpretable features.
  4. Visualise the feature distributions.
  5. Explore relationships among features.
  6. Use dimension reduction or clustering if there are many series.
  7. Return to the original time series to interpret interesting groups or outliers.

This makes the feature vector a bridge between time-series graphics and multivariate exploratory analysis, rather than treating feature extraction as an end in itself.

21 References

The feasts package documentation and source code provide the authoritative definitions of the implemented features. The feature framework is closely related to the feature-based time-series analysis literature developed by Hyndman and collaborators.

For the theoretical background to the STL features, see Hyndman and Athanasopoulos, Forecasting: Principles and Practice, section on measuring trend and seasonality strength.

For intermittent-demand features, see Kostenko and Hyndman (2006).

For spectral entropy, see Gibson and Jung (2006) and Goerg (2013).

For the broader feature-based approach, see Hyndman’s discussion of feature-based time-series analysis.