Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
wannesm committed Jun 14, 2023
1 parent 1ece7ac commit 922027a
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 9 deletions.
7 changes: 6 additions & 1 deletion dtaidistance/dtw.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,12 @@ def distance(s1, s2,
This is the same as passing ub_euclidean() to max_dist
:param only_ub: Only compute the upper bound (Euclidean).
:param inner_dist: Distance between two points in the time series.
One of 'squared euclidean' (default), 'euclidean'
One of 'squared euclidean' (default), 'euclidean'.
When using the pure Python implementation (thus use_c=False) then the argument can also
be an object that has as callable arguments 'inner_dist' and 'result'. The 'inner_dist'
function computes the distance between two points (e.g., squared euclidean) and 'result'
is the function to apply to the final distance (e.g., sqrt when using squared euclidean).
You can also inherit from the 'innerdistance.CustomInnerDist' class.
Returns: DTW distance
"""
Expand Down
15 changes: 8 additions & 7 deletions dtaidistance/dtw_visualisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,18 +357,19 @@ def plot_matrix(distances, shownumbers=False, filename=None, fig=None, ax=None):
ax.yaxis.set_ticks_position('both')

im = ax.imshow(distances)
idxs = [str(i) for i in range(len(distances))]
idxs_y = [str(i) for i in range(distances.shape[0])]
idxs_x = [str(i) for i in range(distances.shape[1])]
# Show all ticks
ax.set_xticks(np.arange(len(idxs)))
ax.set_xticklabels(idxs)
ax.set_yticks(np.arange(len(idxs)))
ax.set_yticklabels(idxs)
ax.set_xticks(np.arange(len(idxs_x)))
ax.set_xticklabels(idxs_x)
ax.set_yticks(np.arange(len(idxs_y)))
ax.set_yticklabels(idxs_y)

ax.set_title("Distances between series", pad=30)

if shownumbers:
for i in range(len(idxs)):
for j in range(len(idxs)):
for i in range(len(idxs_y)):
for j in range(len(idxs_x)):
if not np.isinf(distances[i, j]):
l = "{:.2f}".format(distances[i, j])
ax.text(j, i, l, ha="center", va="center", color="w")
Expand Down
10 changes: 10 additions & 0 deletions dtaidistance/innerdistance.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,20 @@ class CustomInnerDist:

@staticmethod
def inner_dist(x, y):
"""The distance between two points in the series.
For example, for default DTW this would be the Squared Euclidean
distance: (a-b)**2.
"""
raise Exception("Function not defined")

@staticmethod
def result(x):
"""The transformation applied to the sum of all inner distances.
For example, for default DTW, which uses Squared Euclidean, this
would be: sqrt(d). Because d = (a_0-b_0)**2 + (a_1-b_1)**2 ...
"""
raise Exception("Function not defined")


Expand Down
36 changes: 35 additions & 1 deletion tests/test_bugs.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,40 @@ def test_bug_size():
assert d1 == pytest.approx(d2)


@numpyonly
def test_bug5_path():
"""
without psi: [(0, 0), (0, 1), (1, 2), (1, 3), (2, 4)]
with psi: [(0, 1), (1, 2), (1, 3), (2, 4)]
Why is this not (with psi): [(2,4), (1,3), (0,2)] ?
Answer:
Numerical inaccuracies. When choosing the best path from (1,3) the
three options are [1.0, 1.9999999999999996, 0.9999999999999996].
Thus moving left (last one) is chosen instead of the expected diagonal.
In theory:
Path 1: (0,2), (1,3), (2,4) = sqrt(1**2 + 0 + 0) = 1
Path 2: (0,1), (1,2), (1,3), (2,4) = sqrt(0 + 1**2 + 0 + 0) = 1
And path 1 should be chosen because the diagonal move has priority.
In practice, floating point inaccuracies:
Path 1: (2.1-3.1) = 1.0
Path 2: (4.1-3.1) = 0.9999999999999996
"""
with util_numpy.test_uses_numpy() as np:
s1 = np.array([2.1, 4.1, 5.1])
s2 = np.array([1.1, 2.1, 3.1, 4.1, 5.1])
d1, wps = dtw.warping_paths(s1, s2, psi=[0, 0, len(s2), len(s2)])
best_path = dtw.best_path(wps)
print(best_path)

# if directory and not dtwvis.test_without_visualization():
# dtwvis.plot_warpingpaths(s1, s2, wps, best_path, filename=directory / 'bug5_warpingpaths.png')
# dtwvis.plot_matrix(wps, shownumbers=True, filename=directory / 'bug5_matrix.png')


if __name__ == "__main__":
directory = Path(os.environ.get('TESTDIR', Path(__file__).parent))
# with util_numpy.test_uses_numpy() as np:
Expand All @@ -334,5 +368,5 @@ def test_bug_size():
# test_bug1_psi()
# test_bug2()
# test_bug3()
test_bug4()
test_bug5_path()
# test_bug_size()

0 comments on commit 922027a

Please sign in to comment.