tsam ยท duration representation
When a year of hourly data is squeezed into a handful of “typical days,” the new medoid method keeps each signal’s range of values and the way the signals move together — by borrowing the timing of a real day.
A typical day is a stand-in for many real days. To be useful it has to be faithful in two different ways, and these pull in different directions:
The bag of numbers for each signal on its own. Across the day, how often is solar high, medium, low? The set of values should match reality — even if you ignore the clock.
What happens at the same time. When the sun is up, how much wind and demand usually come with it? Signals rise and fall together in patterns that should survive.
The duration representation is built to nail distribution: it collects every value each signal takes inside a cluster and keeps that exact bag. The open question is how to lay those values out across the 24 hours — and that ordering is what decides concurrency.
The original approach orders every signal by its own average shape. Each signal ends up looking sensible by itself — but nothing ties solar’s clock to demand’s clock. A halfway fix, the reference trick, picks one signal as a master clock and forces all the others to follow it. That locks everything into step, so suddenly everything peaks together — a new kind of wrong. The picture below shows what each choice does to the real “what-moves-with-what” pattern.
Which signals move together — real vs. reconstructed
Every cluster already contains real days that actually happened. The medoid is simply the most typical one of them. The new method keeps each signal’s bag of numbers exactly as before, but lays them out in the order that real day had them. Because that day is real, its “what happens together” pattern is real too — for every pair of signals at once. No master signal, no lockstep.
One typical day — the medoid borrows a real day’s shape
The trade is the whole point: every method keeps the distribution identically (duration-curve error is the same to six decimals), so the only thing that changes is how well concurrency survives.
Concurrency error by method — lower is better
How to use it
from tsam import aggregate, ClusterConfig, Distribution aggregate(df, n_clusters=8, cluster=ClusterConfig( method="hierarchical", representation=Distribution(concurrency="medoid"), ))
Add preserve_minmax=True to also pin each signal’s peak value; the medoid places that peak at the hour the real day peaked.