tslearn-team / tslearn

The machine learning toolkit for time series analysis in Python
https://tslearn.readthedocs.io
BSD 2-Clause "Simplified" License
2.92k stars 342 forks source link

Fix error in function silhouette_score when metric is callable #508

Closed YannCabanes closed 9 months ago

YannCabanes commented 9 months ago

Investigating on the issue https://github.com/tslearn-team/tslearn/issues/507, I found a recursion error in the function silhouette_score when the input parameter metric is callable.

The error can be reproduced running the following code:

import numpy as np
from tslearn.clustering.utils import silhouette_score
from tslearn.metrics import dtw

n_ts = 4
sz = 3
d = 2

X = np.random.randn(n_ts, sz, d)
labels = [0] * (n_ts // 2) + [1] * (n_ts // 2)

score = silhouette_score(
    X=X,
    labels=labels,
    metric=dtw)

print(score)

The following error is returned:

RecursionError: maximum recursion depth exceeded while calling a Python object

The error is related to the end of the function silhouette_score:

        def sklearn_metric(x, y):
            return metric(to_time_series(x.reshape((sz, d)),
                                         remove_nans=True),
                          to_time_series(y.reshape((sz, d)),
                                         remove_nans=True))
    metric = "precomputed" if sklearn_metric is None else sklearn_metric

Indeed, since metric is equal to sklearn_metric when sklearn_metric is callable, the function sklearn_metric is therefore calling itself.