Biblioteca para distancias de series temporales (por ejemplo, Dynamic Time Warping) utilizada en el Grupo de Investigación DTAI. La biblioteca ofrece una implementación pura de Python y una implementación rápida en C. La implementación de C solo tiene Cython como dependencia. Es compatible con Numpy y Pandas y se implementa de manera que se eviten operaciones de copia de datos innecesarias.
Documentación: http://dtaidistancia.readthedocs.io
Ejemplo:
from dtaidistance import dtw
import numpy as np
s1 = np.array([0.0, 0, 1, 2, 1, 0, 1, 0, 0])
s2 = np.array([0.0, 1, 2, 0, 0, 0, 0, 0, 0])
d = dtw.distance_fast(s1, s2)
Citando este trabajo:
Wannes Meert, Kilian Hendrickx, Toon Van Craenendonck, Pieter Robberechts, Hendrik Blockeel y Jesse Davis.
DTAIDistancia (Versión v2). Zenodo.
http://doi.org/10.5281/zenodo.5901139
Nuevo en v2 :
ssize_t
en lugar de int
permite estructuras de datos más grandes en máquinas de 64 bits y ser más compatible con Numpy.max_dist
resultó ser similar al trabajo de Silva y Batista en PrunedDTW [7]. La caja de herramientas ahora implementa una versión que es igual a PrunedDTW ya que poda más distancias parciales. Además, se agrega un argumento use_pruning
para establecer automáticamente max_dist
en la distancia euclidiana, como lo sugirieron Silva y Batista, para acelerar el cálculo (hay disponible un nuevo método ub_euclidean
).dtaidistance.dtw_ndim
. $ pip install dtaidistance
o
$ conda install -c conda-forge dtaidistance
La instalación de pip requiere Numpy como dependencia para compilar código C compatible con Numpy (usando Cython). Sin embargo, esta dependencia es opcional y se puede eliminar.
El código fuente está disponible en github.com/wannesm/dtaidistance.
Si encuentra algún problema durante la compilación (por ejemplo, la implementación basada en C o OpenMP no está disponible), consulte la documentación para obtener más opciones.
from dtaidistance import dtw
from dtaidistance import dtw_visualisation as dtwvis
import numpy as np
s1 = np.array([0., 0, 1, 2, 1, 0, 1, 0, 0, 2, 1, 0, 0])
s2 = np.array([0., 1, 2, 3, 1, 0, 0, 0, 2, 1, 0, 0, 0])
path = dtw.warping_path(s1, s2)
dtwvis.plot_warping(s1, s2, path, filename="warp.png")
Sólo la distancia se mide en base a dos secuencias de números:
from dtaidistance import dtw
s1 = [0, 0, 1, 2, 1, 0, 1, 0, 0]
s2 = [0, 1, 2, 0, 0, 0, 0, 0, 0]
distance = dtw.distance(s1, s2)
print(distance)
La versión más rápida (30-300 veces) usa c directamente pero requiere una matriz como entrada (con el tipo doble) y (opcionalmente) también poda los cálculos estableciendo max_dist
en el límite superior euclidiano:
from dtaidistance import dtw
import array
s1 = array.array('d',[0, 0, 1, 2, 1, 0, 1, 0, 0])
s2 = array.array('d',[0, 1, 2, 0, 0, 0, 0, 0, 0])
d = dtw.distance_fast(s1, s2, use_pruning=True)
O puedes usar una matriz numpy (con dtype doble o flotante):
from dtaidistance import dtw
import numpy as np
s1 = np.array([0, 0, 1, 2, 1, 0, 1, 0, 0], dtype=np.double)
s2 = np.array([0.0, 1, 2, 0, 0, 0, 0, 0, 0])
d = dtw.distance_fast(s1, s2, use_pruning=True)
Consulte el __doc__
para obtener información sobre los argumentos disponibles:
print(dtw.distance.__doc__)
Se prevén varias opciones para detener anticipadamente algunos caminos que el algoritmo de programación dinámica está explorando o ajustar el cálculo de la medida de distancia:
window
: Solo permita desplazamientos de hasta esta cantidad lejos de las dos diagonales.max_dist
: Deténgase si la medida de distancia devuelta será mayor que este valor.max_step
: no permite pasos mayores que este valor.max_length_diff
: Devuelve infinito si la diferencia en la longitud de dos series es mayor.penalty
: Penalización a agregar si se aplica compresión o expansión (además de la distancia).psi
: Relajación psi para ignorar el inicio y/o el final de secuencias (para secuencias cíclicas) [2].use_pruning
: elimina los cálculos basados en el límite superior euclidiano. Si, además de la distancia, también desea que la matriz completa vea todos los posibles caminos de deformación:
from dtaidistance import dtw
s1 = [0, 0, 1, 2, 1, 0, 1, 0, 0]
s2 = [0, 1, 2, 0, 0, 0, 0, 0, 0]
distance, paths = dtw.warping_paths(s1, s2)
print(distance)
print(paths)
La matriz con todos los caminos de deformación se puede visualizar de la siguiente manera:
from dtaidistance import dtw
from dtaidistance import dtw_visualisation as dtwvis
import random
import numpy as np
x = np.arange(0, 20, .5)
s1 = np.sin(x)
s2 = np.sin(x - 1)
random.seed(1)
for idx in range(len(s2)):
if random.random() < 0.05:
s2[idx] += (random.random() - 0.5) / 2
d, paths = dtw.warping_paths(s1, s2, window=25, psi=2)
best_path = dtw.best_path(paths)
dtwvis.plot_warpingpaths(s1, s2, paths, best_path)
Observe el parámetro psi
que relaja la coincidencia al principio y al final. En este ejemplo, esto da como resultado una coincidencia perfecta aunque las ondas sinusoidales estén ligeramente desplazadas.
Para calcular las medidas de distancia DTW entre todas las secuencias en una lista de secuencias, utilice el método dtw.distance_matrix
. Puede configurar variables para usar más o menos código C ( use_c
y use_nogil
) y ejecución en paralelo o en serie ( parallel
).
El método distance_matrix
espera una lista de listas/matrices:
from dtaidistance import dtw
import numpy as np
series = [
np.array([0, 0, 1, 2, 1, 0, 1, 0, 0], dtype=np.double),
np.array([0.0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0]),
np.array([0.0, 0, 1, 2, 1, 0, 0, 0])]
ds = dtw.distance_matrix_fast(series)
o una matriz (en caso de que todas las series tengan la misma longitud):
from dtaidistance import dtw
import numpy as np
series = np.matrix([
[0.0, 0, 1, 2, 1, 0, 1, 0, 0],
[0.0, 1, 2, 0, 0, 0, 0, 0, 0],
[0.0, 0, 1, 2, 1, 0, 0, 0, 0]])
ds = dtw.distance_matrix_fast(series)
Puede indicarle al cálculo que solo llene parte de la matriz de medidas de distancia. Por ejemplo, para distribuir los cálculos en varios nodos o comparar solo las series de origen con las series de destino.
from dtaidistance import dtw
import numpy as np
series = np.matrix([
[0., 0, 1, 2, 1, 0, 1, 0, 0],
[0., 1, 2, 0, 0, 0, 0, 0, 0],
[1., 2, 0, 0, 0, 0, 0, 1, 1],
[0., 0, 1, 2, 1, 0, 1, 0, 0],
[0., 1, 2, 0, 0, 0, 0, 0, 0],
[1., 2, 0, 0, 0, 0, 0, 1, 1]])
ds = dtw.distance_matrix_fast(series, block=((1, 4), (3, 5)))
La salida en este caso será:
# 0 1 2 3 4 5
[[ inf inf inf inf inf inf] # 0
[ inf inf inf 1.4142 0.0000 inf] # 1
[ inf inf inf 2.2360 1.7320 inf] # 2
[ inf inf inf inf 1.4142 inf] # 3
[ inf inf inf inf inf inf] # 4
[ inf inf inf inf inf inf]] # 5
Se puede utilizar una matriz de distancia para agrupar series de tiempo. Puede utilizar métodos existentes como scipy.cluster.hierarchy.linkage
o uno de los dos métodos de agrupación incluidos (este último es un contenedor para el método de vinculación de SciPy).
from dtaidistance import clustering
# Custom Hierarchical clustering
model1 = clustering.Hierarchical(dtw.distance_matrix_fast, {})
cluster_idx = model1.fit(series)
# Augment Hierarchical object to keep track of the full tree
model2 = clustering.HierarchicalTree(model1)
cluster_idx = model2.fit(series)
# SciPy linkage clustering
model3 = clustering.LinkageTree(dtw.distance_matrix_fast, {})
cluster_idx = model3.fit(series)
Para los modelos que realizan un seguimiento del árbol de agrupación completo ( HierarchicalTree
o LinkageTree
), el árbol se puede visualizar:
model.plot("myplot.png")
Opcional:
Desarrollo:
DTAI distance code.
Copyright 2016-2022 KU Leuven, DTAI Research Group
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.