How to Work with Clustering in Python
Aspose.PDF FOSS for Python bundles a small, general-purpose hierarchical agglomerative clustering utility — independent of any single PDF operation — for grouping related DataPoint objects into Cluster instances and computing a cluster’s centroid. You decide what each data point represents and supply its coordinates yourself; the utility does not populate them automatically. The library is installed from PyPI with pip.
Step-by-Step Guide
Step 1: Install the Package
Install the Aspose.PDF FOSS package from PyPI:
pip install aspose-pdf-foss-for-pythonVerify the installation by importing Cluster and printing a confirmation message:
from aspose_pdf import Cluster
print("aspose-pdf-foss-for-python is ready.")Step 2: Import Required Classes
Import the three classes that make up the clustering utility:
from aspose_pdf import Cluster, ClusterCollection, DataPointStep 3: Create Data Points
A DataPoint pairs a name string with a value list of floats — the coordinates or feature vector you decide are meaningful for your own grouping logic:
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 does not tie value to any particular PDF concept — page coordinates, font metrics, or any other numeric feature vector all work equally well.
Step 4: Group Data Points into a Cluster
Pass the full membership list to the Cluster constructor. There is no method to add items to a Cluster after it is built, so decide membership first:
from aspose_pdf import Cluster
nearby = Cluster([point_a, point_b])
print(nearby.count) # 2
print(nearby.contains(point_a)) # Truecontains() matches by equality — DataPoint defines equality on name and value together, so a different DataPoint instance with the same name and value is also reported as contained.
Step 5: Compute the Cluster Centroid
DataPoint.get_centroid(cluster) is a static method that averages each dimension of every DataPoint.value in the cluster and returns the result as a new DataPoint named "centroid":
from aspose_pdf import DataPoint
centroid = DataPoint.get_centroid(nearby)
print(centroid.name) # "centroid"
print(centroid.value) # per-dimension average across the clusterEvery DataPoint.value list in the cluster must have the same length — get_centroid averages position by position and does not validate that the lengths match. Calling it on Cluster.empty(), or any cluster with count == 0, raises ValueError.
Step 6: Clone a Cluster or Reuse the Shared Empty Instance
clone() returns an independent Cluster holding the same items; Cluster.empty() always returns the same cached empty-cluster instance rather than allocating a new one:
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) # 0Step 7: Group Multiple Clusters into a ClusterCollection
ClusterCollection bundles several Cluster objects behind a single len()-aware container:
from aspose_pdf import Cluster, ClusterCollection
outliers = Cluster([point_c])
collection = ClusterCollection([nearby, outliers])
print(len(collection)) # 2ClusterCollection does not expose a method to retrieve its clusters back out — keep a reference to the list you pass into the constructor if you need to iterate the clusters afterward.
Common Issues and Fixes
There is no method to add an item to an existing Cluster
Cluster only accepts its membership list at construction time (Cluster(items)). Build the complete list of DataPoint objects first, then construct the Cluster — there is no add() or append() to call afterward.
DataPoint.get_centroid() raises ValueError
This happens when the cluster passed in is empty (count == 0), including Cluster.empty(). Check cluster.count before calling get_centroid, or handle the exception if an empty cluster is a valid input in your workflow.
Centroid values look wrong
get_centroid sums each DataPoint.value position by position and divides by the number of points — it does not check that every value list in the cluster has the same length. Mixing DataPoint objects with mismatched dimensionality produces an incorrect average silently rather than raising.
Expecting Cluster.empty() to create a fresh empty cluster each time
Cluster.empty() is a singleton accessor — every call returns the same cached instance. Use Cluster() (with no arguments) instead if you need a distinct, independent empty cluster.
Expecting the library to group points automatically
DataPoint, Cluster, and ClusterCollection are data structures for representing points, clusters, and a centroid calculation — the module does not include a linkage or distance function that decides which points belong together. Your own code determines cluster membership before constructing a Cluster.
Frequently Asked Questions
Does this module automatically group data points by distance?
No. Cluster, DataPoint, and ClusterCollection are building blocks for representing already-decided groupings and computing a centroid — there is no built-in agglomeration or distance-based assignment step.
What’s the difference between Cluster() and Cluster.empty()?
Cluster(items=None) creates a new instance every time it is called. Cluster.empty() is a classmethod that returns the same cached, shared empty-cluster instance on every call.
Can I add items to a Cluster after creating it?
No. Cluster only exposes contains(), clone(), and the count property beyond construction — membership is fixed once the constructor runs.
Does get_centroid() work with any number of dimensions?
Yes, as long as every DataPoint.value list in the cluster has the same length. get_centroid averages each dimension independently and returns the result as a new DataPoint.
What does ClusterCollection add beyond a plain Python list of clusters?
Mainly a typed container with len() support. It does not add iteration, lookup, or merging behavior beyond what you already have in the list you constructed it from.