Repository: sktime/sktime
Pull request #6294 (2024-04-13 12:00:07)

[BUG] Fix backfill of custom function in window_feature
<!--
Welcome to sktime, and thanks for contributing!
Please have a look at our contribution guide:
https://www.sktime.net/en/latest/get_involved/contributing.html
-->

#### Reference Issues/PRs
<!--
Example: Fixes #1234. See also #3456.

Please use keywords (e.g., Fixes) to create link to the issues or pull requests
you resolved, so that they will automatically be closed when your pull request
is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests.
If no issue exists, you can open one here: https://github.com/sktime/sktime/issues
-->


#### What does this implement/fix? Explain your changes.
<!--
A clear and concise description of what you have implemented.
-->
"In this implementation, within the _window_feature function, if the summarizer is a callable function, it will perform a rolling operation and apply the custom function. However, in the backfill stage, which the previous author set up to use 'Z' for backfilling, the custom function is not applied.
#### Does your contribution introduce a new dependency? If yes, which one?
No
<!--
Only relevant if you changed pyproject.toml.
We try to minimize dependencies in the core dependency set. There
are also further specific instructions to follow for soft dependencies.
See here for handling dependencies in sktime: https://www.sktime.net/en/latest/developer_guide/dependencies.html
-->

#### What should a reviewer concentrate their feedback on?

<!-- This section is particularly useful if you have a pull request that is still in development. You can guide the reviews to focus on the parts that are ready for their comments. We suggest using bullets (indicated by * or -) and filled checkboxes [x] here -->

#### Did you add any tests for the change?

<!-- This section is useful if you have added a test in addition to the existing ones. This will ensure that further changes to these files won't introduce the same kind of bug. It is considered good practice to add tests with newly added code to enforce the fact that the code actually works. This will reduce the chance of introducing logical bugs.
-->

#### Any other comments?
<!--
We value all user contributions, no matter how small or complex they are. If you have any questions, feel free to post
in the dev-chat channel on the sktime discord https://discord.com/invite/54ACzaFsn7. If we are slow to review (>3 working days), likewise feel free to ping us on discord. Thank you for your understanding during the review process.
-->

#### PR checklist
<!--
Please go through the checklist below. Please feel free to remove points if they are not applicable.
-->

##### For all contributions
- [ ] I've added myself to the [list of contributors](https://github.com/sktime/sktime/blob/main/CONTRIBUTORS.md) with any new badges I've earned :-)
  How to: add yourself to the [all-contributors file](https://github.com/sktime/sktime/blob/main/.all-contributorsrc) in the `sktime` root directory (not the `CONTRIBUTORS.md`). Common badges: `code` - fixing a bug, or adding code logic. `doc` - writing or improving documentation or docstrings. `bug` - reporting or diagnosing a bug (get this plus `code` if you also fixed the bug in the PR).`maintenance` - CI, test framework, release.
  See here for [full badge reference](https://allcontributors.org/docs/en/emoji-key)
- [ ] Optionally, for added estimators: I've added myself and possibly to the `maintainers` tag - do this if you want to become the owner or maintainer of an estimator you added.
  See here for further details on the [algorithm maintainer role](https://www.sktime.net/en/latest/get_involved/governance.html#algorithm-maintainers).
- [ ] The PR title starts with either [ENH], [MNT], [DOC], or [BUG]. [BUG] - bugfix, [MNT] - CI, test framework, [ENH] - adding or improving code, [DOC] - writing or improving documentation or docstrings.

##### For new estimators
- [ ] I've added the estimator to the API reference - in `docs/source/api_reference/taskname.rst`, follow the pattern.
- [ ] I've added one or more illustrative usage examples to the docstring, in a pydocstyle compliant `Examples` section.
- [ ] If the estimator relies on a soft dependency, I've set the `python_dependencies` tag and ensured
  dependency isolation, see the [estimator dependencies guide](https://www.sktime.net/en/latest/developer_guide/dependencies.html#adding-a-soft-dependency).

<!--
Thanks for contributing!
-->


fkiraly: FYI @benHeid, as you also reviewed #6081
toandaominh1997: @fkiraly @benHeid I have already added a unit test with the output provider. Please consider reviewing this PR again.
toandaominh1997: @fkiraly  You want to remove 

> Thanks.
> 
> If there's only a single value for the variables (`kwargs`, `column_names`, etc), and no complex fixture generation logic, I would prefer if you assign them inside the test.
In the future, if anyone wants to test WindowSummarizer with the output provider, they can use my function for this case. If you have any ideas or feedback, please let me know.
toandaominh1997: @fkiraly I have fixed the pre-commit in code-quality. Please help me with approval again. Thanks
toandaominh1997: @fkiraly It seems like this PR passed all test cases (docs, code quality). I don't know why all of the other CIs were pushed to the queue. Please consider fixing it
yarnabrina: We run all tests on main branch UTC Sunday 12, and this ensures we are getting higher test coverage. In PR's we run tests only for the modified sections after satisfying some basic criteria (linting, no global imports of soft dependencies, etc.).

It can't be changed right now, and slowness is to be expected. If you can share some CI enhancement ideas, that'll be much appreciated at #5706. 
ShreeshaM07: Apologies for missing out on this bug for the custom `summarizer` function. But I think this PR has not completely solved the issue yet as it gives an error on running `./examples/01c_forecasting_hierarchical_global.ipynb` file . 

```
import numpy as np


def count_gt130(x):
    """Count how many observations lie above threshold 130."""
    return np.sum((x > 700)[::-1])


kwargs = {
    "lag_feature": {
        "lag": [0],
        count_gt130: [[2, 3]],
        "std": [[1, 4]],
    }
}

transformer = WindowSummarizer(**kwargs)
y_transformed = transformer.fit_transform(y_train)

y_transformed.head(10)
```
The error that comes up is 
```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[26], line 18
      9 kwargs = {
     10     "lag_feature": {
     11         "lag": [0],
   (...)
     14     }
     15 }
     17 transformer = WindowSummarizer(**kwargs)
---> 18 y_transformed = transformer.fit_transform(y_train)
     20 y_transformed.head(10)

File [~/sktime_shreesha/sktime/sktime/transformations/base.py:681](https://file+.vscode-resource.vscode-cdn.net/home/shreesha/sktime_shreesha/sktime/examples/~/sktime_shreesha/sktime/sktime/transformations/base.py:681), in BaseTransformer.fit_transform(self, X, y)
    616 """Fit to data, then transform it.
    617 
    618 Fits the transformer to X and y and returns a transformed version of X.
   (...)
    677         Example: i-th instance of the output is the i-th window running over `X`
    678 """
    679 # Non-optimized default implementation; override when a better
    680 # method is possible for a given algorithm.
--> 681 return self.fit(X, y).transform(X, y)

File [~/sktime_shreesha/sktime/sktime/transformations/base.py:595](https://file+.vscode-resource.vscode-cdn.net/home/shreesha/sktime_shreesha/sktime/examples/~/sktime_shreesha/sktime/sktime/transformations/base.py:595), in BaseTransformer.transform(self, X, y)
...
   2469         yield recode_for_categories(
   2470             self.codes[i], self.levels[i], new_levels[i], copy=copy
   2471         )

AssertionError: Length of new_levels (3) must be <= self.nlevels (2)
```
Please let me know if it is reproduced in yours as well @toandaominh1997 
ShreeshaM07: So from my debugging so far I think I have found the place where the error is originating. It is in the `_transform` function in the `summarize.py`. The exact line which is not getting executed and producing error instead is 
```
        Xt_return = pd.concat([Xt_out_df, X.drop(target_cols, axis=1)], axis=1)
```

I had encountered the same error in PR #6037 but it was for the pre-defined `summarizer` functions like mean,std. I fixed it when I kept the order for Multi-Indexed DataFrames to be 1st `shift` then `rolling` else for other DataFrames the order is `rolling` first then `shift`.
But I had not implemented this order in the custom `summarizer` function. So I think that is producing the current error. I will look into this.

fkiraly: hm, indeed. We should find out why it fails the notebook test, and ensure that equivalent coverage is added in `TestAllTransformers` if it is indeed not being caught.
toandaominh1997: Hi Team,

I have thoroughly reviewed the _window_feature method within our WindowSummarizer class and identified an issue with duplicated indices when using the apply method on pandas.core.groupby.SeriesGroupBy objects. This duplication occurred during the application of custom functions, impacting the integrity of our data indexing.

To address this, I've replaced all instances of the apply method with transform. This change effectively prevents index duplication, maintaining the original indexing structure throughout the operation. The updated approach has been integrated and tested thoroughly; it has passed all our existing test cases without introducing new issues.

FYI: @fkiraly  @ShreeshaM07 
fkiraly: Could you kindly explain under which conditions index duplication occurs, and give an example?

We should also test for this being fixed.
toandaominh1997: @fkiraly You can check example like this
```python
import numpy as np
import pandas as pd 
from sktime.transformations.series.summarize import WindowSummarizer

y = pd.read_pickle("examples/global_fc/y.pkl")
def count_gt130(x):
    """Count how many observations lie above threshold 130."""
    return np.sum((x > 130)[::-1])


kwargs = {
    "lag_feature": {
        count_gt130: [[1, 3]],
        "lag": [1],
    }
}
window_summarizer = WindowSummarizer(**kwargs)
output = window_summarizer.fit_transform(y)

output1 = y.groupby(level = [0]).rolling(3, min_periods = 3).apply(count_gt130, raw = True)

output2 = y.groupby(level = [0]).transform(lambda x: x.rolling(window = 3, min_periods = 3).apply(count_gt130, raw = True))

print(f"""
output: \n {output}
output1:\n {output1}
output2:\n {output2.tail(100)}
""")
```

Output:
```
output: 
                       y_count_gt130_1_3  y_lag_1
instances timepoints                            
1         2016-03-15                NaN      NaN
          2016-03-16                NaN   756.67
          2016-03-17                NaN   679.13
          2016-03-18                3.0   633.40
          2016-03-19                3.0  1158.04
...                                 ...      ...
2         2016-04-19                3.0  2407.36
          2016-04-20                3.0  2008.23
          2016-04-21                3.0  1614.98
          2016-04-22                3.0  1597.51
          2016-04-23                3.0  1994.69

[80 rows x 2 columns]
output1:
                                   y
instances instances timepoints     
1         1         2016-03-15  NaN
                    2016-03-16  NaN
                    2016-03-17  3.0
                    2016-03-18  3.0
                    2016-03-19  3.0
...                             ...
2         2         2016-04-19  3.0
                    2016-04-20  3.0
                    2016-04-21  3.0
                    2016-04-22  3.0
                    2016-04-23  3.0

[80 rows x 1 columns]
output2:
                         y
instances timepoints     
1         2016-03-15  NaN
          2016-03-16  NaN
          2016-03-17  3.0
          2016-03-18  3.0
          2016-03-19  3.0
...                   ...
2         2016-04-19  3.0
          2016-04-20  3.0
          2016-04-21  3.0
          2016-04-22  3.0
          2016-04-23  3.0

[80 rows x 1 columns]
```
ShreeshaM07: > Hi Team,
> 
> I have thoroughly reviewed the _window_feature method within our WindowSummarizer class and identified an issue with duplicated indices when using the apply method on pandas.core.groupby.SeriesGroupBy objects. This duplication occurred during the application of custom functions, impacting the integrity of our data indexing.
> 
> To address this, I've replaced all instances of the apply method with transform. This change effectively prevents index duplication, maintaining the original indexing structure throughout the operation. The updated approach has been integrated and tested thoroughly; it has passed all our existing test cases without introducing new issues.
> 
> FYI: @fkiraly @ShreeshaM07

Yes, this change indeed works and passes all existing test cases for Hierarchical and non Hierarchical DataFrames. But would it not be more convenient for future debugging if it is kept in the earlier structure with separate if conditions for Hierarchical and non Hierarchical DataFrames ?
toandaominh1997: > > Hi Team,
> > I have thoroughly reviewed the _window_feature method within our WindowSummarizer class and identified an issue with duplicated indices when using the apply method on pandas.core.groupby.SeriesGroupBy objects. This duplication occurred during the application of custom functions, impacting the integrity of our data indexing.
> > To address this, I've replaced all instances of the apply method with transform. This change effectively prevents index duplication, maintaining the original indexing structure throughout the operation. The updated approach has been integrated and tested thoroughly; it has passed all our existing test cases without introducing new issues.
> > FYI: @fkiraly @ShreeshaM07
> 
> Yes, this change indeed works and passes all existing test cases for Hierarchical and non Hierarchical DataFrames. But would it not be more convenient for future debugging if it is kept in the earlier structure with separate if conditions for Hierarchical and non Hierarchical DataFrames ?

with transform in pandas, it performs a group-specific computation and returns a DataFrame with the same index as the original DataFrame. So you don't need to care about Hierarchical and non-Hierarchical in _window_feature function
If you have any ideas to improve the codebase. Please let's me know
toandaominh1997: @fkiraly Please help me review this PR
toandaominh1997: @fkiraly I have updated with additional more unit test on panel series and implemented to check for duplicate index names. Please help me review this PR again. Thanks

Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index ced77ec75..264884c1d 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2776,8 +2776,19 @@
       ]
     },
     {
-      "login": "slavik57"
-      "name": "Slava Shpitalny"
+      "login": "toandaominh1997",
+      "name": "Henry Dao",
+      "avatar_url": "https://avatars.githubusercontent.com/u/18400648?v=4",
+      "profile": "https://github.com/toandaominh1997",
+      "contributions": [
+        "bug",
+        "code",
+        "test"
+      ]
+    },
+    {
+      "login": "slavik57",
+      "name": "Slava Shpitalny",
       "avatar_url": "https://avatars.githubusercontent.com/u/6184997?v=4",
       "profile": "https://github.com/slavik57",
       "contributions": [
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index dc04d8860..2aca4a900 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-271-orange.svg)](#contributors)
+[![All Contributors](https://img.shields.io/badge/all_contributors-272-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!
@@ -269,9 +269,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/shagn"><img src="https://avatars.githubusercontent.com/u/16029092?v=4?s=100" width="100px;" alt="Sebastian Hagn"/><br /><sub><b>Sebastian Hagn</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=shagn" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/ShivamPathak99"><img src="https://avatars.githubusercontent.com/u/98941325?s=400&v=4?s=100" width="100px;" alt="Shivam Pathak"/><br /><sub><b>Shivam Pathak</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=ShivamPathak99" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/AurumnPegasus"><img src="https://avatars.githubusercontent.com/u/54315149?v=4?s=100" width="100px;" alt="Shivansh Subramanian"/><br /><sub><b>Shivansh Subramanian</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=AurumnPegasus" title="Documentation">📖</a> <a href="https://github.com/sktime/sktime/commits?author=AurumnPegasus" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/ShreeshaM07"><img src="https://avatars.githubusercontent.com/u/120820143?s=400&v=4?s=100" width="100px;" alt="Shreesha M"/><br /><sub><b>Shreesha M</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3AShreeshaM07" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=ShreeshaM07" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=ShreeshaM07" title="Tests">⚠️</a></td>
+      <td align="center" valign="top" width="11.11%"><img src="https://gravatar.com/avatar/cbdbaac712ae282d730cd3028e862d45?s=400&d=robohash&r=x?s=100" width="100px;" alt="Shlok Sabarwal"/><br /><sub><b>Shlok Sabarwal</b></sub><br /><a href="https://github.com/sktime/sktime/commits?author=ssabarwal" title="Code">💻</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/ShreeshaM07"><img src="https://avatars.githubusercontent.com/u/120820143?s=400&v=4?s=100" width="100px;" alt="Shreesha M"/><br /><sub><b>Shreesha M</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3AShreeshaM07" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=ShreeshaM07" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=ShreeshaM07" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/sbuse"><img src="https://avatars.githubusercontent.com/u/24408707?v=4?s=100" width="100px;" alt="Simon B."/><br /><sub><b>Simon B.</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=sbuse" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/solomon-botchway-a1383821b/"><img src="https://avatars.githubusercontent.com/u/62394255?v=4?s=100" width="100px;" alt="Solomon Botchway"/><br /><sub><b>Solomon Botchway</b></sub></a><br /><a href="#maintenance-snnbotchway" title="Maintenance">🚧</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/khrapovs"><img src="https://avatars.githubusercontent.com/u/3774663?v=4?s=100" width="100px;" alt="Stanislav Khrapov"/><br /><sub><b>Stanislav Khrapov</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=khrapovs" title="Code">💻</a></td>
@@ -280,9 +281,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/TNTran92"><img src="https://avatars.githubusercontent.com/u/55965636?v=4?s=100" width="100px;" alt="TNTran92"/><br /><sub><b>TNTran92</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=TNTran92" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Taise228"><img src="https://avatars.githubusercontent.com/u/95762401?s=400&v=4?s=100" width="100px;" alt="Taisei Yamamoto"/><br /><sub><b>Taisei Yamamoto</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=Taise228" title="Code">💻</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://thayeylolu.github.io/portfolio/"><img src="https://avatars.githubusercontent.com/u/13348874?v=4?s=100" width="100px;" alt="Taiwo Owoseni"/><br /><sub><b>Taiwo Owoseni</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=thayeylolu" title="Code">💻</a></td>
-      <td align="center" valign="top" width="11.11%"><a href="https://github.com/lnthach"><img src="https://avatars0.githubusercontent.com/u/7788363?v=4?s=100" width="100px;" alt="Thach Le Nguyen"/><br /><sub><b>Thach Le Nguyen</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=lnthach" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=lnthach" title="Tests">⚠️</a></td>
     </tr>
     <tr>
+      <td align="center" valign="top" width="11.11%"><a href="https://github.com/lnthach"><img src="https://avatars0.githubusercontent.com/u/7788363?v=4?s=100" width="100px;" alt="Thach Le Nguyen"/><br /><sub><b>Thach Le Nguyen</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=lnthach" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=lnthach" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/mathco-wf"><img src="https://avatars.githubusercontent.com/mathco-wf?s=100" width="100px;" alt="TheMathcompay Widget Factory Team"/><br /><sub><b>TheMathcompay Widget Factory Team</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=mathco-wf" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/tombh"><img src="https://avatars.githubusercontent.com/u/160835?s=80&v=4?s=100" width="100px;" alt="Thomas Buckley-Houston"/><br /><sub><b>Thomas Buckley-Houston</b></sub></a><br /><a href="https://github.com/sktime/sktime/issues?q=author%3Atombh" title="Bug reports">🐛</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/xxl4tomxu98"><img src="https://avatars.githubusercontent.com/u/62292177?s=40&v=4?s=100" width="100px;" alt="Tom Xu"/><br /><sub><b>Tom Xu</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=xxl4tomxu98" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=xxl4tomxu98" title="Documentation">📖</a></td>
@@ -291,9 +292,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <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/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/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>
     </tr>
     <tr>
+      <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>
       <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>
@@ -302,9 +303,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
       <td align="center" valign="top" width="11.11%"><a href="https://www.linkedin.com/in/templierw/"><img src="https://github.com/templierw.png?s=100" width="100px;" alt="William Templier"/><br /><sub><b>William Templier</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=templierw" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/magittan"><img src="https://avatars0.githubusercontent.com/u/14024202?v=4?s=100" width="100px;" alt="William Zheng"/><br /><sub><b>William Zheng</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=magittan" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=magittan" title="Tests">⚠️</a></td>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/Xinyu-Wu-0000"><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%3AXinyu-Wu-0000" title="Bug reports">🐛</a> <a href="https://github.com/sktime/sktime/commits?author=Xinyu-Wu-0000" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=Xinyu-Wu-0000" 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>
@@ -313,9 +314,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/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>
-      <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>
     </tr>
     <tr>
+      <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>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/big-o"><img src="https://avatars1.githubusercontent.com/u/1134151?v=4?s=100" width="100px;" alt="big-o"/><br /><sub><b>big-o</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=big-o" title="Code">💻</a> <a href="https://github.com/sktime/sktime/commits?author=big-o" title="Tests">⚠️</a> <a href="#design-big-o" title="Design">🎨</a> <a href="#ideas-big-o" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/sktime/sktime/pulls?q=is%3Apr+reviewed-by%3Abig-o" title="Reviewed Pull Requests">👀</a> <a href="#tutorial-big-o" title="Tutorials">✅</a> <a href="#mentoring-big-o" title="Mentoring">🧑‍🏫</a></td>
@@ -324,9 +325,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/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>
-      <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>
     </tr>
     <tr>
+      <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>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/hiqbal2"><img src="https://avatars3.githubusercontent.com/u/10302415?v=4?s=100" width="100px;" alt="hiqbal2"/><br /><sub><b>hiqbal2</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=hiqbal2" title="Documentation">📖</a></td>
@@ -335,9 +336,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/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>
-      <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>
     </tr>
     <tr>
+      <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>
       <td align="center" valign="top" width="11.11%"><a href="https://github.com/simone-pignotti"><img src="https://avatars1.githubusercontent.com/u/44410066?v=4?s=100" width="100px;" alt="simone-pignotti"/><br /><sub><b>simone-pignotti</b></sub></a><br /><a href="https://github.com/sktime/sktime/commits?author=simone-pignotti" title="Code">💻</a> <a href="https://github.com/sktime/sktime/issues?q=author%3Asimone-pignotti" title="Bug reports">🐛</a></td>
@@ -346,9 +347,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/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>
-      <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>
     </tr>
     <tr>
+      <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>
   </tbody>
diff --git a/docs/source/api_reference/classification.rst b/docs/source/api_reference/classification.rst
index 2fac55d01..36872704b 100644
--- a/docs/source/api_reference/classification.rst
+++ b/docs/source/api_reference/classification.rst
@@ -180,6 +180,7 @@ Kernel-based
     :template: class.rst
 
     TimeSeriesSVC
+    TimeSeriesSVCTslearn
     Arsenal
     RocketClassifier
 
@@ -193,6 +194,7 @@ Shapelet-based
     :template: class.rst
 
     ShapeletTransformClassifier
+    ShapeletLearningClassifierTslearn
     MrSEQL
     MrSQM
 
diff --git a/docs/source/api_reference/regression.rst b/docs/source/api_reference/regression.rst
index 36accbe71..2d9cb278d 100644
--- a/docs/source/api_reference/regression.rst
+++ b/docs/source/api_reference/regression.rst
@@ -83,6 +83,7 @@ Kernel-based
     :toctree: auto_generated/
     :template: class.rst
 
+    TimeSeriesSVRTslearn
     RocketRegressor
 
 Base
diff --git a/pyproject.toml b/pyproject.toml
index 4ab3f010e..45fec5dc1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -77,7 +77,7 @@ dependencies = [
 # or "pip install sktime[all_extras_pandas2]", to install only pandas 2 compatible deps
 #
 all_extras = [
-  "arch>=5.6,<6.4.0",
+  "arch>=5.6,<7.1.0",
   "cloudpickle",
   "dash!=2.9.0",
   "dask<2024.4.2; extra == 'dataframe'",
@@ -118,7 +118,7 @@ all_extras = [
 
 # all soft dependencies compatible with pandas 2
 all_extras_pandas2 = [
-  "arch>=5.6,<6.4.0",
+  "arch>=5.6,<7.1.0",
   "cloudpickle",
   "dash!=2.9.0",
   "dask<2024.4.2; extra == 'dataframe'",
diff --git a/sktime/classification/kernel_based/__init__.py b/sktime/classification/kernel_based/__init__.py
index c99544de3..aa2c4fac8 100644
--- a/sktime/classification/kernel_based/__init__.py
+++ b/sktime/classification/kernel_based/__init__.py
@@ -1,6 +1,7 @@
 """Kernel based time series classifiers."""
-__all__ = ["RocketClassifier", "Arsenal", "TimeSeriesSVC"]
+__all__ = ["RocketClassifier", "Arsenal", "TimeSeriesSVC", "TimeSeriesSVCTslearn"]
 
 from sktime.classification.kernel_based._arsenal import Arsenal
 from sktime.classification.kernel_based._rocket_classifier import RocketClassifier
 from sktime.classification.kernel_based._svc import TimeSeriesSVC
+from sktime.classification.kernel_based._svc_tslearn import TimeSeriesSVCTslearn
diff --git a/sktime/classification/kernel_based/_svc_tslearn.py b/sktime/classification/kernel_based/_svc_tslearn.py
new file mode 100644
index 000000000..ffcf159a9
--- /dev/null
+++ b/sktime/classification/kernel_based/_svc_tslearn.py
@@ -0,0 +1,214 @@
+"""Time series SVC, from tslearn."""
+from sktime.base.adapters._tslearn import _TslearnAdapter
+from sktime.classification.base import BaseClassifier
+
+
+class TimeSeriesSVCTslearn(_TslearnAdapter, BaseClassifier):
+    """Time Series Suppoer Vector Classifier, from tslearn.
+
+    Direct interface to ``tslearn.svm.svm.TimeSeriesSVC``.
+
+    Parameters
+    ----------
+    C : float, optional (default=1.0)
+        Penalty parameter C of the error term.
+
+    kernel : string, optional (default='gak')
+         Specifies the kernel type to be used in the algorithm.
+         It must be one of 'gak' or a kernel accepted by ``sklearn.svm.SVC``.
+         If none is given, 'gak' will be used. If a callable is given it is
+         used to pre-compute the kernel matrix from data matrices; that matrix
+         should be an array of shape ``(n_samples, n_samples)``.
+
+    degree : int, optional (default=3)
+        Degree of the polynomial kernel function ('poly').
+        Ignored by all other kernels.
+
+    gamma : float, optional (default='auto')
+        Kernel coefficient for 'gak', 'rbf', 'poly' and 'sigmoid'.
+        If gamma is 'auto' then:
+
+        - for 'gak' kernel, it is computed based on a sampling of the training
+          set ``tslearn.metrics.gamma_soft_dtw``
+        - for other kernels (eg. 'rbf'), 1/n_features will be used.
+
+    coef0 : float, optional (default=0.0)
+        Independent term in kernel function.
+        It is only significant in 'poly' and 'sigmoid'.
+
+    shrinking : boolean, optional (default=True)
+        Whether to use the shrinking heuristic.
+
+    probability : boolean, optional (default=False)
+        Whether to enable probability estimates. This must be enabled prior
+        to calling `fit`, and will slow down that method.
+        Also, probability estimates are not guaranteed to match predict output.
+        See our :ref:`dedicated user guide section <kernels-ml>`
+        for more details.
+
+    tol : float, optional (default=1e-3)
+        Tolerance for stopping criterion.
+
+    cache_size : float, optional (default=200.0)
+        Specify the size of the kernel cache (in MB).
+
+    class_weight : {dict, 'balanced'}, optional
+        Set the parameter C of class i to class_weight[i]*C for
+        SVC. If not given, all classes are supposed to have
+        weight one.
+        The "balanced" mode uses the values of y to automatically adjust
+        weights inversely proportional to class frequencies in the input data
+        as ``n_samples / (n_classes * np.bincount(y))``
+
+    n_jobs : int or None, optional (default=None)
+        The number of jobs to run in parallel for GAK cross-similarity matrix
+        computations.
+        ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
+        ``-1`` means using all processors. See scikit-learns'
+        `Glossary <https://scikit-learn.org/stable/glossary.html#term-n-jobs>`_
+        for more details.
+
+    verbose : int, default: 0
+        Enable verbose output. Note that this setting takes advantage of a
+        per-process runtime setting in libsvm that, if enabled, may not work
+        properly in a multithreaded context.
+
+    max_iter : int, optional (default=-1)
+        Hard limit on iterations within solver, or -1 for no limit.
+
+    decision_function_shape : 'ovo', 'ovr', default='ovr'
+        Whether to return a one-vs-rest ('ovr') decision function of shape
+        (n_samples, n_classes) as all other classifiers, or the original
+        one-vs-one ('ovo') decision function of libsvm which has shape
+        (n_samples, n_classes * (n_classes - 1) / 2).
+
+    random_state : int, RandomState instance or None, optional (default=None)
+        The seed of the pseudo random number generator to use when shuffling
+        the data.  If int, random_state is the seed used by the random number
+        generator; If RandomState instance, random_state is the random number
+        generator; If None, the random number generator is the RandomState
+        instance used by `np.random`.
+
+    Attributes
+    ----------
+    support_ : array-like, shape = [n_SV]
+        Indices of support vectors.
+
+    n_support_ : array-like, dtype=int32, shape = [n_class]
+        Number of support vectors for each class.
+
+    support_vectors_ : list of arrays of shape [n_SV, sz, d]
+        List of support vectors in tslearn dataset format, one array per class
+
+    dual_coef_ : array, shape = [n_class-1, n_SV]
+        Coefficients of the support vector in the decision function.
+        For multiclass, coefficient for all 1-vs-1 classifiers.
+        The layout of the coefficients in the multiclass case is somewhat
+        non-trivial. See the section about multi-class classification in the
+        SVM section of the User Guide of ``sklearn`` for details.
+
+    coef_ : array, shape = [n_class-1, n_features]
+        Weights assigned to the features (coefficients in the primal
+        problem). This is only available in the case of a linear kernel.
+        `coef_` is a readonly property derived from `dual_coef_` and
+        `support_vectors_`.
+
+    intercept_ : array, shape = [n_class * (n_class-1) / 2]
+        Constants in decision function.
+
+    svm_estimator_ : sklearn.svm.SVC
+        The underlying sklearn estimator
+    """
+
+    _tags = {
+        # packaging info
+        # --------------
+        "authors": ["rtavenar", "yanncabanes", "fkiraly"],
+        # rtavenar and yanncabanes for interfaced estimator
+        "python_dependencies": "tslearn",
+        # estimator type
+        # --------------
+        "capability:multivariate": True,
+        "capability:unequal_length": False,
+    }
+
+    # defines the name of the attribute containing the tslearn estimator
+    _estimator_attr = "_tslearn_svc"
+
+    def _get_tslearn_class(self):
+        """Get tslearn class.
+
+        should import and return tslearn class
+        """
+        from tslearn.svm.svm import TimeSeriesSVC as _TimeSeriesSVC
+
+        return _TimeSeriesSVC
+
+    def __init__(
+        self,
+        C=1.0,
+        kernel="gak",
+        degree=3,
+        gamma="auto",
+        coef0=0.0,
+        shrinking=True,
+        probability=False,
+        tol=0.001,
+        cache_size=200,
+        class_weight=None,
+        n_jobs=None,
+        verbose=0,
+        max_iter=-1,
+        decision_function_shape="ovr",
+        random_state=None,
+    ):
+        self.C = C
+        self.kernel = kernel
+        self.degree = degree
+        self.gamma = gamma
+        self.coef0 = coef0
+        self.shrinking = shrinking
+        self.probability = probability
+        self.tol = tol
+        self.cache_size = cache_size
+        self.class_weight = class_weight
+        self.n_jobs = n_jobs
+        self.verbose = verbose
+        self.max_iter = max_iter
+        self.decision_function_shape = decision_function_shape
+        self.random_state = random_state
+
+        super().__init__()
+
+    @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, 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``
+        """
+        params1 = {"cache_size": 50}
+        params2 = {
+            "C": 0.9,
+            "kernel": "rbf",
+            "gamma": 0.1,
+            "shrinking": False,
+            "probability": True,
+            "tol": 0.01,
+            "cache_size": 55,
+            "decision_function_shape": "ovo",
+        }
+        return [params1, params2]
diff --git a/sktime/classification/shapelet_based/__init__.py b/sktime/classification/shapelet_based/__init__.py
index 88385d63f..7eb51eff3 100644
--- a/sktime/classification/shapelet_based/__init__.py
+++ b/sktime/classification/shapelet_based/__init__.py
@@ -1,6 +1,14 @@
 """Shapelet based time series classifiers."""
-__all__ = ["MrSEQL", "MrSQM", "ShapeletTransformClassifier"]
+__all__ = [
+    "MrSEQL",
+    "MrSQM",
+    "ShapeletLearningClassifierTslearn",
+    "ShapeletTransformClassifier",
+]
 
+from sktime.classification.shapelet_based._learning_tslearn import (
+    ShapeletLearningClassifierTslearn,
+)
 from sktime.classification.shapelet_based._mrseql import MrSEQL
 from sktime.classification.shapelet_based._mrsqm import MrSQM
 from sktime.classification.shapelet_based._stc import ShapeletTransformClassifier
diff --git a/sktime/classification/shapelet_based/_learning_tslearn.py b/sktime/classification/shapelet_based/_learning_tslearn.py
new file mode 100644
index 000000000..35d25019a
--- /dev/null
+++ b/sktime/classification/shapelet_based/_learning_tslearn.py
@@ -0,0 +1,169 @@
+"""Learning Shapelets Classifier, from tslearn."""
+from sktime.base.adapters._tslearn import _TslearnAdapter
+from sktime.classification.base import BaseClassifier
+
+
+class ShapeletLearningClassifierTslearn(_TslearnAdapter, BaseClassifier):
+    """Learning Time Series Shapelets Classifier, from tslearn.
+
+    Direct interface to ``tslearn.shapelets.shapelets.LearningShapelets``.
+
+    Learning Time-Series Shapelets was originally presented in [1]_.
+
+    Parameters
+    ----------
+    n_shapelets_per_size: dict (default: None)
+        Dictionary giving, for each shapelet size (key),
+        the number of such shapelets to be trained (value).
+        If None, `grabocka_params_to_shapelet_size_dict` is used and the
+        size used to compute is that of the shortest time series passed at fit time.
+
+    max_iter: int (default: 10,000)
+        Number of training epochs.
+
+    batch_size: int (default: 256)
+        Batch size to be used.
+
+    optimizer: str or keras.optimizers.Optimizer (default: "sgd")
+        ``keras`` optimizer to use for training.
+
+    weight_regularizer: float or None (default: 0.)
+        Strength of the L2 regularizer to use for training the classification
+        (softmax) layer. If 0, no regularization is performed.
+
+    shapelet_length: float (default: 0.15)
+        The length of the shapelets, expressed as a fraction of the time series length.
+        Used only if ``n_shapelets_per_size`` is None.
+
+    total_lengths: int (default: 3)
+        The number of different shapelet lengths. Will extract shapelets of
+        length i * shapelet_length for i in [1, total_lengths]
+        Used only if ``n_shapelets_per_size`` is None.
+
+    max_size: int or None (default: None)
+        Maximum size for time series to be fed to the model. If None, it is
+        set to the size (number of timestamps) of the training time series.
+
+    scale: bool (default: False)
+        Whether input data should be scaled for each feature of each time
+        series to lie in the [0-1] interval.
+        Default for this parameter is set to `False` in version 0.4 to ensure
+        backward compatibility, but is likely to change in a future version.
+
+    verbose: {0, 1, 2} (default: 0)
+        ``keras`` verbose level.
+
+    random_state : int or None, optional (default: None)
+        The seed of the pseudo random number generator to use when shuffling
+        the data.  If int, ``random_state`` is the seed used by the random number
+        generator; If None, the random number generator is the ``RandomState``
+        instance used by ``np.random``.
+
+    Attributes
+    ----------
+    shapelets_ : numpy.ndarray of objects, each object being a time series
+        Set of time-series shapelets.
+
+    shapelets_as_time_series_ : numpy.ndarray of shape (n_shapelets, sz_shp, d)
+        where ``sz_shp`` is the maximum of all shapelet sizes
+        Set of time-series shapelets formatted as a ``tslearn`` time series dataset.
+
+    transformer_model_ : keras.Model
+        Transforms an input dataset of timeseries into distances to the
+        learned shapelets.
+
+    locator_model_ : keras.Model
+        Returns the indices where each of the shapelets can be found (minimal
+        distance) within each of the timeseries of the input dataset.
+
+    model_ : keras.Model
+        Directly predicts the class probabilities for the input timeseries.
+
+    history_ : dict
+        Dictionary of losses and metrics recorded during fit.
+
+    References
+    ----------
+    .. [1] J. Grabocka et al. Learning Time-Series Shapelets. SIGKDD 2014.
+    """
+
+    _tags = {
+        # packaging info
+        # --------------
+        "authors": ["rtavenar", "yanncabanes", "fspinna", "fkiraly"],
+        "python_dependencies": "tslearn",
+        # estimator type
+        # --------------
+        "capability:multivariate": True,
+        "capability:unequal_length": False,
+    }
+
+    # defines the name of the attribute containing the tslearn estimator
+    _estimator_attr = "_tslearn_shapelets"
+
+    def _get_tslearn_class(self):
+        """Get tslearn class.
+
+        should import and return tslearn class
+        """
+        from tslearn.shapelets.shapelets import LearningShapelets
+
+        return LearningShapelets
+
+    def __init__(
+        self,
+        n_shapelets_per_size=None,
+        max_iter=10000,
+        batch_size=256,
+        optimizer="sgd",
+        weight_regularizer=0.0,
+        shapelet_length=0.15,
+        total_lengths=3,
+        max_size=None,
+        scale=False,
+        verbose=0,
+        random_state=None,
+    ):
+        self.n_shapelets_per_size = n_shapelets_per_size
+        self.max_iter = max_iter
+        self.batch_size = batch_size
+        self.verbose = verbose
+        self.optimizer = optimizer
+        self.weight_regularizer = weight_regularizer
+        self.shapelet_length = shapelet_length
+        self.total_lengths = total_lengths
+        self.max_size = max_size
+        self.scale = scale
+        self.random_state = random_state
+
+        super().__init__()
+
+    @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, 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``
+        """
+        params1 = {"max_iter": 120, "batch_size": 64}
+        params2 = {
+            "max_iter": 100,
+            "batch_size": 128,
+            "optimizer": "adam",
+            "shapelet_length": 0.1,
+            "scale": True,
+        }
+        return [params1, params2]
diff --git a/sktime/regression/kernel_based/__init__.py b/sktime/regression/kernel_based/__init__.py
index 70e5bb8bf..b12868d36 100644
--- a/sktime/regression/kernel_based/__init__.py
+++ b/sktime/regression/kernel_based/__init__.py
@@ -1,4 +1,5 @@
 """Kernel based time series regressors."""
-__all__ = ["RocketRegressor"]
+__all__ = ["RocketRegressor", "TimeSeriesSVRTslearn"]
 
 from sktime.regression.kernel_based._rocket_regressor import RocketRegressor
+from sktime.regression.kernel_based._svr_tslearn import TimeSeriesSVRTslearn
diff --git a/sktime/regression/kernel_based/_svr_tslearn.py b/sktime/regression/kernel_based/_svr_tslearn.py
new file mode 100644
index 000000000..6b452f53e
--- /dev/null
+++ b/sktime/regression/kernel_based/_svr_tslearn.py
@@ -0,0 +1,183 @@
+"""Time series SVR, from tslearn."""
+from sktime.base.adapters._tslearn import _TslearnAdapter
+from sktime.regression.base import BaseRegressor
+
+
+class TimeSeriesSVRTslearn(_TslearnAdapter, BaseRegressor):
+    """Time Series Suppoer Vector Regressor, from tslearn.
+
+    Direct interface to ``tslearn.svm.svm.TimeSeriesSVR``.
+
+    Parameters
+    ----------
+    C : float, optional (default=1.0)
+        Penalty parameter C of the error term.
+
+    kernel : string, optional (default='gak')
+         Specifies the kernel type to be used in the algorithm.
+         It must be one of 'gak' or a kernel accepted by ``sklearn.svm.SVR``.
+         If none is given, 'gak' will be used. If a callable is given it is
+         used to pre-compute the kernel matrix from data matrices; that matrix
+         should be an array of shape ``(n_samples, n_samples)``.
+
+    degree : int, optional (default=3)
+        Degree of the polynomial kernel function ('poly').
+        Ignored by all other kernels.
+
+    gamma : float, optional (default='auto')
+        Kernel coefficient for 'gak', 'rbf', 'poly' and 'sigmoid'.
+        If gamma is 'auto' then:
+
+        - for 'gak' kernel, it is computed based on a sampling of the training
+          set ``tslearn.metrics.gamma_soft_dtw``
+        - for other kernels (eg. 'rbf'), 1/n_features will be used.
+
+    coef0 : float, optional (default=0.0)
+        Independent term in kernel function.
+        It is only significant in 'poly' and 'sigmoid'.
+
+    tol : float, optional (default=1e-3)
+        Tolerance for stopping criterion.
+
+    epsilon : float, optional (default=0.1)
+         Epsilon in the epsilon-SVR model. It specifies the epsilon-tube
+         within which no penalty is associated in the training loss function
+         with points predicted within a distance epsilon from the actual
+         value.
+
+    shrinking : boolean, optional (default=True)
+        Whether to use the shrinking heuristic.
+
+    cache_size :  float, optional (default=200.0)
+        Specify the size of the kernel cache (in MB).
+
+    n_jobs : int or None, optional (default=None)
+        The number of jobs to run in parallel for GAK cross-similarity matrix
+        computations.
+        ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context.
+        ``-1`` means using all processors. See scikit-learns'
+        `Glossary <https://scikit-learn.org/stable/glossary.html#term-n-jobs>`_
+        for more details.
+
+    verbose : int, default: 0
+        Enable verbose output. Note that this setting takes advantage of a
+        per-process runtime setting in libsvm that, if enabled, may not work
+        properly in a multithreaded context.
+
+    max_iter : int, optional (default=-1)
+        Hard limit on iterations within solver, or -1 for no limit.
+
+    Attributes
+    ----------
+    support_ : array-like, shape = [n_SV]
+        Indices of support vectors.
+
+    support_vectors_ : array of shape [n_SV, sz, d]
+        Support vectors in tslearn dataset format
+
+    dual_coef_ : array, shape = [1, n_SV]
+        Coefficients of the support vector in the decision function.
+
+    coef_ : array, shape = [1, n_features]
+        Weights assigned to the features (coefficients in the primal
+        problem). This is only available in the case of a linear kernel.
+        `coef_` is readonly property derived from `dual_coef_` and
+        `support_vectors_`.
+
+    intercept_ : array, shape = [1]
+        Constants in decision function.
+
+    sample_weight : array-like, shape = [n_samples]
+        Individual weights for each sample
+
+    svm_estimator_ : sklearn.svm.SVR
+        The underlying sklearn estimator
+    """
+
+    _tags = {
+        # packaging info
+        # --------------
+        "authors": ["rtavenar", "yanncabanes", "fkiraly"],
+        # rtavenar and yanncabanes for interfaced estimator
+        "python_dependencies": "tslearn",
+        # estimator type
+        # --------------
+        "capability:multivariate": True,
+        "capability:unequal_length": False,
+    }
+
+    # defines the name of the attribute containing the tslearn estimator
+    _estimator_attr = "_tslearn_svr"
+
+    def _get_tslearn_class(self):
+        """Get tslearn class.
+
+        should import and return tslearn class
+        """
+        from tslearn.svm.svm import TimeSeriesSVR as _TimeSeriesSVR
+
+        return _TimeSeriesSVR
+
+    def __init__(
+        self,
+        C=1.0,
+        kernel="gak",
+        degree=3,
+        gamma="auto",
+        coef0=0.0,
+        shrinking=True,
+        tol=0.001,
+        epsilon=0.1,
+        cache_size=200,
+        n_jobs=None,
+        verbose=0,
+        max_iter=-1,
+    ):
+        self.C = C
+        self.kernel = kernel
+        self.degree = degree
+        self.gamma = gamma
+        self.coef0 = coef0
+        self.shrinking = shrinking
+        self.tol = tol
+        self.epsilon = epsilon
+        self.cache_size = cache_size
+        self.n_jobs = n_jobs
+        self.verbose = verbose
+        self.max_iter = max_iter
+
+        super().__init__()
+
+    @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, 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``
+        """
+        params1 = {"cache_size": 50}
+        params2 = {
+            "C": 0.9,
+            "kernel": "poly",
+            "degree": 2,
+            "gamma": 0.1,
+            "coef0": 0.1,
+            "tol": 0.01,
+            "epsilon": 0.2,
+            "shrinking": False,
+            "cache_size": 55,
+        }
+        return [params1, params2]
diff --git a/sktime/transformations/series/summarize.py b/sktime/transformations/series/summarize.py
index 86d000ad7..1063e45e7 100644
--- a/sktime/transformations/series/summarize.py
+++ b/sktime/transformations/series/summarize.py
@@ -451,60 +451,26 @@ def _window_feature(Z, summarizer=None, window=None, bfill=False):
     """
     lag = window[0]
     window_length = window[1]
-
+    feat: pd.DataFrame = pd.DataFrame()
     if summarizer in pd_rolling:
-        if isinstance(Z, pd.core.groupby.generic.SeriesGroupBy):
-            if bfill is False:
-                feat = getattr(
-                    Z.shift(lag).rolling(
-                        window=window_length, min_periods=window_length
-                    ),
-                    summarizer,
-                )()
-            else:
-                feat = getattr(
-                    Z.shift(lag)
-                    .bfill()
-                    .rolling(window=window_length, min_periods=window_length),
-                    summarizer,
-                )()
-            feat = pd.DataFrame(feat)
-        else:
-            if bfill is False:
-                feat = Z.apply(
-                    lambda x: getattr(
-                        x.rolling(window=window_length, min_periods=window_length),
-                        summarizer,
-                    )().shift(lag)
-                )
-            else:
-                feat = Z.apply(
-                    lambda x: getattr(
-                        x.rolling(window=window_length, min_periods=window_length),
-                        summarizer,
-                    )()
-                    .shift(lag)
-                    .bfill()
-                )
+        feat = Z.transform(
+            lambda x: getattr(
+                x.rolling(window=window_length, min_periods=window_length), summarizer
+            )().shift(lag)
+        )
+    elif summarizer == "lag":
+        feat = Z.transform(lambda x: x.shift(lag))
+    elif callable(summarizer):
+        feat = Z.transform(
+            lambda x: x.rolling(window=window_length, min_periods=window_length)
+            .apply(summarizer, raw=True)
+            .shift(lag)
+        )
     else:
-        if isinstance(Z, pd.core.groupby.generic.SeriesGroupBy) and callable(
-            summarizer
-        ):
-            feat = Z.rolling(window_length).apply(summarizer, raw=True)
-        elif not isinstance(Z, pd.core.groupby.generic.SeriesGroupBy) and callable(
-            summarizer
-        ):
-            feat = Z.apply(
-                lambda x: x.rolling(
-                    window=window_length, min_periods=window_length
-                ).apply(summarizer, raw=True)
-            )
-        if bfill is False:
-            feat = Z.shift(lag)
-        else:
-            feat = Z.shift(lag).bfill()
+        raise ValueError("The provided summarizer is not callable.")
+    feat = pd.DataFrame(feat)
 
-        feat = pd.DataFrame(feat)
+    # Handle backfill
     if bfill is True:
         feat = feat.bfill()
 


Test patch:
diff --git a/sktime/tests/_config.py b/sktime/tests/_config.py
index 316b3faa3..131d0ca1d 100644
--- a/sktime/tests/_config.py
+++ b/sktime/tests/_config.py
@@ -224,6 +224,16 @@ EXCLUDED_TESTS = {
     "KNeighborsTimeSeriesClassifierTslearn": ["test_multiprocessing_idempotent"],
     # ShapeletTransformPyts creates nested numpy shapelets sporadically, see #6171
     "ShapeletTransformPyts": ["test_non_state_changing_method_contract"],
+    "TimeSeriesSVRTslearn": [  # not deterministic, see 6274
+        "test_fit_idempotent",
+        "test_multiprocessing_idempotent",
+    ],
+    # ShapeletLearningClassifier is non-pickleable due to DL dependencies
+    "ShapeletLearningClassifierTslearn": [
+        "test_persistence_via_pickle",
+        "test_save_estimators_to_file",
+        "test_fit_idempotent",
+    ],
 }
 
 # We use estimator tags in addition to class hierarchies to further distinguish
diff --git a/sktime/transformations/series/tests/test_window_summarizer.py b/sktime/transformations/series/tests/test_window_summarizer.py
index 7cb16aadf..aa7d687c1 100644
--- a/sktime/transformations/series/tests/test_window_summarizer.py
+++ b/sktime/transformations/series/tests/test_window_summarizer.py
@@ -194,3 +194,129 @@ def test_wrong_column():
     transformer = WindowSummarizer(target_cols=["dummy"])
     Xt = transformer.fit_transform(X_ll_train)
     return Xt
+
+
+def count_unique(x):
+    return len(np.unique(x))
+
+
+def count_greater_zero(x):
+    return np.sum((x > 0)[::-1])
+
+
+kwargs_custom_function = {
+    "lag_feature": {
+        count_unique: [[1, 2]],
+    }
+}
+
+kwargs_lag_custom_function = {
+    "lag_feature": {
+        count_unique: [[1, 2]],
+        count_greater_zero: [[1, 3]],
+        "lag": [1],
+        "sum": [[1, 2]],
+    },
+}
+
+
+@pytest.mark.parametrize(
+    "kwargs, column_names, y, X, target_cols, truncate",
+    [
+        (
+            kwargs_custom_function,
+            ["a_count_unique_1_2"],
+            y_pd,
+            pd.DataFrame({"a_count_unique_1_2": [np.NAN, np.NAN, 2.0, 2.0]}),
+            None,
+            None,
+        ),
+        (
+            kwargs_lag_custom_function,
+            ["a_count_unique_1_2", "a_count_greater_zero_1_3", "a_lag_1", "a_sum_1_2"],
+            y_pd,
+            pd.DataFrame(
+                {
+                    "a_count_unique_1_2": [np.NAN, np.NAN, 2.0, 2.0],
+                    "a_count_greater_zero_1_3": [np.NAN, np.NAN, np.NAN, 3.0],
+                    "a_lag_1": [np.NAN, 1.0, 4.0, 0.5],
+                    "a_sum_1_2": [np.NAN, np.NAN, 5.0, 4.5],
+                },
+            ),
+            None,
+            None,
+        ),
+        (
+            kwargs_lag_custom_function,
+            ["a_count_unique_1_2", "a_count_greater_zero_1_3", "a_lag_1", "a_sum_1_2"],
+            y_multi,
+            pd.DataFrame(
+                {
+                    "var_0_count_unique_1_2": [
+                        np.NAN,
+                        np.NAN,
+                        2.0,
+                        np.NAN,
+                        np.NAN,
+                        2.0,
+                        np.NAN,
+                        np.NAN,
+                        2.0,
+                    ],
+                    "var_0_count_greater_zero_1_3": [
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                        np.NAN,
+                    ],
+                    "var_0_lag_1": [
+                        np.NAN,
+                        1.0,
+                        2.0,
+                        np.NAN,
+                        1.0,
+                        2.0,
+                        np.NAN,
+                        1.0,
+                        2.0,
+                    ],
+                    "var_0_sum_1_2": [
+                        np.NAN,
+                        np.NAN,
+                        3.0,
+                        np.NAN,
+                        np.NAN,
+                        3.0,
+                        np.NAN,
+                        np.NAN,
+                        3.0,
+                    ],
+                    "var_1": y_multi["var_1"],
+                },
+                index=y_multi.index,
+            ),
+            ["var_0"],
+            None,
+        ),
+    ],
+)
+def test_windowsummarizer_with_output(
+    kwargs, column_names, y, X, target_cols, truncate
+):
+    """Test X match kwargs arguments."""
+    if kwargs is not None:
+        transformer = WindowSummarizer(
+            **kwargs, target_cols=target_cols, truncate=truncate
+        )
+    else:
+        transformer = WindowSummarizer(target_cols=target_cols, truncate=truncate)
+    Xt = transformer.fit_transform(y)
+    pd.testing.assert_frame_equal(Xt, X)
+
+    # Checking for duplicate index names
+    assert len(set(Xt.index.names)) == len(X.index.names)