У меня есть некоторые случайные тестовые данные в 2D-массиве формы (500,2) как таковые:
xy = np.random.randint(low=0.1, high=1000, size=[500, 2])
Из этого массива я сначала выбираю 10 случайных выборок, чтобы выбрать 11-ю выборку, я хотел бы выбрать выборку, которая находится дальше всего от исходных 10 выбранных выборок вместе, я использую для этого евклидово расстояние. Мне нужно продолжать делать это, пока не будет выбрана определенная сумма. Вот моя попытка сделать это.
# Function to get the distance between samples
def get_dist(a, b):
return np.sqrt(np.sum(np.square(a - b)))
# Set up variables and empty lists for the selected sample and starting samples
n_xy_to_select = 120
selected_xy = []
starting = []
# This selects 10 random samples and appends them to selected_xy
for i in range(10):
idx = np.random.randint(len(xy))
starting_10 = xy[idx, :]
selected_xy.append(starting_10)
starting.append(starting_10)
xy = np.delete(xy, idx, axis = 0)
starting = np.asarray(starting)
# This performs the selection based on the distances
for i in range(n_xy_to_select - 1):
# Set up an empty array dists
dists = np.zeros(len(xy))
for selected_xy_ in selected_xy:
# Get the distance between each already selected sample, and every other unselected sample
dists_ = np.array([get_dist(selected_xy_, xy_) for xy_ in xy])
# Apply some kind of penalty function - this is the key
dists_[dists_ < 90] -= 25000
# Sum dists_ onto dists
dists += dists_
# Select the largest one
dist_max_idx = np.argmax(dists)
selected_xy.append(xy[dist_max_idx])
xy = np.delete(xy, dist_max_idx, axis = 0)
Ключом к этому является эта строка — штрафная функция
dists_[dists_ < 90] -= 25000
Эта штрафная функция существует для того, чтобы код не мог просто выбрать кольцо выборок на краю пространства, искусственно сокращая близко расположенные значения.
Однако в конечном итоге это нарушается, и выделение начинает группироваться, как показано на изображении. Вы можете ясно видеть, что код может сделать гораздо лучший выбор, прежде чем потребуется какая-либо кластеризация. Я чувствую, что для этого лучше всего подойдет своего рода затухающая экспоненциальная функция, но я не знаю, как ее реализовать.
Итак, мой вопрос; как мне изменить текущую функцию штрафа, чтобы получить то, что я ищу?
Из вашего вопроса я понимаю, что вы ищете периодические граничные условия (PBC). Это означает, что точка, которая находится на левом краю вашего пространства, находится рядом с правой конечной стороной. Таким образом, максимальное расстояние, которое вы можете пройти по одной оси, определяется половиной прямоугольника (то есть между краем и центром).
Чтобы учесть PBC, вам нужно вычислить расстояние по каждой оси и вычесть из него половину поля: Например, если у вас есть точка с x1 = 100 и вторая с x2 = 900, используя PBC, они находятся на расстоянии 200 единиц друг от друга: |x1 - x2| - 500. В общем случае, учитывая 2 координаты и поле половинного размера, вы получите:
В вашем случае это упрощается до:
delta_x[delta_x > 500] = delta_x[delta_x > 500] - 500
В завершение я переписал ваш код, используя новую функцию distance
(обратите внимание, что я удалил некоторые ненужные циклы for):
import numpy as np
def distance(p, arr, 500):
delta_x = np.abs(p[0] - arr[:,0])
delta_y = np.abs(p[1] - arr[:,1])
delta_x[delta_x > 500] = delta_x[delta_x > 500] - 500
delta_y[delta_y > 500] = delta_y[delta_y > 500] - 500
return np.sqrt(delta_x**2 + delta_y**2)
xy = np.random.randint(low=0.1, high=1000, size=[500, 2])
idx = np.random.randint(500, size=10)
selected_xy = list(xy[idx])
_initial_selected = xy[idx]
xy = np.delete(xy, idx, axis = 0)
n_xy_to_select = 120
for i in range(n_xy_to_select - 1):
# Set up an empty array dists
dists = np.zeros(len(xy))
for selected_xy_ in selected_xy:
# Compute the distance taking into account the PBC
dists_ = distance(selected_xy_, xy)
dists += dists_
# Select the largest one
dist_max_idx = np.argmax(dists)
selected_xy.append(xy[dist_max_idx])
xy = np.delete(xy, dist_max_idx, axis = 0)
И действительно, он создает кластеры, и это нормально, так как вы будете стремиться создавать кластеры точек, которые находятся на максимальном расстоянии друг от друга. Более того, из-за граничных условий мы установили, что максимальное расстояние между двумя точками по одной оси равно 500. Таким образом, максимальное расстояние между двумя кластерами также равно 500! И, как вы можете видеть на изображении, это так.
Более того, выбор большего количества чисел начнет рисовать линию для соединения различных кластеров, начиная с центрального, как вы можете видеть здесь:
То, что я искал, называется «Выборка самой дальней точки». У меня есть еще несколько исследований решения, и код Python, используемый для его выполнения, находится здесь: https://minibatchai.com/ai/2021/08/07/FPS.html
Спасибо за ваш ответ, но это не то, что я ищу. Возможно, я плохо сформулировал в своем первоначальном посте, но то, что я ищу, - это «Выборка самой дальней точки». Смотрите мой ответ, я только что обнаружил это сегодня!