Как выполнить кластер с весами / плотностью в python? Что-то вроде kmeans с весами?

мои данные таковы:

powerplantname, latitude, longitude, powergenerated
A, -92.3232, 100.99, 50
B, <lat>, <long>, 10
C, <lat>, <long>, 20
D, <lat>, <long>, 40
E, <lat>, <long>, 5

Я хочу иметь возможность кластеризировать данные в N количество кластеров (скажем, 3). Обычно я бы использовал kmeans:

import numpy as np

import matplotlib.pyplot as plt
from scipy.cluster.vq import kmeans2, whiten
coordinates= np.array([
           [lat, long],
           [lat, long],
            ...
           [lat, long]
           ])
x, y = kmeans2(whiten(coordinates), 3, iter = 20)  
plt.scatter(coordinates[:,0], coordinates[:,1], c=y);
plt.show()

проблема в том, что это не учитывает никакого взвешивания (в этом случае мое значение powergenerated), я хочу, чтобы в идеале мои кластеры учитывали значение "powergenerated", пытаясь сохранить кластеры не только пространственно близко, но и близко к относительно равному общему powergenerated.

должен ли я делать это с kmeans (или каким-либо другим методом)? Или есть что-то еще, что я должен использовать для этой проблемы, что было бы лучше?

1 ответов


или есть что-то еще, что я должен использовать для этой проблемы, что было бы лучше?

чтобы одновременно учитывать географическое расстояние между центральными центрами и генерируемую мощность, вы должны определить правильную метрику. Функция ниже вычисляет расстояние между двумя точками на поверхности Земли от их широты и долготы через haversine формула и добавляет абсолютное значение генерируемой мощности разница умножается на весовой коэффициент. Значение веса определяет относительное влияние расстояния и разности мощностей в процессе кластеризации.

import numpy as np

def custom_metric(central_1, central_2, weight=1):
    lat1, lng1, pow1 = central_1
    lat2, lng2, pow2 = central_2

    lat1, lat2, lng1, lng2 = np.deg2rad(np.asarray([lat1, lat2, lng1, lng2]))

    dlat = lat2 - lat1
    dlng = lng2 - lng1

    h = (1 - np.cos(dlat))/2. + np.cos(lat1)*np.cos(lat2)*(1 - np.cos(dlng))/2.
    km = 2*6371*np.arcsin(np.sqrt(h))

    MW = np.abs(pow2 - pow1)

    return km + weight*MW

должен ли я делать это с kmeans (или каким-либо другим методом)?

к сожалению, в текущей реализации это составляющей kmeans2 и пакет scikit-узнать KMeans поддерживает только евклидово расстояние. Альтернативный метод будет состоять в выполнении иерархическая кластеризация через пакет кластеризации SciPy для группировки центров в соответствии с только что определенной метрикой.

демо

давайте сначала сгенерируем фиктивные данные, а именно векторы функций для 8 центров со случайными значениями:

N = 8
np.random.seed(0)
lat = np.random.uniform(low=-90, high=90, size=N)
lng = np.random.uniform(low=-180, high=180, size=N)
power = np.random.randint(low=5, high=50, size=N)
data = np.vstack([lat, lng, power]).T

содержание переменной data данный фрагмент выше выглядит следующим образом:

array([[   8.7864,  166.9186,   21.    ],
       [  38.7341,  -41.9611,   10.    ],
       [  18.4974,  105.021 ,   20.    ],
       [   8.079 ,   10.4022,    5.    ],
       [ -13.7421,   24.496 ,   23.    ],
       [  26.2609,  153.2148,   40.    ],
       [ -11.2343, -154.427 ,   29.    ],
       [  70.5191, -148.6335,   34.    ]])

чтобы разделить эти данные на три разные группы, мы должны пройти data и custom_metric до linkage функция (проверьте docs чтобы узнать больше о параметре method), а затем передайте возвращенную матрицу связей в