Repository: mne-tools/mne-python

Issue:
TimeFrequency Estimator modifies parameters in constructor
#### Describe the bug
The `mne.decoding.TimeFrequency` transformer modifies constructor arguments, violating [scikit-learn guidance](https://scikit-learn.org/stable/developers/develop.html#instantiation) on estimators. This leads to a cloning error when using the function in a pipeline. I was able to resolve the issue by moving the `_check_tfr_param` call to the transform method, in line with other checks performed at that time. See the changes made to [mne.decoding.time_frequency.py](https://github.com/Lund-Memory-Lab/mne-python/blob/10e8f3b25386c75d608de806d1cd8e8eea4ad9d4/mne/decoding/time_frequency.py)

#### Steps to reproduce
```
import mne
import numpy as np
from sklearn import pipeline, linear_model

tfr_data = np.ones((100, 10, 1000))

freqs = np.array([5.])

estimator = pipeline.make_pipeline(
    mne.decoding.TimeFrequency(freqs, 10, "morlet", freqs/5., output="power"),
    mne.decoding.Vectorizer(),
    linear_model.LogisticRegression(),
)

mne.decoding.cross_val_multiscore(estimator, tfr_data, np.random.binomial(1, 0.5, 100))
```


#### Expected results
Successful completion of cross validation.

#### Actual results
```
Traceback (most recent call last):
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/joblib/parallel.py", line 822, in dispatch_one_batch
    tasks = self._ready_batches.get(block=False)
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/queue.py", line 168, in get
    raise Empty
_queue.Empty
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3552, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-1fb399c6f0bb>", line 1, in <cell line: 1>
    runfile('error.py', wdir='/Users/daniel/Documents/Coding_Projects/GitHub/mne-python')
  File "/Users/daniel/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-0/221.6008.17/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_bundle/pydev_umd.py", line 198, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "/Users/daniel/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-0/221.6008.17/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "error.py", line 15, in <module>
    mne.decoding.cross_val_multiscore(estimator, tfr_data, np.random.binomial(1, 0.5, 100))
  File "<decorator-gen-447>", line 12, in cross_val_multiscore
  File "/Users/daniel/Documents/Coding_Projects/GitHub/mne-python/mne/decoding/base.py", line 435, in cross_val_multiscore
    scores = parallel(
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/joblib/parallel.py", line 1043, in __call__
    if self.dispatch_one_batch(iterator):
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/joblib/parallel.py", line 833, in dispatch_one_batch
    islice = list(itertools.islice(iterator, big_batch_size))
  File "/Users/daniel/Documents/Coding_Projects/GitHub/mne-python/mne/decoding/base.py", line 437, in <genexpr>
    estimator=clone(estimator), X=X, y=y, scorer=scorer, train=train,
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/sklearn/base.py", line 87, in clone
    new_object_params[name] = clone(param, safe=False)
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/sklearn/base.py", line 65, in clone
    return estimator_type([clone(e, safe=safe) for e in estimator])
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/sklearn/base.py", line 65, in <listcomp>
    return estimator_type([clone(e, safe=safe) for e in estimator])
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/sklearn/base.py", line 65, in clone
    return estimator_type([clone(e, safe=safe) for e in estimator])
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/sklearn/base.py", line 65, in <listcomp>
    return estimator_type([clone(e, safe=safe) for e in estimator])
  File "/Users/daniel/miniconda3/envs/mne-python/lib/python3.10/site-packages/sklearn/base.py", line 96, in clone
    raise RuntimeError(
RuntimeError: Cannot clone object TimeFrequency(None), as the constructor either does not set or modifies parameter n_cycles
```


#### Additional information
```
Platform:      macOS-11.6.6-x86_64-i386-64bit
Python:        3.10.5 | packaged by conda-forge | (main, Jun 14 2022, 07:09:13) [Clang 13.0.1 ]
Executable:    /Users/daniel/miniconda3/envs/mne-python/bin/python
CPU:           i386: 4 cores
Memory:        16.0 GB
mne:           0.23.4
numpy:         1.22.4 {blas=NO_ATLAS_INFO, lapack=lapack}
scipy:         1.8.1
matplotlib:    3.5.2 {backend=module://backend_interagg}
sklearn:       1.1.1
numba:         0.55.2
nibabel:       4.0.1
nilearn:       0.6.2
dipy:          1.5.0
cupy:          Not found
pandas:        1.4.3
mayavi:        4.8.0
pyvista:       0.35.2 {pyvistaqt=0.9.0, OpenGL 4.1 ATI-4.6.21 via AMD Radeon R9 M295X OpenGL Engine}
vtk:           
PyQt5:         5.12.3
```



Discussion:

Resolving patch:
diff --git a/mne/decoding/time_frequency.py b/mne/decoding/time_frequency.py
--- a/mne/decoding/time_frequency.py
+++ b/mne/decoding/time_frequency.py
@@ -5,7 +5,7 @@
 import numpy as np
 from .mixin import TransformerMixin
 from .base import BaseEstimator
-from ..time_frequency.tfr import _compute_tfr, _check_tfr_param
+from ..time_frequency.tfr import _compute_tfr
 from ..utils import fill_doc, _check_option, verbose
 
 
@@ -62,10 +62,12 @@ class TimeFrequency(TransformerMixin, BaseEstimator):
     @verbose
     def __init__(self, freqs, sfreq=1.0, method='morlet', n_cycles=7.0,
                  time_bandwidth=None, use_fft=True, decim=1, output='complex',
-                 n_jobs=None, *, verbose=None):  # noqa: D102
-        freqs, sfreq, _, n_cycles, time_bandwidth, decim = \
-            _check_tfr_param(freqs, sfreq, method, True, n_cycles,
-                             time_bandwidth, use_fft, decim, output)
+                 n_jobs=1, verbose=None):  # noqa: D102
+        """Init TimeFrequency transformer."""
+        # Check non-average output
+        output = _check_option('output', output,
+                               ['complex', 'power', 'phase'])
+
         self.freqs = freqs
         self.sfreq = sfreq
         self.method = method
@@ -74,9 +76,9 @@ def __init__(self, freqs, sfreq=1.0, method='morlet', n_cycles=7.0,
         self.use_fft = use_fft
         self.decim = decim
         # Check that output is not an average metric (e.g. ITC)
-        self.output = _check_option('output', output,
-                                    ['complex', 'power', 'phase'])
+        self.output = output
         self.n_jobs = n_jobs
+        self.verbose = verbose
 
     def fit_transform(self, X, y=None):
         """Time-frequency transform of times series along the last axis.
@@ -137,7 +139,8 @@ def transform(self, X):
         # Compute time-frequency
         Xt = _compute_tfr(X, self.freqs, self.sfreq, self.method,
                           self.n_cycles, True, self.time_bandwidth,
-                          self.use_fft, self.decim, self.output, self.n_jobs)
+                          self.use_fft, self.decim, self.output, self.n_jobs,
+                          self.verbose)
 
         # Back to original shape
         if not shape:


Test patch:
diff --git a/mne/decoding/tests/test_time_frequency.py b/mne/decoding/tests/test_time_frequency.py
--- a/mne/decoding/tests/test_time_frequency.py
+++ b/mne/decoding/tests/test_time_frequency.py
@@ -23,6 +23,11 @@ def test_timefrequency():
         pytest.raises(ValueError, TimeFrequency, freqs, output=output)
     tf = clone(tf)
 
+    # Clone estimator
+    freqs_array = np.array(np.asarray(freqs))
+    tf = TimeFrequency(freqs_array, 100, "morlet", freqs_array / 5.)
+    clone(tf)
+
     # Fit
     n_epochs, n_chans, n_times = 10, 2, 100
     X = np.random.rand(n_epochs, n_chans, n_times)