How to cluster data with KMeans in scikit-learn

Unsupervised clustering groups rows when no target labels exist yet. KMeans in scikit-learn fits a fixed number of centroid-based clusters, which makes it a quick way to smoke-test whether numeric rows separate into recognizable segments.

The estimator stores the fitted row assignments in labels_, the learned centroids in cluster_centers_, and the within-cluster sum of squared distances in inertia_. Setting random_state and n_init keeps the small check reproducible while still letting KMeans try multiple centroid seeds.

Cluster numbers are identifiers, not class names. Scale numeric columns before fitting when the units differ, inspect the centers before naming the groups, and treat inertia as a comparison signal between related runs rather than a standalone score.

Steps to cluster data with scikit-learn KMeans:

  1. Save the clustering smoke script as cluster_kmeans.py.
    cluster_kmeans.py
    import numpy as np
    import sklearn
    from sklearn.cluster import KMeans
    from sklearn.preprocessing import StandardScaler
     
     
    customer_names = np.array(
        [
            "trial-01",
            "trial-02",
            "trial-03",
            "steady-01",
            "steady-02",
            "steady-03",
            "premium-01",
            "premium-02",
            "premium-03",
        ]
    )
     
    features = np.array(
        [
            [1.2, 18.0],
            [1.8, 22.0],
            [2.0, 24.0],
            [5.8, 47.0],
            [6.4, 52.0],
            [6.9, 49.0],
            [10.2, 85.0],
            [10.8, 91.0],
            [11.4, 88.0],
        ],
        dtype=float,
    )
     
    scaler = StandardScaler()
    features_scaled = scaler.fit_transform(features)
     
    kmeans = KMeans(n_clusters=3, n_init=10, random_state=42)
    labels = kmeans.fit_predict(features_scaled)
    centers = scaler.inverse_transform(kmeans.cluster_centers_)
     
    print(f"scikit-learn {sklearn.__version__}")
    print("Cluster assignments:")
    for customer, label in zip(customer_names, labels):
        print(f"- {customer}: cluster {label}")
     
    print("\nCluster centers in original units:")
    for cluster_id, center in enumerate(centers):
        visits, spend = center
        print(f"- cluster {cluster_id}: visits={visits:.1f}, avg_order=${spend:.2f}")
     
    print(f"\nInertia: {kmeans.inertia_:.3f}")
    print(f"Iterations: {kmeans.n_iter_}")
     
    new_rows = np.array(
        [
            [2.3, 23.0],
            [10.5, 89.0],
        ],
        dtype=float,
    )
    new_labels = kmeans.predict(scaler.transform(new_rows))
     
    print("\nNew row clusters:")
    for row, label in zip(new_rows, new_labels):
        visits, spend = row
        print(f"- visits={visits:.1f}, avg_order=${spend:.2f}: cluster {label}")

    The sample uses two numeric features, monthly visits and average order value. StandardScaler keeps both columns on comparable scale before KMeans uses Euclidean distance.

  2. Run the script from a Python environment that has scikit-learn installed.
    $ python3 cluster_kmeans.py
    scikit-learn 1.9.0
    Cluster assignments:
    - trial-01: cluster 2
    - trial-02: cluster 2
    - trial-03: cluster 2
    - steady-01: cluster 0
    - steady-02: cluster 0
    - steady-03: cluster 0
    - premium-01: cluster 1
    - premium-02: cluster 1
    - premium-03: cluster 1
    
    Cluster centers in original units:
    - cluster 0: visits=6.4, avg_order=$49.33
    - cluster 1: visits=10.8, avg_order=$88.00
    - cluster 2: visits=1.7, avg_order=$21.33
    
    Inertia: 0.184
    Iterations: 2
    
    New row clusters:
    - visits=2.3, avg_order=$23.00: cluster 2
    - visits=10.5, avg_order=$89.00: cluster 1
  3. Check the fitted labels for the training rows.
    Cluster assignments:
    - trial-01: cluster 2
    - trial-02: cluster 2
    - trial-03: cluster 2
    - steady-01: cluster 0
    - steady-02: cluster 0
    - steady-03: cluster 0
    - premium-01: cluster 1
    - premium-02: cluster 1
    - premium-03: cluster 1

    The cluster IDs can change when the data, seed, or number of clusters changes. Name segments from their centers and source rows, not from the numeric label alone.

  4. Inspect the centers and inertia.
    Cluster centers in original units:
    - cluster 0: visits=6.4, avg_order=$49.33
    - cluster 1: visits=10.8, avg_order=$88.00
    - cluster 2: visits=1.7, avg_order=$21.33
    
    Inertia: 0.184
    Iterations: 2

    The centers were transformed back to the original feature units for readability. Inertia is computed in the scaled feature space, so compare it only with related KMeans runs that use the same preprocessing.

  5. Verify that new rows can be assigned to the fitted clusters.
    New row clusters:
    - visits=2.3, avg_order=$23.00: cluster 2
    - visits=10.5, avg_order=$89.00: cluster 1

    Call predict() on rows transformed by the same scaler that was fitted before KMeans. Refitting the scaler or the clusterer on incoming rows changes the cluster boundary.

  6. Remove the smoke script when the cluster check is complete.
    $ rm cluster_kmeans.py