#python #numpy
#python #numpy
Вопрос:
Мои данные, которые являются довольно непрерывными, но имеют разные области. Я пытаюсь определить центр каждого такого кластера (приблизительно).
По сути, данные представляют собой список 2D векторов в (N,2) массиве numpy. Данные имеют характерную структуру, которая выглядит следующим образом:
Вы можете видеть, что два кластера появляются (частично) в списке, и я хотел бы определить их центры (скажем, путем усреднения). Я вычислил норму градиента L2, и, похоже, она очень хорошо коррелирует с границами между кластерами (конечно).
Далее я хотел бы усреднить все точки данных, которые принадлежат каждому кластеру, как определено величиной градиента (если, конечно, у кого-то нет лучшей идеи). Я бы хотел избежать зацикливания на данных и вместо этого использовать векторизованные операции, чтобы они выполнялись быстро. Это трудность для меня.
Спасибо!
Ограничения:
- Я не могу использовать какую-либо библиотеку, кроме numpy и opencv.
Ответ №1:
Я нашел способ, который использует цикл for , но он повторяется по кластерам, а не по значениям, поэтому у вас все еще есть преимущества векторизованных операций.
Я настроил свой тестовый пример в соответствии с вашим описанием проблемы следующим образом:
data = np.ndarray((30,2))
data[:10] = np.random.random((10,2)) * 4 60
data[10:20] = np.random.random((10,2)) * 4 70
data[20:30] = np.random.random((10,2)) * 4 80
norms = np.linalg.norm(np.gradient(data,axis=0), axis=1)
И я нашел центры, подобные этому:
threshold = 5 # Specify how much greater the gradient is at the threshold
# get cluster boundaries by finding the indexes at which
# the next gradient norm is significantly smaller
boundaries = np.where(norms[:-1] > norms[1:] threshold)
# add start and end boundaries
boundaries = np.hstack((0,*boundaries,None))
centers = np.ndarray((boundaries.size-1,2)) # empty array for centers
# calulate the mean between each set of boundaries
for i in range(boundaries.size-1):
centers[i] = np.mean(data[boundaries[i]:boundaries[i 1]],axis=0)
print(centers)
Что для моего тестового примера дало мне эти центры:
[[62.34829355 62.46226224]
[71.70243459 72.23661627]
[81.54729804 82.25504445]]
Комментарии:
1. Очень приятно! Кажется эффективным!
Ответ №2:
Если вам разрешено использовать OpenCV, я думаю, что самый простой способ выполнить работу — использовать cv2.kmeans
:
import numpy as np
import cv2
import matplotlib.pyplot as plt
vectors = np.array([[64, 20],
[64, 21],
[65, 19],
[65, 20],
[66, 20],
[68, 19],
[74, 4],
[74, 7],
[74, 8],
[75, 3],
[75, 4]], dtype=np.float32)
criteria = (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
flags = cv2.KMEANS_RANDOM_CENTERS
_, labels, centers = cv2.kmeans(data=vectors,
K=2,
bestLabels=None,
criteria=criteria,
attempts=100,
flags=flags)
plt.scatter(vectors[:, 0], vectors[:, 1], s=30)
for x, y in centers:
plt.scatter(x, y, s=300, marker='s')
Ваши данные представлены в виде маленьких точек, в то время как центры кластеров отображаются в виде больших квадратов.
Комментарии:
1. K — это то, что я пытаюсь найти (вместе с центром для каждого кластера)
2. Если количество кластеров неизвестно априори , вы должны оценить его, например, с помощью метода elbow