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.
- sparsekmeans-0.2/PKG-INFO +12 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/README.md +7 -4
- {sparsekmeans-0.1 → sparsekmeans-0.2}/setup.py +3 -3
- {sparsekmeans-0.1 → sparsekmeans-0.2}/sparsekmeans/sparse_kmeans.py +30 -14
- sparsekmeans-0.2/sparsekmeans.egg-info/PKG-INFO +12 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/sparsekmeans.egg-info/requires.txt +0 -1
- sparsekmeans-0.1/PKG-INFO +0 -22
- sparsekmeans-0.1/sparsekmeans.egg-info/PKG-INFO +0 -22
- {sparsekmeans-0.1 → sparsekmeans-0.2}/LICENSE +0 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/setup.cfg +0 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/sparsekmeans/__init__.py +0 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/sparsekmeans.egg-info/SOURCES.txt +0 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/sparsekmeans.egg-info/dependency_links.txt +0 -0
- {sparsekmeans-0.1 → sparsekmeans-0.2}/sparsekmeans.egg-info/top_level.txt +0 -0
|
@@ -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.
|
|
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
|
-
|
|
67
|
-
|
|
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.
|
|
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"
|
|
20
|
-
description="
|
|
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 =
|
|
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
|
|
348
|
-
#
|
|
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.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
self.labels[far_samples_idx]
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|