> For the complete documentation index, see [llms.txt](https://time-series-features.gitbook.io/hctsa-manual/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://time-series-features.gitbook.io/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/interpreting-features.md).

# Interpreting features

A typical data analysis procedure might first involve carefully inspecting your data, then thinking about the patterns you might want to capture, then trying to devise an algorithm to capture them (testing the algorithm carefully on simulated data, perhaps, to test its behavior, dependence on noise level, time-series length, sensitivity to low-order trends or other types of stationarity, etc.). But in *hctsa* we do something somewhat dangerous—apply thousands of methods without even needing to first even look at the data :astonished:

![](https://254595279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LEJuzw5fsQv4fwW3E7P%2Fuploads%2Fgit-blob-317e0463a17593fb38639e954d3636257b48f85b%2Fimage.png?alt=media)

A conventional data analysis pipeline starts with the hard work of thinking about the dynamics and carefully selecting time-series analysis methods with deep knowledge of how they work and the assumptions that make their interpretation valid. This hard work is not avoided in *hctsa*, although if you're lucky, *hctsa* may save you a bunch of work—if it identifies high-performing features, you can focus your energy on interpreting just these. But this hard work comes at the end of an analysis, and is difficult and time-consuming to do well—often involving looking deeply into the algorithms and theory underlying these methods, combined with careful inspection of the data to ensure you are properly interpreting them.

Being the most substantial challenge—one that cannot be automated and requires alot of careful thought—this page provides some basic advice. While it's often challenging to work out what a given feature is capturing in your dataset, it is the most interesting part of an analysis, as it points you to interpretable scientific theory/algorithms, and makes you think carefully about the structure in your time series.

## Finding features to interpret

### 1: Identify significant features

Sometimes you will run `TS_TopFeatures` and find no features that significantly distinguish time series recorded from the labeled groups in your dataset. In this case, there may not be a signal, the signal may be too small given your sample size (and the perhaps large number of candidate time-series features, if using the *hctsa* set), or the right statistical estimators may not be present in *hctsa*.

But other times you will obtain a long list of statistically significant (after multiple hypothesis correction) features (e.g., from `TS_TopFeatures`) with values that significantly distinguish the groups you care about (individuals with some disease diagnosis compared to that of healthy controls). In cases like this, the next step is to obtain some understanding of what's happening.

Consider a long and kinda gnarly list, that is quite hard to make sense of. This is a typical situation because the features names in *hctsa* are long and typically hard to interpret directly. For example, here are the top features (out of 40 listed) for distinguishing seizure from eyes-open EEG, from the `TS_TopFeatures('norm','classification',[],'numNulls',10)` run described in [Finding informative features](/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/ts_topfeatures.md) (in-sample linear classification accuracy, *hctsa* v3.0.0):

```
[974] DN_CompareKSFit_uni_psy (distribution,ksdensity,raw,spreadDependent) -- 99.50%
[1342] EN_LZComplexity_7_diff (entropy,mex) -- 99.50%
[1344] EN_LZComplexity_8_diff (entropy,mex) -- 99.50%
[1346] EN_LZComplexity_9_diff (entropy,mex) -- 99.50%
[3499] NL_TakensEstimator_n1_c001_ac1_1_3 (nonlinear,scaling,dimension,tisean) -- 99.50%
[3529] NL_d2_1_10_ac1_takens05_iqr (nonlinear,tisean,dimension) -- 99.50%
[4193] PP_SchreiberDenoise_5_1_1_03_ac1Change (preprocessing,nonlinear,correlation) -- 99.50%
[4200] PP_SchreiberDenoise_5_1_4_03_ac1Change (preprocessing,nonlinear,correlation) -- 99.50%
[359] CO_Embed2_AngleTau_50_max_thetaac1 (correlation,embedding) -- 99.00%
[1340] EN_LZComplexity_6_diff (entropy,mex) -- 99.00%
[1348] EN_LZComplexity_10_diff (entropy,mex) -- 99.00%
[3500] NL_TakensEstimator_n1_c001_ac1_1_8 (nonlinear,scaling,dimension,tisean) -- 99.00%
[3528] NL_d2_1_10_ac1_takens05_std (nonlinear,tisean,dimension) -- 99.00%
[1366] EN_MSE_1-10_2_015_diff1_sampen_s1 (entropy) -- 98.50%
[1476] EN_SampEn_5_005_sampen2 (entropy) -- 98.50%
[2154] MD_RawHRVMeas_CVI (medical,raw,spreadDependent) -- 98.50%
[3526] NL_d2_1_10_ac1_takens05_max (nonlinear,tisean,dimension) -- 98.50%
[4268] SB_MotifThree_diffquant_cb (symbolic,motifs) -- 98.50%
[4292] SB_MotifThree_diffquant_cba (symbolic,motifs) -- 98.50%
[5172] SC_FluctAnal_2_dfa_50_2_logi_r2_alpha (scaling) -- 98.50%
[6330] WL_DetailCoeffs_db3_max_std_mean (wavelet) -- 98.50%
...
```

In this example, 4694 of the 6126 features were significant (FDR-corrected at 0.05).

If there are hundreds or thousands of statistically significant features, some of which have much higher accuracy than others, you may first want to inspect only a subset of features, e.g., the 50 or 100 top-performing features (with the most significant differences). If you can make sense of these first, you will usually have a pretty good basis for working out what's going on with the features with lower performance (if indeed you want to go beyond interpreting the best-performing features).

### 2: Find groups of similar features

`TS_TopFeatures` is helpful for showing us how features with different, long, and complicated names might cluster into groups of similarly behaving features that are measuring similar properties of the data (cf. the [previous section](/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/ts_topfeatures.md)). This is essential to interpret groups of features that are capturing the same underlying conceptual property of the dynamics. It helps you work out what's going on, and avoids over-interpreting any specific feature in isolation. For example, sometimes features with very complicated sounding names (like nonlinear embedding dimension estimates) exhibit very similar behavior to very simple features (like lag-1 autocorrelation or distributional outlier measures), indicating that the nonlinear embedding dimension estimates are likely sensitive to simpler properties in the time series (e.g., sensitive to outliers in the data). Intepreting the nonlinear embedding dimension in isolation would be a mistake in such a case.

Plotting and inspecting clustered feature–feature correlation plots are crucial for identifying groups of features with similar behavior on the dataset. Then we can inspect and interpret these groups of similar (highly inter-correlated) features together as a group. This should be the first step in interpreting a set of significant features: which groups of features behave similarly, and what common concepts are they measuring?

An example is here, were [Bailey et al. (2023)](https://www.biorxiv.org/content/10.1101/2023.06.23.546355v1) identified 3 important groups of features—measuring stationarity of self-correlation properties, stationarity of distributional properties, and global distributional properties—and then go on to interpret each in turn:

![](https://254595279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LEJuzw5fsQv4fwW3E7P%2Fuploads%2Fgit-blob-5afcc60a0cc963a09e68c396000c05c956e0c314%2Fimage.png?alt=media)

## Interpreting individual features

When such a group of high-performing features capturing a common time-series property has been identified, how can we start to interpret and understand what each individual feature is measuring?

Some features in the group may be easy to interpret directly. For example, `rms` is simply the root-mean-square of the distribution of time-series values. Others have clues in the name: in the list above, features starting with `EN_LZComplexity` measure the Lempel–Ziv complexity of a symbolic encoding of the time series, `EN_SampEn` its sample entropy, `EN_MSE` its multiscale entropy ('mse'), `NL_TakensEstimator` estimate its correlation dimension, and `WL_DetailCoeffs` are to do with wavelet detail coefficients. Similarly, features starting with `FC_LocalSimple_mean` are related to time-series forecasting using local means of the time series.

Below we outline a procedure for how a user can go from a time-series feature selected by *hctsa* towards a deeper understanding of the type of algorithm that feature is derived from, how that algorithm performs across the dataset, and thus how it can provide interpretable information about your specific time-series dataset.

### Inspecting keywords

The simplest way of getting a quick idea of what sort of property a feature might be measuring is from its keywords, that often label individual features by the class of time-series analysis method from which they were derived. Keywords have not been applied exhaustively to features (this is an ongoing challenge), but when they are there they can be useful at giving a sense of what's going on. In the list above, keywords are listed in parentheses (e.g., `entropy`, `dimension`, `spreadDependent`). Some common keywords are:

1. *'forecasting'* (methods related to predicting future values of a time series),
2. *'entropy'* (methods related to predictability and information content in a time series),
3. *'wavelet'* (features derived from wavelet transforms of the time series),
4. *'locationDependent'* (location dependent: features that change under mean shifts of a time series),
5. *'spreadDependent'* (spread dependent: features that change under rescaling about their mean), and
6. *'lengthDependent'* (length dependent: features that are highly sensitive to the length of a time series; since length bias was removed from the features in v3.0.0, only a handful of features, such as `ST_length`, now carry this keyword).

### Inspecting code

To find more specific detailed information about a feature, beyond just a broad categorical label of the literature from which it was derived, the next step is find and inspect the code file that generates the feature of interest. For example, say we were interested in the feature `FC_LocalSimple_mean3_ac1`, which we visualize on a simple dataset of noisy and periodic signals [below](#visualizing-outputs). Its keywords (`model,forecasting`) tell us that it has something to do with forecasting, and the name provides clues about the details (e.g., `FC_` stands for forecasting, the function `FC_LocalSimple` is the one that produces this feature, which, as the name suggests, performs simple local time-series prediction). We can use the feature ID (given in square brackets in the output of `TS_TopFeatures`) to get information from the `Operations` metadata table. IDs depend on the version of the feature library: in *hctsa* v3.0.0, the ID of `FC_LocalSimple_mean3_ac1` is 1668 (you can find it by name, as `Operations(strcmp(Operations.Name,'FC_LocalSimple_mean3_ac1'),:)`):

```
>> Operations(Operations.ID==1668,:)

     ID                 Name                      Keywords                    CodeString             MasterID
    ____    ____________________________    _____________________    ____________________________    ________

    1668    {'FC_LocalSimple_mean3_ac1'}    {'model,forecasting'}    {'FC_LocalSimple_mean3.ac1'}      515
```

Inspecting the text before the dot, `.`, in the `CodeString` field (`FC_LocalSimple_mean3`) tells us the name that *hctsa* uses to describe the Matlab function and its unique set of inputs that produces this feature. Whereas the text following the dot, `.`, in the `CodeString` field (`ac1`), tells us the field of the output structure produced by the Matlab function that was run.

We can use the `MasterID` to get more information about the code that was run using the `MasterOperations` metadata table:

```
>> MasterOperations(MasterOperations.ID==515,:)

    ID              Label                            Code
    ___    ________________________    ________________________________

    515    {'FC_LocalSimple_mean3'}    {'FC_LocalSimple(x_z,'mean',3)'}
```

This tells us that the code used to produce our feature was `FC_LocalSimple(x_z,'mean',3)` (`x_z` being the z-scored input time series that *hctsa* passes to every operation). We can get information about this function in the command line by running a `help` command:

```
>> help FC_LocalSimple
  FC_LocalSimple   How well a simple local rule forecasts the next value.

  Predicts each value of the time series from the trainLength values just before
  it, using a simple rule (the mean, the median, or a straight line fitted to those
  values and extended one step). The residuals are the prediction minus the data
  (e = yp - y), and the outputs describe them with the shared residual summary
  MF_ResidualAnalysis (at its 'core' level), plus the Gaussianity of their
  distribution. The first trainLength values are used for training only and are not
  forecast. If the series is too short to forecast, a NaN is returned.

  ---INPUTS:
  y, the input time series

  forecastMeth, the forecasting method:
           (i) 'mean': the mean of the past trainLength values (default),
           (ii) 'median': the median of the past trainLength values,
           (iii) 'lfit': the next point on a straight line fitted to the past
                          trainLength values.

  trainLength, the number of past values used to forecast the next value (default
           3), or a string that sets it from the series: 'ac' (the first
           zero-crossing of the autocorrelation function of y; discrete, from
           CO_FirstCrossing), 'ac1e' (the floor of its first 1/e crossing), or 'mi'
           (the smaller of the first minimum of the Kraskov automutual information
           and the 'ac1e' delay); see BF_GetTau. For 'lfit' with 'ac', 'ac1e' or
           'mi', this is at least 2 (a line cannot be fitted to a single point).

  ---OUTPUTS:
  meane, mean of the residuals (the bias of the forecast)
  meanabs, mean absolute residual
  stde, standard deviation of the residuals
  maxonstd, largest absolute residual, in units of the residual standard deviation
  ac1, ac2, ac3: autocorrelation of the (z-scored) residuals at lags 1, 2 and 3
  propbth, proportion of the residual autocorrelations at lags 1 to 25 within the
           significance band +/- 2.6/sqrt(N)
  taurat, decorrelation time of the residuals (first zero-crossing of their
           autocorrelation function) divided by that of the time series
  sws, standard deviation across 5 windows of the local standard deviation of the
           residuals, relative to their overall standard deviation
  swm, standard deviation across 5 windows of the local mean of the residuals,
           relative to their overall standard deviation
  normr2, R^2 of a Gaussian fit (DN_SimpleFit) to the kernel-smoothed distribution
           of the residuals: a measure of their Gaussianity
```

We can also inspect the code in the function `FC_LocalSimple` directly for more information. Like all code files for computing time-series features, `FC_LocalSimple.m` is located in the Operations directory of the *hctsa* repository.

Inspecting the code file, we see that running `FC_LocalSimple(y,'mean',3)` does forecasting using local estimates of the time-series mean (since the second input to `FC_LocalSimple`, `forecastMeth` is set to `'mean'`), using the previous three time-series values to make the prediction (since the third input to `FC_LocalSimple`, `trainLength` is set to `3`).

To understand what the specific output quantity from this code is that came up as being highly informative in our `TS_TopFeatures` analysis, we need to look for the output labeled `ac1` of the output structure produced by `FC_LocalSimple`. We discover that this is produced via a shared residual-analysis routine, `MF_ResidualAnalysis`, that `FC_LocalSimple.m` calls on its prediction residuals:

```
% Report the residuals through the shared contract, at the cheap 'core' level, under
% the names the rest of the model-fitting family uses.
residOut = MF_ResidualAnalysis(res, y, 'core');
fields = fieldnames(residOut);
for k = 1:length(fields)
    out.(fields{k}) = residOut.(fields{k});
end
```

Inspecting `MF_ResidualAnalysis.m` (cf. `help MF_ResidualAnalysis`) shows us that its `'core'` output level includes an `ac1` field: the lag-1 autocorrelation of the z-scored residuals (the first of the autocorrelations at lags 1 to 25 computed as `CO_AutoCorr(zscore(e),1:25,'Fourier')`, where `e` are the residuals).

This shows us that, after doing the local mean prediction, `FC_LocalSimple` then outputs some features on whether there is any residual autocorrelation structure in the residuals of the rolling predictions -- our output of interest, `ac1`, being the simplest of these (higher-lag and ratio-based variants, like `ac2` and `taurat`, are also available in the shared contract).

When the local mean prediction still leaves a lot of autocorrelation structure in the residuals, our feature, `FC_LocalSimple_mean3_ac1`, will thus take a high value.

### Visualizing outputs

Once we've seen the code that was used to produce a feature, and started to think about how such an algorithm might be measuring useful structure in our time series, we can then check our intuition by inspecting its performance on our dataset (as described in [Investigating specific operations](/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/feature_summary.md)).

For example, we can run the following:

```
TS_FeatureSummary(1668,'raw',true);
```

which produces a plot like that shown below. We have run this on a dataset of 40 time series of length 500, each a sum of one or two sine waves: 20 with added Gaussian noise, labeled 'noisy' (red), and 20 without noise, labeled 'periodic' (blue):

![](https://254595279-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LEJuzw5fsQv4fwW3E7P%2Fuploads%2Fgit-blob-6459a8edd66ecb7bc68fcc65805809e83bd90125%2FFeatureSummaryForInterpretation.png?alt=media)

In this plot, we see how this feature orders time series (with the distribution of values shown on the left, and split between the two groups: 'noisy', and 'periodic'). The periodic signals all have highly autocorrelated residuals (values from 0.87 to 0.99), whereas the noisy signals have lower values (from −0.06 to 0.73), because the noise is largely uncorrelated from one sample to the next. Our intuition from the code, that time series with longer correlation timescales will have highly autocorrelated residuals after a local mean prediction, appears to hold visually on this dataset: among the periodic signals, those with the slowest oscillations have the highest values.

In general, the mechanism provided by `TS_FeatureSummary` to visualize how a given feature orders time series, including across labeled groups, can be very useful for feature interpretation.

## Summary

*hctsa* contains a large number of features, many of which can be expected to be highly inter-correlated on a given time-series dataset. It is thus crucial to explore how a given feature relates to other features in the library, e.g., using the correlation matrix produced by `TS_TopFeatures` (cf. [Finding informative features](/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/ts_topfeatures.md)), or by searching for features with similar behavior on the dataset to a given feature of interest (cf. [Finding nearest neighbors](/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/sim_search.md)).

In a specific domain context, you may need to decide on the trade-off between more complicated features that may have slightly higher in-sample performance on a given task, and simpler, more interpretable features that may help guide domain understanding. The procedures outlined above are typically the first step to understanding a time-series analysis algorithm, and its relationship to alternatives that have been developed across science.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://time-series-features.gitbook.io/hctsa-manual/installing-and-using-hctsa/analyzing_visualizing/interpreting-features.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
