PySuricata
Exploratory Data Analysis for Python, Built on Streaming Algorithms
What It Does
PySuricata generates self-contained HTML reports from pandas or polars DataFrames. Reports include per-column statistics, histograms, correlation chips, missing value analysis, and outlier detection.
Data is processed in chunks using streaming algorithms, so memory usage stays bounded regardless of dataset size.
It also does two things a profiler usually does not: summarize() returns the same numbers as a versioned JSON payload with no HTML in the way, and pysuricata check compares a dataset against a stored baseline and exits non-zero when a threshold is crossed — so the same single pass can run in a notebook and in CI.
Quick Start
Installation
# using uv (recommended)
uv add pysuricata
# or using pip
pip install pysuricata
With polars support (optional):
uv add pysuricata[polars]
# or: pip install pysuricata[polars]
Generate a Report
import pandas as pd
from pysuricata import profile
url = "https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
df = pd.read_csv(url)
report = profile(df)
report.save_html("titanic_report.html")
The examples below assume a df in scope. The Quick Start frame works, or anything of your own:
import numpy as np
import pandas as pd
rng = np.random.default_rng(0)
df = pd.DataFrame(
{
"age": rng.normal(30, 12, 800).round(1),
"fare": rng.gamma(2, 20, 800).round(2),
"sex": rng.choice(["male", "female"], 800),
"booked": pd.date_range("2024-01-01", periods=800, freq="h"),
}
)
Features
- Streaming architecture — Data is processed in configurable chunks, keeping memory bounded. Useful for datasets that don't fit in RAM.
- Pandas and Polars — Works natively with
pandas.DataFrame,polars.DataFrameandpolars.LazyFrame, plus Parquet files, DuckDB relations and Arrow batches. - Self-contained HTML — Single file with inline CSS, JS, and SVG charts. No external assets needed.
- Configurable — Control chunk size, sample size, correlations and more with keyword options, a
preset=, or aProfileConfig. - Reproducible — Seeded random sampling produces deterministic results across runs.
- Typed — Ships
py.typed;summarize()returns a payload carrying aschema_version. - CLI tool —
profile,summarizeandcheckfrom the command line.
How It Works
PySuricata uses well-known streaming algorithms from the academic literature:
| Algorithm | Purpose | Time | Space |
|---|---|---|---|
| Welford/Pébay | Exact mean, variance, skewness, kurtosis | O(1) per value | O(1) |
| KMV sketch | Distinct count estimation (~2.2% error) | O(log k) per value | O(k) |
| Misra-Gries | Top-k frequent values | O(1) amortized | O(k) |
| Reservoir sampling | Uniform random sample for quantiles | O(1) per value | O(s) |
k = sketch size (max_uniques, default 2048), s = sample size (numeric_sample_size, default 20 000)
KMV's relative standard error is 1/sqrt(k - 2), which is where the ~2.2% comes from. Approximate values are labelled approximate in the report and carry their error bound rather than being printed as exact integers.
All statistics are computed in a single pass over the data.
What's in a Report
Each column is analyzed based on its type:
- Numeric — Mean, variance, skewness, kurtosis, quantiles, histogram, outlier detection (IQR, MAD, z-score), correlations
- Categorical — Top values, distinct count, entropy, Gini impurity, string length statistics
- DateTime — Temporal range, hour/day/month distributions, monotonicity detection
- Boolean — True/false ratios, entropy, balance score
Plus dataset-level metrics: row/column counts, memory usage, missing value percentages, and duplicate row estimates.
Streaming Large Datasets
Process datasets larger than RAM by passing a generator:
import pandas as pd
from pysuricata import profile
def read_in_chunks():
for i in range(100):
yield pd.read_parquet(f"data/part-{i}.parquet")
report = profile(read_in_chunks())
report.save_html("large_report.html")
A Parquet path, a DuckDB relation or an Arrow source can be streamed directly, without loading the whole thing:
import duckdb
from pysuricata import profile
from pysuricata.sources import stream_duckdb, stream_parquet
report = profile(stream_parquet("data/events.parquet"))
relation = duckdb.connect("warehouse.db").sql("SELECT * FROM events")
report = profile(stream_duckdb(relation))
Statistics Only (No HTML)
Use summarize() for CI/CD quality checks. The payload carries a schema_version and is treated as a contract:
from pysuricata import summarize
stats = summarize(df)
assert stats["schema_version"] == 1
assert stats["dataset"]["missing_cells_pct"] < 5.0
assert stats["dataset"]["duplicate_rows_pct_est"] < 1.0
print(f"Mean age: {stats['columns']['age']['mean']:.1f}")
Comparing Two Datasets
compare() runs both through the same single pass and reports what moved:
from pysuricata import compare
last_week, this_week = df.iloc[:3], df.iloc[3:]
diff = compare(last_week, this_week).to_dict()
Configuration
Pass keyword options for the common cases:
from pysuricata import profile
report = profile(
df,
chunk_size=250_000, # default 50_000
sample=20_000,
seed=42,
correlations=True,
title="My Analysis",
)
Or start from a preset — "fast" or "thorough":
from pysuricata import profile
report = profile(df, preset="fast")
For everything else, build a ProfileConfig. Keyword options and config= are mutually exclusive:
from pysuricata import profile, ProfileConfig
config = ProfileConfig()
config.compute.chunk_size = 250_000
config.compute.random_seed = 42
config.compute.corr_threshold = 0.5
config.render.title = "My Analysis"
report = profile(df, config=config)
See the Configuration Guide for all options.
CLI
# Generate an HTML report
pysuricata profile data.csv --output report.html
# Get JSON statistics
pysuricata summarize data.csv
# Compare against a stored baseline; exit non-zero when a threshold is crossed
pysuricata check data.csv --write-baseline baseline.json
pysuricata check data.csv --baseline baseline.json --max-missing-pct 5
check exits 0 on pass, 1 when a threshold is crossed, and 2 when the check could not run — so it drops into CI without a wrapper.
Documentation
Contributing
Contributions are welcome. See the Contributing Guide.
git clone https://github.com/alvarodiez20/pysuricata.git
cd pysuricata
uv sync --dev
uv run pytest
License
MIT License. See LICENSE for details.
Acknowledgments
Built using algorithms from:
- Welford, B.P. (1962) — Streaming moments
- Pébay, P. (2008) — Parallel merging of moments
- Bar-Yossef, Z. et al. (2002) — KMV distinct count estimation
- Misra, J. & Gries, D. (1982) — Streaming heavy hitters
Named after suricatas (meerkats) — small, vigilant animals that work cooperatively and thrive in harsh environments with limited resources.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pysuricata-0.1.1.tar.gz.
File metadata
- Download URL: pysuricata-0.1.1.tar.gz
- Upload date:
- Size: 861.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a3b8c7bf43d284d305dd0e18e47d2816238f8135b06a85ab0b501934115b3dc
|
|
| MD5 |
2000a1881afb0a34ca72cabffa4c306a
|
|
| BLAKE2b-256 |
f70abaf9f14f57c83ffa74dfef9dc4fb32c5f5d105b5311a8763ce697e9d9975
|
Provenance
The following attestation bundles were made for pysuricata-0.1.1.tar.gz:
Publisher:
cd.yml on alvarodiez20/pysuricata
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysuricata-0.1.1.tar.gz -
Subject digest:
2a3b8c7bf43d284d305dd0e18e47d2816238f8135b06a85ab0b501934115b3dc - Sigstore transparency entry: 2498408586
- Sigstore integration time:
-
Permalink:
alvarodiez20/pysuricata@b631e05f0acac8ec75d95d09f91342206484b684 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/alvarodiez20
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@b631e05f0acac8ec75d95d09f91342206484b684 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pysuricata-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pysuricata-0.1.1-py3-none-any.whl
- Upload date:
- Size: 659.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2018f3767f2fc5edeb46610fe80633d447e393fc90d5c28b8bf549aaf6cc2d6
|
|
| MD5 |
68b70a41430f6ad66f0585b92e3d1343
|
|
| BLAKE2b-256 |
36cbf84323995b7cd68e5b0667425036bc0f8158917fa8b348bb85a8f2be624e
|
Provenance
The following attestation bundles were made for pysuricata-0.1.1-py3-none-any.whl:
Publisher:
cd.yml on alvarodiez20/pysuricata
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pysuricata-0.1.1-py3-none-any.whl -
Subject digest:
a2018f3767f2fc5edeb46610fe80633d447e393fc90d5c28b8bf549aaf6cc2d6 - Sigstore transparency entry: 2498408595
- Sigstore integration time:
-
Permalink:
alvarodiez20/pysuricata@b631e05f0acac8ec75d95d09f91342206484b684 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/alvarodiez20
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
cd.yml@b631e05f0acac8ec75d95d09f91342206484b684 -
Trigger Event:
push
-
Statement type: