# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.11.1
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Time series interpolating with sktime

# Suppose we have a set of time series with different lengths, i.e. different number of time points. Currently, most of sktime's functionality requires equal-length time series, so to use sktime, we need to first converted our data into equal-length time series. In this tutorial, you will learn how to use the `TSInterpolator` to do so. 

# + tags=[]
import random

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline

from sktime.classification.interval_based import TimeSeriesForestClassifier
from sktime.datasets import load_basic_motions
from sktime.transformations.panel.compose import ColumnConcatenator
# -

# ## Ordinary situation
#
# Here is a normal situation, when all time series have same length. We load a toy data set from sktime and train a classifier.

# + tags=[]
X, y = load_basic_motions(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)

steps = [
    ("concatenate", ColumnConcatenator()),
    ("classify", TimeSeriesForestClassifier(n_estimators=100)),
]
clf = Pipeline(steps)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)


# -

# ## If time series are unequal length, sktime's algorithm may raise an error
#
# Now we are going to spoil the data set a little bit by randomly cutting the time series. This leads to unequal-length time series. Consequently, we have an error while attempt to train a classifier.

# + tags=[]
# randomly cut the data series in-place
def random_cut(df):
    for row_i in range(df.shape[0]):
        for dim_i in range(df.shape[1]):
            ts = df.iloc[row_i][f"dim_{dim_i}"]
            df.iloc[row_i][f"dim_{dim_i}"] = pd.Series(
                ts.tolist()[: random.randint(len(ts) - 5, len(ts) - 3)]
            )  # here is a problem


X, y = load_basic_motions(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)

for df in [X_train, X_test]:
    random_cut(df)

try:
    steps = [
        ("concatenate", ColumnConcatenator()),
        ("classify", TimeSeriesForestClassifier(n_estimators=100)),
    ]
    clf = Pipeline(steps)
    clf.fit(X_train, y_train)
    clf.score(X_test, y_test)
except ValueError as e:
    print(f"IndexError: {e}")
# -

# # Now the interpolator enters
# Now we use our interpolator to resize time series of different lengths to user-defined length. Internally, it uses linear interpolation from scipy and draws equidistant samples on the user-defined number of points. 
#
# After interpolating the data, the classifier works again.

# + tags=[]
from sktime.transformations.panel.interpolate import TSInterpolator

X, y = load_basic_motions(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)

for df in [X_train, X_test]:
    random_cut(df)

steps = [
    ("transform", TSInterpolator(50)),
    ("concatenate", ColumnConcatenator()),
    ("classify", TimeSeriesForestClassifier(n_estimators=100)),
]
clf = Pipeline(steps)
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
# -


