<h4 dir="auto">Description</h4>
<p dir="auto">Incorrect clustering results when using dbscan clustering with brute algorithm and large values.<br>
See the below code snippet - when the data type is changed to float64 or the algorithm is changed to ball_tree the correct clustering results are obtained.<br>
For context, the large values are geographic coordinates.<br>
I suspect this is because of the use of squared euclidean distance in the brute algorithm, which is outside the range where float32 can provide adequate precision: </p><div class="Box Box--condensed my-2">
  <div class="Box-header f6">
    <p class="mb-0 text-bold">
      <a href="https://github.com/scikit-learn/scikit-learn/blob/7b136e92acf49d46251479b75c88cba632de1937/sklearn/neighbors/base.py#L699">scikit-learn/sklearn/neighbors/base.py</a>
    </p>
    <p class="mb-0 color-fg-muted">
         Line 699
      in
      <a data-pjax="true" class="commit-tease-sha" href="/scikit-learn/scikit-learn/commit/7b136e92acf49d46251479b75c88cba632de1937">7b136e9</a>
    </p>
  </div>
  <div itemprop="text" class="Box-body p-0 blob-wrapper blob-wrapper-embedded data">
    <table class="highlight tab-size mb-0 js-file-line-container" data-tab-size="8" data-paste-markdown-skip="">

        <tbody><tr class="border-0">
          <td id="L699" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="699"></td>
          <td id="LC699" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-c"># for efficiency, use squared euclidean distances</span> </td>
        </tr>
    </tbody></table>
  </div>
</div>
<p></p>
<p dir="auto">Perhaps it would be appropriate to warn if epsilon is so small compared to the values being clustered that it will cause issues with precision.</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="import numpy as np
from sklearn.cluster import DBSCAN
from scipy.spatial import distance_matrix

import platform; print(platform.platform())
import sys; print(&quot;Python&quot;, sys.version)
import numpy; print(&quot;NumPy&quot;, numpy.__version__)
import scipy; print(&quot;SciPy&quot;, scipy.__version__)
import sklearn; print(&quot;Scikit-Learn&quot;, sklearn.__version__)

xy = np.array([[400000, 400000],
               [400000, 400000+1],
               [400000, 400000+5]
               ], dtype=np.float32)

print(distance_matrix(xy, xy))

print('incorrect', DBSCAN(eps=2, min_samples=1, algorithm='brute'
                          ).fit(xy.astype(np.float32)).labels_)
print('correct  ', DBSCAN(eps=2, min_samples=1, algorithm='brute'
                        ).fit(xy.astype(np.float64)).labels_)
print('correct  ', DBSCAN(eps=2, min_samples=1, algorithm='ball_tree'
                          ).fit(xy.astype(np.float32)).labels_)

"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-k">from</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">cluster</span> <span class="pl-k">import</span> <span class="pl-v">DBSCAN</span>
<span class="pl-k">from</span> <span class="pl-s1">scipy</span>.<span class="pl-s1">spatial</span> <span class="pl-k">import</span> <span class="pl-s1">distance_matrix</span>

<span class="pl-k">import</span> <span class="pl-s1">platform</span>; <span class="pl-en">print</span>(<span class="pl-s1">platform</span>.<span class="pl-en">platform</span>())
<span class="pl-k">import</span> <span class="pl-s1">sys</span>; <span class="pl-en">print</span>(<span class="pl-s">"Python"</span>, <span class="pl-s1">sys</span>.<span class="pl-s1">version</span>)
<span class="pl-k">import</span> <span class="pl-s1">numpy</span>; <span class="pl-en">print</span>(<span class="pl-s">"NumPy"</span>, <span class="pl-s1">numpy</span>.<span class="pl-s1">__version__</span>)
<span class="pl-k">import</span> <span class="pl-s1">scipy</span>; <span class="pl-en">print</span>(<span class="pl-s">"SciPy"</span>, <span class="pl-s1">scipy</span>.<span class="pl-s1">__version__</span>)
<span class="pl-k">import</span> <span class="pl-s1">sklearn</span>; <span class="pl-en">print</span>(<span class="pl-s">"Scikit-Learn"</span>, <span class="pl-s1">sklearn</span>.<span class="pl-s1">__version__</span>)

<span class="pl-s1">xy</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([[<span class="pl-c1">400000</span>, <span class="pl-c1">400000</span>],
               [<span class="pl-c1">400000</span>, <span class="pl-c1">400000</span><span class="pl-c1">+</span><span class="pl-c1">1</span>],
               [<span class="pl-c1">400000</span>, <span class="pl-c1">400000</span><span class="pl-c1">+</span><span class="pl-c1">5</span>]
               ], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float32</span>)

<span class="pl-en">print</span>(<span class="pl-en">distance_matrix</span>(<span class="pl-s1">xy</span>, <span class="pl-s1">xy</span>))

<span class="pl-en">print</span>(<span class="pl-s">'incorrect'</span>, <span class="pl-v">DBSCAN</span>(<span class="pl-s1">eps</span><span class="pl-c1">=</span><span class="pl-c1">2</span>, <span class="pl-s1">min_samples</span><span class="pl-c1">=</span><span class="pl-c1">1</span>, <span class="pl-s1">algorithm</span><span class="pl-c1">=</span><span class="pl-s">'brute'</span>
                          ).<span class="pl-en">fit</span>(<span class="pl-s1">xy</span>.<span class="pl-en">astype</span>(<span class="pl-s1">np</span>.<span class="pl-s1">float32</span>)).<span class="pl-s1">labels_</span>)
<span class="pl-en">print</span>(<span class="pl-s">'correct  '</span>, <span class="pl-v">DBSCAN</span>(<span class="pl-s1">eps</span><span class="pl-c1">=</span><span class="pl-c1">2</span>, <span class="pl-s1">min_samples</span><span class="pl-c1">=</span><span class="pl-c1">1</span>, <span class="pl-s1">algorithm</span><span class="pl-c1">=</span><span class="pl-s">'brute'</span>
                        ).<span class="pl-en">fit</span>(<span class="pl-s1">xy</span>.<span class="pl-en">astype</span>(<span class="pl-s1">np</span>.<span class="pl-s1">float64</span>)).<span class="pl-s1">labels_</span>)
<span class="pl-en">print</span>(<span class="pl-s">'correct  '</span>, <span class="pl-v">DBSCAN</span>(<span class="pl-s1">eps</span><span class="pl-c1">=</span><span class="pl-c1">2</span>, <span class="pl-s1">min_samples</span><span class="pl-c1">=</span><span class="pl-c1">1</span>, <span class="pl-s1">algorithm</span><span class="pl-c1">=</span><span class="pl-s">'ball_tree'</span>
                          ).<span class="pl-en">fit</span>(<span class="pl-s1">xy</span>.<span class="pl-en">astype</span>(<span class="pl-s1">np</span>.<span class="pl-s1">float32</span>)).<span class="pl-s1">labels_</span>)
</pre></div>
<h4 dir="auto">Results</h4>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="Windows-10-10.0.17134-SP0
Python 3.7.2 (default, Feb 21 2019, 17:35:59) [MSC v.1915 64 bit (AMD64)]
NumPy 1.16.1
SciPy 1.2.1
Scikit-Learn 0.20.3
[[0. 1. 5.]
 [1. 0. 4.]
 [5. 4. 0.]]
incorrect [0 1 0]
correct   [0 0 1]
correct   [0 0 1]"><pre class="notranslate"><span class="pl-v">Windows</span><span class="pl-c1">-</span><span class="pl-c1">10</span><span class="pl-c1">-</span><span class="pl-c1">10.0</span>.<span class="pl-c1">17134</span><span class="pl-c1">-</span><span class="pl-v">SP0</span>
<span class="pl-v">Python</span> <span class="pl-c1">3.7</span><span class="pl-c1">.2</span> (<span class="pl-s1">default</span>, <span class="pl-v">Feb</span> <span class="pl-c1">21</span> <span class="pl-c1">2019</span>, <span class="pl-c1">17</span>:<span class="pl-c1">35</span>:<span class="pl-c1">59</span>) [<span class="pl-v">MSC</span> <span class="pl-s1">v</span><span class="pl-c1">.1915</span> <span class="pl-c1">64</span> <span class="pl-en">bit</span> (<span class="pl-v">AMD64</span>)]
<span class="pl-v">NumPy</span> <span class="pl-c1">1.16</span><span class="pl-c1">.1</span>
<span class="pl-v">SciPy</span> <span class="pl-c1">1.2</span><span class="pl-c1">.1</span>
<span class="pl-v">Scikit</span><span class="pl-c1">-</span><span class="pl-v">Learn</span> <span class="pl-c1">0.20</span><span class="pl-c1">.3</span>
[[<span class="pl-c1">0.</span> <span class="pl-c1">1.</span> <span class="pl-c1">5.</span>]
 [<span class="pl-c1">1.</span> <span class="pl-c1">0.</span> <span class="pl-c1">4.</span>]
 [<span class="pl-c1">5.</span> <span class="pl-c1">4.</span> <span class="pl-c1">0.</span>]]
<span class="pl-s1">incorrect</span> [<span class="pl-c1">0</span> <span class="pl-c1">1</span> <span class="pl-c1">0</span>]
<span class="pl-s1">correct</span>   [<span class="pl-c1">0</span> <span class="pl-c1">0</span> <span class="pl-c1">1</span>]
<span class="pl-s1">correct</span>   [<span class="pl-c1">0</span> <span class="pl-c1">0</span> <span class="pl-c1">1</span>]</pre></div>
<h4 dir="auto">Description</h4>
<p dir="auto">I noticed that sklearn.metrics.pairwise.pairwise_distances function agrees with np.linalg.norm when using np.float64 arrays, but disagrees when using np.float32 arrays. See the code snippet below.</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="import numpy as np
import scipy
import sklearn.metrics.pairwise

# create 64-bit vectors a and b that are very similar to each other
a_64 = np.array([61.221637725830078125, 71.60662841796875,    -65.7512664794921875],  dtype=np.float64)
b_64 = np.array([61.221637725830078125, 71.60894012451171875, -65.72847747802734375], dtype=np.float64)

# create 32-bit versions of a and b
a_32 = a_64.astype(np.float32)
b_32 = b_64.astype(np.float32)

# compute the distance from a to b using numpy, for both 64-bit and 32-bit
dist_64_np = np.array([np.linalg.norm(a_64 - b_64)], dtype=np.float64)
dist_32_np = np.array([np.linalg.norm(a_32 - b_32)], dtype=np.float32)

# compute the distance from a to b using sklearn, for both 64-bit and 32-bit
dist_64_sklearn = sklearn.metrics.pairwise.pairwise_distances([a_64], [b_64])
dist_32_sklearn = sklearn.metrics.pairwise.pairwise_distances([a_32], [b_32])

# note that the 64-bit sklearn results agree exactly with numpy, but the 32-bit results disagree
np.set_printoptions(precision=200)

print(dist_64_np)
print(dist_32_np)
print(dist_64_sklearn)
print(dist_32_sklearn)"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-k">import</span> <span class="pl-s1">scipy</span>
<span class="pl-k">import</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">metrics</span>.<span class="pl-s1">pairwise</span>

<span class="pl-c"># create 64-bit vectors a and b that are very similar to each other</span>
<span class="pl-s1">a_64</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">61.221637725830078125</span>, <span class="pl-c1">71.60662841796875</span>,    <span class="pl-c1">-</span><span class="pl-c1">65.7512664794921875</span>],  <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float64</span>)
<span class="pl-s1">b_64</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">61.221637725830078125</span>, <span class="pl-c1">71.60894012451171875</span>, <span class="pl-c1">-</span><span class="pl-c1">65.72847747802734375</span>], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float64</span>)

<span class="pl-c"># create 32-bit versions of a and b</span>
<span class="pl-s1">a_32</span> <span class="pl-c1">=</span> <span class="pl-s1">a_64</span>.<span class="pl-en">astype</span>(<span class="pl-s1">np</span>.<span class="pl-s1">float32</span>)
<span class="pl-s1">b_32</span> <span class="pl-c1">=</span> <span class="pl-s1">b_64</span>.<span class="pl-en">astype</span>(<span class="pl-s1">np</span>.<span class="pl-s1">float32</span>)

<span class="pl-c"># compute the distance from a to b using numpy, for both 64-bit and 32-bit</span>
<span class="pl-s1">dist_64_np</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-s1">np</span>.<span class="pl-s1">linalg</span>.<span class="pl-en">norm</span>(<span class="pl-s1">a_64</span> <span class="pl-c1">-</span> <span class="pl-s1">b_64</span>)], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float64</span>)
<span class="pl-s1">dist_32_np</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-s1">np</span>.<span class="pl-s1">linalg</span>.<span class="pl-en">norm</span>(<span class="pl-s1">a_32</span> <span class="pl-c1">-</span> <span class="pl-s1">b_32</span>)], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">np</span>.<span class="pl-s1">float32</span>)

<span class="pl-c"># compute the distance from a to b using sklearn, for both 64-bit and 32-bit</span>
<span class="pl-s1">dist_64_sklearn</span> <span class="pl-c1">=</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">metrics</span>.<span class="pl-s1">pairwise</span>.<span class="pl-en">pairwise_distances</span>([<span class="pl-s1">a_64</span>], [<span class="pl-s1">b_64</span>])
<span class="pl-s1">dist_32_sklearn</span> <span class="pl-c1">=</span> <span class="pl-s1">sklearn</span>.<span class="pl-s1">metrics</span>.<span class="pl-s1">pairwise</span>.<span class="pl-en">pairwise_distances</span>([<span class="pl-s1">a_32</span>], [<span class="pl-s1">b_32</span>])

<span class="pl-c"># note that the 64-bit sklearn results agree exactly with numpy, but the 32-bit results disagree</span>
<span class="pl-s1">np</span>.<span class="pl-en">set_printoptions</span>(<span class="pl-s1">precision</span><span class="pl-c1">=</span><span class="pl-c1">200</span>)

<span class="pl-en">print</span>(<span class="pl-s1">dist_64_np</span>)
<span class="pl-en">print</span>(<span class="pl-s1">dist_32_np</span>)
<span class="pl-en">print</span>(<span class="pl-s1">dist_64_sklearn</span>)
<span class="pl-en">print</span>(<span class="pl-s1">dist_32_sklearn</span>)</pre></div>
<h4 dir="auto">Expected Results</h4>
<p dir="auto">I expect that the results from sklearn.metrics.pairwise.pairwise_distances would agree with np.linalg.norm for both 64-bit and 32-bit. In other words, I expect the following output:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[ 0.0229059506440019884643266578905240749008953571319580078125]
[ 0.02290595136582851409912109375]
[[ 0.0229059506440019884643266578905240749008953571319580078125]]
[[ 0.02290595136582851409912109375]]"><pre class="notranslate"><code class="notranslate">[ 0.0229059506440019884643266578905240749008953571319580078125]
[ 0.02290595136582851409912109375]
[[ 0.0229059506440019884643266578905240749008953571319580078125]]
[[ 0.02290595136582851409912109375]]
</code></pre></div>
<h4 dir="auto">Actual Results</h4>
<p dir="auto">The code snippet above produces the following output for me:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[ 0.0229059506440019884643266578905240749008953571319580078125]
[ 0.02290595136582851409912109375]
[[ 0.0229059506440019884643266578905240749008953571319580078125]]
[[ 0.03125]]"><pre class="notranslate"><code class="notranslate">[ 0.0229059506440019884643266578905240749008953571319580078125]
[ 0.02290595136582851409912109375]
[[ 0.0229059506440019884643266578905240749008953571319580078125]]
[[ 0.03125]]
</code></pre></div>
<h4 dir="auto">Versions</h4>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Darwin-16.6.0-x86_64-i386-64bit
('Python', '2.7.11 | 64-bit | (default, Jun 11 2016, 03:41:56) \n[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)]')
('NumPy', '1.11.3')
('SciPy', '0.19.0')
('Scikit-Learn', '0.18.1')"><pre class="notranslate"><code class="notranslate">Darwin-16.6.0-x86_64-i386-64bit
('Python', '2.7.11 | 64-bit | (default, Jun 11 2016, 03:41:56) \n[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)]')
('NumPy', '1.11.3')
('SciPy', '0.19.0')
('Scikit-Learn', '0.18.1')
</code></pre></div>