Skip to content

Instantly share code, notes, and snippets.

@clintval
Last active February 23, 2021 14:36
Show Gist options
  • Save clintval/e9afc246e77f6488cda79f86e4d37148 to your computer and use it in GitHub Desktop.
Save clintval/e9afc246e77f6488cda79f86e4d37148 to your computer and use it in GitHub Desktop.
k-means silhouette analysis using sklearn and matplotlib on Iris data.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from matplotlib.colors import colorConverter
__license__ = 'MIT'
__author__ = 'clintval'
def darken_rgb(rgb, p):
"""
Will darken an rgb value by p percent
"""
assert 0 <= p <= 1, "Proportion must be [0, 1]"
return [int(x * (1 - p)) for x in rgb]
def lighten_rgb(rgb, p):
"""
Will lighten an rgb value by p percent
"""
assert 0 <= p <= 1, "Proportion must be [0, 1]"
return [int((255 - x) * p + x) for x in rgb]
def is_luminous(rgb):
new_color = []
for c in rgb:
if c <= 0.03928:
new_color.append(c / 12.92)
else:
new_color.append(((c + 0.055) / 1.055) ** 2.4)
L = sum([x * y for x, y in zip([0.2126, 0.7152, 0.0722], new_color)])
return True if L < 0.179 else False
def kmeans_plot(X, y, cluster_centers, ax=None):
import matplotlib.patheffects as path_effects
from sklearn.metrics.pairwise import pairwise_distances_argmin_min
if ax is None:
ax = plt.gca()
colors = cm.spectral(y.astype(float) / len(cluster_centers))
ax.scatter(*list(zip(*X)), lw=0, c=colors, s=30)
offset = max(list(zip(*cluster_centers))[0]) * 0.2
for i, cluster in enumerate(cluster_centers):
index, _ = pairwise_distances_argmin_min(cluster.reshape(1, -1), Y=X)
cluster_color = colorConverter.to_rgb(colors[index[0]])
if is_luminous(cluster_color) is False:
cluster_color = darken_rgb(cluster_color, 0.35)
label = ax.text(x=cluster[0] + offset,
y=cluster[1],
s='{:d}'.format(i + 1),
color=cluster_color)
label.set_path_effects([path_effects.Stroke(lw=2, foreground='white'),
path_effects.Normal()])
limit = max(*ax.get_xlim(), *ax.get_xlim())
ax.set_xlim(0, limit)
ax.set_ylim(0, limit)
ax.set_xlabel("Feature space for the 1st feature")
ax.set_ylabel("Feature space for the 2nd feature")
return ax
def silhouette_plot(X, y, n_clusters, ax=None):
from sklearn.metrics import silhouette_samples, silhouette_score
if ax is None:
ax = plt.gca()
# Compute the silhouette scores for each sample
silhouette_avg = silhouette_score(X, y)
sample_silhouette_values = silhouette_samples(X, y)
y_lower = padding = 2
for i in range(n_clusters):
# Aggregate the silhouette scores for samples belonging to
ith_cluster_silhouette_values = sample_silhouette_values[y == i]
ith_cluster_silhouette_values.sort()
size_cluster_i = ith_cluster_silhouette_values.shape[0]
y_upper = y_lower + size_cluster_i
color = cm.spectral(float(i) / n_clusters)
ax.fill_betweenx(np.arange(y_lower, y_upper),
0,
ith_cluster_silhouette_values,
facecolor=color,
edgecolor=color,
alpha=0.7)
# Label the silhouette plots with their cluster numbers at the middle
ax.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i + 1))
# Compute the new y_lower for next plot
y_lower = y_upper + padding
ax.set_xlabel("The silhouette coefficient values")
ax.set_ylabel("Cluster label")
# The vertical line for average silhoutte score of all the values
ax.axvline(x=silhouette_avg, c='r', alpha=0.8, lw=0.8, ls='-')
ax.annotate('Average',
xytext=(silhouette_avg, y_lower * 1.025),
xy=(0, 0),
ha='center',
alpha=0.8,
c='r')
ax.set_yticks([]) # Clear the yaxis labels / ticks
ax.set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])
ax.set_ylim(0, y_upper + 1)
ax.set_xlim(-0.075, 1.0)
return ax
@shvthr
Copy link

shvthr commented Apr 26, 2019

Hi,
I continuously get error running this code:

`~\AppData\Roaming\Python\Python36\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer, *args, **kwargs)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe)
   2626             renderer.stop_rasterizing()
   2627 
-> 2628         mimage._draw_list_compositing_images(renderer, self, artists)
   2629 
   2630         renderer.close_group('axes')

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136     if not_composite or not has_images:
    137         for a in artists:
--> 138             a.draw(renderer)
    139     else:
    140         # Composite any adjacent images together

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer, *args, **kwargs)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\text.py in draw(self, renderer)
    756                     textrenderer.draw_text(gc, x, y, clean_line,
    757                                            textobj._fontproperties, angle,
--> 758                                            ismath=ismath, mtext=mtext)
    759 
    760         gc.restore()

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\backend_bases.py in draw_text(self, gc, x, y, s, prop, angle, ismath, mtext)
    546         """
    547 
--> 548         self._draw_text_as_path(gc, x, y, s, prop, angle, ismath)
    549 
    550     def _get_text_path_transform(self, x, y, s, prop, angle, ismath):

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath)
    152         color = gc.get_rgb()
    153         gc.set_linewidth(0.0)
--> 154         self.draw_path(gc, path, transform, rgbFace=color)
    155 
    156     def __getattribute__(self, name):

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in draw_path(self, gc, tpath, affine, rgbFace)
    103         for path_effect in self._path_effects:
    104             path_effect.draw_path(self._renderer, gc, tpath, affine,
--> 105                                   rgbFace)
    106 
    107     def draw_markers(self, gc, marker_path, marker_trans, path, *args,

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in draw_path(self, renderer, gc, tpath, affine, rgbFace)
    191         gc0.copy_properties(gc)
    192 
--> 193         gc0 = self._update_gc(gc0, self._gc)
    194         trans = self._offset_transform(renderer, affine)
    195         renderer.draw_path(gc0, tpath, trans, rgbFace)

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in _update_gc(self, gc, new_gc_dict)
     52             set_method = getattr(gc, 'set_' + k, None)
     53             if not callable(set_method):
---> 54                 raise AttributeError('Unknown property {0}'.format(k))
     55             set_method(v)
     56         return gc

AttributeError: Unknown property lw

<Figure size 5500x2000 with 2 Axes>
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~\AppData\Local\Continuum\anaconda3\envs\py36\lib\site-packages\IPython\core\formatters.py in __call__(self, obj)
    339                 pass
    340             else:
--> 341                 return printer(obj)
    342             # Finally look for special method names
    343             method = get_real_method(obj, self.print_method)

~\AppData\Local\Continuum\anaconda3\envs\py36\lib\site-packages\IPython\core\pylabtools.py in <lambda>(fig)
    242 
    243     if 'png' in formats:
--> 244         png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    245     if 'retina' in formats or 'png2x' in formats:
    246         png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))

~\AppData\Local\Continuum\anaconda3\envs\py36\lib\site-packages\IPython\core\pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs)
    126 
    127     bytes_io = BytesIO()
--> 128     fig.canvas.print_figure(bytes_io, **kw)
    129     data = bytes_io.getvalue()
    130     if fmt == 'svg':

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, **kwargs)
   2047                         orientation=orientation,
   2048                         dryrun=True,
-> 2049                         **kwargs)
   2050                     renderer = self.figure._cachedRenderer
   2051                     bbox_artists = kwargs.pop("bbox_extra_artists", None)

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\backends\backend_agg.py in print_png(self, filename_or_obj, *args, **kwargs)
    508 
    509         """
--> 510         FigureCanvasAgg.draw(self)
    511         renderer = self.get_renderer()
    512 

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\backends\backend_agg.py in draw(self)
    400         toolbar = self.toolbar
    401         try:
--> 402             self.figure.draw(self.renderer)
    403             # A GUI class may be need to update a window using this draw, so
    404             # don't forget to call the superclass.

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer, *args, **kwargs)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\figure.py in draw(self, renderer)
   1647 
   1648             mimage._draw_list_compositing_images(
-> 1649                 renderer, self, artists, self.suppressComposite)
   1650 
   1651             renderer.close_group('figure')

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136     if not_composite or not has_images:
    137         for a in artists:
--> 138             a.draw(renderer)
    139     else:
    140         # Composite any adjacent images together

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer, *args, **kwargs)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe)
   2626             renderer.stop_rasterizing()
   2627 
-> 2628         mimage._draw_list_compositing_images(renderer, self, artists)
   2629 
   2630         renderer.close_group('axes')

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136     if not_composite or not has_images:
    137         for a in artists:
--> 138             a.draw(renderer)
    139     else:
    140         # Composite any adjacent images together

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     48                 renderer.start_filter()
     49 
---> 50             return draw(artist, renderer, *args, **kwargs)
     51         finally:
     52             if artist.get_agg_filter() is not None:

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\text.py in draw(self, renderer)
    756                     textrenderer.draw_text(gc, x, y, clean_line,
    757                                            textobj._fontproperties, angle,
--> 758                                            ismath=ismath, mtext=mtext)
    759 
    760         gc.restore()

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\backend_bases.py in draw_text(self, gc, x, y, s, prop, angle, ismath, mtext)
    546         """
    547 
--> 548         self._draw_text_as_path(gc, x, y, s, prop, angle, ismath)
    549 
    550     def _get_text_path_transform(self, x, y, s, prop, angle, ismath):

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath)
    152         color = gc.get_rgb()
    153         gc.set_linewidth(0.0)
--> 154         self.draw_path(gc, path, transform, rgbFace=color)
    155 
    156     def __getattribute__(self, name):

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in draw_path(self, gc, tpath, affine, rgbFace)
    103         for path_effect in self._path_effects:
    104             path_effect.draw_path(self._renderer, gc, tpath, affine,
--> 105                                   rgbFace)
    106 
    107     def draw_markers(self, gc, marker_path, marker_trans, path, *args,

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in draw_path(self, renderer, gc, tpath, affine, rgbFace)
    191         gc0.copy_properties(gc)
    192 
--> 193         gc0 = self._update_gc(gc0, self._gc)
    194         trans = self._offset_transform(renderer, affine)
    195         renderer.draw_path(gc0, tpath, trans, rgbFace)

~\AppData\Roaming\Python\Python36\site-packages\matplotlib\patheffects.py in _update_gc(self, gc, new_gc_dict)
     52             set_method = getattr(gc, 'set_' + k, None)
     53             if not callable(set_method):
---> 54                 raise AttributeError('Unknown property {0}'.format(k))
     55             set_method(v)
     56         return gc

AttributeError: Unknown property lw

<Figure size 5500x2000 with 2 Axes>`

I'm running it on Windows 10,

asn1crypto==0.24.0
astropy==3.1.2
atomicwrites==1.3.0
attrs==19.1.0
backcall==0.1.0
bak==0.2.0
bleach==3.1.0
bokeh==1.1.0
Bottleneck==1.2.1
brewer2mpl==1.4.1
certifi==2019.3.9
cffi==1.12.3
chardet==3.0.4
Click==7.0
cloudpickle==0.8.1
colorama==0.4.1
cryptography==2.6.1
cycler==0.10.0
cytoolz==0.9.0.1
dask==1.2.0
decorator==4.4.0
defusedxml==0.6.0
dill==0.2.9
distributed==1.27.0
entrypoints==0.3
ez-setup==0.9
fast-histogram==0.7
feather-format==0.4.0
Flask==1.0.2
ggplot==0.11.5
glue-core==0.14.2
glue-vispy-viewers==0.11
h5py==2.9.0
heapdict==1.0.0
idna==2.8
imageio==2.5.0
ipykernel==5.1.0
ipython==7.4.0
ipython-genutils==0.2.0
ipywidgets==7.4.2
itsdangerous==1.1.0
jedi==0.13.3
Jinja2==2.10.1
jsonschema==3.0.1
jupyter==1.0.0
jupyter-client==5.2.4
jupyter-console==6.0.0
jupyter-contrib-core==0.3.3
jupyter-contrib-nbextensions==0.5.1
jupyter-core==4.4.0
jupyter-highlight-selected-word==0.2.0
jupyter-latex-envs==1.4.6
jupyter-nbextensions-configurator==0.4.1
jupyterlab==0.35.4
jupyterlab-server==0.2.0
kiwisolver==1.0.1
locket==0.2.0
lxml==4.3.0
MarkupSafe==1.1.1
matplotlib==3.0.3
mistune==0.8.4
mkl-fft==1.0.10
mkl-random==1.0.2
moderngl==5.5.0
more-itertools==7.0.0
mpl-scatter-density==0.5
msgpack==0.6.1
nbconvert==5.4.1
nbformat==4.4.0
networkx==2.3
notebook==5.7.8
numpy==1.16.3
olefile==0.46
packaging==19.0
pandas==0.24.2
pandocfilters==1.4.2
parso==0.4.0
partd==0.3.10
patsy==0.5.1
pickleshare==0.7.5
Pillow==6.0.0
plotly==3.8.1
pluggy==0.9.0
prometheus-client==0.6.0
prompt-toolkit==2.0.9
psutil==5.6.1
py==1.8.0
pyarrow==0.12.0
Pycluster==1.54
pyclustering==0.8.2
pycparser==2.19
Pygments==2.3.1
pyodbc==4.0.26
PyOpenGL==3.1.1a1
pyOpenSSL==19.0.0
pyparsing==2.4.0
pypi==2.1
pyreadline==2.1
pyrsistent==0.14.11
PySocks==1.6.8
pytest==4.4.1
pytest-arraydiff==0.3
pytest-astropy==0.5.0
pytest-doctestplus==0.3.0
pytest-openfiles==0.3.2
pytest-remotedata==0.3.1
python-dateutil==2.8.0
pytz==2019.1
PyWavelets==1.0.3
pywinpty==0.5.5
PyYAML==5.1
pyzmq==18.0.0
qtconsole==4.4.3
QtPy==1.7.0
regex==2019.2.18
requests==2.21.0
retrying==1.3.3
rstudio-spark-install==0.8.0
scikit-image==0.15.0
scikit-learn==0.20.2
scipy==1.2.1
seaborn==0.9.0
Send2Trash==1.5.0
six==1.12.0
sortedcontainers==2.1.0
statsmodels==0.9.0
tblib==1.3.2
terminado==0.8.2
testpath==0.4.2
toolz==0.9.0
tornado==6.0.2
traitlets==4.3.2
unicodecsv==0.14.1
URF==0.0.5
urllib3==1.24.1
wcwidth==0.1.7
webencodings==0.5.1
Werkzeug==0.14.1
widgetsnbextension==3.4.2
win-inet-pton==1.1.0
wincertstore==0.2
xlrd==1.2.0
zict==0.1.4

@cest0
Copy link

cest0 commented Sep 13, 2019

same issue here.

@clintval
Copy link
Author

Ha, I've not seen these comments until now. Have you tried removing the lw=0 call to ax.scatter? Right here: https://gist.github.com/clintval/e9afc246e77f6488cda79f86e4d37148#file-kmeansplots-py-L47

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment