Skip to content

flixopt.clustering

Time Series Aggregation Module for flixopt.

This module provides the Clustering class stored on FlowSystem after clustering, wrapping tsam_xarray's ClusteringResult.

Example usage:

from tsam import ExtremeConfig

fs_clustered = flow_system.transform.cluster(
    n_clusters=8,
    cluster_duration='1D',
    extremes=ExtremeConfig(method='new_cluster', max_value=['Demand|fixed_relative_profile']),
)

clustering = fs_clustered.clustering
print(f'Number of clusters: {clustering.n_clusters}')
print(f'Clustering result: {clustering.clustering_result}')

# Access tsam_xarray AggregationResult (only before saving/loading)
result = clustering.aggregation_result
result.cluster_representatives  # DataArray
result.accuracy  # AccuracyMetrics

# Expand back to full resolution
fs_expanded = fs_clustered.transform.expand()

Classes

Clustering

Clustering(clustering_result: ClusteringResult | dict | None = None, original_timesteps: DatetimeIndex | list[str] | None = None, _aggregation_result: AggregationResult | None = None, _unrename_map: dict[str, str] | None = None)

Clustering information for a FlowSystem.

Wraps tsam_xarray's ClusteringResult for structure access and optionally AggregationResult for full data access (pre-serialization only).

For advanced access to clustering structure (dims, coords, cluster_centers, segment_centers, etc.), use clustering_result directly.

Example

clustering = fs_clustered.clustering clustering.n_clusters 8 clustering.clustering_result # tsam_xarray ClusteringResult for full access

Attributes

clustering_result property
clustering_result: ClusteringResult

tsam_xarray ClusteringResult for reuse with apply_clustering().

n_clusters property
n_clusters: int

Number of clusters (typical periods).

timesteps_per_cluster property
timesteps_per_cluster: int

Number of timesteps in each cluster.

n_original_clusters property
n_original_clusters: int

Number of original periods (before clustering).

n_segments property
n_segments: int | None

Number of segments per cluster, or None if not segmented.

is_segmented property
is_segmented: bool

Whether intra-period segmentation was used.

dim_names property
dim_names: list[str]

Names of extra dimensions, e.g., ['period', 'scenario'].

cluster_assignments property
cluster_assignments: DataArray

Mapping from original periods to cluster IDs.

Returns:

Type Description
DataArray

DataArray with dims [original_cluster, period?, scenario?].

cluster_occurrences property
cluster_occurrences: DataArray

How many original clusters map to each typical cluster.

Returns:

Type Description
DataArray

DataArray with dims [cluster, period?, scenario?].

segment_assignments property
segment_assignments: DataArray | None

For each timestep within a cluster, which segment it belongs to.

Returns:

Type Description
DataArray | None

DataArray with dims [cluster, time, period?, scenario?], or None if not segmented.

segment_durations property
segment_durations: DataArray | None

Duration of each segment in timesteps.

Returns:

Type Description
DataArray | None

DataArray with dims [cluster, segment, period?, scenario?], or None if not segmented.

aggregation_result property
aggregation_result: AggregationResult

The tsam_xarray AggregationResult for full data access.

Only available before serialization. After loading from file, use clustering_result for structure-only access.

The returned object holds the raw tsam_xarray result, on which flixopt's reserved-dim renames are still applied (the period dim is _period). For a friendlier view with the original dim names, use the original / reconstructed / residuals / accuracy properties or compare() instead.

Raises:

Type Description
ValueError

If accessed on a Clustering loaded from JSON/NetCDF.

original property
original: DataArray

Original (input) time series fed to clustering, on the original time axis.

All time-varying inputs are stacked on a variable dim. Dims are (*dim_names, variable, time) — e.g. (period, scenario, variable, time).

Only available before serialization.

reconstructed property
reconstructed: DataArray

Clustered profiles mapped back onto the original time axis.

Same dims and shape as original (dim order aligned with it), so the two can be compared, subtracted, or plotted directly.

Only available before serialization.

residuals property
residuals: DataArray

original - reconstructed, the per-timestep aggregation error.

Only available before serialization.

accuracy property
accuracy: AccuracyMetrics

tsam_xarray AccuracyMetrics (per-variable and column-weighted).

Exposes rmse / mae / rmse_duration (dims (variable, *dim_names)) and the aggregate weighted_rmse / weighted_mae / weighted_rmse_duration (dims (*dim_names,)). Dim names are un-renamed to match original.

Only available before serialization.

Methods:

disaggregate
disaggregate(data: DataArray) -> xr.DataArray

Expand clustered data back to original timesteps.

Delegates to tsam_xarray's ClusteringResult.disaggregate(). Handles the dim rename from flixopt's (cluster, time) to tsam_xarray's (cluster, timestep) convention.

For non-segmented systems, values are repeated for each timestep in the period. For segmented systems, values are placed at segment boundaries with NaN elsewhere — use .ffill(), .interpolate_na(), or .fillna() on the result.

Parameters:

Name Type Description Default
data DataArray

DataArray with (cluster, time) or (cluster, segment) dims.

required

Returns:

Type Description
DataArray

DataArray with time dim restored to original timesteps.

apply
apply(data: DataArray) -> TsamXarrayAggregationResult

Apply the saved clustering to new data.

Parameters:

Name Type Description Default
data DataArray

DataArray with time series data to cluster.

required

Returns:

Type Description
AggregationResult

tsam_xarray AggregationResult with the clustering applied.

to_json
to_json(path: str | Path) -> None

Save the clustering for reuse.

Can be loaded later with Clustering.from_json() and used with flow_system.transform.apply_clustering().

Parameters:

Name Type Description Default
path str | Path

Path to save the JSON file.

required
from_json classmethod
from_json(path: str | Path, original_timesteps: DatetimeIndex | None = None) -> Clustering

Load a clustering from JSON.

The loaded Clustering has full apply() and disaggregate() support because ClusteringResult is fully preserved via serialization.

Parameters:

Name Type Description Default
path str | Path

Path to the JSON file.

required
original_timesteps DatetimeIndex | None

Original timesteps for the new FlowSystem. If None, uses the timesteps stored in the JSON.

None

Returns:

Type Description
Clustering

A Clustering that can be used with apply_clustering().

compare
compare(variable: str | list[str] | None = None) -> xr.Dataset

Tidy original-vs-clustered comparison, ready for plotting.

Returns a Dataset with data_vars original and clustered on the original time axis and matching dim order, so .to_dataframe() and plotting libraries need no reshaping. This is the v7 replacement for the removed clustering.plot.compare().

Parameters:

Name Type Description Default
variable str | list[str] | None

Optional column name (or list) to select from the variable dim. Available names are list(clustering.original['variable'].values). Defaults to all variables.

None

Returns:

Type Description
Dataset

xr.Dataset with variables original and clustered.

Examples:

>>> import plotly.express as px
>>> cmp = clustering.compare('HeatDemand(Q)|fixed_relative_profile')
>>> px.line(cmp.to_dataframe()[['original', 'clustered']]).show()

Only available before serialization.