Evaluación diferida de series temporales

La evaluación perezosa es una estrategia de evaluación que retrasa la evaluación de una expresión hasta que se necesita su valor. Cuando se combina con la memoización, la estrategia de evaluación perezosa evita las evaluaciones repetidas y puede reducir considerablemente el tiempo de ejecución de ciertas funciones.

La biblioteca de series temporales utiliza la evaluación diferida para procesar los datos. En teoría, un gráfico de ejecución se construye a partir de datos de series temporales cuya evaluación solo se activa cuando se materializa su resultado. Supongamos que un objeto se mueve en un espacio unidimensional, cuya posición viene dada por x(t). Puedes calcular la aceleración o frenada brusca (h(t)) de este objeto utilizando sus series temporales de velocidad (v(t)) y aceleración (a(t)) de la siguiente manera:

# 1d location timeseries
x(t) = input location timeseries

# velocity - first derivative of x(t)
v(t) = x(t) - x(t-1)

# acceleration - second derivative of x(t)
a(t) = v(t) - v(t-1)

# harsh acceleration/braking using thresholds on acceleration
h(t) = +1 if a(t) > threshold_acceleration
     = -1 if a(t) < threshold_deceleration
     = 0 otherwise

Esto da como resultado un gráfico de ejecución sencillo con el siguiente formato:

x(t) --> v(t) --> a(t) --> h(t)

Las evaluaciones se activan únicamente cuando se realiza una acción, como por ejemplo compute h(5...10), es decir, compute h(5), ..., h(10). La biblioteca detecta dependencias temporales específicas entre series temporales. En este ejemplo, h(5...10) se necesita a(5...10), lo que a su vez requiere v(4...10), y esto, a su vez, requiere x(3...10). Solo se evalúan las partes x(t) pertinentes de a(t), v(t) y.

h(5...10) <-- a(5...10) <-- v(4...10) <-- x(3...10)

Además, las evaluaciones se almacenan en la memoria y, por lo tanto, pueden reutilizarse en acciones posteriores sobre h. Por ejemplo, cuando una solicitud de h(7...12) sigue a una solicitud de h(5...10), se utilizarían los valores memorizados h(7...10) de; además, h(11...12) se evaluaría utilizando a(11...12), v(10...12) y x(9...12), lo que a su vez utilizaría v(10) y x(9...10) memorizados del cálculo anterior.

En un ejemplo más general, se podría definir una serie temporal de velocidad suavizada de la siguiente manera:

# 1d location timeseries
x(t) = input location timeseries

# velocity - first derivative of x(t)
v(t) = x(t) - x(t-1)

# smoothened velocity
# alpha is the smoothing factor
# n is a smoothing history
v_smooth(t) =  (v(t)*1.0 + v(t-1)*alpha + ... + v(t-n)*alpha^n) / (1 + alpha + ... + alpha^n)

# acceleration - second derivative of x(t)
a(t) = v_smooth(t) - v_smooth(t-1)

En este ejemplo, h(l...u) presenta la siguiente dependencia temporal. La evaluación de h(l...u) se ajustaría estrictamente a esta dependencia temporal mediante la memoización.

h(l...u) <-- a(l...u) <-- v_smooth(l-1...u) <-- v(l-n-1...u) <-- x(l-n-2...u)

Un ejemplo

El siguiente ejemplo muestra un fragmento de código en Python que implementa una aceleración brusca en una serie temporal simple almacenada en memoria. La biblioteca incluye varias transformaciones integradas. En este ejemplo, la transformación de diferencias se aplica dos veces a la serie temporal de posiciones para calcular la serie temporal de aceleraciones. Se aplica una operación de mapeo a la serie temporal de aceleración utilizando una función lambda de tipo «harsh», definida al final del ejemplo de código, que asigna la aceleración a (aceleración brusca), -1+1 (frenada brusca) o 0 (en los demás casos). El filtro selecciona únicamente los casos en los que se observa una aceleración brusca o una frenada brusca. Antes de llamar a la función get_values, se crea un gráfico de ejecución, pero no se realizan cálculos. Al llamar get_values(5, 10)a, la evaluación se lleva a cabo mediante memoización sobre la dependencia temporal más restringida posible en el gráfico de ejecución.

import tspy
from tspy.builders.functions import transformers

x = tspy.time_series([1.0, 2.0, 4.0, 7.0, 11.0, 16.0, 22.0, 29.0, 28.0, 30.0, 29.0, 30.0, 30.0])
v = x.transform(transformers.difference())
a = v.transform(transformers.difference())
h = a.map(harsh).filter(lambda h: h != 0)

print(h[5, 10])

La lambda dura se define de la siguiente manera:

def harsh(a):
    threshold_acceleration = 2.0
    threshold_braking = -2.0

    if (a > threshold_acceleration):
        return +1
    elif (a < threshold_braking):
        return -1
    else:
        return 0

Más información

Para utilizar el tspy SDK de Python, consulta la tspy documentación del SDK de Python.