Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions content/blog/2026-11-09-tsgap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
title: "TSGap: Reproducible missingness simulation for time-series benchmarks"
date: "2026-11-09"
type: blog
url: "/blog/tsgap-time-series-missingness.html"
excerpt: >-
TSGap is a NumPy-native Python package for creating reproducible artificial
missingness scenarios for time-series imputation benchmarks.
author: "Feruz Oripov"
blog_topic: software
toc: true
lastmod: "2026-11-09"
image:
src: "images/blog/2026/tsgap-before-after.png"
alt: >-
A complete multivariate time series beside six examples with missing values
highlighted in red: pointwise, block, monotone, decay, Markov, and
Gilbert-Elliott patterns.
tags:
- time series
- missing data
- imputation
- scientific Python
show_author: true
learn_more:
- title: "TSGap documentation"
url: "https://feruzoripov.github.io/tsgap/"
- title: "TSGap on GitHub"
url: "https://github.com/feruzoripov/tsgap"
- title: "TSGap on PyPI"
url: "https://pypi.org/project/tsgap/"
comments: true
---

Evaluating a time-series imputation method requires known values that can be
hidden and reconstructed. The way those values are hidden matters: isolated
missing points, sensor outages, dropout near the end of a recording, and bursty
packet loss create very different reconstruction problems.

[TSGap](https://github.com/feruzoripov/tsgap) is a lightweight, NumPy-native
Python package for creating these artificial missingness scenarios. It was
recently [peer reviewed and accepted by
pyOpenSci](https://github.com/pyOpenSci/software-submission/issues/354).

## Why separate mechanism from pattern

TSGap describes a scenario using two ideas:

* The **mechanism** describes why a value is selected for masking. TSGap
supports missing completely at random (MCAR), missing at random (MAR), and
missing not at random (MNAR).
* The **pattern** describes how missing values are arranged over time.
Available patterns include pointwise, block, monotone, decay, Markov, and
Gilbert-Elliott burst loss.

Separating these concepts makes experiments easier to describe and reproduce.
For example, researchers can compare isolated MCAR gaps with contiguous MCAR
outages while keeping the requested missing rate fixed.

There is an important interpretation detail. Pointwise MAR and MNAR preserve a
direct relationship between the mechanism score and each masked value.
Non-pointwise patterns reshape a mechanism-informed missingness budget into a
temporal structure. They should therefore be described as structured,
mechanism-informed masks rather than automatically treated as strict MAR or
MNAR data-generating processes.

## Create a block-missingness scenario

Install TSGap from PyPI:

```bash
pip install tsgap
```

The following example masks 20% of the eligible values in a two-feature time
series. Each block spans between 2% and 10% of the series length.

```python
import numpy as np

from tsgap import simulate_missingness

rng = np.random.default_rng(42)
X = rng.normal(size=(1_000, 2))

X_missing, observed_mask = simulate_missingness(
X,
mechanism="mcar",
pattern="block",
missing_rate=0.20,
block_frac=(0.02, 0.10),
seed=42,
)

artificially_missing = ~observed_mask & np.isfinite(X)
achieved_rate = artificially_missing.sum() / np.isfinite(X).sum()

print(f"Artificial missingness rate: {achieved_rate:.1%}")
```

`X_missing` contains `NaN` at the newly hidden positions. The Boolean
`observed_mask` is `False` at those positions and `True` where data remain
observed. The same seed produces the same mask, which supports reproducible
benchmarking.

The missing-rate denominator is the number of originally observed, eligible
entries in the selected target dimensions. Existing `NaN` values are preserved
but do not count toward the requested artificial missingness rate. This
distinction is useful when working with real sensor recordings that already
contain gaps.

## From simulation to evaluation

A typical benchmark can use TSGap in four steps:

1. Start with complete data, or record which values were originally observed.
2. Generate a missingness scenario with a fixed seed.
3. Run an imputation method on `X_missing`.
4. Evaluate predictions only at positions that TSGap artificially masked.

TSGap supports both two-dimensional arrays shaped as time by features and
three-dimensional arrays shaped as samples by time by features. Target
dimensions can be selected so that auxiliary variables remain available while
another variable is masked.

Deterministic patterns aim for exact rate control among eligible entries.
Stochastic state models such as Markov and Gilbert-Elliott masks can vary
between seeds, so studies using them should report achieved rates and aggregate
results across multiple seeds.

## Try TSGap

TSGap is available under the MIT license:

* [Documentation](https://feruzoripov.github.io/tsgap/)
* [Source code](https://github.com/feruzoripov/tsgap)
* [PyPI package](https://pypi.org/project/tsgap/)
* [Version 0.8.0
release](https://github.com/feruzoripov/tsgap/releases/tag/v0.8.0)

Bug reports, feature requests, and contributions are welcome through the
[TSGap issue tracker](https://github.com/feruzoripov/tsgap/issues).
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.