<p dir="auto">I am trying to calculate silhouette_score or silhouette_samples using a sparse matrix but getting the following error:</p>
<p dir="auto"><strong>ValueError: diag requires an array of at least two dimensions</strong></p>
<p dir="auto">The sample code is as follows:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="edges = [
(1, 2, 0.9),
(1, 3, 0.7),
(1, 4, 0.1),
(1, 5, 0),
(1, 6, 0),
(2, 3, 0.8),
(2, 4, 0.2),
(2, 5, 0),
(2, 6, 0.3),
(3, 4, 0.3),
(3, 5, 0.2),
(3, 6, 0.25),
(4, 5, 0.8),
(4, 6, 0.6),
(5, 6, 0.9),
(7, 8, 1.0)]

gg = nx.Graph()

for u,v, w in edges:
    gg.add_edge(u, v, weight=w)


adj = nx.adjacency_matrix(gg)
adj.setdiag(0)

from sklearn.metrics import silhouette_score, silhouette_samples

print(silhouette_score(adj, metric='precomputed', labels=labels))
silhouette_samples(adj, metric='precomputed', labels=labels)"><pre class="notranslate"><code class="notranslate">edges = [
(1, 2, 0.9),
(1, 3, 0.7),
(1, 4, 0.1),
(1, 5, 0),
(1, 6, 0),
(2, 3, 0.8),
(2, 4, 0.2),
(2, 5, 0),
(2, 6, 0.3),
(3, 4, 0.3),
(3, 5, 0.2),
(3, 6, 0.25),
(4, 5, 0.8),
(4, 6, 0.6),
(5, 6, 0.9),
(7, 8, 1.0)]

gg = nx.Graph()

for u,v, w in edges:
    gg.add_edge(u, v, weight=w)


adj = nx.adjacency_matrix(gg)
adj.setdiag(0)

from sklearn.metrics import silhouette_score, silhouette_samples

print(silhouette_score(adj, metric='precomputed', labels=labels))
silhouette_samples(adj, metric='precomputed', labels=labels)
</code></pre></div>
<h4 dir="auto">Describe the bug</h4>
<p dir="auto"><code class="notranslate">silhouette_samples(X, y, metric="precomputed")</code> fails with <code class="notranslate">ValueError: diag requires an array of at least two dimensions</code> if X is sparse.</p>
<h4 dir="auto">Steps/Code to Reproduce</h4>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from sklearn.metrics import silhouette_samples
from sklearn.neighbors import kneighbors_graph
from sklearn.datasets import make_blobs

X, y = make_blobs(n_samples=1000, centers=5, n_features=10)

pdist = kneighbors_graph(X, 5, mode='distance')

# pdist is scipy.sparse.csr.csr_matrix

silhouette_samples(pdist, y, metric=&quot;precomputed&quot;)"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">metrics</span> <span class="pl-k">import</span> <span class="pl-s1">silhouette_samples</span>
<span class="pl-k">from</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">neighbors</span> <span class="pl-k">import</span> <span class="pl-s1">kneighbors_graph</span>
<span class="pl-k">from</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">datasets</span> <span class="pl-k">import</span> <span class="pl-s1">make_blobs</span>

<span class="pl-v">X</span>, <span class="pl-s1">y</span> <span class="pl-c1">=</span> <span class="pl-en">make_blobs</span>(<span class="pl-s1">n_samples</span><span class="pl-c1">=</span><span class="pl-c1">1000</span>, <span class="pl-s1">centers</span><span class="pl-c1">=</span><span class="pl-c1">5</span>, <span class="pl-s1">n_features</span><span class="pl-c1">=</span><span class="pl-c1">10</span>)

<span class="pl-s1">pdist</span> <span class="pl-c1">=</span> <span class="pl-en">kneighbors_graph</span>(<span class="pl-v">X</span>, <span class="pl-c1">5</span>, <span class="pl-s1">mode</span><span class="pl-c1">=</span><span class="pl-s">'distance'</span>)

<span class="pl-c"># pdist is scipy.sparse.csr.csr_matrix</span>

<span class="pl-en">silhouette_samples</span>(<span class="pl-s1">pdist</span>, <span class="pl-s1">y</span>, <span class="pl-s1">metric</span><span class="pl-c1">=</span><span class="pl-s">"precomputed"</span>)</pre></div>
<h4 dir="auto">Expected Results</h4>
<p dir="auto">No error is thrown.</p>
<h4 dir="auto">Actual Results</h4>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
&lt;ipython-input-14-140890024e6c&gt; in &lt;module&gt;
----&gt; 1 silhouette_samples(pdist, y, metric=&quot;precomputed&quot;)

/data1/mschroeder/miniconda3/envs/20-assdc/lib/python3.7/site-packages/sklearn/metrics/cluster/_unsupervised.py in silhouette_samples(X, labels, metric, **kwds)
    216     if metric == 'precomputed':
    217         atol = np.finfo(X.dtype).eps * 100
--&gt; 218         if np.any(np.abs(np.diagonal(X)) &gt; atol):
    219             raise ValueError(
    220                 'The precomputed distance matrix contains non-zero '

&lt;__array_function__ internals&gt; in diagonal(*args, **kwargs)

/data1/mschroeder/miniconda3/envs/20-assdc/lib/python3.7/site-packages/numpy/core/fromnumeric.py in diagonal(a, offset, axis1, axis2)
   1615         return asarray(a).diagonal(offset=offset, axis1=axis1, axis2=axis2)
   1616     else:
-&gt; 1617         return asanyarray(a).diagonal(offset=offset, axis1=axis1, axis2=axis2)
   1618 
   1619 

ValueError: diag requires an array of at least two dimensions"><pre class="notranslate"><code class="notranslate">---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
&lt;ipython-input-14-140890024e6c&gt; in &lt;module&gt;
----&gt; 1 silhouette_samples(pdist, y, metric="precomputed")

/data1/mschroeder/miniconda3/envs/20-assdc/lib/python3.7/site-packages/sklearn/metrics/cluster/_unsupervised.py in silhouette_samples(X, labels, metric, **kwds)
    216     if metric == 'precomputed':
    217         atol = np.finfo(X.dtype).eps * 100
--&gt; 218         if np.any(np.abs(np.diagonal(X)) &gt; atol):
    219             raise ValueError(
    220                 'The precomputed distance matrix contains non-zero '

&lt;__array_function__ internals&gt; in diagonal(*args, **kwargs)

/data1/mschroeder/miniconda3/envs/20-assdc/lib/python3.7/site-packages/numpy/core/fromnumeric.py in diagonal(a, offset, axis1, axis2)
   1615         return asarray(a).diagonal(offset=offset, axis1=axis1, axis2=axis2)
   1616     else:
-&gt; 1617         return asanyarray(a).diagonal(offset=offset, axis1=axis1, axis2=axis2)
   1618 
   1619 

ValueError: diag requires an array of at least two dimensions
</code></pre></div>
<h4 dir="auto">Possible fix</h4>
<p dir="auto">Change <code class="notranslate">np.diagonal(X)</code> to <code class="notranslate">X.diagonal()</code>, because this is implemented for <code class="notranslate">numpy.ndarray</code> as well as <code class="notranslate">scipy.sparse.csr_matrix.diagonal</code>. Or should this rather be fixed in numpy (so that np.diagonal does the right thing for sparse matrices)?</p>
<h4 dir="auto">Versions</h4>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="System:
    python: 3.7.6 | packaged by conda-forge | (default, Jun  1 2020, 18:57:50)  [GCC 7.5.0]
executable: /data1/mschroeder/miniconda3/envs/20-assdc/bin/python
   machine: Linux-4.4.0-109-generic-x86_64-with-debian-stretch-sid

Python dependencies:
       pip: 20.1.1
setuptools: 47.3.1.post20200616
   sklearn: 0.22.2.post1
     numpy: 1.18.5
     scipy: 1.4.1
    Cython: None
    pandas: 1.0.5
matplotlib: 3.2.1
    joblib: 0.15.1

Built with OpenMP: True"><pre class="notranslate"><code class="notranslate">System:
    python: 3.7.6 | packaged by conda-forge | (default, Jun  1 2020, 18:57:50)  [GCC 7.5.0]
executable: /data1/mschroeder/miniconda3/envs/20-assdc/bin/python
   machine: Linux-4.4.0-109-generic-x86_64-with-debian-stretch-sid

Python dependencies:
       pip: 20.1.1
setuptools: 47.3.1.post20200616
   sklearn: 0.22.2.post1
     numpy: 1.18.5
     scipy: 1.4.1
    Cython: None
    pandas: 1.0.5
matplotlib: 3.2.1
    joblib: 0.15.1

Built with OpenMP: True
</code></pre></div>