<p dir="auto">In a number of places, sklearn controls flow according to the existence of some method on an estimator. For example: <code class="notranslate">*SearchCV.score</code> checks for <code class="notranslate">score</code> on the estimator; <code class="notranslate">Scorer</code> and <code class="notranslate">multiclass</code> functions check for <code class="notranslate">decision_function</code>; and it is used for validation in <code class="notranslate">AdaBoostClassifier.fit</code>, <code class="notranslate">multiclass._check_estimator</code> and <code class="notranslate">Pipeline</code>; and for testing in <code class="notranslate">test_common</code>.</p>
<p dir="auto">Meta-estimators such as <code class="notranslate">*SearchCV</code>, <code class="notranslate">Pipeline</code>, <code class="notranslate">RFECV</code>, etc. should respond to such <code class="notranslate">hasattr</code>s in agreement with their underlying estimators (or else the <code class="notranslate">hasattr</code> approach should be avoided).</p>
<p dir="auto">This is possible by implementing such methods with a <code class="notranslate">property</code> that returns the correct method from the sub-estimator (or a closure around it), or raises <code class="notranslate">AttributeError</code> if the sub-estimator is found lacking (see <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="12308912" data-permission-text="Title is private" data-url="https://github.com/scikit-learn/scikit-learn/issues/1801" data-hovercard-type="pull_request" data-hovercard-url="/scikit-learn/scikit-learn/pull/1801/hovercard" href="https://github.com/scikit-learn/scikit-learn/pull/1801">#1801</a>). <code class="notranslate">hasattr</code> would then function correctly. Caveats: the code would be less straightforward in some cases; <code class="notranslate">help()</code>/<code class="notranslate">pydoc</code> won't show the methods as methods (with an argument list, etc.), though the <code class="notranslate">property</code>'s docstring will show.</p>
<h3 dir="auto">Describe the bug</h3>
<p dir="auto">For some samples and requested n_clusters MiniBatchKMeans does not return a proper clustering in terms of the number of clusters and consecutive labels.<br>
The example given below shows, that when requesting 11 clusters the result only consists of 9 and requesting 12 results in 11 clusters. Requesting 13 clusters then yields only 10 clusters.<br>
When using KMeans instead of MiniBatchKMeans there is no such issue.</p>
<h3 dir="auto">Steps/Code to Reproduce</h3>
<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 MiniBatchKMeans

points = [
    [-2636.705, 892.6364, 239.4284], [-2676.219, 922.741, 227.3839], [-2628.628, 902.6482, 245.5609], [-2612.497, 860.9032, 248.924],
    [-2639.552, 993.8482, 211.2253], [-2602.453, 958.7801, 211.5786], [-2598.118, 1032.398, 177.4023], [-2582.155, 972.5088, 203.5048],
    [-2548.377, 803.9934, 279.4388], [-2550.095, 979.9586, 222.6467], [-2746.966, 1021.456, 188.8456], [-2745.181, 984.1931, 199.6674],
    [-2729.113, 973.8251, 201.8876], [-2720.765, 1014.262, 205.0213], [-2747.317, 1099.313, 146.2305], [-2739.32, 1005.173, 200.297]
]

for numClusters in range(7, 17):
    model = MiniBatchKMeans(n_clusters=numClusters, random_state=0)
    clusters = model.fit_predict(points)

    unique = np.unique(clusters)
    print(&quot;requested&quot;, str(numClusters).rjust(2), &quot;clusters and result has&quot;, str(len(unique)).rjust(2), &quot;clusters with labels&quot;, unique)"><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">MiniBatchKMeans</span>

<span class="pl-s1">points</span> <span class="pl-c1">=</span> [
    [<span class="pl-c1">-</span><span class="pl-c1">2636.705</span>, <span class="pl-c1">892.6364</span>, <span class="pl-c1">239.4284</span>], [<span class="pl-c1">-</span><span class="pl-c1">2676.219</span>, <span class="pl-c1">922.741</span>, <span class="pl-c1">227.3839</span>], [<span class="pl-c1">-</span><span class="pl-c1">2628.628</span>, <span class="pl-c1">902.6482</span>, <span class="pl-c1">245.5609</span>], [<span class="pl-c1">-</span><span class="pl-c1">2612.497</span>, <span class="pl-c1">860.9032</span>, <span class="pl-c1">248.924</span>],
    [<span class="pl-c1">-</span><span class="pl-c1">2639.552</span>, <span class="pl-c1">993.8482</span>, <span class="pl-c1">211.2253</span>], [<span class="pl-c1">-</span><span class="pl-c1">2602.453</span>, <span class="pl-c1">958.7801</span>, <span class="pl-c1">211.5786</span>], [<span class="pl-c1">-</span><span class="pl-c1">2598.118</span>, <span class="pl-c1">1032.398</span>, <span class="pl-c1">177.4023</span>], [<span class="pl-c1">-</span><span class="pl-c1">2582.155</span>, <span class="pl-c1">972.5088</span>, <span class="pl-c1">203.5048</span>],
    [<span class="pl-c1">-</span><span class="pl-c1">2548.377</span>, <span class="pl-c1">803.9934</span>, <span class="pl-c1">279.4388</span>], [<span class="pl-c1">-</span><span class="pl-c1">2550.095</span>, <span class="pl-c1">979.9586</span>, <span class="pl-c1">222.6467</span>], [<span class="pl-c1">-</span><span class="pl-c1">2746.966</span>, <span class="pl-c1">1021.456</span>, <span class="pl-c1">188.8456</span>], [<span class="pl-c1">-</span><span class="pl-c1">2745.181</span>, <span class="pl-c1">984.1931</span>, <span class="pl-c1">199.6674</span>],
    [<span class="pl-c1">-</span><span class="pl-c1">2729.113</span>, <span class="pl-c1">973.8251</span>, <span class="pl-c1">201.8876</span>], [<span class="pl-c1">-</span><span class="pl-c1">2720.765</span>, <span class="pl-c1">1014.262</span>, <span class="pl-c1">205.0213</span>], [<span class="pl-c1">-</span><span class="pl-c1">2747.317</span>, <span class="pl-c1">1099.313</span>, <span class="pl-c1">146.2305</span>], [<span class="pl-c1">-</span><span class="pl-c1">2739.32</span>, <span class="pl-c1">1005.173</span>, <span class="pl-c1">200.297</span>]
]

<span class="pl-k">for</span> <span class="pl-s1">numClusters</span> <span class="pl-c1">in</span> <span class="pl-en">range</span>(<span class="pl-c1">7</span>, <span class="pl-c1">17</span>):
    <span class="pl-s1">model</span> <span class="pl-c1">=</span> <span class="pl-v">MiniBatchKMeans</span>(<span class="pl-s1">n_clusters</span><span class="pl-c1">=</span><span class="pl-s1">numClusters</span>, <span class="pl-s1">random_state</span><span class="pl-c1">=</span><span class="pl-c1">0</span>)
    <span class="pl-s1">clusters</span> <span class="pl-c1">=</span> <span class="pl-s1">model</span>.<span class="pl-en">fit_predict</span>(<span class="pl-s1">points</span>)

    <span class="pl-s1">unique</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">unique</span>(<span class="pl-s1">clusters</span>)
    <span class="pl-en">print</span>(<span class="pl-s">"requested"</span>, <span class="pl-en">str</span>(<span class="pl-s1">numClusters</span>).<span class="pl-en">rjust</span>(<span class="pl-c1">2</span>), <span class="pl-s">"clusters and result has"</span>, <span class="pl-en">str</span>(<span class="pl-en">len</span>(<span class="pl-s1">unique</span>)).<span class="pl-en">rjust</span>(<span class="pl-c1">2</span>), <span class="pl-s">"clusters with labels"</span>, <span class="pl-s1">unique</span>)</pre></div>
<h3 dir="auto">Expected Results</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="requested  7 clusters and result has  7 clusters with labels [ 0  1  2  3  4  5  6]
requested  8 clusters and result has  8 clusters with labels [ 0  1  2  3  4  5  6  7]
requested  9 clusters and result has  9 clusters with labels [ 0  1  2  3  4  5  6  7  8]
requested 10 clusters and result has 10 clusters with labels [ 0  1  2  3  4  5  6  7  8  9]
requested 11 clusters and result has 11 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10]
requested 12 clusters and result has 12 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11]
requested 13 clusters and result has 13 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12]
requested 14 clusters and result has 14 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13]
requested 15 clusters and result has 15 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14]
requested 16 clusters and result has 16 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]"><pre class="notranslate"><code class="notranslate">requested  7 clusters and result has  7 clusters with labels [ 0  1  2  3  4  5  6]
requested  8 clusters and result has  8 clusters with labels [ 0  1  2  3  4  5  6  7]
requested  9 clusters and result has  9 clusters with labels [ 0  1  2  3  4  5  6  7  8]
requested 10 clusters and result has 10 clusters with labels [ 0  1  2  3  4  5  6  7  8  9]
requested 11 clusters and result has 11 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10]
requested 12 clusters and result has 12 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11]
requested 13 clusters and result has 13 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12]
requested 14 clusters and result has 14 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13]
requested 15 clusters and result has 15 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14]
requested 16 clusters and result has 16 clusters with labels [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
</code></pre></div>
<h3 dir="auto">Actual Results</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="requested  7 clusters and result has  7 clusters with labels [ 0  1  2  3  4  5  6]
requested  8 clusters and result has  7 clusters with labels [ 0  1  3  4  5  6  7]
requested  9 clusters and result has  9 clusters with labels [ 0  1  2  3  4  5  6  7  8]
requested 10 clusters and result has 10 clusters with labels [ 0  1  2  3  4  5  6  7  8  9]
requested 11 clusters and result has  9 clusters with labels [ 0  1  2  3  5  6  7  8  9]
requested 12 clusters and result has 11 clusters with labels [ 1  2  3  4  5  6  7  8  9 10 11]
requested 13 clusters and result has 10 clusters with labels [ 0  2  4  5  6  7  9 10 11 12]
requested 14 clusters and result has 12 clusters with labels [ 1  2  3  4  5  6  7  8  9 10 11 12]
requested 15 clusters and result has 11 clusters with labels [ 0  1  3  4  6  7  8 10 11 12 14]
requested 16 clusters and result has 13 clusters with labels [ 0  1  3  4  5  6  7  9 10 11 12 13 15]"><pre class="notranslate"><code class="notranslate">requested  7 clusters and result has  7 clusters with labels [ 0  1  2  3  4  5  6]
requested  8 clusters and result has  7 clusters with labels [ 0  1  3  4  5  6  7]
requested  9 clusters and result has  9 clusters with labels [ 0  1  2  3  4  5  6  7  8]
requested 10 clusters and result has 10 clusters with labels [ 0  1  2  3  4  5  6  7  8  9]
requested 11 clusters and result has  9 clusters with labels [ 0  1  2  3  5  6  7  8  9]
requested 12 clusters and result has 11 clusters with labels [ 1  2  3  4  5  6  7  8  9 10 11]
requested 13 clusters and result has 10 clusters with labels [ 0  2  4  5  6  7  9 10 11 12]
requested 14 clusters and result has 12 clusters with labels [ 1  2  3  4  5  6  7  8  9 10 11 12]
requested 15 clusters and result has 11 clusters with labels [ 0  1  3  4  6  7  8 10 11 12 14]
requested 16 clusters and result has 13 clusters with labels [ 0  1  3  4  5  6  7  9 10 11 12 13 15]
</code></pre></div>
<h3 dir="auto">Versions</h3>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="System:
    python: 3.9.7 | packaged by conda-forge | (default, Sep 29 2021, 19:20:16) [MSC v.1916 64 bit (AMD64)]
executable: C:\Users\USER\.conda\envs\sklearn-env\python.exe
   machine: Windows-10-10.0.22000-SP0

Python dependencies:
          pip: 21.3.1
   setuptools: 58.5.3
      sklearn: 1.0.1
        numpy: 1.21.4
        scipy: 1.7.2
       Cython: None
       pandas: None
   matplotlib: 3.4.3
       joblib: 1.1.0
threadpoolctl: 3.0.0

Built with OpenMP: True"><pre class="notranslate">System:
    python: 3.9.7 <span class="pl-k">|</span> packaged by conda-forge <span class="pl-k">|</span> (default, Sep 29 2021, 19:20:16) [MSC v.1916 64 bit (AMD64)]
executable: C:<span class="pl-cce">\U</span>sers<span class="pl-cce">\U</span>SER<span class="pl-cce">\.</span>conda<span class="pl-cce">\e</span>nvs<span class="pl-cce">\s</span>klearn-env<span class="pl-cce">\p</span>ython.exe
   machine: Windows-10-10.0.22000-SP0

Python dependencies:
          pip: 21.3.1
   setuptools: 58.5.3
      sklearn: 1.0.1
        numpy: 1.21.4
        scipy: 1.7.2
       Cython: None
       pandas: None
   matplotlib: 3.4.3
       joblib: 1.1.0
threadpoolctl: 3.0.0

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