Repository: unit8co/darts

Issue:
[BUG] `append_values` incorrectly extends `time_index` of `pd.RangeIndex`-typed `TimeSeries`
**Describe the bug**
When `append_values` is used to add new values to a `pd.RangeIndex`-typed `TimeSeries` , the `time_index` of the entire series is 'reset' so that each value is labeled with its index position. This is particularly unexpected if the `TimeSeries` in question does not have a frequency of `1` and/or does not start with a `time_index` of `0`.

**To Reproduce**
Consider the following example:
```python
from darts.utils.timeseries_generation import linear_timeseries
import numpy as np

series = linear_timeseries(start=1, length=5, freq=2)
print('Before `append_values`:')
print(list(series.time_index))
new_values = np.ones((1,))
print('After `append_values`:')
print(list(series.append_values(new_values).time_index))
```
This yields:
```
Before `append_values`:
[1, 3, 5, 7, 9]
After `append_values`:
[0, 1, 2, 3, 4, 5]
```
Notice how the `time_index` now starts at `0` instead of `1` **and** has a frequency of `1` instead of `2`.

**Expected behavior**
Instead of resetting the `time_index` of `pd.RangeIndex`-typed `TimeSeries`, `append_values` should 'extend' the `time_index`. More explicitly, for the previous example, one should expect the output:
```
Before `append_values`:
[1, 3, 5, 7, 9]
After `append_values`:
[1, 3, 5, 7, 9, 11]
```
Indeed, this is the [behaviour that's advertised by `append_values`' docstring](https://unit8co.github.io/darts/generated_api/darts.timeseries.html#darts.timeseries.TimeSeries.append_values) and, additionally, is how it behaves when dealing with `pd.DatetimeIndex`-typed `TimeSeries`. To see this, consider the following example:
```python
from darts.utils.timeseries_generation import linear_timeseries
import numpy as np
import pandas as pd

series = linear_timeseries(start=pd.Timestamp('1/1/2000'), length=2, freq='2d')
print('Before `append_values`:')
print(list(series.time_index))
new_values = np.ones((1,))
print('After `append_values`:')
print(list(series.append_values(new_values).time_index))
```
This prints:
```
Before `append_values`:
[Timestamp('2000-01-01 00:00:00', freq='2D'), Timestamp('2000-01-03 00:00:00', freq='2D')]
After `append_values`:
[Timestamp('2000-01-01 00:00:00', freq='2D'), Timestamp('2000-01-03 00:00:00', freq='2D'), Timestamp('2000-01-05 00:00:00', freq='2D')]
```
Notice how the `append_values` has simply added the date `2000-01-05` to the `time_index` of `series`.

In cases where the user really wants 'reset' the `time_index` of a `TimeSeries` after appending values, this should probably be achieved by implementing a separate `TimeSeries.reset_index()` method.

**System (please complete the following information):**
 - Python version: 3.10.6
 - darts version: 0.22.0

**Additional context**
N/A

Comments:

Patch:
diff --git a/darts/timeseries.py b/darts/timeseries.py
index fc757f58..b629aa4f 100644
--- a/darts/timeseries.py
+++ b/darts/timeseries.py
@@ -2604,10 +2604,6 @@ class TimeSeries:
             attrs=self._xa.attrs,
         )
 
-        # new_xa = xr.concat(objs=[self._xa, other_xa], dim=str(self._time_dim))
-        if not self._has_datetime_index:
-            new_xa = new_xa.reset_index(dims_or_levels=new_xa.dims[0])
-
         return self.__class__.from_xarray(
             new_xa, fill_missing_dates=True, freq=self._freq_str
         )
@@ -2626,7 +2622,6 @@ class TimeSeries:
         TimeSeries
             A new TimeSeries with the new values appended
         """
-
         if self._has_datetime_index:
             idx = pd.DatetimeIndex(
                 [self.end_time() + i * self._freq for i in range(1, len(values) + 1)],
@@ -2634,9 +2629,10 @@ class TimeSeries:
             )
         else:
             idx = pd.RangeIndex(
-                len(self), len(self) + self.freq * len(values), step=self.freq
+                start=self.end_time() + self._freq,
+                stop=self.end_time() + (len(values) + 1) * self._freq,
+                step=self._freq,
             )
-
         return self.append(
             self.__class__.from_times_and_values(
                 values=values,


Test patch:
diff --git a/darts/tests/test_timeseries.py b/darts/tests/test_timeseries.py
index c4414a13..61688bd2 100644
--- a/darts/tests/test_timeseries.py
+++ b/darts/tests/test_timeseries.py
@@ -621,15 +621,53 @@ class TimeSeriesTestCase(DartsBaseTestClass):
 
     def test_append(self):
         TimeSeriesTestCase.helper_test_append(self, self.series1)
+        # Check `append` deals with `RangeIndex` series correctly:
+        series_1 = linear_timeseries(start=1, length=5, freq=2)
+        series_2 = linear_timeseries(start=11, length=2, freq=2)
+        appended = series_1.append(series_2)
+        expected_vals = np.concatenate(
+            [series_1.all_values(), series_2.all_values()], axis=0
+        )
+        expected_idx = pd.RangeIndex(start=1, stop=15, step=2)
+        self.assertTrue(np.allclose(appended.all_values(), expected_vals))
+        self.assertTrue(appended.time_index.equals(expected_idx))
 
     def test_append_values(self):
         TimeSeriesTestCase.helper_test_append_values(self, self.series1)
+        # Check `append_values` deals with `RangeIndex` series correctly:
+        series = linear_timeseries(start=1, length=5, freq=2)
+        appended = series.append_values(np.ones((2, 1, 1)))
+        expected_vals = np.concatenate(
+            [series.all_values(), np.ones((2, 1, 1))], axis=0
+        )
+        expected_idx = pd.RangeIndex(start=1, stop=15, step=2)
+        self.assertTrue(np.allclose(appended.all_values(), expected_vals))
+        self.assertTrue(appended.time_index.equals(expected_idx))
 
     def test_prepend(self):
         TimeSeriesTestCase.helper_test_prepend(self, self.series1)
+        # Check `prepend` deals with `RangeIndex` series correctly:
+        series_1 = linear_timeseries(start=1, length=5, freq=2)
+        series_2 = linear_timeseries(start=11, length=2, freq=2)
+        prepended = series_2.prepend(series_1)
+        expected_vals = np.concatenate(
+            [series_1.all_values(), series_2.all_values()], axis=0
+        )
+        expected_idx = pd.RangeIndex(start=1, stop=15, step=2)
+        self.assertTrue(np.allclose(prepended.all_values(), expected_vals))
+        self.assertTrue(prepended.time_index.equals(expected_idx))
 
     def test_prepend_values(self):
         TimeSeriesTestCase.helper_test_prepend_values(self, self.series1)
+        # Check `prepend_values` deals with `RangeIndex` series correctly:
+        series = linear_timeseries(start=1, length=5, freq=2)
+        prepended = series.prepend_values(np.ones((2, 1, 1)))
+        expected_vals = np.concatenate(
+            [np.ones((2, 1, 1)), series.all_values()], axis=0
+        )
+        expected_idx = pd.RangeIndex(start=-3, stop=11, step=2)
+        self.assertTrue(np.allclose(prepended.all_values(), expected_vals))
+        self.assertTrue(prepended.time_index.equals(expected_idx))
 
     def test_with_values(self):
         vals = np.random.rand(5, 10, 3)