Repository: sktime/sktime (python)

Issue:
[DOC] Add line in CI docs to remind new devs create a new environment before installing dev version of sktime
#### Describe the issue linked to the documentation
I think it would be helpful if the `Testing and continuous integration` [documents](https://www.sktime.net/en/stable/developer_guide/continuous_integration.html) had a section telling new devs to explicitly create a n empty virtual environment inside the `sktime` directory to install the dev version of sktime into with `pip install -e .[dev]`. This was confusing for me, as I assumed that `pip install -e .[dev]` created the environment for me, which led to a long troubleshooting session. Many devs may know to create a blank env intuitively, but newer people may not.

#### Suggest a potential alternative/fix
In the `Prerequisite: local python environment` section, add a line or two explicitly telling devs to create a blank environment to install the dev version of sktime into.



Pull request:
[DOC] Improves documentation in `Testing and continuous integration` webpage
Fixes #8190

as suggested by @gbilleyPeco 



Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index c132b8812..8e3017487 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -3158,7 +3158,7 @@
       ]
     },
     {
-      "login": "Garve",
+      "login": "RobKuebler",
       "name": "Robert Kübler",
       "avatar_url": "https://avatars.githubusercontent.com/u/932327?v=4",
       "profile": "https://linkedin.com/in/robert-kuebler",
diff --git a/docs/source/developer_guide/continuous_integration.rst b/docs/source/developer_guide/continuous_integration.rst
index e01bca714..170497922 100644
--- a/docs/source/developer_guide/continuous_integration.rst
+++ b/docs/source/developer_guide/continuous_integration.rst
@@ -36,10 +36,14 @@ We recommend:
 Prerequisite: local python environment
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Local testing requires a :doc:`development version </installation>`
-of ``sktime``, follow the link for detail instructions.
+Local testing requires a developer setup
+of ``sktime``. If you do not have this already, we recommend you to create a new
+virtual environment and install an editable development version of ``sktime``.
 
-In your environment, ensure you have an editable development version of sktime with developer dependencies.
+For a full guide on how to obtain a developer setup, read the following link `Full developer setup for contributors and extension developers
+<https://www.sktime.net/en/latest/installation.html#full-developer-setup-for-contributors-and-extension-developers>`__ .
+
+If you have an environment setup already, ensure you have an editable development version of sktime with developer dependencies.
 To install, if not already installed:
 
    .. code:: bash
diff --git a/pyproject.toml b/pyproject.toml
index c5d52a7cb..eab3ce162 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -51,7 +51,7 @@ classifiers = [
 # core dependencies of sktime
 # this set should be kept minimal!
 dependencies = [
-  "joblib<1.5,>=1.2.0",  # required for parallel processing
+  "joblib>=1.2.0,<1.6",  # required for parallel processing
   "numpy>=1.21,<2.3",  # required for framework layer and base class logic
   "packaging",  # for estimator specific dependency parsing
   "pandas<2.3.0,>=1.1",  # pandas is the main in-memory data container
@@ -266,7 +266,7 @@ docs = [
 tests = [
   "pytest>=7.4,<8.4",
   "pytest-randomly>=3.15,<3.17",
-  "pytest-timeout>=2.1,<2.4",
+  "pytest-timeout>=2.1,<2.5",
   "pytest-xdist>=3.3,<3.7",
 ]
 
diff --git a/sktime/forecasting/dummy.py b/sktime/forecasting/dummy.py
index eb190cf87..602ace83d 100644
--- a/sktime/forecasting/dummy.py
+++ b/sktime/forecasting/dummy.py
@@ -1,7 +1,7 @@
 # copyright: sktime developers, BSD-3-Clause License (see LICENSE file)
 """Dummy forecasters."""
 
-__author__ = ["fkiraly"]
+__author__ = ["fkiraly", "RobKuebler"]
 
 import pandas as pd
 
@@ -61,7 +61,7 @@ class ForecastKnownValues(BaseForecaster):
     _tags = {
         # packaging info
         # --------------
-        "authors": ["fkiraly"],
+        "authors": ["fkiraly", "RobKuebler"],
         # estimator type
         # --------------
         "y_inner_mtype": "pd.DataFrame",
@@ -155,16 +155,21 @@ class ForecastKnownValues(BaseForecaster):
         fh_abs = fh.to_absolute_index(self.cutoff)
 
         try:
+            idx = self._y.index
+            if isinstance(idx, pd.MultiIndex):
+                unique_levels = idx.droplevel(-1).unique()
+                fh_abs = pd.MultiIndex.from_tuples(
+                    ((*level, time) for level in unique_levels for time in fh_abs),
+                    name=idx.names,
+                )
+
             y_pred = self._y_known.reindex(fh_abs, **reindex_params)
             y_pred = y_pred.reindex(self._y.columns, axis=1, **reindex_params)
         # TypeError happens if indices are incompatible types
         except TypeError:
-            if self.fill_value is None:
-                y_pred = pd.DataFrame(index=fh_abs, columns=self._y.columns)
-            else:
-                y_pred = pd.DataFrame(
-                    self.fill_value, index=fh_abs, columns=self._y.columns
-                )
+            y_pred = pd.DataFrame(
+                self.fill_value, index=fh_abs, columns=self._y.columns
+            )
 
         return y_pred
 


Test patch:
diff --git a/sktime/forecasting/tests/test_known_values.py b/sktime/forecasting/tests/test_known_values.py
new file mode 100644
index 000000000..b8c10d7d6
--- /dev/null
+++ b/sktime/forecasting/tests/test_known_values.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3 -u
+"""Tests for ForecastKnownValues."""
+# copyright: sktime developers, BSD-3-Clause License (see LICENSE file)
+
+__author__ = ["RobKuebler"]
+
+import numpy as np
+import pandas as pd
+import pytest
+
+from sktime.forecasting.dummy import ForecastKnownValues
+from sktime.forecasting.tests._config import TEST_OOS_FHS
+from sktime.tests.test_switch import run_test_for_class
+
+
+@pytest.fixture
+def y_known():
+    index = pd.MultiIndex.from_product(
+        [["A", "B"], ["X"], [0, 1, 2]],
+        names=["Level1", "Level2", "Date"],
+    )
+    data = range(len(index))
+
+    return pd.DataFrame(data, index=index, columns=["Value"])
+
+
+@pytest.mark.skipif(
+    not run_test_for_class(ForecastKnownValues),
+    reason="run test only if softdeps are present and incrementally (if requested)",
+)
+@pytest.mark.parametrize("fh", TEST_OOS_FHS)
+def test_multiindex(fh, y_known) -> None:
+    """Test multiindex y_known."""
+    f = ForecastKnownValues(y_known=y_known)
+    f.fit(y_known)
+    y_pred = f.predict(fh)
+
+    # Create expected data
+    if not isinstance(fh, np.ndarray):
+        fh = np.array([fh])
+
+    index = pd.MultiIndex.from_product(
+        [["A", "B"], ["X"], fh + 2],
+        names=["Level1", "Level2", "Date"],
+    )
+    expected = pd.DataFrame(None, index=index, columns=["Value"])
+
+    pd.testing.assert_frame_equal(
+        y_pred,
+        expected,
+        check_dtype=False,
+        check_index_type=False,
+    )
+
+
+@pytest.mark.skipif(
+    not run_test_for_class(ForecastKnownValues),
+    reason="run test only if softdeps are present and incrementally (if requested)",
+)
+@pytest.mark.parametrize("fh", TEST_OOS_FHS)
+def test_singleindex(fh, y_known) -> None:
+    """Test singleindex y_known."""
+    f = ForecastKnownValues(y_known=y_known.loc["A", "X"])
+    f.fit(y_known.loc["A", "X"])
+    y_pred = f.predict(fh)
+
+    # Create expected data
+    if not isinstance(fh, np.ndarray):
+        fh = np.array([fh])
+
+    expected = pd.DataFrame(None, index=fh + 2, columns=["Value"])
+
+    pd.testing.assert_frame_equal(
+        y_pred,
+        expected,
+        check_dtype=False,
+        check_index_type=False,
+    )