From e38d0e74ac11b5e175b02d84dc9fd5615b574d8b Mon Sep 17 00:00:00 2001 From: Larry Gray Date: Sat, 24 Jun 2023 16:19:42 -0600 Subject: [PATCH 1/5] Add ability to add DensityEstimator as a Clusterer --- yellowbrick/cluster/base.py | 4 ++-- yellowbrick/cluster/silhouette.py | 2 +- yellowbrick/utils/types.py | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/yellowbrick/cluster/base.py b/yellowbrick/cluster/base.py index 464be069e..421246d36 100644 --- a/yellowbrick/cluster/base.py +++ b/yellowbrick/cluster/base.py @@ -17,7 +17,7 @@ ## Imports ########################################################################## -from yellowbrick.utils import isclusterer +from yellowbrick.utils import isclusterer, isdensity from yellowbrick.base import ScoreVisualizer from yellowbrick.exceptions import YellowbrickTypeError @@ -41,7 +41,7 @@ class ClusteringScoreVisualizer(ScoreVisualizer): """ def __init__(self, estimator, ax=None, fig=None, force_model=False, **kwargs): - if not force_model and not isclusterer(estimator): + if not force_model and not isclusterer(estimator) and not isdensity(estimator): raise YellowbrickTypeError( "The supplied model is not a clustering estimator; try a " "classifier or regression score visualizer instead!" diff --git a/yellowbrick/cluster/silhouette.py b/yellowbrick/cluster/silhouette.py index 0e5572fa2..abb0f1c29 100644 --- a/yellowbrick/cluster/silhouette.py +++ b/yellowbrick/cluster/silhouette.py @@ -188,7 +188,7 @@ def fit(self, X, y=None, **kwargs): # Compute the number of available clusters from the estimator if hasattr(self.estimator, "n_clusters"): self.n_clusters_ = self.estimator.n_clusters - else: + else: unique_labels = set(labels) n_noise_clusters = 1 if -1 in unique_labels else 0 self.n_clusters_ = len(unique_labels) - n_noise_clusters diff --git a/yellowbrick/utils/types.py b/yellowbrick/utils/types.py index 4fc1a24fc..362a356f7 100644 --- a/yellowbrick/utils/types.py +++ b/yellowbrick/utils/types.py @@ -116,6 +116,25 @@ def is_clusterer(estimator): isclusterer = is_clusterer +def is_density(estimator): + """ + Returns True if the given estimator is a Density Estimator. + + Parameters + ---------- + estimator : class or instance + The object to test if it is a Scikit-Learn clusterer, especially a + Scikit-Learn estimator or Yellowbrick visualizer + """ + + # Test the _estimator_type property + return getattr(estimator, "_estimator_type", None) == "DensityEstimator" + + +# Alias for closer name to isinstance and issubclass +isdensity = is_density + + def is_gridsearch(estimator): """ Returns True if the given estimator is a clusterer. From 9737dbadcdac8c758c0918121b2cb31cb4130ebe Mon Sep 17 00:00:00 2001 From: Larry Gray Date: Sat, 29 Jul 2023 07:16:01 -0600 Subject: [PATCH 2/5] Set self.n_clusters equal to n_components if estimator has it as an Attribute --- yellowbrick/cluster/silhouette.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yellowbrick/cluster/silhouette.py b/yellowbrick/cluster/silhouette.py index abb0f1c29..b5cad2ba9 100644 --- a/yellowbrick/cluster/silhouette.py +++ b/yellowbrick/cluster/silhouette.py @@ -188,6 +188,8 @@ def fit(self, X, y=None, **kwargs): # Compute the number of available clusters from the estimator if hasattr(self.estimator, "n_clusters"): self.n_clusters_ = self.estimator.n_clusters + elif hasattr(self.estimator, "n_components"): + self.n_clusters_ = self.estimator.n_components else: unique_labels = set(labels) n_noise_clusters = 1 if -1 in unique_labels else 0 From 1b4e35169da529d35536b9fb57e269ff1b97585a Mon Sep 17 00:00:00 2001 From: Larry Gray Date: Sat, 29 Jul 2023 14:46:39 -0600 Subject: [PATCH 3/5] Test usage of Density Estimator in Silhouette visualizer --- ...integrated_gaussian_mixture_silhouette.png | Bin 0 -> 13197 bytes tests/test_cluster/test_silhouette.py | 22 ++++++++++++++++++ yellowbrick/cluster/silhouette.py | 6 ++--- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/baseline_images/test_cluster/test_silhouette/test_integrated_gaussian_mixture_silhouette.png diff --git a/tests/baseline_images/test_cluster/test_silhouette/test_integrated_gaussian_mixture_silhouette.png b/tests/baseline_images/test_cluster/test_silhouette/test_integrated_gaussian_mixture_silhouette.png new file mode 100644 index 0000000000000000000000000000000000000000..47f8cbc9db57894f64427b8495da1beedb49987c GIT binary patch literal 13197 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 literal 0 HcmV?d00001 diff --git a/tests/test_cluster/test_silhouette.py b/tests/test_cluster/test_silhouette.py index 0c68a4868..2acce5e6f 100644 --- a/tests/test_cluster/test_silhouette.py +++ b/tests/test_cluster/test_silhouette.py @@ -25,6 +25,7 @@ from sklearn.datasets import make_blobs from sklearn.cluster import KMeans, MiniBatchKMeans from sklearn.cluster import SpectralClustering, AgglomerativeClustering +from sklearn.mixture import GaussianMixture from unittest import mock from tests.base import VisualTestCase @@ -84,6 +85,27 @@ def test_integrated_mini_batch_kmeans_silhouette(self): self.assert_images_similar(visualizer, remove_legend=True) + @pytest.mark.xfail(sys.platform == "win32", reason="images not close on windows") + def test_integrated_gaussian_mixture_silhouette(self): + """ + Test Density Estimator works with silhouette visualizer + """ + # NOTE see #182: cannot use occupancy dataset because of memory usage + + # Generate a blobs data set + X, y = make_blobs( + n_samples=1000, n_features=12, centers=8, shuffle=False, random_state=0 + ) + + fig = plt.figure() + ax = fig.add_subplot() + + visualizer = SilhouetteVisualizer(GaussianMixture(n_components=5, random_state=0), ax=ax) + visualizer.fit(X) + visualizer.finalize() + + self.assert_images_similar(visualizer, remove_legend=True) + @pytest.mark.skip(reason="no negative silhouette example available yet") def test_negative_silhouette_score(self): """ diff --git a/yellowbrick/cluster/silhouette.py b/yellowbrick/cluster/silhouette.py index b5cad2ba9..0490fcd0e 100644 --- a/yellowbrick/cluster/silhouette.py +++ b/yellowbrick/cluster/silhouette.py @@ -188,9 +188,9 @@ def fit(self, X, y=None, **kwargs): # Compute the number of available clusters from the estimator if hasattr(self.estimator, "n_clusters"): self.n_clusters_ = self.estimator.n_clusters - elif hasattr(self.estimator, "n_components"): - self.n_clusters_ = self.estimator.n_components - else: + elif hasattr(self.estimator, "n_components"): + self.n_clusters_ = self.estimator.n_components + else: unique_labels = set(labels) n_noise_clusters = 1 if -1 in unique_labels else 0 self.n_clusters_ = len(unique_labels) - n_noise_clusters From e53ffb9c524e350941388fee91459ce2e1fc97ba Mon Sep 17 00:00:00 2001 From: Larry Gray Date: Sat, 29 Jul 2023 15:11:07 -0600 Subject: [PATCH 4/5] dedent line --- yellowbrick/cluster/silhouette.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yellowbrick/cluster/silhouette.py b/yellowbrick/cluster/silhouette.py index 0490fcd0e..ada4a6a45 100644 --- a/yellowbrick/cluster/silhouette.py +++ b/yellowbrick/cluster/silhouette.py @@ -189,7 +189,7 @@ def fit(self, X, y=None, **kwargs): if hasattr(self.estimator, "n_clusters"): self.n_clusters_ = self.estimator.n_clusters elif hasattr(self.estimator, "n_components"): - self.n_clusters_ = self.estimator.n_components + self.n_clusters_ = self.estimator.n_components else: unique_labels = set(labels) n_noise_clusters = 1 if -1 in unique_labels else 0 From 78c8c6abe5e1dac15911d560f627993c158b79b3 Mon Sep 17 00:00:00 2001 From: Larry Gray Date: Sat, 29 Jul 2023 15:28:08 -0600 Subject: [PATCH 5/5] Fix Linting and tab/spacing --- tests/test_cluster/test_silhouette.py | 4 +++- yellowbrick/cluster/silhouette.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_cluster/test_silhouette.py b/tests/test_cluster/test_silhouette.py index 2acce5e6f..4ab8f0a2d 100644 --- a/tests/test_cluster/test_silhouette.py +++ b/tests/test_cluster/test_silhouette.py @@ -100,7 +100,9 @@ def test_integrated_gaussian_mixture_silhouette(self): fig = plt.figure() ax = fig.add_subplot() - visualizer = SilhouetteVisualizer(GaussianMixture(n_components=5, random_state=0), ax=ax) + visualizer = SilhouetteVisualizer( + GaussianMixture(n_components=5, random_state=0), ax=ax + ) visualizer.fit(X) visualizer.finalize() diff --git a/yellowbrick/cluster/silhouette.py b/yellowbrick/cluster/silhouette.py index ada4a6a45..01bc081e5 100644 --- a/yellowbrick/cluster/silhouette.py +++ b/yellowbrick/cluster/silhouette.py @@ -189,7 +189,7 @@ def fit(self, X, y=None, **kwargs): if hasattr(self.estimator, "n_clusters"): self.n_clusters_ = self.estimator.n_clusters elif hasattr(self.estimator, "n_components"): - self.n_clusters_ = self.estimator.n_components + self.n_clusters_ = self.estimator.n_components else: unique_labels = set(labels) n_noise_clusters = 1 if -1 in unique_labels else 0