Repository: sktime/sktime
Pull request #7923 (2025-03-02 21:29:48)

[ENH] trigger differential tests if core framework modules change
This PR tries to cover some blind spots in the testing framework.

It triggers differential tests for estimators and object where previously they would not have been triggered, namely if one of the three key framework modules changes: `datatypes`, `tests`, and `utils`.

This condition is added to the `run_test_for_class` switch.


Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 581d6b9a3..cb46f769b 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -3320,10 +3320,12 @@
     {
       "login": "jgyasu",
       "name": "Jigyasu",
-      "avatar_url": "https://avatars0.githubusercontent.com/u/51952076?v=4",
+      "avatar_url": "https://avatars.githubusercontent.com/u/94278611",
       "profile": "https://jgyasu.github.io",
       "contributions": [
         "code",
+        "bug",
+        "test",
         "doc"
       ]
     },
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index ff0c98b88..60b4ccd36 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -6,7 +6,7 @@ Contributors
 <!-- Please add your badges to .all-contibutorsrc -->
 
 <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
-[![All Contributors](https://img.shields.io/badge/all_contributors-335-orange.svg)](#contributors)
+[![All Contributors](https://img.shields.io/badge/all_contributors-336-orange.svg)](#contributors)
 <!-- ALL-CONTRIBUTORS-BADGE:END -->
 
 This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
@@ -177,7 +177,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/whackteachers"><img src="https://avatars0.githubusercontent.com/u/33785383?v=4?s=100" width="100px;" alt="Jason Pong"/><br /><sub><b>Jason Pong</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=whackteachers" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=whackteachers" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/mateuja"><img src="https://avatars.githubusercontent.com/mateuja?s=100" width="100px;" alt="Jaume Mateu"/><br /><sub><b>Jaume Mateu</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=mateuja" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/javiber"><img src="https://avatars.githubusercontent.com/u/3588715?v=4?s=100" width="100px;" alt="Javier Berneche"/><br /><sub><b>Javier Berneche</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=javiber" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=javiber" title="Documentation">📖</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://jgyasu.github.io"><img src="https://avatars0.githubusercontent.com/u/51952076?v=4?s=100" width="100px;" alt="Jigyasu"/><br /><sub><b>Jigyasu</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=jgyasu" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=jgyasu" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="11.11%"><a href="https://jgyasu.github.io"><img src="https://avatars.githubusercontent.com/u/94278611?s=100" width="100px;" alt="Jigyasu"/><br /><sub><b>Jigyasu</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=jgyasu" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Ajgyasu" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=jgyasu" title="Tests">⚠️</a> <a href="https://github.com/sktime/sktime/commits?author=jgyasu" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Z-Fran"><img src="https://avatars.githubusercontent.com/u/49083766?v=4?s=100" width="100px;" alt="Jindong Zhang"/><br /><sub><b>Jindong Zhang</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Z-Fran" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tajir0"><img src="https://avatars.githubusercontent.com/u/36022362?s=100" width="100px;" alt="Jinrong Tang"/><br /><sub><b>Jinrong Tang</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=tajir0" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=tajir0" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/pirnerjonas"><img src="https://avatars.githubusercontent.com/u/48887249?v=4?s=100" width="100px;" alt="Jonas Pirner"/><br /><sub><b>Jonas Pirner</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=pirnerjonas" title="Documentation">📖</a></td>
@@ -357,9 +357,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tch"><img src="https://avatars3.githubusercontent.com/u/184076?v=4?s=100" width="100px;" alt="Tomasz Chodakowski"/><br /><sub><b>Tomasz Chodakowski</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=tch" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=tch" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Atch" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="http://www.timeseriesclassification.com"><img src="https://avatars1.githubusercontent.com/u/9594042?v=4?s=100" width="100px;" alt="Tony Bagnall"/><br /><sub><b>Tony Bagnall</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=TonyBagnall" title="Code">💻</a> <a href="#business-TonyBagnall" title="Business development">💼</a> <a href="https://github.com/sktime/sktime/commits?author=TonyBagnall" title="Documentation">📖</a> <a href="#design-TonyBagnall" title="Design">🎨</a> <a href="#eventOrganizing-TonyBagnall" title="Event Organizing">📋</a> <a href="#fundingFinding-TonyBagnall" title="Funding Finding">🔍</a> <a href="#ideas-TonyBagnall" title="Ideas, Planning, & Feedback">🤔</a> <a href="#projectManagement-TonyBagnall" title="Project Management">📆</a> <a href="#question-TonyBagnall" title="Answering Questions">💬</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3ATonyBagnall" title="Reviewed Pull Requests">👀</a> <a href="#talk-TonyBagnall" title="Talks">📢</a> <a href="#data-TonyBagnall" title="Data">🔣</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/onyekaugochukwu"><img src="https://avatars.githubusercontent.com/u/92909501?v=4?s=100" width="100px;" alt="Ugochukwu Onyeka"/><br /><sub><b>Ugochukwu Onyeka</b></sub></a><br /><a href="#business-onyekaugochukwu" title="Business development">💼</a> <a href="https://github.com/sktime/sktime/commits?author=onyekaugochukwu" title="Documentation">📖</a> <a href="#ideas-onyekaugochukwu" title="Ideas, Planning, & Feedback">🤔</a> <a href="#mentoring-onyekaugochukwu" title="Mentoring">🧑‍🏫</a> <a href="#projectManagement-onyekaugochukwu" title="Project Management">📆</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/utsavcoding"><img src="https://avatars3.githubusercontent.com/u/55446385?v=4?s=100" width="100px;" alt="Utsav Kumar Tiwari"/><br /><sub><b>Utsav Kumar Tiwari</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=utsavcoding" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=utsavcoding" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/Utkarsh-Aggarwal"><img src="https://avatars.githubusercontent.com/Utkarsh-Aggarwal?s=100" width="100px;" alt="Utkarsh Aggarwal"/><br /><sub><b>Utkarsh Aggarwal</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Utkarsh-Aggarwal" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=Utkarsh-Aggarwal" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/issues?q=author%3AUtkarsh-Aggarwal" title="Bug reports">🐛</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/utsavcoding"><img src="https://avatars3.githubusercontent.com/u/55446385?v=4?s=100" width="100px;" alt="Utsav Kumar Tiwari"/><br /><sub><b>Utsav Kumar Tiwari</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=utsavcoding" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=utsavcoding" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/vagechirkov"><img src="https://avatars.githubusercontent.com/vagechirkov?s=100" width="100px;" alt="Valerii Chirkov"/><br /><sub><b>Valerii Chirkov</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vagechirkov" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=vagechirkov" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/vandit98"><img src="https://avatars.githubusercontent.com/u/91458535?v=4?s=100" width="100px;" alt="Vandit Tyagi"/><br /><sub><b>Vandit Tyagi</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vandit98" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Vasudeva-bit"><img src="https://avatars.githubusercontent.com/u/70791259?v=4?s=100" width="100px;" alt="Vasudeva Kilaru"/><br /><sub><b>Vasudeva Kilaru</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Vasudeva-bit" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=Vasudeva-bit" title="Documentation">📖</a></td>
@@ -368,9 +369,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/VjayRam"><img src="https://avatars.githubusercontent.com/u/76694566?v=4?s=100" width="100px;" alt="Vijay Ram Enaganti"/><br /><sub><b>Vijay Ram Enaganti</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=VjayRam" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/victordremov"><img src="https://avatars.githubusercontent.com/u/32140716?s=100" width="100px;" alt="Viktor Dremov"/><br /><sub><b>Viktor Dremov</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=victordremov" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/ViktorKaz"><img src="https://avatars0.githubusercontent.com/u/33499138?v=4?s=100" width="100px;" alt="ViktorKaz"/><br /><sub><b>ViktorKaz</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ViktorKaz" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=ViktorKaz" title="Documentation">📖</a> <a href="#design-ViktorKaz" title="Design">🎨</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/vnicholson1"><img src="?s=100" width="100px;" alt="Vincent Nicholson"/><br /><sub><b>Vincent Nicholson</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vnicholson1" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/vnicholson1"><img src="?s=100" width="100px;" alt="Vincent Nicholson"/><br /><sub><b>Vincent Nicholson</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vnicholson1" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/VyomkeshVyas"><img src="?s=100" width="100px;" alt="Vyomkesh Vyas"/><br /><sub><b>Vyomkesh Vyas</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=VyomkeshVyas" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=VyomkeshVyas" title="Documentation">📖</a> <a href="#example-VyomkeshVyas" title="Examples">💡</a> <a href="https://github.com/sktime/sktime/commits?author=VyomkeshVyas" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/wayneadams"><img src="https://avatars.githubusercontent.com/u/15034841?s=400&u=d717e9945910bcc844c5e64cd56d570c6cc4e8e6&v=4?s=100" width="100px;" alt="Wayne Adams"/><br /><sub><b>Wayne Adams</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=wayneadams" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/fr1ll"><img src="https://avatars.githubusercontent.com/u/29168593?v=4?s=100" width="100px;" alt="Will Sanger"/><br /><sub><b>Will Sanger</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=fr1ll" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=fr1ll" title="Documentation">📖</a></td>
@@ -379,9 +380,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/wirrywoo"><img src="https://avatars.githubusercontent.com/u/148647848?v=4?s=100?s=100" width="100px;" alt="Wilson Cheung"/><br /><sub><b>Wilson Cheung</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=wirrywoo" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=wirrywoo" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/wpdonders"><img src="https://avatars.githubusercontent.com/u/1868824?v=4?s=100?s=100" width="100px;" alt="Wouter Donders"/><br /><sub><b>Wouter Donders</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=wpdonders" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=wpdonders" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/XinyuWuu"><img src="https://avatars.githubusercontent.com/u/57612792?v=4?s=100" width="100px;" alt="Xinyu Wu"/><br /><sub><b>Xinyu Wu</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3AXinyuWuu" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=XinyuWuu" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=XinyuWuu" title="Tests">⚠️</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/yairbeer"><img src="https://avatars.githubusercontent.com/yairbeer?s=100" width="100px;" alt="Yair Beer"/><br /><sub><b>Yair Beer</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=yairbeer" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/yairbeer"><img src="https://avatars.githubusercontent.com/yairbeer?s=100" width="100px;" alt="Yair Beer"/><br /><sub><b>Yair Beer</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=yairbeer" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/yann-hallouard/"><img src="https://avatars.githubusercontent.com/YHallouard?s=100" width="100px;" alt="Yann Hallouard"/><br /><sub><b>Yann Hallouard</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=YHallouard" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=YHallouard" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/MEMEO-PRO"><img src="https://avatars.githubusercontent.com/memeo-pro?s=100" width="100px;" alt="Yash Edake"/><br /><sub><b>Yash Edake</b></sub></a><br /><a href="#maintenance-memeo-pro" title="Maintenance">🚧</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Amemeo-pro" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/YashKhare20"><img src="https://avatars.githubusercontent.com/u/92680366?s=400?s=100" width="100px;" alt="Yash Khare"/><br /><sub><b>Yash Khare</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=YashKhare20" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=YashKhare20" title="Documentation">📖</a></td>
@@ -390,9 +391,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/y-mx"><img src="https://avatars.githubusercontent.com/y-mx?s=100" width="100px;" alt="Yimeng Xie"/><br /><sub><b>Yimeng Xie</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=y-mx" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/sz85512678"><img src="https://avatars.githubusercontent.com/sz85512678?s=100" width="100px;" alt="Zhen Shao"/><br /><sub><b>Zhen Shao</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=sz85512678" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/ZiyaoWei"><img src="https://avatars.githubusercontent.com/u/940823?v=4?s=100" width="100px;" alt="Ziyao Wei"/><br /><sub><b>Ziyao Wei</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ZiyaoWei" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/aa25desh"><img src="https://avatars1.githubusercontent.com/u/29518290?v=4?s=100" width="100px;" alt="aa25desh"/><br /><sub><b>aa25desh</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=aa25desh" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Aaa25desh" title="Bug reports">🐛</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/aa25desh"><img src="https://avatars1.githubusercontent.com/u/29518290?v=4?s=100" width="100px;" alt="aa25desh"/><br /><sub><b>aa25desh</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=aa25desh" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Aaa25desh" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/abandus"><img src="https://avatars2.githubusercontent.com/u/46486474?v=4?s=100" width="100px;" alt="abandus"/><br /><sub><b>abandus</b></sub></a><br /><a href="#ideas-abandus" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/commits?author=abandus" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/adoherty21"><img src="https://avatars.githubusercontent.com/u/52799751?s=400&v=4?s=100" width="100px;" alt="adoherty21"/><br /><sub><b>adoherty21</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Aadoherty21" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/bethrice44"><img src="https://avatars.githubusercontent.com/u/11226988?v=4?s=100" width="100px;" alt="bethrice44"/><br /><sub><b>bethrice44</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Abethrice44" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=bethrice44" title="Code">💻</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3Abethrice44" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/sktime/sktime/commits?author=bethrice44" title="Tests">⚠️</a></td>
@@ -401,9 +402,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/brettkoonce"><img src="https://avatars2.githubusercontent.com/u/11281814?v=4?s=100" width="100px;" alt="brett koonce"/><br /><sub><b>brett koonce</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=brettkoonce" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/btrtts"><img src="https://avatars3.githubusercontent.com/u/66252156?v=4?s=100" width="100px;" alt="btrtts"/><br /><sub><b>btrtts</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=btrtts" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/chizzi25"><img src="https://avatars3.githubusercontent.com/u/67911243?v=4?s=100" width="100px;" alt="chizzi25"/><br /><sub><b>chizzi25</b></sub></a><br /><a href="#blog-chizzi25" title="Blogposts">📝</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/chrisholder"><img src="https://avatars.githubusercontent.com/u/4674372?v=4?s=100" width="100px;" alt="chrisholder"/><br /><sub><b>chrisholder</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Tests">⚠️</a> <a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Documentation">📖</a> <a href="#design-chrisholder" title="Design">🎨</a> <a href="#example-chrisholder" title="Examples">💡</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/chrisholder"><img src="https://avatars.githubusercontent.com/u/4674372?v=4?s=100" width="100px;" alt="chrisholder"/><br /><sub><b>chrisholder</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Tests">⚠️</a> <a href="https://github.com/sktime/sktime/commits?author=chrisholder" title="Documentation">📖</a> <a href="#design-chrisholder" title="Design">🎨</a> <a href="#example-chrisholder" title="Examples">💡</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/morestart"><img src="https://avatars.githubusercontent.com/u/35556811?s=100" width="100px;" alt="ctl"/><br /><sub><b>ctl</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Amorestart" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/danbartl"><img src="https://avatars.githubusercontent.com/u/19947407?v=4?s=100" width="100px;" alt="danbartl"/><br /><sub><b>danbartl</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Adanbartl" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=danbartl" title="Code">💻</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3Adanbartl" title="Reviewed Pull Requests">👀</a> <a href="#talk-danbartl" title="Talks">📢</a> <a href="https://github.com/sktime/sktime/commits?author=danbartl" title="Tests">⚠️</a> <a href="#tutorial-danbartl" title="Tutorials">✅</a> <a href="#video-danbartl" title="Videos">📹</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/hamzahiqb"><img src="https://avatars3.githubusercontent.com/u/10302415?v=4?s=100" width="100px;" alt="hamzahiqb"/><br /><sub><b>hamzahiqb</b></sub></a><br /><a href="#infra-hamzahiqb" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
@@ -412,9 +413,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/jschemm"><img src="https://avatars.githubusercontent.com/u/81151346?v=4?s=100" width="100px;" alt="jschemm"/><br /><sub><b>jschemm</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=jschemm" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/kkoziara"><img src="https://avatars1.githubusercontent.com/u/4346849?v=4?s=100" width="100px;" alt="kkoziara"/><br /><sub><b>kkoziara</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=kkoziara" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Akkoziara" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/matteogales"><img src="https://avatars0.githubusercontent.com/u/9269326?v=4?s=100" width="100px;" alt="matteogales"/><br /><sub><b>matteogales</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=matteogales" title="Code">💻</a> <a href="#design-matteogales" title="Design">🎨</a> <a href="#ideas-matteogales" title="Ideas, Planning, & Feedback">🤔</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/oleskiewicz"><img src="https://avatars1.githubusercontent.com/u/5682158?v=4?s=100" width="100px;" alt="oleskiewicz"/><br /><sub><b>oleskiewicz</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Tests">⚠️</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/oleskiewicz"><img src="https://avatars1.githubusercontent.com/u/5682158?v=4?s=100" width="100px;" alt="oleskiewicz"/><br /><sub><b>oleskiewicz</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=oleskiewicz" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/pabworks"><img src="https://avatars.githubusercontent.com/u/32725127?v=4?s=100" width="100px;" alt="pabworks"/><br /><sub><b>pabworks</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=pabworks" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=pabworks" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/xloem"><img src="?s=100" width="100px;" alt="patiently pending world peace"/><br /><sub><b>patiently pending world peace</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=xloem" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/raishubham1"><img src="https://avatars3.githubusercontent.com/u/29356417?v=4?s=100" width="100px;" alt="raishubham1"/><br /><sub><b>raishubham1</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=raishubham1" title="Documentation">📖</a></td>
@@ -423,9 +424,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/sri1419"><img src="https://avatars2.githubusercontent.com/u/65078278?v=4?s=100" width="100px;" alt="sri1419"/><br /><sub><b>sri1419</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=sri1419" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tensorflow-as-tf"><img src="https://avatars.githubusercontent.com/u/51345718?v=4?s=100" width="100px;" alt="tensorflow-as-tf"/><br /><sub><b>tensorflow-as-tf</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=tensorflow-as-tf" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/vedazeren"><img src="https://avatars3.githubusercontent.com/u/63582874?v=4?s=100" width="100px;" alt="vedazeren"/><br /><sub><b>vedazeren</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vedazeren" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=vedazeren" title="Tests">⚠️</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/vincent-nich12"><img src="https://avatars3.githubusercontent.com/u/36476633?v=4?s=100" width="100px;" alt="vincent-nich12"/><br /><sub><b>vincent-nich12</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vincent-nich12" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/vincent-nich12"><img src="https://avatars3.githubusercontent.com/u/36476633?v=4?s=100" width="100px;" alt="vincent-nich12"/><br /><sub><b>vincent-nich12</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vincent-nich12" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/vollmersj"><img src="https://avatars2.githubusercontent.com/u/12613127?v=4?s=100" width="100px;" alt="vollmersj"/><br /><sub><b>vollmersj</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=vollmersj" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/xiaobenbenecho"><img src="https://avatars.githubusercontent.com/u/17461849?v=4?s=100" width="100px;" alt="xiaobenbenecho"/><br /><sub><b>xiaobenbenecho</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=xiaobenbenecho" title="Code">💻</a></td>
     </tr>
diff --git a/docs/source/api_reference/forecasting.rst b/docs/source/api_reference/forecasting.rst
index 4d4c89085..580e9f97a 100644
--- a/docs/source/api_reference/forecasting.rst
+++ b/docs/source/api_reference/forecasting.rst
@@ -580,6 +580,14 @@ Intermittent time series forecasters
 
     Croston
 
+.. currentmodule:: sktime.forecasting.statsforecast
+
+.. autosummary::
+    :toctree: auto_generated/
+    :template: class.rst
+
+    StatsForecastADIDA
+
 Ensembles and stacking
 ----------------------
 
diff --git a/docs/source/api_reference/transformations.rst b/docs/source/api_reference/transformations.rst
index 0616a61b5..d39ffcf43 100644
--- a/docs/source/api_reference/transformations.rst
+++ b/docs/source/api_reference/transformations.rst
@@ -599,6 +599,14 @@ Seasonality and Date-Time Features
 
     FourierTransform
 
+.. currentmodule:: sktime.transformations.series.dummies
+
+.. autosummary::
+    :toctree: auto_generated/
+    :template: class.rst
+
+    SeasonalDummiesOneHot
+
 .. currentmodule:: sktime.transformations.series.basisfunction
 
 .. autosummary::
diff --git a/sktime/classification/deep_learning/fcn.py b/sktime/classification/deep_learning/fcn.py
index 2d0b9b5fd..4bb63ea71 100644
--- a/sktime/classification/deep_learning/fcn.py
+++ b/sktime/classification/deep_learning/fcn.py
@@ -13,7 +13,7 @@ from sktime.utils.dependencies import _check_dl_dependencies
 
 
 class FCNClassifier(BaseDeepClassifier):
-    """Fully Connected Neural Network (FCN), as described in [1]_.
+    """Fully Connected Neural Network (FCN), as described in [1]_ and [2]_.
 
     Adapted from the implementation from Fawaz et. al
     https://github.com/hfawaz/dl-4-tsc/blob/master/classifiers/fcn.py
@@ -44,9 +44,13 @@ class FCNClassifier(BaseDeepClassifier):
 
     References
     ----------
-    .. [1] Zhao et. al, Convolutional neural networks for time series classification,
+    .. [1] Zhao et al, Convolutional neural networks for time series classification,
     Journal of Systems Engineering and Electronics, 28(1):2017.
 
+    .. [2] Wang et al, Time series classification from scratch with
+    deep neural networks: A strong baseline.
+    2017 International Joint Conference on Neural Networks (IJCNN)
+
     Examples
     --------
     >>> from sktime.classification.deep_learning.fcn import FCNClassifier
diff --git a/sktime/classification/deep_learning/mlp.py b/sktime/classification/deep_learning/mlp.py
index 7e6c9f7d0..af743fbb7 100644
--- a/sktime/classification/deep_learning/mlp.py
+++ b/sktime/classification/deep_learning/mlp.py
@@ -44,7 +44,7 @@ class MLPClassifier(BaseDeepClassifier):
 
     References
     ----------
-    .. [1] Wang et. al, Time series classification from
+    .. [1] Wang et al, Time series classification from
     scratch with deep neural networks: A strong baseline,
     International joint conference on neural networks (IJCNN), 2017.
 
diff --git a/sktime/classification/deep_learning/resnet.py b/sktime/classification/deep_learning/resnet.py
index 6a2a84b28..9c71ae8ed 100644
--- a/sktime/classification/deep_learning/resnet.py
+++ b/sktime/classification/deep_learning/resnet.py
@@ -43,7 +43,7 @@ class ResNetClassifier(BaseDeepClassifier):
 
     References
     ----------
-        .. [1] Wang et. al, Time series classification from
+        .. [1] Wang et al, Time series classification from
     scratch with deep neural networks: A strong baseline,
     International joint conference on neural networks (IJCNN), 2017.
 
diff --git a/sktime/forecasting/base/_fh.py b/sktime/forecasting/base/_fh.py
index fd0b9ac1a..b1c0e5d2b 100644
--- a/sktime/forecasting/base/_fh.py
+++ b/sktime/forecasting/base/_fh.py
@@ -170,7 +170,7 @@ def _check_freq(obj):
     elif isinstance(obj, (pd.Period, pd.Index)):
         return _extract_freq_from_cutoff(obj)
     elif isinstance(obj, str) or obj is None:
-        with _suppress_pd22_warning:
+        with _suppress_pd22_warning():
             offset = to_offset(obj)
         return offset
     else:
@@ -400,7 +400,7 @@ class ForecastingHorizon:
             freq_from_self = None
 
         if freq_from_self is not None and freq_from_obj is not None:
-            with _suppress_pd22_warning:
+            with _suppress_pd22_warning():
                 freqs_unequal = freq_from_self != freq_from_obj
             if freqs_unequal:
                 raise ValueError(
diff --git a/sktime/forecasting/compose/_reduce.py b/sktime/forecasting/compose/_reduce.py
index b191a253d..2169a0f7b 100644
--- a/sktime/forecasting/compose/_reduce.py
+++ b/sktime/forecasting/compose/_reduce.py
@@ -42,6 +42,7 @@ from sktime.transformations.compose import FeatureUnion
 from sktime.transformations.series.summarize import WindowSummarizer
 from sktime.utils.datetime import _shift
 from sktime.utils.estimators.dispatch import construct_dispatch
+from sktime.utils.multiindex import apply_method_per_series
 from sktime.utils.sklearn import is_sklearn_estimator, prep_skl_df, sklearn_scitype
 from sktime.utils.validation import check_window_length
 from sktime.utils.warnings import warn
@@ -2516,8 +2517,9 @@ class RecursiveReductionForecaster(BaseForecaster, _ReducerMixin):
 
         for _ in y_lags_no_gaps:
             if hasattr(self.fh, "freq") and self.fh.freq is not None:
-                y_plus_preds = y_plus_preds.asfreq(self.fh.freq)
-
+                y_plus_preds = apply_method_per_series(
+                    y_plus_preds, "asfreq", self.fh.freq
+                )
             Xt = lagger_y_to_X.transform(y_plus_preds)
 
             lag_plus = Lag(lags=1, index_out="extend")
@@ -2545,16 +2547,15 @@ class RecursiveReductionForecaster(BaseForecaster, _ReducerMixin):
             # otherwise proceed as per direct reduction algorithm
             else:
                 y_pred_i = estimator.predict(Xtt_predrow)
-            # 2D numpy array with col index = (var) and 1 row
-            y_pred_list.append(y_pred_i)
 
             y_pred_new_idx = self._get_expected_pred_idx(fh=[predict_idx])
             y_pred_new = pd.DataFrame(y_pred_i, columns=y_cols, index=y_pred_new_idx)
+
+            y_pred_list.append(y_pred_new)
             y_plus_preds = y_plus_preds.combine_first(y_pred_new)
 
-        y_pred = np.concatenate(y_pred_list)
-        y_pred = pd.DataFrame(y_pred, columns=y_cols, index=y_abs_no_gaps)
-        y_pred = slice_at_ix(y_pred, fh_idx)
+        y_pred = pd.concat(y_pred_list).sort_index()
+        y_pred = y_pred.loc[fh_idx]
 
         return y_pred
 
diff --git a/sktime/forecasting/statsforecast.py b/sktime/forecasting/statsforecast.py
index c34c05135..008f9f077 100644
--- a/sktime/forecasting/statsforecast.py
+++ b/sktime/forecasting/statsforecast.py
@@ -10,6 +10,7 @@ __all__ = [
     "StatsForecastAutoTBATS",
     "StatsForecastAutoTheta",
     "StatsForecastMSTL",
+    "StatsForecastADIDA",
 ]
 from typing import Optional, Union
 
@@ -983,3 +984,108 @@ class StatsForecastMSTL(_GeneralisedStatsForecastAdapter):
             ]
 
         return params
+
+
+class StatsForecastADIDA(_GeneralisedStatsForecastAdapter):
+    """StatsForecast ADIDA (Aggregate-Disaggregate Intermittent Demand Approach) model.
+
+    Direct interface to ``statsforecast.models.ADIDA`` by Nixtla.
+
+    This estimator directly interfaces ``ADIDA``,
+    from ``statsforecast`` [1]_ by Nixtla.
+
+    Aggregate-Disagregate Intermittent Demand Approach: Uses temporal aggregation to
+    reduce the number of zero observations. Once the data has been aggregated, it uses
+    the optimized SES to generate the forecasts at the new level. It then breaks down
+    the forecast to the original level using equal weights.
+
+    ADIDA specializes on sparse or intermittent series are series with very few
+    non-zero observations.They are notoriously hard to forecast, and so, different
+    methods have been developed specifically for them.
+
+    Parameters
+    ----------
+    prediction_intervals : ConformalIntervals, optional
+        Information to compute conformal prediction intervals.
+
+    References
+    ----------
+    .. [1] https://nixtlaverse.nixtla.io/statsforecast/docs/models/adida.html#introduction
+
+    Examples
+    --------
+    >>> from sktime.datasets import load_airline
+    >>> from sktime.forecasting.statsforecast import StatsForecastADIDA
+    >>> y = load_airline()
+    >>> model = StatsForecastADIDA()
+    >>> fitted_model = model.fit(y) # doctest: +SKIP
+    >>> y_pred = fitted_model.predict(fh=[1, 2, 3]) # doctest: +SKIP
+    """
+
+    _tags = {
+        # packaging info
+        # --------------
+        "authors": ["AzulGarza", "yarnabrina", "vedantag17"],
+        "maintainers": ["vedantag17"],
+        # "python_dependencies": "statsforecast"
+        # inherited from _GeneralisedStatsForecastAdapter
+        # estimator type
+        # --------------
+        "ignores-exogeneous-X": True,
+        "capability:pred_int": True,
+        "capability:pred_int:insample": True,
+        "python_dependencies": ["statsforecast>=1.4.0"],
+    }
+
+    def __init__(
+        self,
+        prediction_intervals: Optional[object] = None,
+    ):
+        self.prediction_intervals = prediction_intervals
+
+        super().__init__()
+
+        if prediction_intervals is None:
+            self.set_tags(**{"capability:pred_int": False})
+            self.set_tags(**{"capability:pred_int:insample": False})
+
+    def _get_statsforecast_class(self):
+        """Get the class of the statsforecast forecaster."""
+        from statsforecast.models import ADIDA
+
+        return ADIDA
+
+    def _get_statsforecast_params(self):
+        """Get the parameters for the statsforecast model."""
+        return {
+            "prediction_intervals": self.prediction_intervals,
+        }
+
+    @classmethod
+    def get_test_params(cls, parameter_set="default"):
+        """Return testing parameter settings for the estimator.
+
+        Parameters
+        ----------
+        parameter_set : str, default="default"
+            Name of the set of test parameters to return, for use in tests. If no
+            special parameters are defined for a value, will return ``"default"`` set.
+
+        Returns
+        -------
+        params : dict or list of dict
+            Parameters to create testing instances of the class
+        """
+        del parameter_set  # to avoid being detected as unused by ``vulture`` etc.
+
+        if _check_soft_dependencies("statsforecast", severity="none"):
+            from statsforecast.utils import ConformalIntervals
+
+            params = [
+                {},
+                {"prediction_intervals": ConformalIntervals()},
+            ]
+
+            return params
+        else:
+            return [{}, {}]
diff --git a/sktime/param_est/base.py b/sktime/param_est/base.py
index a2cee5a78..dcc888fc9 100644
--- a/sktime/param_est/base.py
+++ b/sktime/param_est/base.py
@@ -4,8 +4,8 @@
     class name: BaseParamFitter
 
 Scitype defining methods:
-    fitting                - fit(X)
-    updating               - update(X)
+    fitting                - fit(X, y=None)
+    updating               - update(X, y=None)
     get fitted parameters  - get_fitted_params() -> dict
 
 Inspection methods:
@@ -17,7 +17,7 @@ State:
     fitted state inspection - check_is_fitted()
 """
 
-__author__ = ["fkiraly"]
+__author__ = ["fkiraly", "satvshr"]
 
 __all__ = ["BaseParamFitter"]
 
@@ -30,6 +30,7 @@ from sktime.datatypes import (
     update_data,
 )
 from sktime.datatypes._dtypekind import DtypeKind
+from sktime.utils.adapters._safe_call import _safe_call
 from sktime.utils.dependencies import _check_estimator_deps
 from sktime.utils.sklearn import is_sklearn_transformer
 from sktime.utils.warnings import warn
@@ -50,15 +51,27 @@ class BaseParamFitter(BaseEstimator):
     parameter fitter have to implement.
 
     Specific implementations of these methods is deferred to concrete instances.
+
+    Attributes
+    ----------
+    _is_fitted : bool
+        Flag indicating whether the estimator has been fitted.
+    _X : sktime compatible container
+        Stored feature data from fit and update.
+    _y : array-like
+        Stored target data from fit and update.
     """
 
     # default tag values - these typically make the "safest" assumption
     _tags = {
         "object_type": "param_est",  # type of object
-        "X_inner_mtype": "pd.DataFrame",  # which types do _fit/_predict, support for X?
+        "X_inner_mtype": "pd.DataFrame",  # which types do _fit/_predict support for X?
+        "y_inner_mtype": "pd.DataFrame",  # which types do _fit/_predict support for y?
         "scitype:X": "Series",  # which X scitypes are supported natively?
+        "scitype:y": "Series",  # which y scitypes are supported natively?
         "capability:missing_values": False,  # can estimator handle missing data?
         "capability:multivariate": False,  # can estimator handle multivariate data?
+        "capability:pairwise": False,  # can handle pairwise parameter estimation?
         "python_version": None,  # PEP 440 python version specifier to limit versions
         "python_dependencies": None,  # string or str list of pkg soft dependencies
         "authors": "sktime developers",  # author(s) of the object
@@ -69,6 +82,7 @@ class BaseParamFitter(BaseEstimator):
         self._is_fitted = False
 
         self._X = None
+        self._y = None
 
         super().__init__()
         _check_estimator_deps(self)
@@ -146,7 +160,7 @@ class BaseParamFitter(BaseEstimator):
         else:
             return NotImplemented
 
-    def fit(self, X):
+    def fit(self, X, y=None):
         """Fit estimator and estimate parameters.
 
         State change:
@@ -155,20 +169,41 @@ class BaseParamFitter(BaseEstimator):
         Writes to self:
             Sets self._is_fitted flag to True.
             Writes `X` to self._X.
+            Writes `y` to self._y if y is not None.
             Sets fitted model attributes ending in "_".
 
         Parameters
         ----------
-        X : time series in sktime compatible data container format
-                Time series to which to fit the forecaster in the update.
-            y can be in one of the following formats, must be same scitype as in fit:
-            Series scitype: pd.Series, pd.DataFrame, or np.ndarray (1D or 2D)
-            Panel scitype: pd.DataFrame with 2-level row MultiIndex,
-                3D np.ndarray, list of Series pd.DataFrame, or nested pd.DataFrame
-            Hierarchical scitype: pd.DataFrame with 3 or more level row MultiIndex
-            For further details:
-                on usage, see forecasting tutorial examples/01_forecasting.ipynb
-                on specification of formats, examples/AA_datatypes_and_datasets.ipynb
+        X : time series in ``sktime`` compatible data container format.
+            Time series to which to fit the parameter estimator.
+
+            Individual data formats in ``sktime`` are so-called :term:`mtype`
+            specifications, each mtype implements an abstract :term:`scitype`.
+
+            * ``Series`` scitype = individual time series, vanilla forecasting.
+              ``pd.DataFrame``, ``pd.Series``, or ``np.ndarray`` (1D or 2D)
+
+            * ``Panel`` scitype = collection of time series, global/panel forecasting.
+              ``pd.DataFrame`` with 2-level row ``MultiIndex`` ``(instance, time)``,
+              ``3D np.ndarray`` ``(instance, variable, time)``,
+              ``list`` of ``Series`` typed ``pd.DataFrame``
+
+            * ``Hierarchical`` scitype = hierarchical collection, for
+              hierarchical forecasting. ``pd.DataFrame`` with 3 or more level row
+              ``MultiIndex`` ``(hierarchy_1, ..., hierarchy_n, time)``
+
+            Whether the estimator supports panel or hierarchical data is determined
+            by the scitype tags ``scitype:X`` and ``scitype:y``.
+
+            For further details on data format, see glossary on :term:`mtype`.
+
+        y : time series in ``sktime`` compatible data container format.
+            Second time series to which to fit the parameter estimator.
+
+            Only required if the estimator is a pairwise estimator,
+            i.e., if the tag ``capability:pairwise`` is True.
+
+            The input is ignored otherwise, and will not throw an exception.
 
         Returns
         -------
@@ -181,21 +216,21 @@ class BaseParamFitter(BaseEstimator):
         self.reset()
 
         # check and convert X/y
-        X_inner = self._check_X(X=X)
+        X_inner, y_inner = self._check_X_y(X=X, y=y)
 
         # set internal X to the new X
-        self._update_X(X_inner)
+        self._update_X_y(X_inner, y_inner)
 
         # checks and conversions complete, pass to inner fit
         #####################################################
-        self._fit(X=X_inner)
+        _safe_call(self._fit, args=(), kwargs={"X": X_inner, "y": y_inner})
 
         # this should happen last
         self._is_fitted = True
 
         return self
 
-    def update(self, X):
+    def update(self, X, y=None):
         """Update fitted parameters on more data.
 
         If no estimator-specific update method has been implemented,
@@ -216,16 +251,36 @@ class BaseParamFitter(BaseEstimator):
 
         Parameters
         ----------
-        X : time series in sktime compatible data container format
-                Time series to which to fit the forecaster in the update.
-            y can be in one of the following formats, must be same scitype as in fit:
-            Series scitype: pd.Series, pd.DataFrame, or np.ndarray (1D or 2D)
-            Panel scitype: pd.DataFrame with 2-level row MultiIndex,
-                3D np.ndarray, list of Series pd.DataFrame, or nested pd.DataFrame
-            Hierarchical scitype: pd.DataFrame with 3 or more level row MultiIndex
-            For further details:
-                on usage, see forecasting tutorial examples/01_forecasting.ipynb
-                on specification of formats, examples/AA_datatypes_and_datasets.ipynb
+        X : time series in ``sktime`` compatible data container format.
+            Time series to which to fit the parameter estimator.
+
+            Individual data formats in ``sktime`` are so-called :term:`mtype`
+            specifications, each mtype implements an abstract :term:`scitype`.
+
+            * ``Series`` scitype = individual time series, vanilla forecasting.
+              ``pd.DataFrame``, ``pd.Series``, or ``np.ndarray`` (1D or 2D)
+
+            * ``Panel`` scitype = collection of time series, global/panel forecasting.
+              ``pd.DataFrame`` with 2-level row ``MultiIndex`` ``(instance, time)``,
+              ``3D np.ndarray`` ``(instance, variable, time)``,
+              ``list`` of ``Series`` typed ``pd.DataFrame``
+
+            * ``Hierarchical`` scitype = hierarchical collection, for
+              hierarchical forecasting. ``pd.DataFrame`` with 3 or more level row
+              ``MultiIndex`` ``(hierarchy_1, ..., hierarchy_n, time)``
+
+            Whether the estimator supports panel or hierarchical data is determined
+            by the scitype tags ``scitype:X`` and ``scitype:y``.
+
+            For further details on data format, see glossary on :term:`mtype`.
+
+        y : time series in ``sktime`` compatible data container format.
+            Second time series to which to fit the parameter estimator.
+
+            Only required if the estimator is a pairwise estimator,
+            i.e., if the tag ``capability:pairwise`` is True.
+
+            The input is ignored otherwise, and will not throw an exception.
 
         Returns
         -------
@@ -241,31 +296,26 @@ class BaseParamFitter(BaseEstimator):
             return self
 
         # input checks and minor coercions on X, y
-        X_inner = self._check_X(X=X)
+        X_inner, y_inner = self._check_X_y(X=X, y=y)
 
-        # update internal X with the new X
-        self._update_X(X_inner)
+        # update internal X, y with the new X, y
+        self._update_y_X(y_inner, X_inner)
 
         # checks and conversions complete, pass to inner update
-        self._update(X=X_inner)
+        _safe_call(self._update, args=(), kwargs={"X": X_inner, "y": y_inner})
 
         return self
 
-    def _check_X(self, X=None):
-        """Check and coerce X for fit/update functions.
+    def _check_X_y(self, X=None, y=None):
+        """Check and coerce X and y for fit/update functions.
 
         Parameters
         ----------
         X : time series in sktime compatible data container format
-                Time series to which to fit the forecaster in the update.
-            y can be in one of the following formats, must be same scitype as in fit:
-            Series scitype: pd.Series, pd.DataFrame, or np.ndarray (1D or 2D)
-            Panel scitype: pd.DataFrame with 2-level row MultiIndex,
-                3D np.ndarray, list of Series pd.DataFrame, or nested pd.DataFrame
-            Hierarchical scitype: pd.DataFrame with 3 or more level row MultiIndex
-            For further details:
-                on usage, see forecasting tutorial examples/01_forecasting.ipynb
-                on specification of formats, examples/AA_datatypes_and_datasets.ipynb
+            Time series to check.
+
+        y : time series in sktime compatible data container format, optional
+            Second time series to check.
 
         Returns
         -------
@@ -275,101 +325,144 @@ class BaseParamFitter(BaseEstimator):
                 converted/coerced version of X, mtype determined by "X_inner_mtype" tag
             Case 2: None if X was None
 
+        y_inner : Series, Panel, or Hierarchical object or None
+                compatible with self.get_tag("y_inner_mtype") format
+            Case 1: self.get_tag("y_inner_mtype") supports scitype of y, then
+                converted/coerced version of y, mtype determined by "y_inner_mtype" tag
+            Case 2: None if y was None or if the estimator is not pairwise
+
         Raises
         ------
-        TypeError if X is not one of the permissible Series mtypes
-        TypeError if X is of a different scitype as self.get_tag("scitype:X")
+        TypeError
+            If X or y is not one of the permissible Series mtypes
+            If X or y is of a different scitype as self.get_tag("scitype:X")
+            or self.get_tag("scitype:y")
         """
-        if X is None:
-            return None
+        X_inner = self._validate_data(X, var_name="X")
+        y_inner = self._validate_data(y, var_name="y")
+        return X_inner, y_inner
 
-        X_inner_mtype = _coerce_to_list(self.get_tag("X_inner_mtype"))
-        # X_inner_scitype = mtype_to_scitype(X_inner_mtype, return_unique=True)
+    def _validate_data(self, data, var_name="data"):
+        """Validate input data (X or y)."""
+        if data is None:
+            return None
 
-        ALLOWED_SCITYPES = _coerce_to_list(self.get_tag("scitype:X"))
+        ALLOWED_SCITYPES = _coerce_to_list(self.get_tag(f"scitype:{var_name}"))
         FORBIDDEN_MTYPES = ["numpyflat", "pd-wide"]
 
+        # Prepare a message about allowed mtypes
         for scitype in ALLOWED_SCITYPES:
             mtypes = set(scitype_to_mtype(scitype))
             mtypes = list(mtypes.difference(FORBIDDEN_MTYPES))
             mtypes_msg = f'"For {scitype} scitype: {mtypes}. '
 
-        # checking X
-        X_valid, _, X_metadata = check_is_scitype(
-            X, scitype=ALLOWED_SCITYPES, return_metadata=["feature_kind"], var_name="X"
+        data_valid, _, data_metadata = check_is_scitype(
+            data,
+            scitype=ALLOWED_SCITYPES,
+            return_metadata=["feature_kind"],
+            var_name=var_name,
         )
+
         msg = (
-            "X must be in an sktime compatible format, "
-            f"of scitypes {ALLOWED_SCITYPES}, "
-            "for instance a pandas.DataFrame with sktime compatible time indices, "
-            "or with MultiIndex and last(-1) level an sktime compatible time index."
+            f"{var_name} must be in an sktime compatible format, "
+            f"of scitypes {ALLOWED_SCITYPES}, for example a pandas.DataFrame with "
+            "an sktime compatible time index."
             " See data format tutorial examples/AA_datatypes_and_datasets.ipynb,"
             "If you think X is already in an sktime supported input format, "
             "run sktime.datatypes.check_raise(X, mtype) to diagnose the error, "
-            "where mtype is the string of the type specification you want for y. "
+            "where mtype is the string of the type specification you want for X. "
             "Possible mtype specification strings are as follows. "
         )
-        if not X_valid:
+
+        if not data_valid:
             raise TypeError(msg + mtypes_msg)
 
-        if DtypeKind.CATEGORICAL in X_metadata["feature_kind"]:
+        if DtypeKind.CATEGORICAL in data_metadata["feature_kind"]:
             raise TypeError(
-                "Parameter estimators do not support categorical features in X."
+                "Parameter estimators do not support categorical features "
+                f" in {var_name}. "
             )
 
-        X_scitype = X_metadata["scitype"]
-        X_mtype = X_metadata["mtype"]
-        # end checking X
+        data_scitype = data_metadata["scitype"]
+        data_mtype = data_metadata["mtype"]
+        data_inner_mtype = _coerce_to_list(self.get_tag(f"{var_name}_inner_mtype"))
 
-        # converts X, converts None to None if X is None
-        X_inner = convert(
-            X,
-            from_type=X_mtype,
-            to_type=X_inner_mtype,
-            as_scitype=X_scitype,
+        return convert(
+            data,
+            from_type=data_mtype,
+            to_type=data_inner_mtype,
+            as_scitype=data_scitype,
         )
 
-        return X_inner
-
-    def _update_X(self, X):
+    def _update_X_y(self, X, y):
         """Update internal memory of seen training data.
 
         Accesses in self:
         _X : only if exists, then assumed same type as X and same cols
             these assumptions should be guaranteed by calls
+        _y : only if exists, then assumed same type as y
 
         Writes to self:
         _X : same type as X - new rows from X are added to current _X
             if _X does not exist, stores X as _X
+        _y : same type as y - new rows from y are added to current _y
+            if _y does not exist, stores y as _y
 
-        _X is guaranteed to be one of mtypes:
-            pd.DataFrame, pd.Series, np.ndarray, pd-multiindex, numpy3D,
-            pd_multiindex_hier
+        _X is guaranteed to be one of mtypes in the tag "X_inner_mtype"
+        _y is guaranteed to be one of mtypes in the tag "y_inner_mtype"
 
         Parameters
         ----------
-        X : time series in sktime compatible data container format
-                Time series to which to fit the forecaster in the update.
-            y can be in one of the following formats, must be same scitype as in fit:
-            Series scitype: pd.Series, pd.DataFrame, or np.ndarray (1D or 2D)
-            Panel scitype: pd.DataFrame with 2-level row MultiIndex,
-                3D np.ndarray, list of Series pd.DataFrame, or nested pd.DataFrame
-            Hierarchical scitype: pd.DataFrame with 3 or more level row MultiIndex
-            For further details:
-                on usage, see forecasting tutorial examples/01_forecasting.ipynb
-                on specification of formats, examples/AA_datatypes_and_datasets.ipynb
+        X : time series in ``sktime`` compatible data container format.
+            Time series to which to fit the parameter estimator.
+
+            Individual data formats in ``sktime`` are so-called :term:`mtype`
+            specifications, each mtype implements an abstract :term:`scitype`.
+
+            * ``Series`` scitype = individual time series, vanilla forecasting.
+              ``pd.DataFrame``, ``pd.Series``, or ``np.ndarray`` (1D or 2D)
+
+            * ``Panel`` scitype = collection of time series, global/panel forecasting.
+              ``pd.DataFrame`` with 2-level row ``MultiIndex`` ``(instance, time)``,
+              ``3D np.ndarray`` ``(instance, variable, time)``,
+              ``list`` of ``Series`` typed ``pd.DataFrame``
+
+            * ``Hierarchical`` scitype = hierarchical collection, for
+              hierarchical forecasting. ``pd.DataFrame`` with 3 or more level row
+              ``MultiIndex`` ``(hierarchy_1, ..., hierarchy_n, time)``
+
+            Whether the estimator supports panel or hierarchical data is determined
+            by the scitype tags ``scitype:X`` and ``scitype:y``.
+
+            For further details on data format, see glossary on :term:`mtype`.
+
+        y : time series in ``sktime`` compatible data container format.
+            Second time series to which to fit the parameter estimator.
+
+            Only required if the estimator is a pairwise estimator,
+            i.e., if the tag ``capability:pairwise`` is True.
+        """
+        self._update_data(X, "_X")
+        self._update_data(y, "_y")
+
+    def _update_data(self, data, self_data):
+        """Update internal memory of seen training data.
+
+        Updates attribute in self_data with data.
         """
+        X = data
         if X is not None:
             # unwrap X if VectorizedDF
             if isinstance(X, VectorizedDF):
                 X = X.X_multiindex
             # if _X does not exist yet, initialize it with X
             if not hasattr(self, "_X") or self._X is None or not self.is_fitted:
-                self._X = X
+                setattr(self, self_data, X)
             else:
-                self._X = update_data(self._X, X)
+                self_X = getattr(self, self_data)
+                setattr(self, self_data, update_data(self_X, X))
 
-    def _fit(self, X):
+    def _fit(self, X, y=None):
         """Fit estimator and estimate parameters.
 
         private _fit containing the core logic, called from fit
@@ -381,6 +474,9 @@ class BaseParamFitter(BaseEstimator):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             Time series to which to fit the estimator.
+        y : guaranteed to be of a type in self.get_tag("y_inner_mtype")
+            Second time series to which to fit the estimator.
+            None if estimator is not pairwise.
 
         Returns
         -------
@@ -388,7 +484,7 @@ class BaseParamFitter(BaseEstimator):
         """
         raise NotImplementedError("abstract method")
 
-    def _update(self, X):
+    def _update(self, X, y):
         """Update fitted parameters on more data.
 
         private _update containing the core logic, called from update
@@ -406,6 +502,9 @@ class BaseParamFitter(BaseEstimator):
         ----------
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             Time series with which to update the estimator.
+        y : guaranteed to be of a type in self.get_tag("y_inner_mtype")
+            Second time series with which to update the estimator.
+            None if estimator is not pairwise.
 
         Returns
         -------
@@ -420,7 +519,7 @@ class BaseParamFitter(BaseEstimator):
             obj=self,
         )
         # refit with updated data, not only passed data
-        self.fit(X=self._X)
+        self.fit(X=self._X, y=self._y)
         # todo: should probably be self._fit, not self.fit
         # but looping to self.fit for now to avoid interface break
 
diff --git a/sktime/param_est/compose/_func_fitter.py b/sktime/param_est/compose/_func_fitter.py
index a293c424d..c816c56f5 100644
--- a/sktime/param_est/compose/_func_fitter.py
+++ b/sktime/param_est/compose/_func_fitter.py
@@ -160,7 +160,15 @@ class FunctionParamFitter(BaseParamFitter):
             ``create_test_instance`` uses the first (or only) dictionary in ``params``
         """
         params = [
-            {"param": "param", "func": lambda X: "foo"},
-            {"param": "param", "func": lambda X, kwarg: "foo", "kw_args": {"kwarg": 1}},
+            {"param": "param", "func": _lambda_test_simple},
+            {"param": "param", "func": _lambda_test_kwarg, "kw_args": {"kwarg": 1}},
         ]
         return params
+
+
+def _lambda_test_simple(X):
+    return "foo"
+
+
+def _lambda_test_kwarg(X, kwarg):
+    return "foo"
diff --git a/sktime/param_est/lag.py b/sktime/param_est/lag.py
index cc1dc1b31..796fbcea6 100644
--- a/sktime/param_est/lag.py
+++ b/sktime/param_est/lag.py
@@ -25,28 +25,38 @@ class ARLagOrderSelector(BaseParamFitter):
     ----------
     maxlag : int
         Maximum number of lags to consider
+
     ic : str, default="bic"
         Information criterion to use for model selection:
+
         - "aic" : Akaike Information Criterion
         - "bic" : Bayesian Information Criterion (default)
         - "hqic" : Hannan-Quinn Information Criterion
+
     glob : bool, default=False
         If True, searches globally across all lag combinations up to maxlag.
         If False, searches sequentially by adding one lag at a time.
+
     trend : str, default="c"
         Trend to include in the model:
+
         - "n" : No trend
         - "c" : Constant only
         - "t" : Time trend only
         - "ct" : Constant and time trend
+
     seasonal : bool, default=False
         Whether to include seasonal dummies in the model
+
     hold_back : int, optional (default=None)
         Number of initial observations to exclude from the estimation sample
+
     period : int, optional (default=None)
         Period of the data (used only if seasonal=True)
+
     missing : str, default="none"
         How to handle missing values:
+
         - "none" : No handling
         - "drop" : Drop missing observations
         - "raise" : Raise an error
@@ -88,6 +98,7 @@ class ARLagOrderSelector(BaseParamFitter):
         "scitype:X": "Series",
         "capability:missing_values": False,
         "capability:multivariate": False,
+        "capability:pairwise": True,
         "authors": "satvshr",
         "python_dependencies": "statsmodels",
     }
@@ -114,10 +125,10 @@ class ARLagOrderSelector(BaseParamFitter):
         self.missing = missing
         super().__init__()
 
-    def _fit(self, X):
+    def _fit(self, X, y=None):
         """Fit estimator and estimate parameters.
 
-        private _fit containing the core logic, called from fit
+        Private _fit containing the core logic, called from fit.
 
         Writes to self:
             Sets fitted model attributes ending in "_".
@@ -127,6 +138,9 @@ class ARLagOrderSelector(BaseParamFitter):
         X : guaranteed to be of a type in self.get_tag("X_inner_mtype")
             Time series to which to fit the estimator.
             If exog is passed in fit, X is the endogenous variable.
+        y : array-like, optional (default=None)
+            Exogenous variables used for fitting the estimator.
+            Acts as the exog parameter in the underlying model.
 
         Returns
         -------
@@ -136,6 +150,7 @@ class ARLagOrderSelector(BaseParamFitter):
 
         self.results = ar_select_order(
             X,
+            exog=y,
             maxlag=self.maxlag,
             ic=self.ic,
             trend=self.trend,
@@ -150,13 +165,6 @@ class ARLagOrderSelector(BaseParamFitter):
 
         return self
 
-    def get_fitted_params(self):
-        """Get fitted parameters."""
-        return {
-            "selected_lags": self.selected_model_,
-            "ic_value": self.ic_value_,
-        }
-
     @classmethod
     def get_test_params(cls, parameter_set="default"):
         """Return testing parameter settings for the estimator.
diff --git a/sktime/param_est/seasonality.py b/sktime/param_est/seasonality.py
index 0f7034d68..6c2b5bf92 100644
--- a/sktime/param_est/seasonality.py
+++ b/sktime/param_est/seasonality.py
@@ -360,15 +360,18 @@ class SeasonalityACFqstat(BaseParamFitter):
         self.pvalues_ = pvalues
 
         if candidate_sp is not None:
-            qstat_cand = qstat[candidate_sp]
-            pvalues_cand = pvalues[candidate_sp]
+            if isinstance(candidate_sp, int):
+                candidate_sp = [candidate_sp]
+            csp_ixer = [c - 1 for c in candidate_sp]
+            qstat_cand = qstat[csp_ixer]
+            pvalues_cand = pvalues[csp_ixer]
         else:
             qstat_cand = qstat
             pvalues_cand = pvalues
             candidate_sp = range(2, nlags + 1)
 
         self.qstat_cand_ = qstat_cand
-        self.pvalues_cand = pvalues_cand
+        self.pvalues_cand_ = pvalues_cand
 
         if p_adjust != "none":
             reject_cand, pvals_adj, _, _ = multipletests(
@@ -498,12 +501,11 @@ class SeasonalityPeriodogram(BaseParamFitter):
             thresh=self.thresh,
         )
 
-        seasons = [x[0] for x in seasons]
-
         if seasons is None or len(seasons) == 0:
             self.sp_ = 1
             self.sp_significant_ = []
         else:
+            seasons = [x[0] for x in seasons]
             self.sp_significant_ = seasons
             self.sp_ = self.sp_significant_[0]
 
diff --git a/sktime/param_est/stationarity/_arch.py b/sktime/param_est/stationarity/_arch.py
index f52958838..6a6342561 100644
--- a/sktime/param_est/stationarity/_arch.py
+++ b/sktime/param_est/stationarity/_arch.py
@@ -758,10 +758,9 @@ class StationarityZivotAndrews(BaseParamFitter):
         """
         params1 = {}
         params2 = {
-            "lags": 5,
             "trend": "ct",
             "trim": 0.1,
-            "max_lags": 10,
+            "max_lags": 5,
             "method": "t-stat",
             "p_threshold": 0.1,
         }
diff --git a/sktime/param_est/stationarity/_statsmodels.py b/sktime/param_est/stationarity/_statsmodels.py
index 6598bc79d..1e9bb0131 100644
--- a/sktime/param_est/stationarity/_statsmodels.py
+++ b/sktime/param_est/stationarity/_statsmodels.py
@@ -169,13 +169,12 @@ class StationarityKPSS(BaseParamFitter):
     ----------
     p_threshold : float, optional, default=0.05
         significance threshold to apply in testing for stationarity
+
     regression : str, one of {"c","ct","ctt","n"}, optional, default="c"
         Constant and trend order to include in regression.
 
         * "c" : constant only (default).
         * "ct" : constant and trend.
-        * "ctt" : constant, and linear and quadratic trend.
-        * "n" : no constant, no trend.
 
     nlags : str or int, optional, default="auto". If int, must be positive.
         Indicates the number of lags to be used internally in ``kpss``.
@@ -286,6 +285,6 @@ class StationarityKPSS(BaseParamFitter):
             ``create_test_instance`` uses the first (or only) dictionary in ``params``
         """
         params1 = {}
-        params2 = {"p_threshold": 0.1, "regression": "ctt", "nlags": 5}
+        params2 = {"p_threshold": 0.1, "regression": "ct", "nlags": 5}
 
         return [params1, params2]
diff --git a/sktime/registry/_tags.py b/sktime/registry/_tags.py
index 7a57c557a..72cabdcfd 100644
--- a/sktime/registry/_tags.py
+++ b/sktime/registry/_tags.py
@@ -2448,6 +2448,12 @@ ESTIMATOR_TAG_REGISTER = [
         "bool",
         "is aligner capable of aligning multiple series (True) or only two (False)?",
     ),
+    (
+        "capability:pairwise",
+        "param_est",
+        "bool",
+        "Indicates whether the estimator supports pairwise parameter estimation.",
+    ),
     (
         "capability:distance",
         "aligner",
diff --git a/sktime/regression/deep_learning/mlp.py b/sktime/regression/deep_learning/mlp.py
index ea752b81c..0598d96d1 100644
--- a/sktime/regression/deep_learning/mlp.py
+++ b/sktime/regression/deep_learning/mlp.py
@@ -43,7 +43,7 @@ class MLPRegressor(BaseDeepRegressor):
 
     References
     ----------
-    .. [1] Wang et. al, Time series classification from
+    .. [1] Wang et al, Time series classification from
     scratch with deep neural networks: A strong baseline,
     International joint conference on neural networks (IJCNN), 2017.
 
diff --git a/sktime/regression/deep_learning/resnet.py b/sktime/regression/deep_learning/resnet.py
index 557b2dc49..641f72766 100644
--- a/sktime/regression/deep_learning/resnet.py
+++ b/sktime/regression/deep_learning/resnet.py
@@ -42,7 +42,7 @@ class ResNetRegressor(BaseDeepRegressor):
 
     References
     ----------
-        .. [1] Wang et. al, Time series classification from
+        .. [1] Wang et al, Time series classification from
     scratch with deep neural networks: A strong baseline,
     International joint conference on neural networks (IJCNN), 2017.
 
diff --git a/sktime/transformations/series/dummies.py b/sktime/transformations/series/dummies.py
new file mode 100644
index 000000000..b30917cf2
--- /dev/null
+++ b/sktime/transformations/series/dummies.py
@@ -0,0 +1,256 @@
+# copyright: sktime developers, BSD-3-Clause License (see LICENSE file)
+"""Dummy variables for time series."""
+
+__author__ = ["ericjb"]
+
+import calendar
+from typing import Optional
+
+import pandas as pd
+
+from sktime.transformations.base import BaseTransformer
+
+
+class SeasonalDummiesOneHot(BaseTransformer):
+    r"""Seasonal Dummy Features for time series seasonality.
+
+    A standard approach to capture seasonal effects is to add dummy exogenous variables,
+    one for each season. e.g. for monthly seasonality add binary dummy variables
+    Jan, Feb, .... For time 't', these variables are set to 1 (resp 0) if 't' occurs
+    (resp does not occur) on that season. To avoid collinearity, one season is dropped
+    when an intercept is also part of the model.
+
+    In the language of machine learning, the use of seasonal dummies is one hot encoding
+    for the seasonal categorical variable.
+
+    Currently the following frequencies are supported:
+    - Monthly: 'M'
+    - Quarterly: 'Q'
+    - Weekly: 'W'
+    - Daily: 'D'
+    - Hourly: 'H'
+
+    Parameters
+    ----------
+    sp : int, optional, default = None
+        Only used if the index of X (or y if X is None) passed to _transform()
+        is a DatetimeIndex.
+        The seasonal periodicity of the time series (e.g. 12 for monthly data).
+        Can be omitted even in this case if freq is provided.
+        (e.g. if index.freq is available, or freq='M')
+    freq : str, optional, default = None
+        Only used if the index of X (or y if X is None) passed to _transform()
+        is a DatetimeIndex and sp is not provided.
+        The frequency of the time series (e.g. 'M' for monthly data).
+        Can be omitted even in this case if index.freq is available.
+    drop : bool, default = True
+        Drop the first seasonal dummy? (Should be True if model contains an intercept)
+
+    Examples
+    --------
+    >>> from sktime.transformations.series.dummies import SeasonalDummiesOneHot
+    >>> from sktime.datasets import load_airline
+    >>> y = load_airline()
+    >>> transformer = SeasonalDummiesOneHot()
+    >>> X = transformer.fit_transform(y=y, X=None)
+    """
+
+    _tags = {
+        # packaging info
+        # --------------
+        "authors": ["ericjb"],
+        # estimator type
+        # --------------
+        "scitype:transform-input": "Series",
+        # what is the scitype of X: Panel
+        "scitype:transform-output": "Series",
+        # what scitype is returned: Primitives, Series, Panel
+        "scitype:transform-labels": "None",
+        # what is the scitype of y: None (not needed), Primitives, Series, Panel
+        "scitype:instancewise": True,  # is this an instance-wise transform?
+        "capability:inverse_transform": False,  # can the transformer inverse transform?
+        "univariate-only": False,  # can the transformer handle multivariate X?
+        "X_inner_mtype": [
+            "pd.DataFrame",
+            "pd.Series",
+        ],  # which mtypes do _fit/_predict support for X?
+        # this can be a Panel mtype even if transform-input is Series, vectorized
+        "y_inner_mtype": [
+            "pd.Series",
+        ],  # which mtypes do _fit/_predict support for y?
+        "requires_y": False,  # does y need to be passed in fit?
+        "enforce_index_type": [
+            pd.PeriodIndex,
+            pd.DatetimeIndex,
+        ],  # index type that needs to be enforced
+        # in X/y
+        "fit_is_empty": True,  # is fit empty and can be skipped? Yes = True
+        "X-y-must-have-same-index": True,  # can estimator handle different X/y index?
+        "transform-returns-same-time-index": True,
+        # does transform return have the same time index as input X
+        "skip-inverse-transform": True,  # is inverse-transform skipped when called?
+        "capability:unequal_length": False,
+        # can the transformer handle unequal length time series (if passed Panel)?
+        "capability:unequal_length:removes": False,
+        # is transform result always guaranteed to be equal length (and series)?
+        #   not relevant for transformers that return Primitives in transform-output
+        "handles-missing-data": True,  # can estimator handle missing data?
+        # todo: rename to capability:missing_values
+        "capability:missing_values:removes": True,
+        # is transform result always guaranteed to contain no missing values?
+    }
+
+    def __init__(
+        self,
+        sp: Optional[int] = None,
+        freq: Optional[str] = None,
+        drop: Optional[bool] = True,
+    ):
+        self.sp = sp
+        self.freq = freq
+        self.drop = drop
+
+        super().__init__()
+
+    def _fit(self, X=None, y=None):
+        """Fit transformer to X and y.
+
+        Does nothing.
+
+        Parameters
+        ----------
+        X : Series or Panel of mtype X_inner_mtype
+            if X_inner_mtype is list, _fit must support all types in it
+            Data to fit transform to
+        y : Series or Panel of mtype y_inner_mtype, default=None
+            Additional data, e.g., labels for transformation
+
+        Returns
+        -------
+        self: reference to self
+        """
+        return self
+
+    def _transform(self, X=None, y=None):
+        """Transform X and return a transformed version.
+
+        private _transform containing core logic, called from transform
+
+        Logic:
+          - if X is not None, use its index to create the seasonal dummies
+            else if X is None, use y's index to create the seasonal dummies
+          - if sp is not None, use it to determine which dummies to create
+          - else if sp is None, get the freq from self.freq
+          - if sp is None and freq is None
+              - get the freq from the index
+              - if the index does not have a freq, raise an error
+
+
+        Parameters
+        ----------
+        X : Series or Panel of mtype X_inner_mtype
+            if X_inner_mtype is list, _transform must support all types in it
+            Data to be transformed
+        y : Series or Panel of mtype y_inner_mtype, default=None
+            Additional data, e.g., labels for transformation
+
+        Returns
+        -------
+        transformed version of X
+        """
+
+        def number_to_freq(number):
+            number_map = {1: "A", 4: "Q", 12: "M", 52: "W", 365: "D", 8760: "H"}
+            freq = number_map.get(number, None)
+            if freq is None:
+                raise ValueError(f"Unsupported seasonal periodicity: {number}")
+            return freq
+
+        if X is not None:
+            index = X.index
+        elif y is not None:
+            index = y.index
+        else:
+            raise ValueError("Both X and y cannot be None")
+
+        if isinstance(index, pd.PeriodIndex):
+            period_index = index
+        else:
+            if self.sp is not None:
+                freq = number_to_freq(self.sp)
+            else:
+                freq = self.freq
+                if freq is None:
+                    freq = index.freq
+                    if freq is None:
+                        raise ValueError("Frequency can't be determined from the index")
+
+            period_index = index.to_period(freq)
+
+        # Extract the appropriate attribute based on the frequency of the period index
+        if period_index.freqstr == "M":
+            time_index = period_index.month
+        elif period_index.freqstr == "Q":
+            time_index = period_index.quarter
+        elif period_index.freqstr == "W":
+            time_index = period_index.week
+        elif period_index.freqstr == "D":
+            time_index = period_index.day
+        elif period_index.freqstr == "H":
+            time_index = period_index.hour
+        else:
+            raise ValueError(f"Unsupported frequency: {period_index.freqstr}")
+
+        # Create dummy variables for the time periods
+        dummies = pd.get_dummies(time_index, prefix="", prefix_sep="")
+        if period_index.freqstr == "M":
+            dummies.columns = dummies.columns.map(lambda x: calendar.month_abbr[int(x)])
+        elif period_index.freqstr == "Q":
+            dummies.columns = dummies.columns.map(lambda x: f"Q{int(x)}")
+        elif period_index.freqstr == "W":
+            dummies.columns = dummies.columns.map(lambda x: f"W{int(x)}")
+        elif period_index.freqstr == "D":
+            dummies.columns = dummies.columns.map(lambda x: f"D{int(x)}")
+        elif period_index.freqstr == "H":
+            dummies.columns = dummies.columns.map(lambda x: f"H{int(x)}")
+        dummies = dummies.astype(int)  # Convert boolean values to integers
+
+        dummies.index = index
+
+        if self.drop:
+            dummies = dummies.iloc[
+                :, 1:
+            ]  # drop the first column to avoid multicollinearity
+
+        if X is not None:
+            X_transformed = pd.concat([X, dummies], axis=1, copy=True)
+        else:
+            X_transformed = dummies
+
+        return X_transformed
+
+    @classmethod
+    def get_test_params(cls, parameter_set="default"):
+        """Return testing parameter settings for the estimator.
+
+        Parameters
+        ----------
+        parameter_set : str, default="default"
+            Name of the set of test parameters to return, for use in tests. If no
+            special parameters are defined for a value, will return ``"default"`` set.
+            There are currently no reserved values for transformers.
+
+        Returns
+        -------
+        params : dict or list of dict, default = {}
+            Parameters to create testing instances of the class
+            Each dict are parameters to construct an "interesting" test instance, i.e.,
+            ``MyClass(**params)`` or ``MyClass(**params[i])`` creates a valid test
+            instance.
+            ``create_test_instance`` uses the first (or only) dictionary in ``params``
+        """
+        param0 = {}
+        param1 = {"sp": 12}
+        param2 = {"sp": 12, "drop": False}
+
+        return [param0, param1, param2]
diff --git a/sktime/utils/datetime.py b/sktime/utils/datetime.py
index 38dd029b0..a1bb68ad4 100644
--- a/sktime/utils/datetime.py
+++ b/sktime/utils/datetime.py
@@ -71,7 +71,7 @@ def _get_intervals_count_and_unit(freq: str) -> tuple[int, str]:
     if freq is None:
         raise ValueError("frequency is missing")
     else:
-        with _suppress_pd22_warning:
+        with _suppress_pd22_warning():
             offset = pd.tseries.frequencies.to_offset(freq)
         count, unit = offset.n, offset.base.freqstr
         return count, unit
diff --git a/sktime/utils/dependencies/_safe_import.py b/sktime/utils/dependencies/_safe_import.py
index 4e477307e..483b7f84b 100644
--- a/sktime/utils/dependencies/_safe_import.py
+++ b/sktime/utils/dependencies/_safe_import.py
@@ -45,19 +45,20 @@ def _safe_import(import_path, pkg_name=None):
     Examples
     --------
     >>> # Import a top-level module
-    >>> torch = safe_import("torch")
+    >>> torch = _safe_import("torch")
 
     >>> # Import a submodule
-    >>> nn = safe_import("torch.nn")
+    >>> nn = _safe_import("torch.nn")
 
     >>> # Import a specific class
-    >>> Linear = safe_import("torch.nn.Linear")
+    >>> Linear = _safe_import("torch.nn.Linear")
 
     >>> # Import with different package name
-    >>> cv2 = safe_import("cv2", pkg_name="opencv-python")
+    >>> cv2 = _safe_import("cv2", pkg_name="opencv-python")
     """
+    path_list = import_path.split(".")
+
     if pkg_name is None:
-        path_list = import_path.split(".")
         pkg_name = path_list[0]
 
     if _check_soft_dependencies(pkg_name, severity="none"):
@@ -71,7 +72,7 @@ def _safe_import(import_path, pkg_name=None):
             return importlib.import_module(import_path)
     else:
         mock_obj = MagicMock()
-        mock_obj.__call__ = MagicMock(
-            return_value=f"Please install {pkg_name} to use this functionality."
+        mock_obj.__str__.return_value = (
+            f"Please install {pkg_name} to use this functionality."
         )
         return mock_obj
diff --git a/sktime/utils/multiindex.py b/sktime/utils/multiindex.py
index a02acc0c0..55a20674f 100644
--- a/sktime/utils/multiindex.py
+++ b/sktime/utils/multiindex.py
@@ -127,3 +127,41 @@ def apply_split(y, iloc_ix):
     y_np = [iloc_ixer.loc[x].to_numpy().flatten() for x in y_loc]
     y_iloc = np.concatenate(y_np)
     return y_iloc
+
+
+def apply_method_per_series(y, method_name, *args, **kwargs):
+    """
+    Apply a method to each series in a multiindex pandas object.
+
+    Parameters
+    ----------
+    y : pd.DataFrame or pd.Series
+        Data to apply method to
+    method_name : str
+        Name of method to apply
+    args : list
+        Positional arguments to pass to method
+    kwargs : dict
+        Keyword arguments to pass to method
+
+    Returns
+    -------
+    pd.DataFrame or pd.Series
+        Data after applying method to each series
+    """
+    if y.index.nlevels == 1:
+        # Apply method directly
+        return getattr(y, method_name)(*args, **kwargs)
+
+    series_idx_tuples = y.index.droplevel(-1).unique().to_list()
+    series_list = []
+    for group_keys in series_idx_tuples:
+        y_series = y.loc[group_keys]
+        y_series = getattr(y_series, method_name)(*args, **kwargs)
+        # Add multiindex
+        y_series.index = pd.MultiIndex.from_tuples(
+            [(*group_keys, idx) for idx in y_series.index], names=y.index.names
+        )
+        series_list.append(y_series)
+    y = pd.concat(series_list).sort_index()
+    return y
diff --git a/sktime/utils/warnings.py b/sktime/utils/warnings.py
index f077baf30..b84dddb72 100644
--- a/sktime/utils/warnings.py
+++ b/sktime/utils/warnings.py
@@ -65,7 +65,8 @@ class _SuppressWarningPattern:
         warnings.filterwarnings("default", category=self.warning_type)
 
 
-_suppress_pd22_warning = _SuppressWarningPattern(
-    FutureWarning,
-    r"'[A-Z]+' is deprecated and will be removed in a future version, please use '[A-Z]+' instead",  # noqa
-)
+def _suppress_pd22_warning():
+    return _SuppressWarningPattern(
+        FutureWarning,
+        r"'[A-Z]+' is deprecated and will be removed in a future version, please use '[A-Z]+' instead",  # noqa
+    )


Test patch:
diff --git a/sktime/tests/_config.py b/sktime/tests/_config.py
index 72803c47d..1db91f5f2 100644
--- a/sktime/tests/_config.py
+++ b/sktime/tests/_config.py
@@ -245,6 +245,8 @@ EXCLUDED_TESTS = {
         "test_inheritance",
         "test_create_test_instance",
     ],
+    # see PR 7921
+    "RocketClassifier": ["test_classifier_on_basic_motions"],
 }
 
 # exclude tests but keyed by test name
diff --git a/sktime/tests/test_switch.py b/sktime/tests/test_switch.py
index 806e84693..7b33134cd 100644
--- a/sktime/tests/test_switch.py
+++ b/sktime/tests/test_switch.py
@@ -24,7 +24,7 @@ def run_test_for_class(cls, return_reason=False):
        If yes, behaviour depends on ONLY_CHANGED_MODULES setting:
        if off (False), always runs the test (return True);
        if on (True), runs test if and only if
-       at least one of conditions 2, 3, 4 below are met.
+       at least one of conditions 2, 3, 4, 5 below are met.
 
     2. Condition 2:
 
@@ -43,13 +43,19 @@ def run_test_for_class(cls, return_reason=False):
       for any of its dependencies have changed in ``pyproject.toml``,
       condition 4 is met.
 
+    5. Condition 5:
+
+      If the object is an sktime ``BaseObject``,
+      and one of the core framework modules ``datatypes``, ``tests``, ``utils``
+      have changed, then condition 5 is met.
+
     cls can also be a list of classes or functions,
     in this case the test is run if and only if both of the following are True:
 
     * all required soft dependencies are present
     * if ``ONLY_CHANGED_MODULES`` is True, additionally,
       if any of the estimators in the list should be tested by
-      at least one of criteria 2-4 above.
+      at least one of criteria 2-5 above.
       If ``ONLY_CHANGED_MODULES`` is False, this condition is always True.
 
     Also checks whether the class or function is on the exclude override list,
@@ -76,6 +82,7 @@ def run_test_for_class(cls, return_reason=False):
         * "True_pyproject_change" - run reason, dep(s) in ``pyproject.toml`` changed
         * "True_changed_tests" - run reason, test(s) covering class have changed
         * "True_changed_class" - run reason, module(s) containing class changed
+        * "True_changed_framework" - run reason, core framework modules changed
 
         If multiple reasons are present, the first one in the above list is returned.
 
@@ -118,6 +125,7 @@ def run_test_for_class(cls, return_reason=False):
             "True_pyproject_change",
             "True_changed_tests",
             "True_changed_class",
+            "True_changed_framework",
         ]
         for pos_reason in POS_REASONS:
             if any(reason == pos_reason for reason in reasons):
@@ -159,12 +167,17 @@ def _run_test_for_class(cls):
         * "True_pyproject_change" - run reason, dep(s) in ``pyproject.toml`` changed
         * "True_changed_tests" - run reason, test(s) covering class have changed
         * "True_changed_class" - run reason, module(s) containing class changed
+        * "True_changed_framework" - run reason, core framework modules changed
 
         If multiple reasons are present, the first one in the above list is returned.
     """
     from sktime.tests.test_all_estimators import ONLY_CHANGED_MODULES
     from sktime.utils.dependencies import _check_estimator_deps
-    from sktime.utils.git_diff import get_packages_with_changed_specs, is_class_changed
+    from sktime.utils.git_diff import (
+        get_packages_with_changed_specs,
+        is_class_changed,
+        is_module_changed,
+    )
 
     PACKAGE_REQ_CHANGED = get_packages_with_changed_specs()
 
@@ -245,6 +258,15 @@ def _run_test_for_class(cls):
     if cond2:
         return True, "True_changed_class"
 
+    # Condition 5:
+    # if the object is an sktime BaseObject, and one of the core framework modules
+    # datatypes, tests, utils have changed, then run the test
+    datatypes_changed = is_module_changed("sktime.datatypes")
+    tests_changed = is_module_changed("sktime.tests")
+    utils_changed = is_module_changed("sktime.utils")
+    if any([datatypes_changed, tests_changed, utils_changed]):
+        return True, "True_changed_framework"
+
     # if none of the conditions are met, do not run the test
     # reason is that there was no change
     return False, "False_no_change"
diff --git a/sktime/tests/tests/test_test_utils.py b/sktime/tests/tests/test_test_utils.py
index fec1c5370..cb1d77b91 100644
--- a/sktime/tests/tests/test_test_utils.py
+++ b/sktime/tests/tests/test_test_utils.py
@@ -97,7 +97,12 @@ def test_run_test_for_class():
     assert isinstance(run_nodep, bool)
     assert isinstance(reason_nodep, str)
 
-    POS_REASONS = ["True_pyproject_change", "True_changed_class", "True_changed_tests"]
+    POS_REASONS = [
+        "True_pyproject_change",
+        "True_changed_class",
+        "True_changed_tests",
+        "True_changed_framework",
+    ]
 
     if not ONLY_CHANGED_MODULES:
         assert run_nodep
diff --git a/sktime/transformations/series/tests/test_dummies.py b/sktime/transformations/series/tests/test_dummies.py
new file mode 100644
index 000000000..884c60a9b
--- /dev/null
+++ b/sktime/transformations/series/tests/test_dummies.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3 -u
+# copyright: sktime developers, BSD-3-Clause License (see LICENSE file)
+"""Test for SeasonalDummiesOneHot transformer."""
+
+__author__ = ["ericjb"]
+__all__ = []
+
+import pandas as pd
+import pytest
+
+from sktime.tests.test_switch import run_test_for_class
+from sktime.transformations.series.dummies import SeasonalDummiesOneHot
+from sktime.utils.dependencies import _check_soft_dependencies
+
+
+@pytest.mark.skipif(
+    not run_test_for_class([SeasonalDummiesOneHot])
+    or _check_soft_dependencies("pandas<2.0.0", severity="none"),
+    # pandas 2.0.0 does not accept ME freq
+    reason="run test only if softdeps are present and incrementally (if requested)",
+)
+def test_seasonal_dummies():
+    date_range = pd.date_range(start="2022-01-01", periods=4, freq="ME")
+    y = pd.Series([1, 2, 3, 4], index=date_range)
+    transformer = SeasonalDummiesOneHot()
+    X = transformer.fit_transform(y=y, X=None)
+    expected_columns = ["Jan", "Feb", "Mar", "Apr"]
+    X_expected = pd.DataFrame(
+        [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
+        columns=expected_columns,
+        index=date_range,
+    )
+    X_expected = X_expected.astype(int)
+    X_expected = X_expected.iloc[:, 1:]  # drop the first dummy
+    assert X.equals(X_expected), "Test failed: X does not match X_expected."
diff --git a/sktime/utils/_testing/scenarios_param_est.py b/sktime/utils/_testing/scenarios_param_est.py
index 4d4b6f540..398eea42f 100644
--- a/sktime/utils/_testing/scenarios_param_est.py
+++ b/sktime/utils/_testing/scenarios_param_est.py
@@ -15,6 +15,7 @@ from sktime.utils._testing.scenarios import TestScenario
 
 # random seed for generating data to keep scenarios exactly reproducible
 RAND_SEED = 42
+RAND_SD2 = 84
 
 
 def get_tag(obj, tag_name):
@@ -53,13 +54,13 @@ class ParamFitterTestScenario(TestScenario, BaseObject):
 class ParamFitterUnivariate(ParamFitterTestScenario):
     """Estimate parameters on a univariate series."""
 
-    _tags = {"X_univariate": False, "is_enabled": True}
+    _tags = {"X_univariate": True, "pairwise": False, "is_enabled": True}
 
     @property
     def args(self):
         return {
             "fit": {
-                "X": _make_series(n_timepoints=20, n_columns=1, random_state=RAND_SEED)
+                "X": _make_series(n_timepoints=30, n_columns=1, random_state=RAND_SEED)
             },
         }
 
@@ -69,13 +70,30 @@ class ParamFitterUnivariate(ParamFitterTestScenario):
 class ParamFitterMultivariate(ParamFitterTestScenario):
     """Estimate parameters on a multivariate series."""
 
-    _tags = {"X_univariate": False, "is_enabled": True}
+    _tags = {"X_univariate": False, "pairwise": False, "is_enabled": True}
 
     @property
     def args(self):
         return {
             "fit": {
-                "X": _make_series(n_timepoints=20, n_columns=2, random_state=RAND_SEED)
+                "X": _make_series(n_timepoints=30, n_columns=2, random_state=RAND_SEED)
+            },
+        }
+
+    default_method_sequence = ["fit", "get_fitted_params"]
+
+
+class ParamFitterPairwiseUnivariate(ParamFitterTestScenario):
+    """Estimate parameters on two univariate series."""
+
+    _tags = {"X_univariate": True, "pairwise": True, "is_enabled": True}
+
+    @property
+    def args(self):
+        return {
+            "fit": {
+                "X": _make_series(n_timepoints=30, n_columns=1, random_state=RAND_SEED),
+                "y": _make_series(n_timepoints=30, n_columns=1, random_state=RAND_SD2),
             },
         }
 
@@ -85,4 +103,5 @@ class ParamFitterMultivariate(ParamFitterTestScenario):
 scenarios_param_est = [
     ParamFitterUnivariate,
     ParamFitterMultivariate,
+    ParamFitterPairwiseUnivariate,
 ]
diff --git a/sktime/utils/_testing/tests/test_safe_import.py b/sktime/utils/_testing/tests/test_safe_import.py
new file mode 100644
index 000000000..28bc0f311
--- /dev/null
+++ b/sktime/utils/_testing/tests/test_safe_import.py
@@ -0,0 +1,61 @@
+__author__ = ["jgyasu"]
+
+from unittest.mock import MagicMock
+
+from sktime.utils.dependencies import _check_soft_dependencies, _safe_import
+
+
+def test_import_present_module():
+    """Test importing a dependency that is installed."""
+    result = _safe_import("pandas")
+    assert result is not None
+    assert _check_soft_dependencies("pandas", severity="error")
+
+
+def test_import_missing_module():
+    """Test importing a dependency that is not installed."""
+    result = _safe_import("nonexistent_module")
+    assert isinstance(result, MagicMock)
+    assert str(result) == (
+        "Please install nonexistent_module to use this functionality."
+    )
+
+
+def test_import_without_pkg_name():
+    """Test importing a dependency with the same name as package name."""
+    result = _safe_import("torch", pkg_name="torch")
+    assert result is not None
+
+
+def test_import_with_different_pkg_name_1():
+    """Test importing a dependency with a different package name."""
+    result = _safe_import("skbase", pkg_name="scikit-base")
+    assert result is not None
+
+
+def test_import_with_different_pkg_name_2():
+    """Test importing another dependency with a different package name."""
+    result = _safe_import("cv2", pkg_name="opencv-python")
+    assert result is not None
+
+
+def test_import_submodule():
+    """Test importing a submodule."""
+    result = _safe_import("torch.nn")
+    assert result is not None
+
+
+def test_import_class():
+    """Test importing a class."""
+    result = _safe_import("torch.nn.Linear")
+    assert result is not None
+
+
+def test_import_existing_object():
+    """Test importing an existing object."""
+    result = _safe_import("pandas.DataFrame")
+    assert result is not None
+    assert result.__name__ == "DataFrame"
+    from pandas import DataFrame
+
+    assert result is DataFrame