unit8co / darts

A python library for user-friendly forecasting and anomaly detection on time series.
https://unit8co.github.io/darts/
Apache License 2.0
7.91k stars 857 forks source link

Allow broadcasting for element-wise operations on TimeSeries #2475

Closed Joelius300 closed 1 month ago

Joelius300 commented 1 month ago

Is your feature request related to a current problem? Please describe.

Currently, you can only do a select few broadcasting operations on TimeSeries, namely with scalar values (e.g. series + 3). This means you cannot add a TimeSeries with 10 elements, 1 component and 1 sample to a TimeSeries with 10 elements, 1 component and 5 samples. When working with probabilistic forecasts, this was something that I was missing because I regularly shifted a multi-sample forecast by some non-constant series.

times = pd.date_range("20130101", "20130110", freq="D")
# time-series with multiple samples
# time: 10, components: 1, samples: 3
seriesA = TimeSeries.from_times_and_values(
    times,
    np.expand_dims([
        range(10),
        range(5, 15),
        range(15, 25),
    ], axis=1).T
)

# time-series with one sample
# time: 10, components: 1, samples: 1
seriesB = TimeSeries.from_series(
    pd.Series([2 for _ in range(10)], index=times)
)

broadcast_constant = seriesA + 2
broadcast_series = seriesA + seriesB
assert broadcast_constant == broadcast_series, "Broadcasting scalar to TimeSeries does not give same result as Broadcasting single-sample TimeSeries to multi-sample TimeSeries"

Expected result: Runs without error or assertion failure

Actual result: ValueError: Attempted to perform operation on two TimeSeries of unequal shapes.

Describe proposed solution

I propose allowing certain types of broadcasting, especially on sample level because I think that's the most useful case. \ This might be a good opportunity to discuss what other types of broadcasting could be useful.

Describe potential alternatives

At the moment, you need to extend the series explicitly when you want to add it to another.

E.g. this is what I did to work around this limitation and make my function compatible with both single- and multi-sample series.

if num_samples > 1:
    series = darts.concatenate([series] * num_samples, axis=2)

Additional context

I'm currently testing some things in this regard and will open a draft pull request later on. Edit: See #2476

dennisbader commented 1 month ago

Hi @Joelius300, and thanks for raising this issue. Indeed, broadcasting on the sample level should be allowed. Thanks also for opening the PR, let's discuss further there 🚀