You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scikit-learn的Gap Statistics代码中实现1标准误规则?

Fixing Gap Statistic with 1-Standard-Error Rule for Optimal K

Let's tackle your problem step by step. The main issues with your current code are twofold: you're not generating reference data that matches your original data's distribution, and you're missing the 1-standard-error rule logic needed to pick a reasonable optimal K when the gap curve keeps rising. Here's how to fix both:

Modified Implementation with 1-Standard-Error Rule

First, here's the updated code that includes the required S(k) calculation and the 1-standard-error check:

import numpy as np
import pandas as pd
from sklearn.cluster import KMeans

def optimalK(data, nrefs=3, maxClusters=15):
    gaps = np.zeros((len(range(1, maxClusters)),))
    s_vals = np.zeros((len(range(1, maxClusters)),))
    resultsdf = pd.DataFrame({'clusterCount': [], 'gap': [], 's_val': []})
    
    for gap_index, k in enumerate(range(1, maxClusters)):
        refDisps = np.zeros(nrefs)
        
        # Generate reference data that matches original data's range (critical fix!)
        min_vals = data.min(axis=0)
        max_vals = data.max(axis=0)
        
        for i in range(nrefs):
            randomReference = np.random.uniform(min_vals, max_vals, size=data.shape)
            km = KMeans(n_clusters=k, random_state=42)
            km.fit(randomReference)
            refDisps[i] = km.inertia_
        
        # Fit KMeans on original data
        km = KMeans(n_clusters=k, random_state=42)
        km.fit(data)
        origDisp = km.inertia_
        
        # Calculate gap statistic and S(k)
        refDisp_mean = np.mean(refDisps)
        refDisp_sd = np.std(refDisps)
        gap = np.log(refDisp_mean) - np.log(origDisp)
        s_val = refDisp_sd * np.sqrt(1 + 1/nrefs)  # Your required S(k) formula
        
        # Store results
        gaps[gap_index] = gap
        s_vals[gap_index] = s_val
        resultsdf = resultsdf.append(
            {'clusterCount': k, 'gap': gap, 's_val': s_val}, 
            ignore_index=True
        )
    
    # Apply 1-standard-error rule: find smallest K where GAP(K) > GAP(K+1) - S(K+1)
    optimal_k = maxClusters - 1  # Fallback to max if no condition met
    for k in range(1, maxClusters - 1):
        gap_k = resultsdf.loc[resultsdf['clusterCount'] == k, 'gap'].values[0]
        gap_k1 = resultsdf.loc[resultsdf['clusterCount'] == k+1, 'gap'].values[0]
        s_k1 = resultsdf.loc[resultsdf['clusterCount'] == k+1, 's_val'].values[0]
        
        if gap_k > gap_k1 - s_k1:
            optimal_k = k
            break  # Pick the first (smallest) K that satisfies the condition
    
    return (optimal_k, resultsdf)

Key Changes Explained

  1. Fixed Reference Data Generation
    Your original code used np.random.random_sample which creates data in the [0,1] range. This is a common mistake—reference data should match the min/max range of your original dataset to ensure valid inertia comparisons. The updated code uses np.random.uniform(min_vals, max_vals) to fix this, which aligns with the original Gap Statistic paper.

  2. Calculated S(k) as Required
    For each K, we now compute the standard deviation of the reference dispersions (refDisp_sd), then apply your formula:

    s_val = refDisp_sd * np.sqrt(1 + 1/nrefs)
    

    Where nrefs is the number of Monte Carlo samples (your B value).

  3. Implemented 1-Standard-Error Rule
    We iterate through possible K values and find the smallest K where GAP(K) > GAP(K+1) - S(K+1). If no such K is found, we fall back to the maximum cluster count (though fixing the reference data should prevent this from happening).

    If you prefer the alternative interpretation of the 1-standard-error rule (finding the smallest K where GAP(K) ≥ GAP(K*) - S(K*), where K* is the K with the maximum gap), you can replace the rule section with this code:

    # Alternative rule: smallest K <= K* where gap(K) >= gap(K*) - s(K*)
    k_star_row = resultsdf.loc[resultsdf['gap'].idxmax()]
    k_star = k_star_row['clusterCount']
    gap_star = k_star_row['gap']
    s_star = k_star_row['s_val']
    
    optimal_k = k_star
    for k in range(1, int(k_star) + 1):
        gap_k = resultsdf.loc[resultsdf['clusterCount'] == k, 'gap'].values[0]
        if gap_k >= gap_star - s_star:
            optimal_k = k
            break
    

Why Your Original Code Had Rising Gaps

The most likely culprit is the mismatched reference data distribution. When reference data is in [0,1] but your real data has a different range, the inertia of the reference clusters will be artificially low, leading to a gap that keeps increasing as K grows. Fixing the reference data should make the gap curve stabilize, and the 1-standard-error rule will then pick a meaningful optimal K.

内容的提问来源于stack exchange,提问作者justin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:49:18