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
X_train, X_test, y_train, y_test = train_test_split(
    range(len(y)), test_size=0.2, stratify=y, random_state=42
)
X_train = [x.iloc[X_train] for x in X]
X_test = [x.iloc[X_test] for x in X]
y_train, y_test = y[X_train], y[X_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
    inner_cv=1,            # Internal CV folds (1 = single split)
    val_size=0.5,          # Validation size when inner_cv=1
    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))

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.0.tar.gz (20.8 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.0-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for sublimex-0.1.0.tar.gz
Algorithm Hash digest
SHA256 10a811dbdb24ae97a1221bcb4dfc4ba4c024e3e0233c07f64b4afb521fddbc60
MD5 fa4d56bc614054042d5afb23bf5eea71
BLAKE2b-256 103c21b508a38480b77dd6ed054f555bcb1b8f9589cad05329deb311ba47db49

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for sublimex-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fd1d1706ea80ccdc6ec382c7bbbe512c541a00644087480944a5bb1f867cdfd2
MD5 7e4673a3cc58de19930feb8a1f21487b
BLAKE2b-256 4bc741cfdc3ac43e50c15299bd83f99c3fc17d35fdc10f98c1802df51996eb48

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