Skip to main content

Skyulf Core

Skyulf Core (skyulf-core) is a standalone, installable Python ML library for teams who want sklearn's dependable estimators with a cohesive, Polars-friendly pipeline layer around them. It unifies preprocessing, classification, regression, clustering, text models, hyperparameter tuning, evaluation, and optional SHAP explanations behind one composable API.

Use it when you want to move from a notebook to a repeatable model artifact without assembling a different interface for every transformer and estimator. Skyulf builds on and is validated against scikit-learn rather than replacing it: sklearn remains the modeling foundation while Skyulf provides pipeline configuration, artifacts, metrics, and safe execution conventions.

Docs PyPI License Downloads issues contributors

Website & Documentation

Installation

Skyulf Core currently packages for Python 3.12+.

pip install skyulf-core

# EDA-focused install (core EDA + optional advanced EDA + visualization)
pip install skyulf-core[eda,viz]

# For visualization support (Rich dashboard + Matplotlib plots)
pip install skyulf-core[viz]

# For advanced EDA add-ons (sentiment + causal discovery)
pip install skyulf-core[eda]

# For hyperparameter tuning engines
pip install skyulf-core[tuning]

# For SHAP explainability
pip install skyulf-core[explainability]

# For dense SentenceEmbedder support
pip install skyulf-core[nlp]

# For imbalance-aware preprocessing (e.g., SMOTE)
pip install skyulf-core[preprocessing-imbalanced]

# For XGBoost modeling nodes
pip install skyulf-core[modeling-xgboost]

# For LightGBM modeling nodes
pip install skyulf-core[modeling-lightgbm]

# For geospatial feature engineering (H3 indexing, spatial stats)
pip install skyulf-core[geo]

# For text sentiment features
pip install skyulf-core[text]

# All non-geo optional runtime features
pip install skyulf-core[all]

all intentionally excludes the native geospatial stack; add [geo] only when you need geospatial nodes.

Quick start

import polars as pl
from skyulf import SkyulfPipeline

customers = pl.read_csv("customers.csv")  # contains a `purchased` target
pipeline = SkyulfPipeline(
    {
        "preprocessing": [
            {
                "name": "split",
                "transformer": "TrainTestSplitter",
                "params": {"target_column": "purchased", "test_size": 0.2, "random_state": 42},
            },
            {
                "name": "impute_income",
                "transformer": "SimpleImputer",
                "params": {"columns": ["income"], "strategy": "median"},
            },
            {
                "name": "encode_city",
                "transformer": "OneHotEncoder",
                "params": {"columns": ["city"], "drop_original": True, "handle_unknown": "ignore"},
            },
        ],
        "modeling": {
            "type": "logistic_regression",
            "params": {"max_iter": 500, "random_state": 42},
        },
    }
)

pipeline.fit(customers, target_column="purchased")
pipeline.save("customer_model.pkl")
predictions = SkyulfPipeline.load("customer_model.pkl").predict(pl.read_csv("new_customers.csv"))

See examples/00_quickstart.ipynb for a complete, executed save/load round trip.

How it fits together

flowchart LR
    subgraph Input
        A[Polars DataFrame]
    end
    subgraph SkyulfPipeline
        B[FeatureEngineer<br/>preprocessing steps]
        C[ModelEstimator<br/>sklearn-backed model]
    end
    subgraph Output
        D[Fitted artifact<br/>.pkl]
        E[Metrics report]
        F[Predictions]
    end
    A --> B --> C
    C --> E
    B -.fit once, reuse.-> D
    D --> C
    A2[New data] --> B
    C --> F

    style A fill:#e8f4fd,stroke:#1a73e8
    style A2 fill:#e8f4fd,stroke:#1a73e8
    style D fill:#fef7e0,stroke:#f9ab00
    style E fill:#e6f4ea,stroke:#188038
    style F fill:#e6f4ea,stroke:#188038

SkyulfPipeline wraps two collaborators: a FeatureEngineer that runs the declared preprocessing steps in order, and a ModelEstimator that fits/ applies the configured sklearn-backed model. fit() runs both once and returns a metrics report; predict() re-applies the already-fitted preprocessing artifacts (no re-fitting, no leakage) before calling the model.

Which API? Use SkyulfPipeline (above) by default. The Calculator/ Applier pairs it wraps (e.g. SimpleImputerCalculator/Applier) are lower-level — only use them directly to embed a single step in a custom (e.g. sklearn) pipeline.

Custom evaluation harnesses: if you want to compare raw sklearn/XGBoost/ CatBoost/etc. estimators against the exact preprocessed split SkyulfPipeline would use internally — instead of reimplementing the split/target-extraction/Polars-to-pandas conversion yourself and risking a different split than fit() actually used — call pipeline.get_fitted_split(data, target_column="..."). It runs the same configured preprocessing chain and returns (X_train, y_train, X_test, y_test) as plain pandas objects, raising a clear error if the config doesn't include a TrainTestSplitter step:

X_train, y_train, X_test, y_test = pipeline.get_fitted_split(customers, target_column="purchased")

Threshold tuning: the default decision rule (argmax for multiclass, 0.5 for binary) is rarely optimal for imbalanced classes or a metric you actually care about (F1, MCC, balanced accuracy, ...). pipeline.optimize_thresholds( X_val, y_val, metric=...) searches per-class thresholds against a metric you supply — evaluated on validation data you pass in explicitly (never the pipeline's internal split; get a clean holdout via get_fitted_split() above) — and predict(use_tuned_thresholds=True) then applies them. The same search is available as standalone array-level functions, from skyulf.modeling import optimize_thresholds, apply_thresholds, for use outside a pipeline. See the Threshold Tuning guide for signatures, a full example, and how the binary grid / multiclass Nelder-Mead search works.

Naming: preprocessing names are PascalCase (SimpleImputer, TrainTestSplitter), modeling names are snake_case (logistic_regression). A few preprocessing nodes are snake_case exceptions: feature_target_split, tokenizer, tfidf_vectorizer, count_vectorizer, hashing_vectorizer, sentence_embedder, feature_selection. Unsure of a name? Use NodeRegistry.list_transformers()/.list_models(). "Split" is a deprecated alias for "TrainTestSplitter".

Data leakage safety

Split before any data-dependent preprocessing. Fitting an imputer, scaler, learned encoder, feature selector, learned binning step, outlier detector, or Count/TF-IDF vectorizer on rows that include validation/test data contaminates evaluation. Put TrainTestSplitter first in a pipeline, or explicitly create a SplitDataset first and then fit FeatureEngineer/SkyulfPipeline.

The larger Skyulf platform validates DAGs and hard-blocks data-dependent nodes upstream of a train/test split. Skyulf Core is also useful on its own, so the same ordering is an essential API-level contract.

sequenceDiagram
    participant D as Raw data
    participant S as TrainTestSplitter
    participant Tr as Train split
    participant Te as Test split
    participant P as Learned steps<br/>(Imputer/Encoder/Scaler/...)
    participant M as Model

    D->>S: full dataset
    S->>Tr: train rows
    S->>Te: test rows (held out, untouched)
    Tr->>P: fit(train)
    P->>Tr: transform(train)
    Tr->>M: fit(model)
    P->>Te: transform(test)  Note over P,Te: uses train-fitted statistics only
    Te->>M: evaluate (honest, unseen)

Any step that learns a statistic (imputer medians, encoder categories, scaler mean/std, TF-IDF vocabulary, feature selectors, outlier detectors, learned binning) must come after the split in the preprocessing list. Deterministic, row-independent parses (string splitting, unit conversions, date-part extraction) are safe before the split. Read and run examples/01_house_prices_regression.ipynb to see this pattern applied to a real regression dataset, including a deliberate pre-split/post-split split of "safe" vs. "learned" feature steps.

Use the opt-in static check before fitting:

warnings = skyulf.validate_leakage_safety(config)
warnings = SkyulfPipeline(config).validate_leakage_safety()

Polars-native, no hidden pandas

Users can pass polars.DataFrame inputs directly. PolarsEngine and SklearnBridge convert Polars straight to NumPy at the sklearn boundary—there is no user-facing pandas round trip. Arrow integrations use the explicit to_arrow() path (and therefore pyarrow). Every notebook in examples/ uses Polars + NumPy only — no pandas import appears anywhere in the example code.

Examples

Runnable Jupyter notebooks in examples/, covering the full feature set end-to-end on real datasets. Open with jupyter lab or jupyter notebook from the repository root — see examples/README.md for dataset sourcing notes and a per-notebook breakdown of what's demonstrated.

# Notebook Dataset Task Highlights
00 00_quickstart.ipynb Synthetic Classification Config, fit, save/load, predict, geo features (GeoDistance + H3Index)
01 01_house_prices_regression.ipynb House Prices Regression EDA, leakage-safe null handling, outlier handling (Winsorize vs. IQR removal), Optuna tuning, SHAP
02 02_disaster_tweets_text_classification.ipynb Disaster Tweets Text classification TF-IDF, hash encoding, Naive Bayes vs. tuned LogReg vs. stacking, char n-gram experiment, sentence embeddings
03 03_mall_customers_segmentation.ipynb Mall Customers Clustering Unsupervised EDA, k-selection by silhouette, multi-algorithm comparison
04 04_forest_cover_multiclass_ensemble.ipynb Covertype Multiclass Ensembles, tuning, per-class metrics
05 05_santander_imbalanced_classification.ipynb Santander Imbalanced classification Drift checks, feature selection strategies (Variance/Correlation vs. Univariate vs. Model-Based), resampling-aware evaluation
06 06_credit_card_fraud_extreme_imbalance.ipynb Credit Card Fraud Extreme imbalance PR-AUC focus, precision/recall tradeoffs
07 07_spaceship_titanic_classification.ipynb Spaceship Titanic Classification Structured-string feature parsing, feature generation (interactions + polynomial), Grid vs. Random Search tuning, voting + stacking ensembles
08 08_online_retail_customer_segmentation.ipynb UCI Online Retail Clustering (RFM segmentation) Raw-transaction-to-RFM feature engineering, 4-algorithm comparison, business-named segments, bonus time-series features (DateFeatures/LagFeatures/RollingAggregate)

Automated EDA

Skyulf Core includes automated exploratory data analysis for Polars frames — data quality, distributions, outliers, correlations/target-association, optional temporal/geospatial analysis, and a PCA-based exploratory clustering pass, all from one .analyze() call.

import polars as pl
from skyulf import EDAAnalyzer, EDAVisualizer

df = pl.read_csv("data.csv")
profile = EDAAnalyzer(df).analyze(
    target_col="target",   # Optional: unlocks target-association analysis
    date_col="timestamp",  # Optional: unlocks temporal analysis
    lat_col="latitude",    # Optional: unlocks geospatial analysis
    lon_col="longitude",   # Optional
)

EDAVisualizer(profile, df).summary()  # Rich terminal dashboard (skyulf-core[viz])
EDAVisualizer(profile, df).plot()     # Matplotlib figures (skyulf-core[viz])

Everything the visualizer renders is also available as plain data on profile, so you can build your own dashboards, logs, or CI gates:

print(f"Rows: {profile.row_count}  Missing cells: {profile.missing_cells_percentage:.2f}%")

# Human-readable, prioritized data-quality findings
for alert in profile.alerts[:5]:
    print("-", alert.message)

# Actionable next-step suggestions (e.g. "consider log-transforming X")
for rec in profile.recommendations[:5]:
    print(f"- [{rec.action}] {rec.column or ''}: {rec.reason} -> {rec.suggestion}")

# Outlier detection (IsolationForest by default) across numeric columns
if profile.outliers is not None:
    print(f"Outliers: {profile.outliers.outlier_percentage:.1f}% ({profile.outliers.method})")

# Feature-vs-target association (ANOVA-style p-value + boxplot data)
income_interaction = next(
    (ti for ti in (profile.target_interactions or []) if ti.feature == "income"), None
)
if income_interaction is not None and income_interaction.p_value is not None:
    print(f"income vs target -> p-value: {income_interaction.p_value:.2e}")

# Rule-tree feature importances (a fast, model-free "what matters" signal)
if profile.rule_tree is not None:
    top = sorted(profile.rule_tree.feature_importances, key=lambda d: -d["importance"])[:5]
    for f in top:
        print(f"  {f['feature']:20s} {f['importance']:.4f}")

See any of examples/00_quickstart.ipynb through 08_online_retail_customer_segmentation.ipynb for the full EDA pass run against real datasets, both with and without a target column.

Features

mindmap
  root((Skyulf Core))
    Preprocessing
      Imputation / Encoding / Scaling
      Outlier detection / Binning
      Date, geospatial, lag/rolling, interaction features
      Text cleaning + Count/TF-IDF/Hashing vectorizers
    Modeling
      Classification / Regression
      Clustering
      Voting + Stacking ensembles
      Naive Bayes (text)
    Tuning
      Grid Search
      Random Search
      Optuna
    Evaluation
      Standardized metrics
      SHAP explainability
    EDA
      Data quality / distributions
      Outliers / temporal / geospatial
      Target correlation + rule-tree importances
    Engine
      Polars-native
      NumPy bridge to sklearn
      Explicit Arrow export
  • Unified pipelines: Serializable preprocessing and model artifacts with readable descriptions, fingerprints, model cards, and prediction APIs.
  • Leakage-aware execution: Split-first guidance and platform validation guard against fitting learned preprocessing on held-out rows.
  • Preprocessing and feature engineering: Cleaning, casting, imputation, encoders, scalers, outliers, selection, binning, dates, geospatial features, time-series lags/rolling windows, and interactions.
  • Modeling: sklearn-backed classification, regression, segmentation, and Voting/Stacking ensembles, plus text-specific Naive Bayes models.
  • Tuning and evaluation: Grid Search, Random Search, Optuna, standardized classification/regression/clustering metrics, and optional SHAP.
  • Automated EDA: Data quality, distributions, outliers, temporal, geospatial, and target analysis for Polars data.
  • Polars and Arrow: Native Polars support with direct NumPy bridging for sklearn and an explicit Arrow export path.

License

This project is licensed under the terms of the Apache 2.0 license.

Download files

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

Source Distribution

skyulf_core-0.5.6.tar.gz (578.9 kB view details)

Uploaded Source

Built Distribution

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

skyulf_core-0.5.6-py3-none-any.whl (364.4 kB view details)

Uploaded Python 3

File details

Details for the file skyulf_core-0.5.6.tar.gz.

File metadata

  • Download URL: skyulf_core-0.5.6.tar.gz
  • Upload date:
  • Size: 578.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for skyulf_core-0.5.6.tar.gz
Algorithm Hash digest
SHA256 8efa360d4ab6e2c7f8ad9f270c2165e4a3cfe84cbbd13d6b6e1f453997957a33
MD5 953f06027d1f9033593e629f1ac93c3a
BLAKE2b-256 391f0620236fe5fa736840d7aa6ebe8c50c9b5a949e42600456c40ee29c07de1

See more details on using hashes here.

Provenance

The following attestation bundles were made for skyulf_core-0.5.6.tar.gz:

Publisher: release.yml on flyingriverhorse/Skyulf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file skyulf_core-0.5.6-py3-none-any.whl.

File metadata

  • Download URL: skyulf_core-0.5.6-py3-none-any.whl
  • Upload date:
  • Size: 364.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for skyulf_core-0.5.6-py3-none-any.whl
Algorithm Hash digest
SHA256 f0c9ae0611d632e2cb8d2496e3a3d86afd6781638816c4a5d9165f0dcd28abf6
MD5 2f97a93b283d5263dfd1612c4adbe7ca
BLAKE2b-256 0c1ce58d26d54301ec7826fe93628a34a29d24830afa4ce228ce7b0f05252687

See more details on using hashes here.

Provenance

The following attestation bundles were made for skyulf_core-0.5.6-py3-none-any.whl:

Publisher: release.yml on flyingriverhorse/Skyulf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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