Skip to content

Cluster

Production-grade clustering for mixed numerical and categorical tabular data. Supports K-Means, K-Modes, K-Prototypes, DBSCAN, and GMM with data-driven feature weighting, sample size estimation, and comprehensive evaluation metrics.

Core

bitbullet.cluster.core.base.BaseClusterer

Bases: ABC

Abstract base class for all clusterers.

This implements the Strategy pattern, allowing clusterers to be swapped at runtime while maintaining a consistent interface.

Design Principles: - Immutability: Config is frozen after creation - Explicit state: Fitted parameters are clearly separated - Type safety: Full type hints with runtime validation - Performance: Designed for vectorization and parallel execution

cluster_centers_ property

Get cluster centers (scikit-learn compatible).

config property

Get the immutable configuration.

is_fitted property

Check if the clusterer has been fitted.

labels_ property

Get cluster labels (scikit-learn compatible).

n_clusters_ property

Get number of clusters found (scikit-learn compatible).

state property

Get the current state.

__init__(config)

Initialize clusterer with configuration.

Parameters:

Name Type Description Default
config ClusterConfig

Immutable configuration for this clusterer

required

__repr__()

String representation.

fit(data, verbose=False)

Fit the clusterer to data.

Parameters:

Name Type Description Default
data DataFrame

Input data

required
verbose bool

Whether to print progress messages

False

Returns:

Type Description
BaseClusterer

Self for method chaining

Raises:

Type Description
ValueError

If data is invalid

RuntimeError

If already fitted

fit_predict(data, verbose=False)

Fit the clusterer and return cluster labels.

Parameters:

Name Type Description Default
data DataFrame

Input data

required
verbose bool

Whether to print progress messages

False

Returns:

Type Description
ndarray

Cluster labels

get_params()

Get parameters (scikit-learn compatible).

Returns:

Type Description
Dict[str, Any]

Parameter dictionary

predict(data)

Predict cluster labels for new data.

Parameters:

Name Type Description Default
data DataFrame

Input data to assign to clusters

required

Returns:

Type Description
ndarray

Cluster labels

Raises:

Type Description
RuntimeError

If not fitted

ValueError

If data is incompatible

set_params(**params)

Set parameters (scikit-learn compatible).

Parameters:

Name Type Description Default
**params Any

Parameters to set

{}

Returns:

Type Description
BaseClusterer

Self for method chaining

Raises:

Type Description
RuntimeError

If already fitted

bitbullet.cluster.core.base.ClusterConfig

Bases: BaseModel

Configuration for a clustering algorithm.

This uses Pydantic for validation and serialization, ensuring type safety at runtime.

model_dump_json(**kwargs)

Override to handle non-serializable types.

bitbullet.cluster.core.base.ClusterState

Bases: BaseModel

Encapsulates the fitted state of a clusterer.

This allows perfect reproducibility in production by storing all parameters learned during fitting.

Algorithms — Partitional

bitbullet.cluster.algorithms.partitional.kmeans.KMeansClusterer

Bases: BaseClusterer

K-Means clustering for numerical data only.

Features: - Standard K-Means or MiniBatch variant for large datasets - Multiple initialization strategies - Automatic elbow method support - Production-ready centroid saving/loading

Note

K-Means only works with numerical features. For mixed data, use K-Prototypes instead.

get_centroids()

Get cluster centroids as DataFrame.

Returns:

Type Description
DataFrame

DataFrame with centroids (means for all features)

Raises:

Type Description
RuntimeError

If not fitted

get_inertia()

Get the sum of squared distances to nearest cluster center.

Returns:

Type Description
float

Inertia value (lower is better)

Raises:

Type Description
RuntimeError

If not fitted

bitbullet.cluster.algorithms.partitional.kmodes.KModesClusterer

Bases: BaseClusterer

K-Modes clustering for pure categorical data.

Features: - Cao initialization for optimal starting centroids - Data-driven categorical feature weighting - Memory-efficient distance computation - Production-ready centroid saving/loading - Automatic binning of numerical features if present

Based on: - Huang, Z. (1998). Extensions to the k-Means Algorithm for Clustering Large Data Sets with Categorical Values. Data Mining and Knowledge Discovery, 2(3), 283-304. - Cao, F. et al. (2009). A New Initialization Method for Categorical Data Clustering. Expert Systems with Applications, 36(7), 10223-10228.

get_centroids()

Get cluster centroids as DataFrame.

Returns:

Type Description
DataFrame

DataFrame with centroids (modes for all categorical features)

Raises:

Type Description
RuntimeError

If not fitted

get_cost()

Get the total clustering cost (sum of categorical dissimilarities).

This is the K-Modes equivalent of inertia for K-Means. It represents the sum of weighted Hamming distances from each point to its cluster mode (centroid).

Returns:

Type Description
float

Total cost value (lower is better, decreases with more clusters)

Raises:

Type Description
RuntimeError

If not fitted

get_descriptive_centroids()

Get cluster centroids with descriptive labels for binned numerical features.

Returns:

Type Description
DataFrame

DataFrame with centroids where binned numerical features show value ranges

Raises:

Type Description
RuntimeError

If not fitted

bitbullet.cluster.algorithms.partitional.kprototypes.KPrototypesClusterer

Bases: BaseClusterer

K-Prototypes clustering for mixed numerical and categorical data.

Features: - Automatic or manual gamma estimation - Data-driven categorical feature weighting - Custom distance function with weights - Memory-efficient distance computation - Production-ready centroid saving/loading

get_centroids()

Get cluster centroids as DataFrame.

Returns:

Type Description
DataFrame

DataFrame with centroids (medians for numerical, modes for categorical)

Raises:

Type Description
RuntimeError

If not fitted

get_cost()

Get the total clustering cost (SSE for numerical + weighted mismatches for categorical).

This is the K-Prototypes equivalent of inertia/WCSS for K-Means. It represents the sum of: - Squared Euclidean distances for numerical features - Gamma-weighted Hamming distances for categorical features

Returns:

Type Description
float

Total cost value (lower is better, decreases with more clusters)

Raises:

Type Description
RuntimeError

If not fitted

Algorithms — Density

bitbullet.cluster.algorithms.density.dbscan.DBSCANClusterer

Bases: BaseClusterer

DBSCAN clustering for arbitrary-shaped clusters.

Features: - Density-based clustering (no need to specify n_clusters) - Automatic outlier detection (noise points labeled as -1) - Works with arbitrary cluster shapes - Supports custom distance metrics

Note

DBSCAN does not support prediction on new data. Use fit_predict on all data at once, or use a classifier trained on DBSCAN labels.

get_core_points()

Get indices of core points (points with >= min_samples neighbors).

Returns:

Type Description
ndarray

Array of core point indices

Raises:

Type Description
RuntimeError

If not fitted

get_n_clusters()

Get number of clusters found (excluding noise).

Returns:

Type Description
int

Number of clusters

Raises:

Type Description
RuntimeError

If not fitted

get_noise_points()

Get indices of points classified as noise.

Returns:

Type Description
ndarray

Array of indices where labels == -1

Raises:

Type Description
RuntimeError

If not fitted

Algorithms — Model-Based

bitbullet.cluster.algorithms.model_based.gmm.GMMClusterer

Bases: BaseClusterer

Gaussian Mixture Model for probabilistic clustering.

Features: - Soft clustering (probabilities for each cluster) - Models overlapping clusters - Multiple covariance types (full, tied, diag, spherical) - Supports convergence diagnostics

Note

GMM only works with numerical features. For mixed data, use K-Prototypes instead.

get_aic(data)

Compute Akaike Information Criterion (AIC) for model selection.

AIC is similar to BIC but with less penalty for model complexity. Lower is better.

AIC = -2 * log_likelihood + 2 * n_params

Parameters:

Name Type Description Default
data DataFrame

Data used for fitting (same data used in fit())

required

Returns:

Type Description
float

AIC score (lower is better)

Raises:

Type Description
RuntimeError

If not fitted

get_bic(data)

Compute Bayesian Information Criterion (BIC) for model selection.

BIC balances model fit with complexity. Lower is better. Use this for elbow-like plots when selecting optimal number of clusters.

BIC = -2 * log_likelihood + n_params * log(n_samples)

Parameters:

Name Type Description Default
data DataFrame

Data used for fitting (same data used in fit())

required

Returns:

Type Description
float

BIC score (lower is better)

Raises:

Type Description
RuntimeError

If not fitted

get_centroids()

Get cluster centroids (means) as DataFrame.

Returns:

Type Description
DataFrame

DataFrame with centroids

Raises:

Type Description
RuntimeError

If not fitted

get_covariances()

Get covariance matrices for each cluster.

Returns:

Type Description
ndarray

Covariance matrices (shape depends on covariance_type)

Raises:

Type Description
RuntimeError

If not fitted

get_weights()

Get mixture weights (prior probabilities of each cluster).

Returns:

Type Description
ndarray

Array of weights summing to 1.0

Raises:

Type Description
RuntimeError

If not fitted

predict_proba(data)

Get probability of each cluster for each data point.

Parameters:

Name Type Description Default
data DataFrame

Data to get probabilities for

required

Returns:

Type Description
ndarray

Array of shape (n_samples, n_clusters) with probabilities

Raises:

Type Description
RuntimeError

If not fitted

score(data)

Compute log-likelihood of data under the model.

Parameters:

Name Type Description Default
data DataFrame

Data to score

required

Returns:

Type Description
float

Log-likelihood score (higher is better)

Raises:

Type Description
RuntimeError

If not fitted

Feature Weighting

bitbullet.cluster.weights.categorical_weights.CategoricalWeights

Factory for calculating categorical feature weights.

Provides multiple data-driven weighting strategies: - Relevance: Combines entropy, cardinality, and missingness - Entropical: Information-theoretic weighting - Balanced: Based on category dominance patterns - Uniform: Equal weights for all features

calculate(data, method='relevance', categorical_columns=None) staticmethod

Calculate categorical feature weights.

Parameters:

Name Type Description Default
data DataFrame

Input data

required
method str

Weighting method ("relevance", "entropical", "balanced", "uniform")

'relevance'
categorical_columns Optional[List[str]]

List of categorical column names. If None, auto-detect.

None

Returns:

Type Description
Dict[str, float]

Dictionary mapping column names to weights

Raises:

Type Description
ValueError

If method is unknown

to_array(weights, columns) staticmethod

Convert weight dictionary to array in column order.

Parameters:

Name Type Description Default
weights Dict[str, float]

Weight dictionary

required
columns List[str]

Column names in desired order

required

Returns:

Type Description
ndarray

Weights as numpy array

validate_manual_weights(weights, columns) staticmethod

Validate and convert manual weights to dictionary format.

Parameters:

Name Type Description Default
weights Union[Dict[str, float], List[float], ndarray]

Manual weights as dict, list, or array

required
columns List[str]

Column names

required

Returns:

Type Description
Dict[str, float]

Dictionary mapping column names to weights

Raises:

Type Description
ValueError

If weights are invalid

bitbullet.cluster.weights.gamma_estimation.GammaEstimator

Factory for estimating gamma values in mixed-type clustering.

Gamma balances numerical (Euclidean) and categorical (matching) distances.

calculate(data, numerical_columns, categorical_columns, method='huang', sample_size=1000) staticmethod

Calculate gamma value(s) for mixed-type clustering.

Parameters:

Name Type Description Default
data DataFrame

Input data

required
numerical_columns List[str]

List of numerical column names

required
categorical_columns List[str]

List of categorical column names

required
method Union[str, float]

Estimation method ("huang", "variance_matching") or manual float value

'huang'
sample_size Optional[int]

Sample size for distance calculations (for efficiency). None = use all data.

1000

Returns:

Type Description
Union[float, ndarray]

Gamma value (float) or per-feature gamma values (np.ndarray)

Raises:

Type Description
ValueError

If method is unknown or data is invalid

calculate_with_weights(gamma, categorical_weights, categorical_columns=None) staticmethod

Combine gamma with per-feature categorical weights.

Parameters:

Name Type Description Default
gamma float

Base gamma value

required
categorical_weights Union[Dict[str, float], ndarray, List[float]]

Per-feature weights

required
categorical_columns Optional[List[str]]

Column names (required if weights is dict)

None

Returns:

Type Description
ndarray

Per-feature gamma values

Raises:

Type Description
ValueError

If weights format is invalid

validate_gamma(gamma, n_categorical_features) staticmethod

Validate gamma value(s).

Parameters:

Name Type Description Default
gamma Union[float, ndarray, str]

Gamma value, array, or method name

required
n_categorical_features int

Number of categorical features

required

Returns:

Type Description
Union[float, ndarray]

Validated gamma value(s)

Raises:

Type Description
ValueError

If gamma is invalid

Sample Size

bitbullet.cluster.utils.sample_size.SampleSizeEstimator

Intelligent sample size estimation for clustering.

Combines multiple estimation approaches: 1. Statistical power analysis (cluster detection) 2. Dimensionality-based rules 3. Algorithm-specific requirements 4. Adaptive stability detection (optional) 5. Computational budget constraints

Example

estimator = SampleSizeEstimator() estimate = estimator.estimate( data=df, algorithm='kmeans', numerical_columns=['age', 'income'], categorical_columns=['region'], min_cluster_proportion=0.02, ) print(f"Recommended: {estimate.recommended}") print(f"Reasoning: {estimate.reasoning}")

__init__(default_confidence=0.95, default_target_runtime=60.0, adaptive_pilot_size=500)

Initialize the estimator.

Parameters:

Name Type Description Default
default_confidence float

Default confidence level for statistical estimates

0.95
default_target_runtime float

Default target runtime in seconds

60.0
adaptive_pilot_size int

Sample size for adaptive stability testing

500

estimate(data, algorithm, numerical_columns=None, categorical_columns=None, min_cluster_proportion=0.02, confidence_level=None, expected_k=None, k_range=None, target_runtime_seconds=None, memory_limit_mb=None, run_adaptive=False, verbose=False)

Estimate optimal sample size for clustering.

Parameters:

Name Type Description Default
data DataFrame

The full dataset

required
algorithm str

Clustering algorithm ('kmeans', 'kprototypes', etc.)

required
numerical_columns Optional[List[str]]

List of numerical column names

None
categorical_columns Optional[List[str]]

List of categorical column names

None
min_cluster_proportion float

Minimum cluster size to detect (e.g., 0.02 = 2%)

0.02
confidence_level Optional[float]

Statistical confidence level (default: 0.95)

None
expected_k Optional[int]

Expected number of clusters (if known)

None
k_range Optional[range]

Range of K values to test (for auto-K)

None
target_runtime_seconds Optional[float]

Target runtime budget

None
memory_limit_mb Optional[float]

Memory limit in MB

None
run_adaptive bool

Whether to run adaptive stability detection

False
verbose bool

Print detailed estimation info

False

Returns:

Type Description
SampleSizeEstimate

SampleSizeEstimate with minimum, recommended, optimal, and reasoning

sample_data(data, estimate, stratify_columns=None, random_state=42)

Sample the data based on the estimate.

Parameters:

Name Type Description Default
data DataFrame

Full dataset

required
estimate SampleSizeEstimate

SampleSizeEstimate from estimate()

required
stratify_columns Optional[List[str]]

Columns to stratify by (preserves distribution)

None
random_state int

Random seed for reproducibility

42

Returns:

Type Description
Tuple[DataFrame, ndarray]

Tuple of (sampled_data, sample_indices)

bitbullet.cluster.utils.sample_size.SampleSizeEstimate dataclass

Result of sample size estimation.

to_dict()

Convert to dictionary for JSON serialization.

bitbullet.cluster.utils.sample_size.estimate_sample_size(data, algorithm='kmeans', numerical_columns=None, categorical_columns=None, min_cluster_proportion=0.02, **kwargs)

Convenience function to estimate optimal sample size.

Parameters:

Name Type Description Default
data DataFrame

The dataset to cluster

required
algorithm str

Clustering algorithm name

'kmeans'
numerical_columns Optional[List[str]]

Numerical feature columns

None
categorical_columns Optional[List[str]]

Categorical feature columns

None
min_cluster_proportion float

Minimum cluster size to detect

0.02
**kwargs

Additional arguments passed to SampleSizeEstimator.estimate()

{}

Returns:

Type Description
SampleSizeEstimate

SampleSizeEstimate with recommendations