def _test_pw_equal_single_dists(x: np.ndarray, y: np.ndarray, distance_function: Callable, conical_name: str) -> None: """Test pairwise result is equal to individual distance. Parameters ---------- x: np.ndarray (1d, 2d or 3d array) First timeseries y: np.ndarray (1d, 2d or 3d array) Second timeseries distance_function: Callable Distance function to test conical_name: str Name of the metric """ if x.ndim < 2: return # use euclidean distance so this test will fail if conical_name == "lcss" or conical_name == "edr" or conical_name == "erp": return pw_result = pairwise_distance(x, y, metric=conical_name) matrix = np.zeros((len(x), len(y))) for i in range(len(x)): curr_x = x[i] for j in range(len(y)): curr_y = y[j] matrix[i, j] = distance_function(curr_x, curr_y) assert np.array_equal(matrix, pw_result)
def _pairwise_path(x, y, metric): pw_matrix = pairwise_distance(x, y, metric=metric) path = [] for i in range(pw_matrix.shape[0]): for j in range(pw_matrix.shape[1]): if i == j: path.append((i, j)) return path, pw_matrix.trace(), pw_matrix
def _3D_distance(X, X2, **params): msg = "X and X2 must be np.ndarray, of dim 1, 2, or 3" if not isinstance(X, np.ndarray) or not isinstance(X2, np.ndarray): raise TypeError(msg) if X.ndim > 3 or X2.ndim > 3 or X.ndim < 1 or X2.ndim < 1: raise TypeError(msg) if X.ndim < 3 and X2.ndim < 3: return distance(X, X2, **params) else: return pairwise_distance(X, X2, metric=distance, **params)
def _validate_pairwise_result( x: np.ndarray, y: np.ndarray, metric_str: str, distance_factory: Callable, distance_function: Callable, distance_numba_class: NumbaDistance, kwargs_dict: dict = None, ): """Validate the pairwise distance gives desired result. Parameters ---------- x: np.ndarray (1d, 2d or 3d array) First timeseries. y: np.ndarray (1d, 2d or 3d array) Second timeseries. metric_str: str Metric string name. distance_factory: Callable Distance factory callable distance_function: Callable Distance function callable distance_numba_class: Callable NumbaDistance class kwargs_dict: dict Extra kwargs """ if kwargs_dict is None: kwargs_dict = {} metric_str_result = pairwise_distance(x, y, metric=metric_str, **kwargs_dict) expected_size = (len(x), len(y)) if x.ndim <= 1: expected_size = (1, 1) assert metric_str_result.shape == expected_size, ( f'The result for a pairwise using the string: {metric_str} as the "metric" ' f"parameter should be of the shape {expected_size}. " f"Instead the result was of shape {metric_str_result.shape}.") assert isinstance(metric_str_result, np.ndarray), ( f"The result for a pairwise using the string: {metric_str} as the " f'"metric" parameter should return a 2d numpy array. The return type provided ' f"is of type {type(metric_str_result)}") metric_factory_result = pairwise_distance(x, y, metric=distance_factory, **kwargs_dict) metric_numba_class_result = pairwise_distance(x, y, metric=distance_numba_class, **kwargs_dict) metric_dist_func_result = pairwise_distance(x, y, metric=distance_function, **kwargs_dict) assert isinstance(metric_factory_result, np.ndarray), ( f"The result for a pairwise using the distance factory: " f'{distance_factory} as the "metric" parameter should return a 2d numpy ' f"The return type provided is of type {type(metric_factory_result)}") assert isinstance(metric_numba_class_result, np.ndarray), ( f"The result for a pairwise using the NumbaDistance class: " f'{distance_numba_class} as the "metric" parameter should return a 2d ' f"numpy The return type provided is of type " f"{type(metric_numba_class_result)}") assert np.array_equal(metric_str_result, metric_factory_result), ( f'The result of using the string: {metric_str} as the "metric" parameter' f"result does not equal the result of using the distance factory: " f'{distance_factory} as the "metric" parameter. These results should be ' f"equal. The result of the pairwise calculation where metric={metric_str} " f"is {distance_factory}. The result of the distance calculation where " f"metric={distance_factory} is {metric_factory_result}.") assert np.array_equal(metric_str_result, metric_numba_class_result), ( f'The result of using the string: {metric_str} as the "metric" parameter' f"result does not equal the result of using the NumbaDistance class: " f'{distance_numba_class} as the "metric" parameter. These results should ' f"be equal." f"The result of the pairwise calculation where metric={metric_str} is " f"{metric_str_result}. The result of the distance calculation where " f"metric={distance_numba_class} is {metric_numba_class_result}.") assert np.array_equal(metric_str_result, metric_dist_func_result), ( f'The result of using the string: {metric_str} as the "metric" parameter' f"result does not equal the result of using a NumbaDistance class: " f'{distance_function} as the "metric" parameter. These results should be ' f"equal." f"The result of the pairwise calculation where metric={metric_str} is " f"{metric_str_result}. The result of the distance calculation where " f"metric={distance_function} is {metric_dist_func_result}.") metric_str_result_to_self = pairwise_distance(x, x, metric=metric_str, **kwargs_dict) if metric_str != "lcss": assert metric_str_result_to_self.trace() == 0, ( f"The pairwise distance when given two of the same timeseries e.g." f"pairwise_distance(x, x, ...), diagonal should equal 0." f"(np.trace(result)). Instead for the pairwise metric given where " f"metric={metric_str} is {metric_str_result_to_self.trace()}") assert _check_symmetric(metric_str_result_to_self) is True, ( f"The pairwise distance when given two of the same timeseries e.g." f"pairwise_distance(x, x, ...), should produce a symmetric matrix. This" f"means the left of the center diagonal should equal the right of the " f"center diagonal. This criteria is not met for the pairwise metric " f"{metric_str}") _test_pw_equal_single_dists(x, y, distance_function, metric_str)
def _plot_path( x: np.ndarray, y: np.ndarray, metric: str, dist_kwargs: dict = None, title: str = "", plot_over_pw: bool = False, ): if dist_kwargs is None: dist_kwargs = {} try: path, dist, cost_matrix = distance_alignment_path( x, y, metric=metric, return_cost_matrix=True, **dist_kwargs) if metric == "lcss": _path = [] for tup in path: _path.append(tuple(x + 1 for x in tup)) path = _path if plot_over_pw is True: if metric == "lcss": pw = pairwise_distance(x, y, metric="euclidean") cost_matrix = np.zeros_like(cost_matrix) cost_matrix[1:, 1:] = pw else: pw = pairwise_distance(x, y, metric="squared") cost_matrix = pw except NotImplementedError: path, dist, cost_matrix = _pairwise_path(x, y, metric) plt.figure(1, figsize=(8, 8)) x_size = x.shape[0] # definitions for the axes left, bottom = 0.01, 0.1 w_ts = h_ts = 0.2 left_h = left + w_ts + 0.02 width = height = 0.65 bottom_h = bottom + height + 0.02 rect_s_y = [left, bottom, w_ts, height] rect_gram = [left_h, bottom, width, height] rect_s_x = [left_h, bottom_h, width, h_ts] ax_gram = plt.axes(rect_gram) ax_s_x = plt.axes(rect_s_x) ax_s_y = plt.axes(rect_s_y) _path_mask(cost_matrix, path, ax_gram) ax_gram.axis("off") ax_gram.autoscale(False) # ax_gram.plot([j for (i, j) in path], [i for (i, j) in path], "w-", # linewidth=3.) ax_s_x.plot(np.arange(x_size), y, "b-", linewidth=3.0, color="#818587") ax_s_x.axis("off") ax_s_x.set_xlim((0, x_size - 1)) ax_s_y.plot(-x, np.arange(x_size), "b-", linewidth=3.0, color="#818587") ax_s_y.axis("off") ax_s_y.set_ylim((0, x_size - 1)) ax_s_x.set_title(title, size=10) return plt