Как работать с кластеризацией в Python

Как работать с кластеризацией в Python

Aspose.PDF FOSS for Python поставляет небольшую, универсальную иерархическую агломеративную утилиту кластеризации — независимую от любой отдельной операции с PDF — для группировки связанных объектов DataPoint в экземпляры Cluster и вычисления центроида кластера. Вы решаете, что представляет собой каждая точка данных, и задаёте её координаты самостоятельно; утилита не заполняет их автоматически. Библиотека устанавливается командой ниже.

Пошаговое руководство

Шаг 1: Установить пакет

Установите пакет Aspose.PDF FOSS:

git clone https://github.com/aspose-pdf-foss/Aspose-PDF-FOSS-for-Python.git
cd Aspose-PDF-FOSS-for-Python
pip install -e .

Проверьте установку, импортировав Cluster и выведя сообщение подтверждения:

from aspose_pdf import Cluster
print("aspose-pdf-foss-for-python is ready.")

Шаг 2: Импортировать необходимые классы

Импортируйте три класса, составляющих утилиту кластеризации:

from aspose_pdf import Cluster, ClusterCollection, DataPoint

Шаг 3: Создать точки данных

Объект DataPoint связывает строку name со списком value из чисел с плавающей точкой — координаты или вектор признаков, которые вы считаете значимыми для своей логики группировки:

from aspose_pdf import DataPoint

point_a = DataPoint("item-1", [12.0, 45.0])
point_b = DataPoint("item-2", [13.5, 44.0])
point_c = DataPoint("item-3", [98.0, 5.0])

print(point_a.name, point_a.value)

DataPoint не связывает value с какой-либо конкретной концепцией PDF — координаты страниц, метрики шрифтов или любой другой числовой вектор признаков работают одинаково хорошо.


Шаг 4: Сгруппировать точки данных в кластер

Передайте полный список членов конструктору Cluster. После создания Cluster нет метода для добавления элементов, поэтому определите членство заранее:

from aspose_pdf import Cluster

nearby = Cluster([point_a, point_b])
print(nearby.count)              # 2
print(nearby.contains(point_a))  # True

contains() сравнивается по равенству — DataPoint определяет равенство по сочетанию name и value, поэтому другой экземпляр DataPoint с тем же именем и значением также считается содержащимся.


Шаг 5: Вычислить центр кластера

DataPoint.get_centroid(cluster) — это статический метод, который усредняет каждое измерение каждого DataPoint.value в кластере и возвращает результат в виде нового DataPoint с именем "centroid":

from aspose_pdf import DataPoint

centroid = DataPoint.get_centroid(nearby)
print(centroid.name)   # "centroid"
print(centroid.value)  # per-dimension average across the cluster

Каждый список DataPoint.value в кластере должен иметь одинаковую длину — get_centroid усредняет позицию за позицией и не проверяет совпадение длины. Вызов его для Cluster.empty() или любого кластера с count == 0 вызывает ValueError.


Шаг 6: Клонировать кластер или повторно использовать общий пустой экземпляр

clone() возвращает независимый Cluster, содержащий те же элементы; Cluster.empty() всегда возвращает один и тот же кэшированный пустой экземпляр кластера вместо создания нового:

from aspose_pdf import Cluster

backup = nearby.clone()
print(backup is nearby)          # False -- independent copy
print(backup.contains(point_a))  # True -- same items

placeholder = Cluster.empty()
print(placeholder.count)         # 0

Шаг 7: Объединить несколько кластеров в ClusterCollection

ClusterCollection объединяет несколько объектов Cluster за единым контейнером, поддерживающим len():

from aspose_pdf import Cluster, ClusterCollection

outliers = Cluster([point_c])
collection = ClusterCollection([nearby, outliers])
print(len(collection))  # 2

ClusterCollection не предоставляет метод для извлечения своих кластеров — сохраните ссылку на список, переданный в конструктор, если вам понадобится позже перебирать кластеры.

Распространённые проблемы и решения

Нет метода добавить элемент к существующему Cluster

Cluster принимает список членов только во время создания (Cluster(items)). Сначала сформируйте полный список объектов DataPoint, затем создайте Cluster — после этого нет add() или append(), которые можно было бы вызвать.

DataPoint.get_centroid() raises ValueError

Это происходит, когда переданный кластер пуст (count == 0), включая Cluster.empty(). Проверьте cluster.count перед вызовом get_centroid или обработайте исключение, если пустой кластер является допустимым вводом в вашем рабочем процессе.

Значения центроидов выглядят неверно

get_centroid суммирует каждую позицию DataPoint.value по позициям и делит на количество точек — он не проверяет, что каждый список value в кластере имеет одинаковую длину. Смешивание объектов DataPoint с несоответствующей размерностью приводит к неверному среднему значению без предупреждения, а не к исключению.

Expecting Cluster.empty() создать новый пустой кластер каждый раз

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

Ожидая, что библиотека автоматически группирует точки

DataPoint, Cluster и ClusterCollection — это структуры данных для представления точек, кластеров и вычисления центроида — модуль не содержит функции связывания или расстояния, определяющей, какие точки принадлежат вместе. Ваш собственный код определяет принадлежность к кластеру перед созданием Cluster.

Часто задаваемые вопросы

Автоматически ли этот модуль группирует точки данных по расстоянию?

Нет. Cluster, DataPoint и ClusterCollection являются строительными блоками для представления уже определённых группировок и вычисления центроида — встроенного шага агрегации или назначения на основе расстояния нет.

В чём разница между Cluster() и Cluster.empty()?

Cluster(items=None) создает новый экземпляр каждый раз, когда вызывается. Cluster.empty() — это метод класса, который возвращает один и тот же кэшированный, общий пустой кластер при каждом вызове.

Могу ли я добавить элементы в Cluster после его создания?

Нет. Cluster раскрывает только contains(), clone() и свойство count помимо конструктора — состав фиксируется после выполнения конструктора.

Работает ли get_centroid() с любым количеством измерений?

Да, при условии, что каждый список DataPoint.value в кластере имеет одинаковую длину. get_centroid усредняет каждое измерение независимо и возвращает результат в виде нового DataPoint.

Что ClusterCollection добавляет к обычному списку кластеров Python?

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

См. также:

 Русский