Repository: sktime/sktime

Issue:
[BUG] sporadic failure - `RandomIntervalSegmenter` produces `DataFrame` with duplicate columns
It seems that, sporadically, `RandomIntervalSegmenter` produces a `DataFrame` with duplicate column names. See this CI run (mac 3.10): https://github.com/alan-turing-institute/sktime/runs/7271784537


Discussion:

Resolving patch:
diff --git a/sktime/transformations/panel/segment.py b/sktime/transformations/panel/segment.py
--- a/sktime/transformations/panel/segment.py
+++ b/sktime/transformations/panel/segment.py
@@ -125,9 +125,10 @@ def _transform(self, X, y=None):
         new_column_names = []
         for interval in self.intervals_:
             start, end = interval[0], interval[-1]
-            interval = X[:, start:end]
-            intervals.append(interval)
-            new_column_names.append(f"{column_names}_{start}_{end}")
+            if f"{column_names}_{start}_{end}" not in new_column_names:
+                interval = X[:, start:end]
+                intervals.append(interval)
+                new_column_names.append(f"{column_names}_{start}_{end}")
 
         # Return nested pandas DataFrame.
         Xt = pd.DataFrame(_concat_nested_arrays(intervals))


Test patch:
diff --git a/sktime/transformations/panel/tests/test_segment.py b/sktime/transformations/panel/tests/test_segment.py
--- a/sktime/transformations/panel/tests/test_segment.py
+++ b/sktime/transformations/panel/tests/test_segment.py
@@ -1,21 +1,24 @@
 # -*- coding: utf-8 -*-
+"""Tests for RandomIntervalSegmenter."""
 import numpy as np
 import pandas as pd
 import pytest
 
-from sktime.transformations.panel.segment import RandomIntervalSegmenter
-from sktime.transformations.panel.segment import _rand_intervals_fixed_n
-from sktime.transformations.panel.segment import _rand_intervals_rand_n
+from sktime.transformations.panel.segment import (
+    RandomIntervalSegmenter,
+    _rand_intervals_fixed_n,
+    _rand_intervals_rand_n,
+)
 from sktime.utils._testing.panel import _make_nested_from_array
 
 N_ITER = 10
 
 
-# Test output format and dimensions.
 @pytest.mark.parametrize("n_instances", [1, 3])
 @pytest.mark.parametrize("n_timepoints", [10, 20])
 @pytest.mark.parametrize("n_intervals", [0.1, 1.0, 1, 3, 10, "sqrt", "random", "log"])
 def test_output_format_dim(n_timepoints, n_instances, n_intervals):
+    """Test output format and dimensions."""
     X = _make_nested_from_array(
         np.ones(n_timepoints), n_instances=n_instances, n_columns=1
     )
@@ -30,29 +33,29 @@ def test_output_format_dim(n_timepoints, n_instances, n_intervals):
     # Check number of generated intervals/columns.
     if n_intervals != "random":
         if np.issubdtype(type(n_intervals), np.floating):
-            assert Xt.shape[1] == np.maximum(1, int(n_timepoints * n_intervals))
+            assert Xt.shape[1] <= np.maximum(1, int(n_timepoints * n_intervals))
         elif np.issubdtype(type(n_intervals), np.integer):
-            assert Xt.shape[1] == n_intervals
+            assert Xt.shape[1] <= n_intervals
         elif n_intervals == "sqrt":
-            assert Xt.shape[1] == np.maximum(1, int(np.sqrt(n_timepoints)))
+            assert Xt.shape[1] <= np.maximum(1, int(np.sqrt(n_timepoints)))
         elif n_intervals == "log":
-            assert Xt.shape[1] == np.maximum(1, int(np.log(n_timepoints)))
+            assert Xt.shape[1] <= np.maximum(1, int(np.log(n_timepoints)))
 
 
-# Check that exception is raised for bad input args.
 @pytest.mark.parametrize("bad_interval", [0, -0, "str", 1.2, -1.2, -1])
 def test_bad_input_args(bad_interval):
+    """Check that exception is raised for bad input args."""
     X = _make_nested_from_array(np.ones(10), n_instances=10, n_columns=2)
     with pytest.raises(ValueError):
         RandomIntervalSegmenter(n_intervals=bad_interval).fit(X)
 
 
-# Helper function for checking generated intervals.
 @pytest.mark.parametrize(
     "random_state", list(np.random.randint(100, size=10))
 )  # run repeatedly
 @pytest.mark.parametrize("n_intervals", ["sqrt", "log", 0.1, 1, 3])
 def test_rand_intervals_fixed_n(random_state, n_intervals):
+    """Helper function for checking generated intervals."""
     n_timepoints = 30
     x = np.arange(n_timepoints)
 
@@ -73,6 +76,7 @@ def test_rand_intervals_fixed_n(random_state, n_intervals):
     "random_state", list(np.random.randint(100, size=10))
 )  # run repeatedly
 def test_rand_intervals_rand_n(random_state):
+    """Test random intervals."""
     n_timepoints = 30
     x = np.arange(n_timepoints)
 
@@ -93,6 +97,7 @@ def test_rand_intervals_rand_n(random_state):
 @pytest.mark.parametrize("max_length", [4, 5])
 @pytest.mark.parametrize("n_intervals", ["sqrt", "log", 0.1, 1, 3])
 def test_rand_intervals_fixed_n_min_max_length(n_intervals, min_length, max_length):
+    """Test random interval length."""
     n_timepoints = 30
     x = np.arange(n_timepoints)