Оценка равномерности распределения точек в многомерном пространстве

Рейтинг: 1Ответов: 2Опубликовано: 25.07.2025

Подскажите метод оценки равномерности распределения точек в многомерном пространстве, желательно не использующий сравнение с эталонной последовательностью и применимый к пространствам очень высокой размерности (n >> 1000)

Ответы

▲ 4Принят

Простой подход с параметром кластеризации не прошел. Поэтому предлагаю План Б

План Б.

Математическое ожидание квадрата расстояния между равномерно распределенными точками равно в n-мерном пространстве равно n/6. По предельной теореме эта случайная величина нормально распределена. Чат-ГПТ подсказал мне, что sigma^2 = 7n/180. Поэтому можно использовать оценку D^2 для проверки равномерности распределения

Пусть даны N точек, выберем из них k, для каждой из точек выберем m случайных. Вычислим средний квадрат для каждой из k точек (усреднение по m), затем усредним по k. И сравним c n/6

def mean_square_distance(points, k, m):
    """
    Вычисляет среднее значение квадрата расстояния между случайно выбранными точками.

    Возвращает среднее значение квадрата расстояния между k случайно выбранными точками,
    усредненное по m случайным точкам, выбранным для каждой из k точек.
    """
    points_num = len(points)
    if k > points_num:
        k = points_num
    if m > points_num:
        m = points_num
    indices = np.random.choice(points_num, k, replace=False)
    selected_points = points[indices]
    
    distance_squares = []
    for point in selected_points:
        # Выбираем m случайных точек
        random_indices = np.random.choice(points_num, m, replace=False)
        random_points = points[random_indices]
        
        # Вычисляем расстояния до m случайных точек
        dists = np.linalg.norm(random_points - point, axis=1)
        distance_squares.append(np.mean(dists**2))  # усредняем по m
    
    return np.mean(distance_squares)  # усредняем по k

def p_value_distance_squared(actual_mean_distance_square: float, dimensions: int, sample_size: int) -> float:
    """
    Вычисляет p-value для наблюдаемого среднего квадрата расстояния (D^2)
    между парами точек в dimensions-мерном пространстве.

    Параметры:
        actual_mean_distance_square — наблюдаемое значение среднего D^2
        dimensions  — размерность пространства
        sample_size — число пар точек, по которым усреднено D^2

    Возвращает:
        p-value (вероятность того, что отклонение случайно)
    """

    # Теоретическое среднее и дисперсия D^2
    mu = dimensions / 6
    var = (7 * dimensions) / 180

    sigma_squared = var / sample_size
    # Z-статистика: Z = (X̄ - μ) / σ
    z_stat = (actual_mean_distance_square - mu) ** 2 / sigma_squared
    # p-value для двустороннего критерия
    p_value = 2 * stats.norm.sf(abs(z_stat))
    return p_value

Я попробовал для 1000 точек в пространстве размерностью 10_000.

  • равномерное распределение, выборка 100 точек, для каждой случайные 100 соседей. Средний квадрат расстояний 1665.6168, теоретическое значение: 1666.6667

  • нормальное распределение с дисперсией 0.25, выборка 100 точек, для каждой случайные 100 соседей. Средний квадрат расстояний 1150.9005, теоретическое значение: 1666.6667

  • достоверность гипотезы о равномерном распределении для E[D^2] = 1665.6168 равна 0.9977

  • достоверность гипотезы при E[D^2] = 1150.9005 равна 0.

Ну вот и ответ.

План А.

Точной математической модели у меня нет, скорее соображения.

Если у вас точек не очень много, то вы можете для каждой точке посчитать расстояние до k ближайших соседей. В качестве параметра "кластеризации" можно взять медианное расстояние до k соседей, или среднее расстояние.

Затем посчитайте среднее значение параметра кластеризации и его дисперсию по всем точкам. Если дисперсия около нуля, то точки распределены регулярно. Если же дисперсия сравнима с параметром кластеризации, то скорее всего есть сгущения и разрежения, то есть распередение не совсем равномерное.

Либо же можно посмотреть на гистограмму параметра кластеризации для каждой точки. Если там несколько пиков, то значит несколько кластеров. Если есть чётко выраженный пик, то скорее всего распеределено равномерно (при неравномерном распеределении, например точки кучкуются вокруг некоторого центра, параметр кластеризации будет размазан, у дальних точек будет бОльшее расстояние до соседей, чем у центральных)

UPDATE

Так просто это не сработало, увы.

Вот гистограмма расстояния до ближайшего соседа в 1000-мерном пространстве для равномерно распределенных точек в единичном кубе

введите сюда описание изображения

Среднее расстояние: 12.10480341041847 Дисперсия: 0.013365256264672094

А вот гистограмма для нормально распределенных вокруг центра единичного куба (стандартное отклонение 0.25)

введите сюда описание изображения

Среднее расстояние: 10.019942895837655 Дисперсия расстояний: 0.014727889301131388

Видно, что распределения расстояний до ближайшего соседа в обоих случаях похожи.

Зато если есть кластеры с разными параметрами кластеризации, то гистограмма выглядит убедительно ))

введите сюда описание изображения (два центра с нормально распределенными точками, в распределениях разные дисперсии)

код для равномерного распределения

# Генерация N точек в n-мерном кубе, расчет расстояний до ближайшего соседа, гистограмма, среднее и дисперсия
import numpy as np
from scipy.spatial import cKDTree
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("Qt5Agg")
n = 1000  # размерность пространства
N = 3000  # число точек

# Генерация точек
points = np.random.rand(N, n)
print(f'Сгенерированы {N} точек в {n}-мерном пространстве.')

# Поиск ближайших соседей через KD-дерево
tree = cKDTree(points)
distances, idx = tree.query(points, k=2)  # k=2: первая - сама точка, вторая - ближайший сосед
nearest = distances[:, 1]
print(f'Найдено {len(nearest)} расстояний до ближайших соседей.')

# Гистограмма
plt.hist(nearest, bins=50, edgecolor='black')
plt.xlabel('Расстояние до ближайшего соседа')
plt.ylabel('Частота')
plt.title('Гистограмма расстояний до ближайшего соседа')
plt.show()

# Среднее и дисперсия
mean = np.mean(nearest)
var = np.var(nearest)
print(f'Среднее расстояние: {mean}')
print(f'Дисперсия: {var}')

код для нормально распределенных точек

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import KDTree
import matplotlib

matplotlib.use("Qt5Agg")

n = 1000  # размерность пространства
N = 3000  # число точек

center = np.full(n, 0.5)

# Генерация точек: нормальное распределение вокруг центра, обрезка в пределах куба
points = np.random.normal(loc=center, scale=0.25, size=(N, n))
points = np.clip(points, 0, 1)
print(
    f"Сгенерированы {len(points)} точек в {n}-мерном пространстве"
    "с нормальным распределением вокруг центра единичного куба."
)

# Поиск ближайших соседей
kdtree = KDTree(points)
distances, _ = kdtree.query(points, k=2)
nearest_distances = distances[:, 1]
print(f"Найдено {len(nearest_distances)} расстояний до ближайших соседей.")

# Гистограмма
plt.hist(nearest_distances, bins=50, edgecolor="black")
plt.xlabel("Расстояние до ближайшего соседа")
plt.ylabel("Число точек")
plt.title("Гистограмма расстояний до ближайшего соседа (нормальное распределение)")
plt.show()

# Среднее и дисперсия
mean_dist = np.mean(nearest_distances)
var_dist = np.var(nearest_distances)
print(f"Среднее расстояние: {mean_dist}")
print(f"Дисперсия расстояний: {var_dist}")
▲ 1

Полностью заполняем область пространства одинаковыми n-мерными кубиками по количеству точек. Точки в центрах этих кубиков - равномерное распределение. Область пространства может быть любой формы. Считаем, что точек очень много.

Критерий равномерности - относительный объём занятого пространства. От 1 - равномерное распредление до ~0 - распределение отсутствует (точки слились в одну).

Для расчёта можно использовать такой алгоритм.