Skip to main content

SublimeX: Supervised Bottom-Up Localized Multi-Representative Feature eXtraction for time series and spatial data

Project description

SublimeX

Supervised Bottom-Up Localized Multi-Representative Feature eXtraction

PyPI version Python 3.11+ License: MIT

SublimeX is an interpretable feature extraction framework for time series and spatial data. It discovers a minimal set of task-specific features through Bayesian optimization, where each feature has explicit, human-readable semantics.

Key Features

  • Minimal feature sets: Typically 5-15 features vs. thousands from other methods
  • Full interpretability: Each feature = statistic over optimized segment of transformed signal
  • Competitive performance: Matches deep learning on many tasks
  • Modular design: Custom transforms, objectives, and ML models

Installation

pip install sublimex

Or install from source:

git clone https://github.com/Prgrmmrjns/SublimeX.git
cd SublimeX
pip install -e .

Quick Start

import sublimex
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# Your data: list of arrays/DataFrames, one per channel
# Each array has shape (n_samples, n_time_points)
X = [channel1_data, channel2_data, channel3_data]  # 3 channels
y = labels  # Binary or continuous targets

# Split data (indices then index each channel by them)
idx_train, idx_test = train_test_split(
    range(len(y)), test_size=0.2, stratify=y, random_state=42
)
X_train = [x.iloc[idx_train] for x in X]
X_test = [x.iloc[idx_test] for x in X]
y_train, y_test = y[idx_train], y[idx_test]

# Fit SublimeX
model = sublimex.SublimeX(metric='auc', n_trials=100, verbose=True)
train_features = model.fit_transform(X_train, y_train)
test_features = model.transform(X_test)

# Use with any classifier
clf = RandomForestClassifier()
clf.fit(train_features, y_train)
predictions = clf.predict(test_features)

# Interpret discovered features
for desc in model.get_feature_descriptions():
    print(desc)

How It Works

SublimeX discovers discriminative features through a simple but effective process:

  1. Signal Transformation: Apply multiple transforms to create different "views" of the data (raw, z-score normalized, derivative, FFT power spectrum)

  2. Segment Optimization: Use Bayesian optimization (Optuna) to find segments that maximize downstream model performance

  3. Feature Extraction: Compute statistics (e.g., mean) over discovered segments

  4. Iterative Discovery: Repeat until adding new features no longer improves performance

Each discovered feature is fully interpretable:

"Mean of z-score normalized signal in channel 2, positions 40-60"

Configuration

Basic Parameters

import sublimex

model = sublimex.SublimeX(
    metric='auc',          # 'auc', 'accuracy', or 'rmse'
    n_trials=300,          # Optimization trials per feature
    max_features=None,     # Cap number of features (None = stop when no improvement)
    inner_cv=1,            # Internal CV folds (1 = single split)
    val_size=0.5,          # Validation size when inner_cv=1
    random_state=42,       # Reproducibility (CV + Optuna)
    verbose=True,          # Print progress
)

Custom Transforms

import sublimex

# Register a custom transform
def hilbert_envelope(data):
    from scipy.signal import hilbert
    return np.abs(hilbert(data, axis=-1))

sublimex.register_transform('hilbert', hilbert_envelope)

# Use specific transforms
model = sublimex.SublimeX(
    transforms={
        'raw': lambda x: x,
        'hilbert': hilbert_envelope,
    }
)

Custom Objective Functions

import sublimex
import numpy as np

# Create objective with custom aggregation
def rms(segment):
    """Root mean square."""
    return np.sqrt((segment ** 2).mean(axis=1, keepdims=True))

rms_objective = sublimex.create_custom_objective(rms, 'rms')
model = sublimex.SublimeX(objective_fn=rms_objective)

Custom ML Models

import sublimex
from sklearn.ensemble import RandomForestClassifier

# Wrap any sklearn estimator
rf = RandomForestClassifier(n_estimators=100)
model = sublimex.SublimeX(model=sublimex.SklearnModelWrapper(rf))

Extending SublimeX

SublimeX is designed so you can plug in your own components without forking the repo.

Component How to extend
Transforms sublimex.register_transform('name', callable) or pass transforms={'name': fn}. Each transform receives shape (..., n_time) and returns the same shape.
Objectives Implement (trial, ctx) -> float: use trial.suggest_* for segment/params, read ctx['transformed'], ctx['n_channels'], ctx['n_time'], etc., set ctx['last_feature'] when ctx['extract_only'] is True, else return mean CV score. See sublimex.objectives module docstring for full ctx fields.
Models Any object with evaluate(X_train, y_train, X_val, y_val, metric) -> float and test(X_train, y_train, X_test, y_test, metric) -> float. Optionally predict(X) / predict_proba(X) after fitting. See sublimex.models module docstring.

For ablation or fixed-size feature sets, set max_features=N so discovery stops after N features even if the metric could still improve.

Built-in Options

Transforms

Transform Description Use Case
raw Identity (original signal) Amplitude differences
zscore Z-score normalization Shape differences
derivative First-order gradient Rate of change, transitions
fft_power FFT power spectrum Frequency content, periodicity

Aggregations (for aggregate_objective)

Aggregation Description
mean Average value (default)
min, max Extreme values
range max - min
std Standard deviation
median Robust central tendency
argmin, argmax Position of extrema

Objectives

Objective Description
mean_objective Mean over segment (default, most interpretable)
aggregate_objective Choose from 8 aggregations
pattern_objective B-spline pattern matching

Visualization

import sublimex
import matplotlib.pyplot as plt

# Compare feature values across classes
fig = sublimex.plot_feature_distributions(features, y, feature_idx=0)
plt.show()

# Show where a feature's segment falls on the signal
fig = sublimex.plot_segment_on_signal(
    signal, model.extracted_features[0], model.n_time
)
plt.show()

# Compare all transforms for a sample
fig = sublimex.plot_transform_comparison(signal, sublimex.TRANSFORMS)
plt.show()

Saving and Loading

import sublimex

# Save discovered features
model.save_features('features.json')

# Load and reuse
new_model = sublimex.SublimeX()
new_model.load_features('features.json')
features = new_model.transform(X_new)

Complete Example: Synthetic Multi-Channel Time Series

"""Complete example with synthetic data."""
import sublimex
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
import lightgbm as lgb

# Generate synthetic multi-channel time series
np.random.seed(42)
n_samples, n_channels, n_bins = 500, 5, 200

X = []
y = np.random.randint(0, 2, n_samples)

for channel in range(n_channels):
    channel_data = []
    for i in range(n_samples):
        t = np.linspace(0, 4 * np.pi, n_bins)
        if y[i] == 1:
            # Class 1: Higher amplitude with distinctive peak
            signal = 2.0 * np.sin(t) + 1.5 * np.sin(2 * t)
            peak = n_bins // 2
            signal[peak-20:peak+20] += 1.5 * np.exp(-((np.arange(40) - 20) ** 2) / 50)
        else:
            # Class 0: Lower amplitude, more noise
            signal = 1.0 * np.sin(t) + 0.5 * np.sin(3 * t)
        signal += np.random.normal(0, 0.3, n_bins)
        channel_data.append(signal)
    
    X.append(pd.DataFrame(channel_data, columns=[f'bin_{j}' for j in range(n_bins)]))

# Split data
idx_train, idx_test = train_test_split(
    range(len(y)), test_size=0.2, stratify=y, random_state=42
)
X_train = [x.iloc[idx_train].astype(np.float32) for x in X]
X_test = [x.iloc[idx_test].astype(np.float32) for x in X]
y_train, y_test = y[idx_train], y[idx_test]

# Discover features with SublimeX
model = sublimex.SublimeX(metric='auc', n_trials=50, verbose=True)
train_features = model.fit_transform(X_train, y_train)
test_features = model.transform(X_test)

print(f"Discovered {train_features.shape[1]} features")

# Train classifier
clf = lgb.LGBMClassifier(n_estimators=100, verbose=-1)
clf.fit(train_features, y_train)
auc = roc_auc_score(y_test, clf.predict_proba(test_features)[:, 1])
print(f"Test AUC: {auc:.4f}")

# Interpret features
print("\nDiscovered Features:")
for desc in model.get_feature_descriptions():
    print(f"  {desc}")

# Visualize
import matplotlib.pyplot as plt

fig = sublimex.plot_feature_importance(
    clf.feature_importances_, 
    model.get_feature_descriptions()
)
plt.show()

Comparison with Other Methods

Method Features Interpretability Optimization
SublimeX 5-15 High (explicit segments) Bayesian
tsfresh 100-800 Medium (statistical) Filter
catch22 22 Medium (fixed set) None
MiniRocket ~10,000 Low Deterministic
RDST 2k-10k Medium (shapelets) Random

Citation

If you use SublimeX in your research, please cite:

@software{sublimex2025,
  title={SublimeX: Supervised Bottom-Up Localized Multi-Representative Feature eXtraction},
  author={Wolber, J.C.},
  year={2026},
  url={https://github.com/Prgrmmrjns/SublimeX}
}

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sublimex-0.1.1.tar.gz (22.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sublimex-0.1.1-py3-none-any.whl (17.6 kB view details)

Uploaded Python 3

File details

Details for the file sublimex-0.1.1.tar.gz.

File metadata

  • Download URL: sublimex-0.1.1.tar.gz
  • Upload date:
  • Size: 22.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for sublimex-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9507a3f42aefe7415c6bbc77deaed403a043b7d3e2aba8de2205ec22eb4ec5eb
MD5 f36d77ca6363dfa1dfee697c3e1b58f9
BLAKE2b-256 d610971b27858e98c8dc42b1490f5caad9ed6950bc8ec52290cf6fd412cd192c

See more details on using hashes here.

File details

Details for the file sublimex-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: sublimex-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 17.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for sublimex-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0182ca9ae531ada99670c342c52bd14968f70af0365156422a3ae9b1a113b330
MD5 4461e96af0486e40786fc533fd3e815a
BLAKE2b-256 f28405a9f949349ece9832d86390637e428ae3bea36e8e577b8e338c36161783

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page