0
# Visualization Tools
1
2
Comprehensive visualization capabilities for DTW analysis, including warping path plots, distance matrix heatmaps, and time series alignment visualizations. These tools provide essential insights into DTW behavior, sequence relationships, and clustering results for both 1D and multi-dimensional data.
3
4
## Capabilities
5
6
### Warping Path Visualization
7
8
Visualize the optimal warping path between two time series, showing how elements from one sequence align with elements from another.
9
10
```python { .api }
11
def plot_warping(s1, s2, path, filename=None):
12
"""
13
Plot optimal warping between two sequences.
14
15
Creates a visualization showing both time series with connecting lines
16
that illustrate the optimal warping path alignment between sequences.
17
18
Parameters:
19
- s1, s2: array-like, input time series sequences
20
- path: list, warping path as sequence of (i, j) coordinate pairs
21
- filename: str, optional file path to save the plot
22
23
Returns:
24
tuple: (figure, axes) matplotlib objects
25
"""
26
27
def plot_warpingpaths(s1, s2, paths, path=None, filename=None,
28
shownumbers=False):
29
"""
30
Plot warping paths matrix with sequences.
31
32
Displays the full accumulated cost matrix (warping paths) as a heatmap
33
alongside the input sequences, with optional overlay of the optimal path.
34
35
Parameters:
36
- s1, s2: array-like, input sequences
37
- paths: 2D array, warping paths matrix from dtw.warping_paths()
38
- path: list, optional optimal path to overlay
39
- filename: str, optional file path to save plot
40
- shownumbers: bool, display numerical values in matrix cells
41
42
Returns:
43
tuple: (figure, axes) matplotlib objects
44
"""
45
```
46
47
### Sequence Warping Visualization
48
49
Visualize the transformation of one sequence to match another through DTW warping.
50
51
```python { .api }
52
def plot_warp(from_s, to_s, new_s, path, filename=None):
53
"""
54
Plot warped sequence relationships.
55
56
Shows the original sequence, target sequence, and the warped result
57
to illustrate how DTW transforms one sequence to better match another.
58
59
Parameters:
60
- from_s: array-like, original source sequence
61
- to_s: array-like, target sequence to warp towards
62
- new_s: array-like, warped version of source sequence
63
- path: list, warping path used for transformation
64
- filename: str, optional file path to save plot
65
66
Returns:
67
tuple: (figure, axes) matplotlib objects
68
"""
69
```
70
71
### Distance Matrix Visualization
72
73
Create heatmap visualizations of distance matrices for analyzing relationships between multiple time series.
74
75
```python { .api }
76
def plot_matrix(distances, filename=None, ax=None, shownumbers=False):
77
"""
78
Plot distance matrix as heatmap.
79
80
Creates a color-coded heatmap representation of the distance matrix
81
showing pairwise similarities between all sequences in a collection.
82
83
Parameters:
84
- distances: 2D array, symmetric distance matrix
85
- filename: str, optional file path to save plot
86
- ax: matplotlib axis, optional axis for plotting
87
- shownumbers: bool, display distance values in matrix cells
88
89
Returns:
90
tuple: (figure, axes) matplotlib objects
91
"""
92
```
93
94
## Usage Examples
95
96
### Basic Warping Path Visualization
97
98
```python
99
from dtaidistance import dtw
100
from dtaidistance.dtw_visualisation import plot_warping, plot_warpingpaths
101
import matplotlib.pyplot as plt
102
103
# Create two sequences with different temporal patterns
104
s1 = [0, 1, 2, 3, 2, 1, 0, 0]
105
s2 = [0, 0, 1, 2, 2, 3, 2, 1, 0]
106
107
# Compute optimal warping path
108
path = dtw.warping_path(s1, s2)
109
print("Warping path:", path)
110
111
# Visualize the warping between sequences
112
fig, ax = plot_warping(s1, s2, path)
113
plt.title('DTW Warping Path Between Sequences')
114
plt.show()
115
116
# Also compute and visualize the full warping paths matrix
117
distance, paths_matrix = dtw.warping_paths(s1, s2)
118
119
fig, ax = plot_warpingpaths(s1, s2, paths_matrix, path)
120
plt.title('DTW Warping Paths Matrix')
121
plt.show()
122
```
123
124
### Advanced Warping Path Analysis
125
126
```python
127
from dtaidistance import dtw
128
from dtaidistance.dtw_visualisation import plot_warping, plot_warpingpaths
129
import numpy as np
130
import matplotlib.pyplot as plt
131
132
# Create sequences with clear temporal distortions
133
t1 = np.linspace(0, 4*np.pi, 50)
134
t2 = np.linspace(0, 4*np.pi, 40)
135
136
s1 = np.sin(t1) + 0.1 * np.random.randn(len(t1))
137
s2 = np.sin(t2 * 1.2) + 0.1 * np.random.randn(len(t2)) # Slightly faster
138
139
# Compute warping with constraints
140
distance, paths = dtw.warping_paths(s1, s2, window=10)
141
path = dtw.best_path(paths)
142
143
# Create comprehensive visualization
144
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
145
146
# Plot original sequences
147
ax1.plot(s1, 'b-', label='Sequence 1', linewidth=2)
148
ax1.plot(s2, 'r-', label='Sequence 2', linewidth=2)
149
ax1.set_title('Original Sequences')
150
ax1.legend()
151
ax1.grid(True)
152
153
# Plot warping paths matrix
154
im = ax2.imshow(paths, cmap='viridis', origin='lower')
155
ax2.set_title('Warping Paths Matrix')
156
ax2.set_xlabel('Sequence 2 Index')
157
ax2.set_ylabel('Sequence 1 Index')
158
plt.colorbar(im, ax=ax2)
159
160
# Plot optimal path on matrix
161
if path:
162
path_i, path_j = zip(*path)
163
ax2.plot(path_j, path_i, 'white', linewidth=2, alpha=0.8)
164
165
# Plot warping connections
166
ax3.plot(s1, 'b-o', label='Sequence 1', markersize=4)
167
ax3.plot(s2, 'r-o', label='Sequence 2', markersize=4)
168
169
# Draw warping connections
170
for i, j in path[::5]: # Show every 5th connection for clarity
171
if i < len(s1) and j < len(s2):
172
ax3.plot([i, j], [s1[i], s2[j]], 'gray', alpha=0.3, linewidth=0.5)
173
174
ax3.set_title('Warping Connections (every 5th)')
175
ax3.legend()
176
ax3.grid(True)
177
178
# Show warping path coordinates
179
ax4.plot([p[0] for p in path], label='Sequence 1 indices', marker='o', markersize=3)
180
ax4.plot([p[1] for p in path], label='Sequence 2 indices', marker='s', markersize=3)
181
ax4.set_title('Warping Path Coordinates')
182
ax4.set_xlabel('Path Step')
183
ax4.set_ylabel('Sequence Index')
184
ax4.legend()
185
ax4.grid(True)
186
187
plt.tight_layout()
188
plt.show()
189
190
print(f"DTW distance: {distance:.3f}")
191
print(f"Path length: {len(path)}")
192
```
193
194
### Sequence Warping Transformation Visualization
195
196
```python
197
from dtaidistance import dtw
198
from dtaidistance.dtw_visualisation import plot_warp
199
import numpy as np
200
import matplotlib.pyplot as plt
201
202
# Create source and target sequences
203
source = np.array([1, 2, 4, 6, 4, 2, 1, 1, 1])
204
target = np.array([1, 3, 5, 3, 1])
205
206
# Perform sequence warping
207
warped_source, path = dtw.warp(source, target)
208
209
# Visualize the warping transformation
210
fig, ax = plot_warp(source, target, warped_source, path)
211
plt.title('Sequence Warping Transformation')
212
plt.show()
213
214
# Additional analysis plot
215
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 8))
216
217
# Original sequences
218
ax1.plot(source, 'b-o', label='Original Source', linewidth=2, markersize=6)
219
ax1.plot(target, 'r-o', label='Target', linewidth=2, markersize=6)
220
ax1.set_title('Original Sequences')
221
ax1.legend()
222
ax1.grid(True)
223
224
# Warped source vs target
225
ax2.plot(warped_source, 'g-o', label='Warped Source', linewidth=2, markersize=6)
226
ax2.plot(target, 'r--', label='Target (reference)', linewidth=2, alpha=0.7)
227
ax2.set_title('Warped Source vs Target')
228
ax2.legend()
229
ax2.grid(True)
230
231
# Difference after warping
232
difference = np.array(warped_source) - np.array(target)
233
ax3.bar(range(len(difference)), difference, color='purple', alpha=0.7)
234
ax3.set_title('Difference After Warping')
235
ax3.set_xlabel('Time Point')
236
ax3.set_ylabel('Difference')
237
ax3.grid(True)
238
239
plt.tight_layout()
240
plt.show()
241
242
print(f"Original DTW distance: {dtw.distance(source, target):.3f}")
243
print(f"Warped sequence distance: {dtw.distance(warped_source, target):.3f}")
244
```
245
246
### Distance Matrix Heatmap
247
248
```python
249
from dtaidistance import dtw
250
from dtaidistance.dtw_visualisation import plot_matrix
251
import numpy as np
252
import matplotlib.pyplot as plt
253
254
# Create diverse time series collection
255
np.random.seed(42)
256
series = []
257
258
# Group 1: Sine waves with different frequencies
259
for freq in [1, 1.5, 2]:
260
t = np.linspace(0, 4*np.pi, 50)
261
s = np.sin(freq * t) + 0.1 * np.random.randn(50)
262
series.append(s)
263
264
# Group 2: Cosine waves
265
for freq in [1, 1.5]:
266
t = np.linspace(0, 4*np.pi, 50)
267
s = np.cos(freq * t) + 0.1 * np.random.randn(50)
268
series.append(s)
269
270
# Group 3: Linear trends
271
for slope in [0.5, 1.0, 1.5]:
272
t = np.linspace(0, 1, 50)
273
s = slope * t + 0.1 * np.random.randn(50)
274
series.append(s)
275
276
# Compute distance matrix
277
distances = dtw.distance_matrix(series, parallel=True)
278
279
# Visualize distance matrix
280
fig, ax = plot_matrix(distances, shownumbers=False)
281
plt.title('DTW Distance Matrix Heatmap')
282
283
# Add series type labels
284
group_labels = ['Sin 1Hz', 'Sin 1.5Hz', 'Sin 2Hz', 'Cos 1Hz', 'Cos 1.5Hz',
285
'Linear 0.5', 'Linear 1.0', 'Linear 1.5']
286
ax.set_xticks(range(len(group_labels)))
287
ax.set_yticks(range(len(group_labels)))
288
ax.set_xticklabels(group_labels, rotation=45, ha='right')
289
ax.set_yticklabels(group_labels)
290
291
plt.tight_layout()
292
plt.show()
293
294
# Analyze distance patterns
295
print("Distance matrix analysis:")
296
print(f"Matrix shape: {distances.shape}")
297
print(f"Mean distance: {np.mean(distances[np.triu_indices_from(distances, k=1)]):.3f}")
298
print(f"Min distance: {np.min(distances[np.triu_indices_from(distances, k=1)]):.3f}")
299
print(f"Max distance: {np.max(distances):.3f}")
300
```
301
302
### Multi-Panel Comprehensive Analysis
303
304
```python
305
from dtaidistance import dtw
306
from dtaidistance.dtw_visualisation import plot_warping, plot_warpingpaths, plot_matrix
307
import numpy as np
308
import matplotlib.pyplot as plt
309
310
# Generate test data
311
np.random.seed(42)
312
s1 = np.cumsum(np.random.randn(30)) + np.sin(np.linspace(0, 4*np.pi, 30))
313
s2 = np.cumsum(np.random.randn(25)) + np.cos(np.linspace(0, 3*np.pi, 25))
314
s3 = np.linspace(0, 5, 35) + 0.5 * np.random.randn(35)
315
316
series_collection = [s1, s2, s3]
317
318
# Compute various DTW analyses
319
distance_12, paths_12 = dtw.warping_paths(s1, s2, window=5)
320
path_12 = dtw.best_path(paths_12)
321
distances_matrix = dtw.distance_matrix(series_collection)
322
323
# Create comprehensive visualization
324
fig = plt.figure(figsize=(15, 10))
325
326
# Layout: 2 rows, 3 columns
327
ax1 = plt.subplot(2, 3, 1)
328
ax2 = plt.subplot(2, 3, 2)
329
ax3 = plt.subplot(2, 3, 3)
330
ax4 = plt.subplot(2, 3, 4)
331
ax5 = plt.subplot(2, 3, 5)
332
ax6 = plt.subplot(2, 3, 6)
333
334
# Plot 1: Original time series
335
for i, s in enumerate(series_collection):
336
ax1.plot(s, f'C{i}-', label=f'Series {i+1}', linewidth=2)
337
ax1.set_title('Original Time Series')
338
ax1.legend()
339
ax1.grid(True)
340
341
# Plot 2: Warping paths matrix
342
im2 = ax2.imshow(paths_12, cmap='viridis', origin='lower')
343
ax2.set_title('Warping Paths Matrix (S1 vs S2)')
344
ax2.set_xlabel('Series 2 Index')
345
ax2.set_ylabel('Series 1 Index')
346
if path_12:
347
path_i, path_j = zip(*path_12)
348
ax2.plot(path_j, path_i, 'white', linewidth=2)
349
350
# Plot 3: Distance matrix heatmap
351
im3 = ax3.imshow(distances_matrix, cmap='hot', origin='lower')
352
ax3.set_title('Distance Matrix')
353
ax3.set_xlabel('Series Index')
354
ax3.set_ylabel('Series Index')
355
plt.colorbar(im3, ax=ax3)
356
357
# Plot 4: Warping between S1 and S2
358
ax4.plot(s1, 'b-o', label='Series 1', markersize=4)
359
ax4.plot(s2, 'r-o', label='Series 2', markersize=4)
360
361
# Draw some warping connections
362
for i, (idx1, idx2) in enumerate(path_12[::3]): # Every 3rd connection
363
if idx1 < len(s1) and idx2 < len(s2):
364
ax4.plot([idx1, idx2], [s1[idx1], s2[idx2]], 'gray', alpha=0.4, linewidth=0.5)
365
366
ax4.set_title('Warping Connections (S1 vs S2)')
367
ax4.legend()
368
ax4.grid(True)
369
370
# Plot 5: Path analysis
371
if path_12:
372
path_i, path_j = zip(*path_12)
373
ax5.plot(path_i, 'b-', label='Series 1 indices', linewidth=2)
374
ax5.plot(path_j, 'r-', label='Series 2 indices', linewidth=2)
375
ax5.set_title('Optimal Path Indices')
376
ax5.set_xlabel('Path Step')
377
ax5.set_ylabel('Sequence Index')
378
ax5.legend()
379
ax5.grid(True)
380
381
# Plot 6: Distance comparison
382
distances_pairwise = [
383
dtw.distance(series_collection[0], series_collection[1]),
384
dtw.distance(series_collection[0], series_collection[2]),
385
dtw.distance(series_collection[1], series_collection[2])
386
]
387
pair_labels = ['S1-S2', 'S1-S3', 'S2-S3']
388
389
bars = ax6.bar(pair_labels, distances_pairwise, color=['blue', 'green', 'red'], alpha=0.7)
390
ax6.set_title('Pairwise DTW Distances')
391
ax6.set_ylabel('DTW Distance')
392
393
# Add value labels on bars
394
for bar, distance in zip(bars, distances_pairwise):
395
height = bar.get_height()
396
ax6.text(bar.get_x() + bar.get_width()/2., height + 0.1,
397
f'{distance:.2f}', ha='center', va='bottom')
398
399
plt.tight_layout()
400
plt.show()
401
402
print(f"DTW distance S1-S2: {distance_12:.3f}")
403
print(f"Path length: {len(path_12)}")
404
print(f"Series lengths: S1={len(s1)}, S2={len(s2)}, S3={len(s3)}")
405
```
406
407
### Interactive Visualization with Constraints
408
409
```python
410
from dtaidistance import dtw
411
from dtaidistance.dtw_visualisation import plot_warpingpaths
412
import numpy as np
413
import matplotlib.pyplot as plt
414
415
def compare_constraints(s1, s2, constraints_list):
416
"""Compare DTW results with different constraint settings."""
417
418
fig, axes = plt.subplots(2, len(constraints_list), figsize=(15, 8))
419
420
for i, constraints in enumerate(constraints_list):
421
# Compute DTW with constraints
422
distance, paths = dtw.warping_paths(s1, s2, **constraints)
423
path = dtw.best_path(paths)
424
425
# Plot warping paths matrix
426
im = axes[0, i].imshow(paths, cmap='viridis', origin='lower')
427
axes[0, i].set_title(f'Window={constraints.get("window", "None")}\\n'
428
f'Distance={distance:.2f}')
429
430
if path:
431
path_i, path_j = zip(*path)
432
axes[0, i].plot(path_j, path_i, 'white', linewidth=2)
433
434
# Plot sequences with warping connections
435
axes[1, i].plot(s1, 'b-o', label='S1', markersize=3)
436
axes[1, i].plot(s2, 'r-o', label='S2', markersize=3)
437
438
# Draw warping connections (sample every 5th)
439
for j, (idx1, idx2) in enumerate(path[::5]):
440
if idx1 < len(s1) and idx2 < len(s2):
441
axes[1, i].plot([idx1, idx2], [s1[idx1], s2[idx2]],
442
'gray', alpha=0.3, linewidth=0.5)
443
444
axes[1, i].set_title(f'Warping Connections')
445
axes[1, i].grid(True)
446
if i == 0:
447
axes[1, i].legend()
448
449
plt.tight_layout()
450
plt.show()
451
452
# Test different constraint settings
453
s1 = np.sin(np.linspace(0, 4*np.pi, 40)) + 0.1*np.random.randn(40)
454
s2 = np.sin(np.linspace(0, 3*np.pi, 30)) + 0.1*np.random.randn(30)
455
456
constraint_sets = [
457
{}, # No constraints
458
{'window': 5}, # Narrow window
459
{'window': 15}, # Wide window
460
{'window': 10, 'max_dist': 20.0} # Window + early stopping
461
]
462
463
compare_constraints(s1, s2, constraint_sets)
464
```
465
466
This comprehensive visualization module provides essential tools for understanding DTW behavior, analyzing sequence relationships, and validating clustering results through intuitive graphical representations.