У меня есть несколько различных форм в больших массивах numpy
, и я хочу рассчитать между ними эвклидовое расстояние между ними с помощью numpy
и scipy
.Минимальное эвклидовое расстояние между мечеными компонентами в массиве numpy
Примечание: Я сделал поиск и это отличается от предыдущих других вопросов здесь на стеке, как я хочу, чтобы получить наименьшее расстояние между помеченными пластырей в массиве, а не между точками или отдельными массивами, как и другие вопросы, просили.
Мой текущий подход работает с использованием KDTree, но он ужасно неэффективен для больших массивов. По сути, я просматриваю координаты каждого помеченного компонента и вычисляю расстояние между всеми другими компонентами. Наконец, в качестве примера вычисляется среднее минимальное расстояние.
Я ищу более разумный подход с использованием python и, желательно, без дополнительных модулей.
import numpy
from scipy import spatial
from scipy import ndimage
# Testing array
a = numpy.zeros((8,8), dtype=numpy.int)
a[2,2] = a[3,1] = a[3,2] = 1
a[2,6] = a[2,7] = a[1,6] = 1
a[5,5] = a[5,6] = a[6,5] = a[6,6] = a[7,5] = a[7,6] = 1
# label it
labeled_array,numpatches = ndimage.label(a)
# For number of patches
closest_points = []
for patch in [x+1 for x in range(numpatches)]:
# Get coordinates of first patch
x,y = numpy.where(labeled_array==patch)
coords = numpy.vstack((x,y)).T # transform into array
# Built a KDtree of the coords of the first patch
mt = spatial.cKDTree(coords)
for patch2 in [i+1 for i in range(numpatches)]:
if patch == patch2: # If patch is the same as the first, skip
continue
# Get coordinates of second patch
x2,y2 = numpy.where(labeled_array==patch2)
coords2 = numpy.vstack((x2,y2)).T
# Now loop through points
min_res = []
for pi in range(len(coords2)):
dist, indexes = mt.query(coords2[pi]) # query the distance and index
min_res.append([dist,pi])
m = numpy.vstack(min_res)
# Find minimum as closed point and get index of coordinates
closest_points.append(coords2[m[numpy.argmin(m,axis=0)[0]][1]])
# The average euclidean distance can then be calculated like this:
spatial.distance.pdist(closest_points,metric = "euclidean").mean()
EDIT Просто испытания @morningsun предлагаемого решения, и это огромное улучшение скорости. Однако возвращаемые значения немного отличаются:
# Consider for instance the following array
a = numpy.zeros((8,8), dtype=numpy.int)
a[2,2] = a[2,6] = a[5,5] = 1
labeled_array, numpatches = ndimage.label(cl_array,s)
# Previous approach using KDtrees and pdist
b = kd(labeled_array,numpatches)
spatial.distance.pdist(b,metric = "euclidean").mean()
#> 3.0413115592767102
# New approach using the lower matrix and selecting only lower distances
b = numpy.tril(feature_dist(labeled_array))
b[b == 0 ] = numpy.nan
numpy.nanmean(b)
#> 3.8016394490958878
EDIT 2
Ах, понял это. spaces.distance.pdist не возвращает правильную матрицу расстояний, и, следовательно, значения были неправильными.
Спасибо за это! Я только что проверил его на одном из моих наборов данных, и он работает почти на 89% быстрее. Мощность векторизации. Хотя я не совсем понимаю, почему «sqeuclidean» был рассчитан. Он также возвращает разные значения, если попытается вычислить, например, среднее значение всех различий (см. Редактирование в вопросе). – Curlew
Ahh, понял это (см. Выше). Pdist не возвращает правильную матрицу расстояний, и поэтому мои предыдущие значения были неправильными ... Еще раз спасибо за ваше решение! – Curlew
@Curlew - Квадратичный евклид быстрее вычисляется. Обратите внимание, что я использовал его только для промежуточных результатов; квадратный корень берется в операторе return. –