bitskitten
A small toolbox of things I kept re-typing in every EDA notebook:
quick dataset reports, a few plots, and sklearn-compatible encoders that can be
dropped straight into a Pipeline / GridSearchCV.
Nothing here is a framework. Each helper is one function or one class you can read in a minute.
Install
pip install bitskitten
The latest commit: pip install git+https://github.com/Caerno/bitskit.git.
On Kaggle / Colab the same lines work in a cell (!pip install ...).
For a kernel with no internet, add
the archive GitHub builds for every commit
as a dataset and install it from disk:
pip install --no-deps /kaggle/input/<your-dataset>/bitskit-main.zip
Requires Python ≥ 3.9, numpy, pandas, scikit-learn, matplotlib, seaborn.
Quick start
import pandas as pd
from bitskitten import informer, informer_print, imperfection, text_imperfection, NanFixer, Digitalize
df = pd.concat([train, test]) # test rows have no target -> NaN
informer_print(informer(df, "Survived"))
# Features: 12
# Observations: 891/418
# train dataset: 68.0%
# classes: 0: 61.6%, 1: 38.4%
imperfection(df) # what is missing and how varied it is
imperfection returns a frame sorted by NA share — share of missing values, number of
unique values, dtype and the values themselves. By default it shows non-numeric columns
only; numeric=True adds the rest, no_nan=True keeps only columns that actually have gaps.
informer switches to describe() of the target when it has more than max_classes distinct
values (regression).
text_imperfection(texts) is the same look at a corpus: one row per defect — empty, too short,
control characters, not NFKC-normalized, URL, HTML tag, base64-like run, repeated character or
word, Cyrillic and Latin mixed inside a word, few letters, exact duplicate — with share, count
and a few offending examples. Rows with zero hits are hidden (no_clean=False shows them).
Encoders
Both are TransformerMixin + BaseEstimator and pass check_estimator: statistics are
learned at fit and applied at transform, so the test set never leaks into its own filling,
a batch that lacks a category still comes out with the same columns, get_feature_names_out,
set_output(transform="pandas"), check_is_fitted, clone and grid search all work.
A frame in gives a frame out; an array in gives an array out (or a frame with set_output).
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
pipe = Pipeline([
("nan", NanFixer()), # fill the gaps
("dig", Digitalize()), # make everything numeric
("clf", RandomForestClassifier()),
])
grid = GridSearchCV(pipe, {
"nan__lim": [0.1, 0.225, 0.5],
"nan__method": ["mean", "median"],
"dig__mode": ["raw", "cut", "qcut"],
"dig__ncut": [5, 10],
})
NanFixer(lim=0.225, method="mean", filler="U", random_state=None) — picks a filling
strategy per column at fit time, measures it on the fit data, applies it at transform:
| column | condition (on the fit data) | fill |
|---|---|---|
| numeric | NA share ≤ lim |
method (mean/median) of the fit data |
| numeric | NA share > lim |
draws from N(mean, std) of the fit data, clipped to its range |
| object | one value dominates (>25%) | that mode |
| object | otherwise | the filler string |
Columns of any other dtype pass through untouched. The plan is in plan_ after fit.
Digitalize(alim=10, mode="raw", ncut=5) — turns a mixed frame into a numeric one:
| column | condition (on the fit data) | result |
|---|---|---|
| numeric | < 3 unique values | as is |
| any | < alim unique values |
one-hot over the categories seen at fit: col_value; an unseen value → all zeros |
| numeric | mode="cut" / "qcut" |
col_cut5 / col_qcut5: ncut equal-spaced / equal-sized bins with edges from the fit data; out-of-range values go to the edge bins |
| numeric | mode="raw" |
as is |
| non-numeric | ≥ alim unique values |
col_top5: frequency rank from the fit data (most frequent → 1, the tail and unseen values → ncut) |
Output column names are get_feature_names_out(); up to 0.2 they were pd.get_dummies
prefixes (col[:5]_value) and depended on the batch.
FeatureTrans(series) is the shared kit both encoders are built from, and it is
iterable: it yields every transformation that makes sense for that column's dtype —
handy when you want to throw candidate features at a selector.
candidates = pd.concat(list(FeatureTrans(df["Fare"])), axis=1)
# Fare_count, Fare_freqn, Fare_top5..., Fare_cut5..., Fare_qcut5..., Fare_sqrt, Fare_log2, Fare_pow2 ...
Its pieces are static methods you can also call directly: count, freq, top,
cut, qcut, math, bilabel, fuzzy_mean, robust, fill.
Metric
from bitskitten import harmonic_score, harmonic_scorer
harmonic_score(y_true, y_pred) # 1.0 perfect, 0.0 if any class is lost
cross_val_score(clf, X, y, scoring=harmonic_scorer)
GridSearchCV(pipe, grid, scoring=harmonic_scorer)
harmonic_score is the harmonic mean of every per-class precision and every per-class
recall — a class-agnostic generalization of F1 for when no class is "the positive one".
A class that is never predicted or never recovered pulls it to 0. harmonic_from_confusion(conf)
returns (score, precision[], recall[]) from a ready confusion matrix.
Plots
from bitskitten import plot_conf_map, plot_some_scatters
hm, precision, recall = plot_conf_map(confusion_matrix(y_true, y_pred), title="RF")
plot_conf_map draws the confusion matrix as shares, with a precision row, a recall
column and — in the corner — harmonic_score. Returns (harmonic_mean, precision, recall);
blind=True computes without drawing.
plot_some_scatters(X, y) — pairwise scatter plots of every column combination, coloured by y.
multicolumn(series, cols=5) folds a long series into several columns so a wide
df.sample().T fits on one screen.
Image compression demo
img_squeeze reduces the dimensionality of an RGBA image by unrolling channels into a
flat 2D matrix, running any sklearn decomposition on it and rebuilding the picture:
from sklearn.decomposition import PCA
from bitskitten import img_framaker
img_framaker(img, 20, PCA) # image restored from 20 components
Estimator from scratch
SlowPolyLinearReg(gener=[0, 1], norm=None, alpha=None) — linear regression written on
numpy/pandas, with polynomial and non-linear feature generation (0 = intercept,
integers = powers, "sqrt"/"cbrt"/"log2"/"ln"/"log10"), optional normalization
("minmax", "std", "max", "mean") and L2 regularization. Closed-form solution,
so it warns you about singular and ill-conditioned matrices instead of hiding them
(and falls back to the least-norm solution rather than giving up).
It exists to show the mechanics, not to compete with sklearn.linear_model.
The default gener=[0, 1] is the plain intercept + x. Drop the 0 only if you
really want the line forced through the origin.
model = SlowPolyLinearReg(gener=[0, 1, 2], norm="std", alpha=0.01)
model.fit(X, y)
model.score(X, y) # R²
Input
number("How many? ", t=int, op=">=", limit=0) — keeps asking until the answer parses
and satisfies the condition.
Development
pip install -e .[test]
pytest
The repo root is the package (flat layout), so the clone has to be in a folder named
bitskitten for pytest to import it; CI installs the wheel first for the same reason.
The drop-in zip in download/ is a plain stdlib call, no notebook needed:
python -m zipfile -c download/bitskitten.zip __init__.py classes.py input.py metrics.py output.py text.py
Metadata
Release files for bitskitten 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| bitskitten-0.3.0.tar.gz | 26.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bitskitten-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.1 kB
Release files / bitskitten-0.3.0.tar.gz
| Download URL | bitskitten-0.3.0.tar.gz |
|---|---|
| Size | 26.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d32a408b83692093c1a8fc1b2ec2216847044feed26823d916cf9ec4f4024d78
|
|
BLAKE2b-256 checksum How to use checksums |
eaa13ae9a6c9adc670c6f2f3abe660ea2d935d9cf13fced5e9cdf64c1e77b1b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logRelease files / bitskitten-0.3.0-py3-none-any.whl
| Download URL | bitskitten-0.3.0-py3-none-any.whl |
|---|---|
| Size | 22.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
765cac1d12b46a793ca92418e3786cfcfe1392693d709a645f283e6684574e4c
|
|
BLAKE2b-256 checksum How to use checksums |
8474edc31820aa9f435b4b77e47e7995e33868368bdd8bafaa22d2242475f612
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logSecurity for bitskitten 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for bitskitten-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.