Source code for vtool.distance

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
import numpy as np
import utool as ut
import ubelt as ub
import itertools
from six.moves import range, zip
from collections import OrderedDict
import scipy.spatial.distance as spdist
from .util_math import TAU

TEMP_VEC_DTYPE = np.float64


[docs]def testdata_hist(): import vtool as vt rng = np.random.RandomState(0) hist1 = vt.demodata.testdata_dummy_sift(rng=rng) hist2 = vt.demodata.testdata_dummy_sift(rng=rng) return hist1, hist2
[docs]def testdata_sift2(): sift1 = np.zeros(128) sift2 = np.ones(128) sift3 = np.zeros(128) sift4 = np.zeros(128) sift5 = np.zeros(128) sift1[0] = 1 sift3[-1] = 1 sift4[0::2] = 1 sift5[1::2] = 1 def normalize_sift(sift): # normalize sift_norm = sift / np.linalg.norm(sift) # clip sift_norm = np.clip(sift_norm, 0, 0.2) # re-normalize sift_norm = sift_norm / np.linalg.norm(sift_norm) # cast hack sift_norm = np.clip(sift_norm * 512.0, 0, 255).astype(np.uint8) return sift_norm sift1 = normalize_sift(sift1) sift2 = normalize_sift(sift2) sift3 = normalize_sift(sift3) sift4 = normalize_sift(sift4) sift5 = normalize_sift(sift5) return sift1, sift2, sift3, sift4, sift5
[docs]def wrapped_distance(arr1, arr2, base, out=None): """ base = TAU corresponds to ori diff """ arr_diff = np.subtract(arr1, arr2) abs_diff = np.abs(arr_diff) mod_diff1 = np.mod(abs_diff, base) mod_diff2 = np.subtract(base, mod_diff1) arr_dist = np.minimum(mod_diff1, mod_diff2) if out is not None: out[:] = arr_dist return arr_dist
[docs]def signed_ori_distance(ori1, ori2): r""" Args: ori1 (ndarray): ori2 (ndarray): Returns: ndarray: ori_dist CommandLine: python -m vtool.distance --exec-signed_ori_distance Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> ori1 = np.array([0, 0, 3, 4, 0, 0]) >>> ori2 = np.array([3, 4, 0, 0, np.pi, np.pi - .1]) >>> ori_dist = signed_ori_distance(ori1, ori2) >>> result = ('ori_dist = %s' % (ub.repr2(ori_dist, precision=3),)) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) """ ori_dist = ori2 - ori1 ori_dist = (ori_dist + np.pi) % TAU - np.pi return ori_dist
[docs]def ori_distance(ori1, ori2, out=None): r""" Returns the unsigned distance between two angles References: http://stackoverflow.com/questions/1878907/the-smallest-difference-between-2-angles CommandLine: python -m vtool.distance --test-ori_distance Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> rng = np.random.RandomState(0) >>> ori1 = (rng.rand(10) * TAU) - np.pi >>> ori2 = (rng.rand(10) * TAU) - np.pi >>> dist_ = ori_distance(ori1, ori2) >>> result = ub.repr2(ori1, precision=1) >>> result += '\n' + ub.repr2(ori2, precision=1) >>> result += '\n' + ub.repr2(dist_, precision=1) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> ori1 = np.array([ 0.3, 7.0, 0.0, 3.1], dtype=np.float64) >>> ori2 = np.array([ 6.8, -1.0, 0.0, -3.1], dtype=np.float64) >>> dist_ = ori_distance(ori1, ori2) >>> result = ub.repr2(dist_, precision=2) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> ori1 = .3 >>> ori2 = 6.8 >>> dist_ = ori_distance(ori1, ori2) >>> result = ub.repr2(dist_, precision=2) >>> print(result) """ return cyclic_distance(ori1, ori2, modulo=TAU, out=out)
[docs]def cyclic_distance(arr1, arr2, modulo, out=None): r""" returns an unsigned distance Args: arr1 (ndarray): arr2 (ndarray): modulo (float or int): out (ndarray): (default = None) Returns: ndarray: arr_dist CommandLine: python -m vtool.distance cyclic_distance Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> out = None >>> modulo = 8 >>> offset = 0 # doesnt matter what offset is >>> arr1 = np.hstack([np.arange(offset, modulo + offset), np.nan]) >>> arr2 = arr1[:, None] >>> arr_dist = cyclic_distance(arr1, arr2, modulo, out) >>> result = ('arr_dist =\n%s' % (ub.repr2(arr_dist),)) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) """ arr_diff = np.subtract(arr1, arr2, out=out) abs_diff = np.abs(arr_diff, out=out) mod_diff1 = np.mod(abs_diff, modulo, out=out) mod_diff2 = np.subtract(modulo, mod_diff1) arr_dist = np.minimum(mod_diff1, mod_diff2, out=out) return arr_dist
[docs]def signed_cyclic_distance(arr1, arr2, modulo, out=None): arr_diff = np.subtract(arr1, arr2, out=out) half_mod = modulo / 2 arr_dist = (arr_diff + half_mod) % modulo - half_mod return arr_dist
[docs]def det_distance(det1, det2): """Returns how far off determinants are from one another Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> rng = np.random.RandomState(53) >>> det1 = rng.rand(5) >>> det2 = rng.rand(5) >>> scaledist = det_distance(det1, det2) >>> result = ub.repr2(scaledist, precision=2, threshold=2) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) """ det_dist = det1 / det2 # Flip ratios that are less than 1 _flip_flag = det_dist < 1 det_dist[_flip_flag] = np.reciprocal(det_dist[_flip_flag]) return det_dist
[docs]def L1(hist1, hist2, dtype=TEMP_VEC_DTYPE): """returns L1 (aka manhatten or grid) distance between two histograms""" return (np.abs(np.asarray(hist1, dtype) - np.asarray(hist2, dtype))).sum(-1)
[docs]def L2_sqrd(hist1, hist2, dtype=TEMP_VEC_DTYPE): """returns the squared L2 distance # FIXME: if hist1.shape = (0,) and hist.shape = (0,) then result=0.0 SeeAlso: L2 Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> import numpy >>> ut.exec_funckw(L2_sqrd, globals()) >>> rng = np.random.RandomState(53) >>> hist1 = rng.rand(5, 2) >>> hist2 = rng.rand(5, 2) >>> l2dist = L2_sqrd(hist1, hist2) >>> result = ub.repr2(l2dist, precision=2, threshold=2) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> hist1 = 3 >>> hist2 = 0 >>> result = L2_sqrd(hist1, hist2) >>> print(result) """ # Carefull, this will not return the correct result if the types are unsigned. hist1_ = np.asarray(hist1, dtype) hist2_ = np.asarray(hist2, dtype) return ((hist1_ - hist2_) ** 2).sum(-1) # this is faster
[docs]def understanding_pseudomax_props(mode=2): """ Function showing some properties of distances between normalized pseudomax vectors CommandLine: python -m vtool.distance --test-understanding_pseudomax_props Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> for mode in [0, 1, 2, 3]: ... print('+---') ... print('mode = %r' % (mode,)) ... result = understanding_pseudomax_props(mode) ... print('L___') >>> print(result) """ import vtool as vt pseudo_max = 512 rng = np.random.RandomState(0) num = 10 if mode == 0: dim = 2 p1_01 = vt.normalize_rows(rng.rand(num, dim)) p2_01 = vt.normalize_rows(rng.rand(num, dim)) elif mode == 1: p1_01 = vt.demodata.testdata_dummy_sift(num, rng) / pseudo_max p2_01 = vt.demodata.testdata_dummy_sift(num, rng) / pseudo_max elif mode == 2: # Build theoretically maximally distant normalized vectors (type 1) dim = 128 p1_01 = np.zeros((1, dim)) p2_01 = np.zeros((1, dim)) p2_01[:, 0::2] = 1 p1_01[:, 1::2] = 1 p1_01 = vt.normalize_rows(p1_01) p2_01 = vt.normalize_rows(p2_01) elif mode == 3: # Build theoretically maximally distant vectors (type 2) # This mode will clip if cast to uint8, thus failing the test dim = 128 p1_01 = np.zeros((1, dim)) p2_01 = np.zeros((1, dim)) p2_01[:, 0] = 1 p1_01[:, 1:] = 1 p1_01 = vt.normalize_rows(p1_01) p2_01 = vt.normalize_rows(p2_01) pass print('ndims = %r' % (p1_01.shape[1])) p1_01 = p1_01.astype(TEMP_VEC_DTYPE) p2_01 = p2_01.astype(TEMP_VEC_DTYPE) p1_256 = p1_01 * pseudo_max p2_256 = p2_01 * pseudo_max dist_sqrd_01 = vt.L2_sqrd(p1_01, p2_01) dist_sqrd_256 = vt.L2_sqrd(p1_256, p2_256) dist_01 = np.sqrt(dist_sqrd_01) dist_256 = np.sqrt(dist_sqrd_256) print('dist_sqrd_01 = %s' % (ub.repr2(dist_sqrd_01, precision=2),)) print('dist_sqrd_256 = %s' % (ub.repr2(dist_sqrd_256, precision=2),)) print('dist_01 = %s' % (ub.repr2(dist_01, precision=2),)) print('dist_256 = %s' % (ub.repr2(dist_256, precision=2),)) print('--') print('sqrt(2) = %f' % (np.sqrt(2))) print('--') assert np.all(dist_01 == vt.L2(p1_01, p2_01)) assert np.all(dist_256 == vt.L2(p1_256, p2_256)) const_sqrd = dist_sqrd_256 / dist_sqrd_01 const = dist_256 / dist_01 print('const = %r' % (const[0],)) print('const_sqrd = %r' % (const_sqrd[0],)) print('1 / const = %r' % (1 / const[0],)) print('1 / const_sqrd = %r' % (1 / const_sqrd[0],)) assert ub.allsame(const) assert ub.allsame(const_sqrd) assert np.all(const == np.sqrt(const_sqrd)) # Assert that distance conversions work assert np.all(dist_256 / const == dist_01) assert np.all(dist_sqrd_256 / const_sqrd == dist_sqrd_01) print('Conversions work') print( 'Maximal L2 distance between any two NON-NEGATIVE L2-NORMALIZED' ' vectors should always be sqrt(2)' )
[docs]def L2(hist1, hist2): """returns L2 (aka euclidean or standard) distance between two histograms""" return np.sqrt(L2_sqrd(hist1, hist2))
[docs]def hist_isect(hist1, hist2): """returns histogram intersection distance between two histograms""" numer = (np.dstack([hist1, hist2])).min(-1).sum(-1) denom = hist2.sum(-1) hisect_dist = 1 - (numer / denom) if len(hisect_dist) == 1: hisect_dist = hisect_dist[0] return hisect_dist
VALID_DISTS = [ 'L1', 'L2', 'L2_sift', 'L2_sqrd', 'bar_L2_sift', 'bar_cos_sift', 'cos_sift', 'det_distance', 'emd', 'hist_isect', 'nearest_point', 'ori_distance', ]
[docs]def compute_distances(hist1, hist2, dist_list=['L1', 'L2']): r""" Args: hist1 (ndarray): hist2 (ndarray): dist_list (list): (default = ['L1', 'L2']) Returns: dict: dist_dict CommandLine: python -m vtool.distance --test-compute_distances Example: >>> # DISABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> hist1 = np.array([[1, 2], [2, 1], [0, 0]]) >>> hist2 = np.array([[1, 2], [3, 1], [2, 2]]) >>> dist_list = ['L1', 'L2'] >>> dist_dict = compute_distances(hist1, hist2, dist_list) >>> result = ub.repr2(dist_dict, precision=3) >>> print(result) """ dtype_ = np.float64 hist1 = np.array(hist1, dtype=dtype_) hist2 = np.array(hist2, dtype=dtype_) # TODO: enumerate value distances dist_funcs = [globals()[type_] for type_ in dist_list] val_list = [func(hist1, hist2) for func in dist_funcs] dist_dict = OrderedDict(list(zip(dist_list, val_list))) return dist_dict
[docs]def bar_L2_sift(hist1, hist2): """ Normalized SIFT L2 Args: hist1 (ndarray): Nx128 array of uint8 with pseudomax trick hist2 (ndarray): Nx128 array of uint8 with pseudomax trick CommandLine: python -m vtool.distance --test-bar_L2_sift Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> hist1, hist2 = testdata_hist() >>> barl2_dist = bar_L2_sift(hist1, hist2) >>> result = ub.repr2(barl2_dist, precision=2) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) """ return 1.0 - L2_sift(hist1, hist2)
[docs]def L2_sift(hist1, hist2): """ Normalized SIFT L2 Args: hist1 (ndarray): Nx128 array of uint8 with pseudomax trick hist2 (ndarray): Nx128 array of uint8 with pseudomax trick Returns: ndarray: euclidean distance between 0-1 normalized sift descriptors CommandLine: python -m vtool.distance --test-L2_sift Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> hist1, hist2 = testdata_hist() >>> sift1, sift2, sift3, sift4, sift5 = testdata_sift2() >>> l2_dist = L2_sift(hist1, hist2) >>> max_dist = L2_sift(sift4, sift5) >>> assert np.isclose(max_dist, 1.0) >>> result = ub.repr2(l2_dist, precision=2) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) """ # The corret number is 512, because thats what is used in siftdesc.cpp # remove the pseudo max hack psuedo_max = 512.0 max_l2_dist = np.sqrt(2) # maximum L2 distance should always be sqrt 2 sift1 = hist1.astype(TEMP_VEC_DTYPE) / psuedo_max sift2 = hist2.astype(TEMP_VEC_DTYPE) / psuedo_max l2_dist = L2(sift1, sift2) sift_dist = l2_dist / max_l2_dist return sift_dist
[docs]def L2_root_sift(hist1, hist2): """ Normalized Root-SIFT L2 Args: hist1 (ndarray): Nx128 array of uint8 with pseudomax trick hist2 (ndarray): Nx128 array of uint8 with pseudomax trick Returns: ndarray: euclidean distance between 0-1 normalized sift descriptors """ # remove the pseudo max hack psuedo_max = 512.0 max_root_l2_dist = 2 # This is a guess sift1 = hist1.astype(TEMP_VEC_DTYPE) / psuedo_max sift2 = hist2.astype(TEMP_VEC_DTYPE) / psuedo_max root_sift1 = np.sqrt(sift1) root_sift2 = np.sqrt(sift2) l2_dist = L2(root_sift1, root_sift2) # Usure if correct; l2_root_dist = l2_dist / max_root_l2_dist return l2_root_dist
[docs]def L2_sift_sqrd(hist1, hist2): """ Normalized SIFT L2**2 Args: hist1 (ndarray): Nx128 array of uint8 with pseudomax trick hist2 (ndarray): Nx128 array of uint8 with pseudomax trick Returns: ndarray: squared euclidean distance between 0-1 normalized sift descriptors """ # remove the pseudo max hack psuedo_max = 512.0 max_l2_dist_sqrd = 2 sift1 = hist1.astype(TEMP_VEC_DTYPE) / psuedo_max sift2 = hist2.astype(TEMP_VEC_DTYPE) / psuedo_max l2_sqrd_dist = L2_sqrd(sift1, sift2) return l2_sqrd_dist / max_l2_dist_sqrd
[docs]def bar_cos_sift(hist1, hist2): """1 - cos dist""" return 1.0 - cos_sift(hist1, hist2)
[docs]def cos_sift(hist1, hist2): """ cos dist CommandLine: python -m vtool.distance --test-cos_sift Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> hist1, hist2 = testdata_hist() >>> l2_dist = cos_sift(hist1, hist2) >>> #xdoctest: +IGNORE_WHITESPACE >>> result = ub.repr2(l2_dist, precision=2) >>> print(result) """ psuedo_max = 512.0 sift1 = hist1.astype(TEMP_VEC_DTYPE) / psuedo_max sift2 = hist2.astype(TEMP_VEC_DTYPE) / psuedo_max return (sift1 * sift2).sum(-1)
[docs]def cosine_dist(hist1, hist2): return (hist1 * hist2).sum(-1)
def _assert_siftvec(sift): import vtool as vt assert vt.check_sift_validity(sift)
[docs]def emd(hist1, hist2, cost_matrix='sift'): """ earth mover's distance by robjects(lpSovle::lp.transport) require: lpsolve55-5.5.0.9.win32-py2.7.exe CommandLine: python -m vtool.distance --test-emd Example: >>> # DISABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> hist1, hist2 = testdata_hist() >>> emd_dists = emd(hist1, hist2) >>> result = ub.repr2(emd_dists, precision=2) >>> #xdoctest: +IGNORE_WHITESPACE >>> print(result) np.array([ 2063.99, 2078.02, 2109.03, 2011.99, 2130.99, 2089.01, 2030.99, 2294.98, 2026.02, 2426.01]) References: pip install pyemd https://github.com/andreasjansson/python-emd http://www.cs.huji.ac.il/~werman/Papers/ECCV2008.pdf http://stackoverflow.com/questions/15706339/compute-emd-2umpy-arrays-using-opencv http://www.cs.huji.ac.il/~ofirpele/FastEMD/code/ http://www.cs.huji.ac.il/~ofirpele/publications/ECCV2008.pdf """ import pyemd if cost_matrix == 'sift': # Build cost matrix where bin-to-bin cost is 0, # neighbor cost is 1, and other cost is 2 N = 8 cost_matrix = np.full((128, 128), 2) i, j = np.meshgrid(np.arange(128), np.arange(128)) cost_matrix[i == j] = 0 absdiff = np.abs(i - j) is_neighbor = np.abs(np.minimum(absdiff, N - absdiff)) == 1 cost_matrix[is_neighbor] = 1.0 # print(cost_matrix[0:16, 0:16]) if len(hist1.shape) == 2: dist = np.array( [ pyemd.emd(hist1_.astype(np.float), hist2_.astype(np.float), cost_matrix) for hist1_, hist2_ in zip(hist1, hist2) ] ) else: dist = pyemd.emd(hist1.astype(np.float), hist2.astype(np.float), cost_matrix) return dist
[docs]def nearest_point(x, y, pts, conflict_mode='next', __next_counter=[0]): """finds the nearest point(s) in pts to (x, y) TODO: depricate """ # with ut.embed_on_exception_context: dists = (pts.T[0] - x) ** 2 + (pts.T[1] - y) ** 2 fx = dists.argmin() mindist = dists[fx] other_fx = np.where(mindist == dists)[0] if len(other_fx) > 0: if conflict_mode == 'random': np.random.shuffle(other_fx) fx = other_fx[0] elif conflict_mode == 'next': __next_counter[0] += 1 idx = __next_counter[0] % len(other_fx) fx = other_fx[idx] elif conflict_mode == 'all': fx = other_fx elif conflict_mode == 'first': fx = fx else: raise AssertionError('unknown conflict_mode=%r' % (conflict_mode,)) return fx, mindist
[docs]def closest_point(pt, pt_arr, distfunc=L2_sqrd): """finds the nearest point(s) in pts to (x, y) pt = np.array([1]) pt_arr = np.array([1.1, 2, .95, 20])[:, None] distfunc = vt.L2_sqrd """ # import vtool as vt assert len(pt_arr) > 0 dists = distfunc(pt, pt_arr) xlist = dists.argsort() if len(xlist) > 1: if dists[xlist[0]] == dists[xlist[1]]: print('conflict') index = xlist[0] dist = dists[index] return index, dist
[docs]def haversine(latlon1, latlon2): r""" Calculate the great circle distance between two points on the earth (specified in decimal degrees) Args: latlon1 (ndarray): latlon2 (ndarray): References: en.wikipedia.org/wiki/Haversine_formula gis.stackexchange.com/questions/81551/matching-gps-tracks stackoverflow.com/questions/4913349/haversine-distance-gps-points CommandLine: python -m vtool.distance --exec-haversine Example: >>> # ENABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> import scipy.spatial.distance as spdist >>> import vtool as vt >>> import functools >>> gpsarr_track_list_ = [ ... np.array([[ -80.21895315, -158.81099213], ... [ -12.08338926, 67.50368014], ... [ -11.08338926, 67.50368014], ... [ -11.08338926, 67.50368014],] ... ), ... np.array([[ 9.77816711, -17.27471498], ... [ -51.67678814, -158.91065495],]) ... ] >>> latlon1 = gpsarr_track_list_[0][0] >>> latlon2 = gpsarr_track_list_[0][1] >>> kilometers = vt.haversine(latlon1, latlon2) >>> haversin_pdist = functools.partial(spdist.pdist, metric=vt.haversine) >>> dist_vector_list = list(map(haversin_pdist, gpsarr_track_list_)) >>> dist_matrix_list = list(map(spdist.squareform, dist_vector_list)) >>> #xdoctest: +IGNORE_WHITESPACE >>> result = ('dist_matrix_list = %s' % (ut.repr3(dist_matrix_list, precision=2, with_dtype=True),)) >>> print(result) """ # FIXME; lat, lon should be different columns not different rows # convert decimal degrees to radians lat1, lon1 = np.radians(latlon1) lat2, lon2 = np.radians(latlon2) # haversine formula dlon = lon2 - lon1 dlat = lat2 - lat1 a = (np.sin(dlat / 2) ** 2) + np.cos(lat1) * np.cos(lat2) * (np.sin(dlon / 2) ** 2) c = 2 * np.arcsin(np.sqrt(a)) EARTH_RADIUS_KM = 6367.0 kilometers = EARTH_RADIUS_KM * c return kilometers
[docs]def safe_pdist(arr, *args, **kwargs): """ Kwargs: metric = ut.absdiff SeeAlso: scipy.spatial.distance.pdist """ if arr is None or len(arr) < 2: return None else: if len(arr.shape) == 1: return spdist.pdist(arr[:, None], *args, **kwargs) else: return spdist.pdist(arr, *args, **kwargs)
[docs]def pdist_indicies(num): return list(itertools.combinations(range(num), 2))
[docs]def pdist_argsort(x): """ Sorts 2d indicies by their distnace matrix output from scipy.spatial.distance x = np.array([ 3.05555556e-03, 1.47619797e+04, 1.47619828e+04]) Args: x (ndarray): Returns: ndarray: sortx_2d CommandLine: python -m vtool.distance --test-pdist_argsort Example: >>> # DISABLE_DOCTEST >>> from vtool.distance import * # NOQA >>> x = np.array([ 21695.78, 10943.76, 10941.44, 25867.64, 10752.03, >>> 10754.35, 4171.86, 2.32, 14923.89, 14926.2 ], >>> dtype=np.float64) >>> sortx_2d = pdist_argsort(x) >>> result = ('sortx_2d = %s' % (str(sortx_2d),)) >>> print(result) sortx_2d = [(2, 3), (1, 4), (1, 2), (1, 3), (0, 3), (0, 2), (2, 4), (3, 4), (0, 1), (0, 4)] """ OLD = True # compare_idxs = [(r, c) for r, c in itertools.product(range(len(x) / 2), # range(len(x) / 2)) if (c > r)] if OLD: mat = spdist.squareform(x) matu = np.triu(mat) sortx_row, sortx_col = np.unravel_index(matu.ravel().argsort(), matu.shape) # only take where col is larger than row due to upper triu sortx_2d = [(r, c) for r, c in zip(sortx_row, sortx_col) if (c > r)] else: num_rows = len(x) // 2 compare_idxs = ut.flatten( [[(r, c) for c in range(r + 1, num_rows)] for r in range(num_rows)] ) sortx = x.argsort() sortx_2d = ut.take(compare_idxs, sortx) return sortx_2d
if __name__ == '__main__': r""" CommandLine: python -m vtool.distance all """ import xdoctest xdoctest.doctest_module(__file__)