Representation Methods#
Comparison of different cluster representation methods: medoid, maxoid, mean, minmax, and duration.
Author: Maximilian Hoffmann
Import pandas and the relevant time series aggregation class
[1]:
%load_ext autoreload
%autoreload 2
import pandas as pd
import plotly.express as px
import plotly.io as pio
import tsam
from tsam import ClusterConfig
pio.renderers.default = "notebook"
Input data#
Read in time series from testdata.csv with pandas
[2]:
raw = pd.read_csv("testdata.csv", index_col=0)
Create a plot function for the temperature for a visual comparison of the time series
[3]:
# Use tsam.unstack_to_periods() with plotly for heatmap visualization
# px.imshow(unstacked["column"].values.T) creates interactive heatmaps
Hierarchical aggregation with medoid representation at hourly resolution#
Initialize an aggregation class object with hierarchical as method for eight typical days
[4]:
result_medoid = tsam.aggregate(
raw,
n_clusters=8,
period_duration=24,
cluster=ClusterConfig(method="hierarchical", representation="medoid"),
)
Create the typical periods
[5]:
cluster_representatives_medoid = result_medoid.cluster_representatives
Predict original data
[6]:
reconstructed_medoid = result_medoid.reconstructed
Get accuracy indicators
[7]:
result_medoid.accuracy
[7]:
AccuracyMetrics(
rmse=0.1075 (mean),
mae=0.0766 (mean),
rmse_duration=0.0366 (mean)
)
Hierarchical aggregation with maxoid representation at hourly resolution#
[8]:
result_maxoid = tsam.aggregate(
raw,
n_clusters=8,
period_duration=24,
cluster=ClusterConfig(method="hierarchical", representation="maxoid"),
preserve_column_means=False,
)
Create the typical periods
[9]:
cluster_representatives_maxoid = result_maxoid.cluster_representatives
Predict original data
[10]:
reconstructed_maxoid = result_maxoid.reconstructed
Get accuracy indicators
[11]:
result_maxoid.accuracy
[11]:
AccuracyMetrics(
rmse=0.1842 (mean),
mae=0.1329 (mean),
rmse_duration=0.1017 (mean)
)
Hierarchical aggregation with mean representation and 10 typical days at hourly resolution#
[12]:
result_mean = tsam.aggregate(
raw,
n_clusters=20,
period_duration=24,
cluster=ClusterConfig(method="hierarchical", representation="mean"),
)
Create the typical periods
[13]:
cluster_representatives_mean = result_mean.cluster_representatives
Predict original data
[14]:
reconstructed_mean = result_mean.reconstructed
Get accuracy indicators
[15]:
result_mean.accuracy
[15]:
AccuracyMetrics(
rmse=0.0784 (mean),
mae=0.0554 (mean),
rmse_duration=0.0261 (mean)
)
Hierarchical aggregation with minmax representation and 10 typical days at hourly resolution#
[16]:
result_minmax = tsam.aggregate(
raw,
n_clusters=20,
period_duration=24,
cluster=ClusterConfig(method="hierarchical", representation="minmax_mean"),
preserve_column_means=False,
)
Create the typical periods
[17]:
cluster_representatives_minmax = result_minmax.cluster_representatives
Predict original data
[18]:
reconstructed_minmax = result_minmax.reconstructed
Get accuracy indicators
[19]:
result_minmax.accuracy
[19]:
AccuracyMetrics(
rmse=0.0784 (mean),
mae=0.0554 (mean),
rmse_duration=0.0261 (mean)
)
Hierarchical aggregation with distribution representation and 10 typical days at hourly resolution#
[20]:
result_duration = tsam.aggregate(
raw,
n_clusters=20,
period_duration=24,
cluster=ClusterConfig(method="hierarchical", representation="distribution"),
preserve_column_means=False,
)
Create the typical periods
[21]:
cluster_representatives_duration = result_duration.cluster_representatives
Predict original data
[22]:
reconstructed_duration = result_duration.reconstructed
Get accuracy indicators
[23]:
result_duration.accuracy
[23]:
AccuracyMetrics(
rmse=0.0928 (mean),
mae=0.0643 (mean),
rmse_duration=0.0049 (mean)
)
Comparison of the aggregations#
It was shown for the temperature, but both times all four time series have been aggregated. Therefore, we compare here also the duration curves of the electrical load for the original time series, the aggregation with k-mean, and the hierarchical aggregation including peak periods.
[24]:
# Compare duration curves using plotly express
comparison_data = {
"Original": raw,
"Medoid": reconstructed_medoid,
"Maxoid": reconstructed_maxoid,
"Mean": reconstructed_mean,
"Minmax": reconstructed_minmax,
}
frames = []
for name, df in comparison_data.items():
sorted_vals = df["Load"].sort_values(ascending=False).reset_index(drop=True)
frames.append(
pd.DataFrame(
{"Hour": range(len(sorted_vals)), "Load": sorted_vals, "Method": name}
)
)
long_df = pd.concat(frames, ignore_index=True)
px.line(
long_df,
x="Hour",
y="Load",
color="Method",
title="Duration Curve Comparison - Load",
)
[25]:
param = "GHI"
[26]:
# Original data heatmap
unstacked_orig = tsam.unstack_to_periods(raw, period_duration=24)
px.imshow(
unstacked_orig[param].values.T,
labels={"x": "Day", "y": "Hour", "color": param},
title=f"Original {param}",
aspect="auto",
)
[27]:
# Medoid representation heatmap
unstacked_medoid = tsam.unstack_to_periods(reconstructed_medoid, period_duration=24)
px.imshow(
unstacked_medoid[param].values.T,
labels={"x": "Day", "y": "Hour", "color": param},
title=f"Medoid {param}",
aspect="auto",
)
[28]:
# Maxoid representation heatmap
unstacked_maxoid = tsam.unstack_to_periods(reconstructed_maxoid, period_duration=24)
px.imshow(
unstacked_maxoid[param].values.T,
labels={"x": "Day", "y": "Hour", "color": param},
title=f"Maxoid {param}",
aspect="auto",
)
[29]:
# Mean representation heatmap
unstacked_mean = tsam.unstack_to_periods(reconstructed_mean, period_duration=24)
px.imshow(
unstacked_mean[param].values.T,
labels={"x": "Day", "y": "Hour", "color": param},
title=f"Mean {param}",
aspect="auto",
)
[30]:
# Minmax representation heatmap
unstacked_minmax = tsam.unstack_to_periods(reconstructed_minmax, period_duration=24)
px.imshow(
unstacked_minmax[param].values.T,
labels={"x": "Day", "y": "Hour", "color": param},
title=f"Minmax {param}",
aspect="auto",
)
[31]:
# Distribution representation heatmap
unstacked_dist = tsam.unstack_to_periods(reconstructed_duration, period_duration=24)
px.imshow(
unstacked_dist[param].values.T,
labels={"x": "Day", "y": "Hour", "color": param},
title=f"Distribution {param}",
aspect="auto",
)
[32]:
# Time slice comparison using plotly express
frames = []
for name, df in comparison_data.items():
sliced = df.loc["20100210":"20100218", ["Load"]].copy()
sliced["Method"] = name
frames.append(sliced)
long_df = pd.concat(frames).reset_index(names="Time")
px.line(
long_df,
x="Time",
y="Load",
color="Method",
title="Time Slice Comparison - Load (Feb 10-18)",
)
Validation#
Check that the means of the original time series and the predicted ones are the same.
[33]:
raw.mean()
[33]:
GHI 110.990183
Load 450.260335
T 7.790616
Wind 3.057306
dtype: float64
[34]:
reconstructed_medoid.mean()
[34]:
GHI 110.990183
Load 450.260335
T 7.790616
Wind 3.057306
dtype: float64
[35]:
reconstructed_maxoid.mean()
[35]:
GHI 119.566553
Load 418.473753
T 8.349030
Wind 4.385788
dtype: float64
[36]:
reconstructed_mean.mean()
[36]:
GHI 110.990183
Load 450.260335
T 7.790616
Wind 3.057306
dtype: float64
[37]:
reconstructed_minmax.mean()
[37]:
GHI 110.990183
Load 450.260335
T 7.790616
Wind 3.057306
dtype: float64
[38]:
reconstructed_duration.mean()
[38]:
GHI 110.990183
Load 450.260335
T 7.790616
Wind 3.057306
dtype: float64
[ ]: