sparsekmeans 0.1__tar.gz → 0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.1
2
+ Name: sparsekmeans
3
+ Version: 0.2
4
+ Home-page: https://github.com/cjlin1/sparsekmeans
5
+ Author: Chih-Jen Lin, He-Zhe Lin, Khoi Nguyen Pham Dang
6
+ Author-email: cjlin@csie.ntu.edu.tw
7
+ License: MIT
8
+ Requires-Python: >=3.10
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy
11
+ Requires-Dist: python-graphblas
12
+ Requires-Dist: scipy
@@ -12,7 +12,11 @@ pip install sparsekmeans
12
12
 
13
13
  ## Usage
14
14
 
15
- We support two popular K-means algorithms: Lloyd's method and Elkan's method. Use the following steps to cluster a data set:
15
+ We support two popular K-means algorithms: Lloyd's method and Elkan's method. While both require distance calculations between data points and centroids, Elkan's method reduces some computations by exploiting information from the previous iteration, specifically using the triangle inequality to maintain two distance-related bounds.
16
+
17
+ When the data set is small (e.g., the number of samples is less than 10,000), it is recommended to use Lloyd's. Lloyd's relies primarily on sparse matrix multiplications, whereas Elkan's mechanism to reduce distance calculations may cause non-negligible overhead for small problems. Conversely, for large data sets or the situation of a huge number of clusters (K), Elkan's method may significantly outperforms Lloyd's.
18
+
19
+ Use the following steps to cluster a data set:
16
20
 
17
21
  ```
18
22
  from sparsekmeans import LloydKmeans, ElkanKmeans
@@ -63,9 +67,8 @@ import urllib.request
63
67
  import bz2
64
68
 
65
69
  with urllib.request.urlopen("https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/news20_tfidf_train.svm.bz2") as r:
66
- r = bz2.BZ2File(r)
67
- r = io.TextIOWrapper(io.BufferedReader(r))
68
- _, X = svm_read_problem(r, return_scipy=True)
70
+ with bz2.open(r, 'rt') as f:
71
+ _, X = svm_read_problem(f, return_scipy=True)
69
72
 
70
73
  kmeans = LloydKmeans(n_clusters=100)
71
74
  labels = kmeans.fit(X)
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  PACKAGE_DIR = ["sparsekmeans"]
4
4
  PACKAGE_NAME = "sparsekmeans"
5
- VERSION = "0.1"
5
+ VERSION = "0.2"
6
6
 
7
7
 
8
8
  # license parameters
@@ -16,8 +16,8 @@ setup(
16
16
  packages=PACKAGE_DIR,
17
17
  version=VERSION,
18
18
  python_requires=">=3.10",
19
- install_requires=["numpy","python-graphblas","scipy","libsvm-official>=3.36.0"],
20
- description="A package for efficient K-means clustering on sparse dataset",
19
+ install_requires=["numpy","python-graphblas","scipy"],
20
+ description="",
21
21
  long_description_content_type="",
22
22
  author="Chih-Jen Lin, He-Zhe Lin, Khoi Nguyen Pham Dang",
23
23
  author_email="cjlin@csie.ntu.edu.tw",
@@ -3,6 +3,7 @@ import sys
3
3
  import numpy as np
4
4
  import scipy.sparse as sparse
5
5
  import time
6
+ import warnings
6
7
  from typing import Union
7
8
 
8
9
  import graphblas as gb
@@ -44,7 +45,7 @@ def check_centroids_density(centroids: gb.Matrix):
44
45
  is_centroid_dense = False
45
46
  centroids_density = centroids.nvals / (centroids.nrows * centroids.ncols)
46
47
  if centroids_density > gamma:
47
- is_centroid_dense = False
48
+ is_centroid_dense = True
48
49
  centroids(mask=~centroids.S) << 0
49
50
 
50
51
  return centroids, is_centroid_dense
@@ -58,7 +59,7 @@ def predict_labels(X: gb.Matrix, centroids: gb.Matrix, is_centroid_dense: bool,
58
59
  c_squared_norms = squared_row_norms(centroids, n_threads)
59
60
  XCt = Matrix(dtypes.FP64, nrows=n_samples, ncols=n_clusters)
60
61
 
61
- # For X * Ct, we use CSR x (fullc or CSC) for the best efficiency.
62
+ # For X * Ct, we use CSR (for X format) x (fullc or CSC for C format) for the best efficiency.
62
63
  # We use row format to store centroids in other places, so must convert them here to column format.
63
64
  if is_centroid_dense:
64
65
  centroids = centroids.ss.export("fullc")
@@ -197,6 +198,13 @@ class SparseKmeans:
197
198
  n_local_trials = 2 + int(np.log(n_clusters))
198
199
 
199
200
  for j in range(1, n_clusters):
201
+
202
+ if current_potential < 1e-12:
203
+ warnings.warn(f"The specified number of clusters is larger than the number of distinct points.\nChange the number of clusters to the number of distinct points.", category=UserWarning)
204
+ centroids = centroids[:j, :]
205
+ self.n_clusters = j
206
+ break
207
+
200
208
  # Choose centroids candidates by sampling with probability smallest_sq_dist/current_potential, where
201
209
  # current_potential is the sum of smallest_sq_dist
202
210
  rand_vals = self.random_state.uniform(size=n_local_trials) * current_potential
@@ -241,7 +249,6 @@ class SparseKmeans:
241
249
  # Allowing the kmeans procedure to allocate and initialize internal variables
242
250
  self._setup_internal_state(X)
243
251
 
244
- # Calculate the mean feature variance
245
252
  self.mean_feature_variance = self._cal_feature_variance(X)
246
253
  if self.mean_feature_variance <= sys.float_info.min:
247
254
  return
@@ -344,20 +351,29 @@ class SparseKmeans:
344
351
  empty_clusters = np.where(cluster_sizes == 0)[0]
345
352
  n_empty = len(empty_clusters)
346
353
 
347
- # To handle empty clusters, we follow the setting in scikit-learn
348
- # We consider points with the largest distances to their assigned clusters, and reassign them to the empty clusters
354
+ # To handle empty clusters, we consider points with the largest distances to their assigned clusters, and reassign them to the empty clusters
355
+ # If a selected point belongs to a cluster with only one point, we skip it and move on to the next furthest point.
349
356
  # Each empty cluster gets one point
350
357
  # We also need to update cluster_sizes
351
358
  if n_empty > 0:
352
- far_samples_idx = np.argpartition(self.sample_centroids_closest_distance, -n_empty)[
353
- : -n_empty - 1 : -1
354
- ]
355
- affected_clusters, counts = np.unique(
356
- self.labels[far_samples_idx], return_counts=True
357
- )
358
- cluster_sizes[affected_clusters] -= counts
359
- cluster_sizes[empty_clusters] = 1
360
- self.labels[far_samples_idx] = empty_clusters
359
+ far_samples_idx = np.argsort(self.sample_centroids_closest_distance)[:: -1]
360
+ i, j = 0, 0
361
+
362
+ while i < n_samples - n_empty and j < n_empty:
363
+ origin_cluster = self.labels[far_samples_idx[i]]
364
+ i += 1
365
+
366
+ if cluster_sizes[origin_cluster] == 1:
367
+ continue
368
+
369
+ target_empty_cluster = empty_clusters[j]
370
+ cluster_sizes[origin_cluster] -= 1
371
+ cluster_sizes[target_empty_cluster] = 1
372
+ self.labels[far_samples_idx[i]] = target_empty_cluster
373
+ j += 1
374
+
375
+ if i == n_samples - n_empty:
376
+ raise ValueError("Not enough distinct points to assign to empty clusters.")
361
377
 
362
378
  # Get the weight of each sample in its corresponding cluster
363
379
  weights = 1 / cluster_sizes
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.1
2
+ Name: sparsekmeans
3
+ Version: 0.2
4
+ Home-page: https://github.com/cjlin1/sparsekmeans
5
+ Author: Chih-Jen Lin, He-Zhe Lin, Khoi Nguyen Pham Dang
6
+ Author-email: cjlin@csie.ntu.edu.tw
7
+ License: MIT
8
+ Requires-Python: >=3.10
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy
11
+ Requires-Dist: python-graphblas
12
+ Requires-Dist: scipy
@@ -1,4 +1,3 @@
1
1
  numpy
2
2
  python-graphblas
3
3
  scipy
4
- libsvm-official>=3.36.0
sparsekmeans-0.1/PKG-INFO DELETED
@@ -1,22 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: sparsekmeans
3
- Version: 0.1
4
- Summary: A package for efficient K-means clustering on sparse dataset
5
- Home-page: https://github.com/cjlin1/sparsekmeans
6
- Author: Chih-Jen Lin, He-Zhe Lin, Khoi Nguyen Pham Dang
7
- Author-email: cjlin@csie.ntu.edu.tw
8
- License: MIT
9
- Requires-Python: >=3.10
10
- License-File: LICENSE
11
- Requires-Dist: numpy
12
- Requires-Dist: python-graphblas
13
- Requires-Dist: scipy
14
- Requires-Dist: libsvm-official>=3.36.0
15
- Dynamic: author
16
- Dynamic: author-email
17
- Dynamic: home-page
18
- Dynamic: license
19
- Dynamic: license-file
20
- Dynamic: requires-dist
21
- Dynamic: requires-python
22
- Dynamic: summary
@@ -1,22 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: sparsekmeans
3
- Version: 0.1
4
- Summary: A package for efficient K-means clustering on sparse dataset
5
- Home-page: https://github.com/cjlin1/sparsekmeans
6
- Author: Chih-Jen Lin, He-Zhe Lin, Khoi Nguyen Pham Dang
7
- Author-email: cjlin@csie.ntu.edu.tw
8
- License: MIT
9
- Requires-Python: >=3.10
10
- License-File: LICENSE
11
- Requires-Dist: numpy
12
- Requires-Dist: python-graphblas
13
- Requires-Dist: scipy
14
- Requires-Dist: libsvm-official>=3.36.0
15
- Dynamic: author
16
- Dynamic: author-email
17
- Dynamic: home-page
18
- Dynamic: license
19
- Dynamic: license-file
20
- Dynamic: requires-dist
21
- Dynamic: requires-python
22
- Dynamic: summary
File without changes
File without changes