Worked examples for every pickbuckets bucketer and policy. For a terse symbol
lookup see api-reference.md; for the optional dataframe and
scikit-learn adapters see integrations.md.
- The common bucketer shape
- Numeric buckets
- Categorical buckets
- AutoBucket
- Supervised buckets
- Policies and errors
- Rule portability
- Streaming (experimental)
- Plotting
Every single-column bucketer follows the same shape:
bucket.fit(values) # supervised bucketers: fit(values, y)
bucket.transform(new_values)
bucket.summary()
bucket.to_dict()
bucket.to_json()transform() uses only the saved rules_ object; it does not need the original
training data.
from pickbuckets import EqualWidthBucket
bucket = EqualWidthBucket(n_bins=4, labels="ordinal")
bucket.fit([0, 10, 20, 30, 40])
assert bucket.transform([5, 15, 40]) == [0, 1, 3]Intervals are left-closed and right-open, except the final interval, which is closed on both sides so the maximum fitted value maps into the last bucket.
from pickbuckets import EqualFrequencyBucket
bucket = EqualFrequencyBucket(n_bins=4, duplicates="drop")
bucket.fit([1, 1, 2, 3, 5, 8, 13, 21])
print(bucket.rules_.edges)
print(bucket.transform([1, 4, 21]))Repeated values can produce duplicate quantile edges. By default this raises an
error. Use duplicates="drop" to keep only the unique intervals that can be
formed.
from pickbuckets import CustomBoundaryBucket
bucket = CustomBoundaryBucket(
edges=[0, 18, 35, 60, 100],
labels=["child", "young_adult", "adult", "senior"],
boundary_strategy="error",
).fit()
assert bucket.transform([18, 35, 60]) == ["young_adult", "adult", "senior"]Custom edges must be sorted and unique. Open-ended boundaries are supported:
bucket = CustomBoundaryBucket(
edges=[float("-inf"), 0, float("inf")],
labels=["negative", "non_negative"],
).fit()When exported to JSON, infinite edges are encoded as the strings "-inf" and
"inf" so the file remains portable JSON.
RareCategoryBucket keeps frequent categories and maps rare or unseen
categories to a fallback label by default.
from pickbuckets import RareCategoryBucket
bucket = RareCategoryBucket(min_frequency=2, other_label="Other")
bucket.fit(["FR", "FR", "US", "DE"])
assert bucket.transform(["FR", "US", "CA", None]) == [
"FR",
"Other",
"Other",
"Missing",
]min_frequency accepts either an absolute count (2) or a ratio (0.05).
Category keys are stored as strings in the exported rule so JSON payloads remain
portable.
AutoBucket fits one rule per column and chooses the strategy from each column
dtype. Numeric columns use equal-frequency binning by default; categorical and
string columns use rare-category folding. Explicit per-column overrides win over
automatic inference.
from pickbuckets import AutoBucket, EqualWidthBucket
frame = {
"age": [18, 25, 34, 52, 70],
"country": ["FR", "FR", "US", "DE", "DE"],
}
auto = AutoBucket(
n_bins=3,
min_frequency=2,
overrides={"age": EqualWidthBucket(n_bins=3, labels="interval")},
).fit(frame)
print(auto.transform(frame))
print(auto.summary())Plain dict[str, list], pandas DataFrame, Polars DataFrame, and Polars
LazyFrame inputs return the same container type on transform. Transform input
must contain the same column names fitted by the bucketer; missing or unexpected
columns raise InvalidBucketingError. See integrations.md for
the dataframe adapters.
Supervised bucketers require fit(values, y) and never require y during
transform(). Target data is stored only as aggregate fit statistics such as
counts, target rates, WoE, and IV.
from pickbuckets import WoEBucket
bucket = WoEBucket(
n_bins=3,
output="woe",
min_bin_size=0.1,
monotonic="auto",
smoothing=0.5,
)
bucket.fit([10, 20, 30, 40, 50, 60], [0, 0, 0, 1, 1, 1])
print(bucket.transform([15, 45]))
print(bucket.iv_summary())
print(bucket.summary_table())DecisionTreeBucket learns numeric edges from a shallow scikit-learn decision
tree, so it requires pickbuckets[sklearn]. ChiMergeBucket and WoEBucket
are dependency-free. WoEBucket supports count- or ratio-based min_bin_size
constraints and optional monotonic WoE constraints with monotonic="ascending",
"descending", or "auto". ExternalSplitBucket imports externally learned
split points, for example from OptBinning, into a normal portable numeric rule.
Policies are stored on the unified rule and survive serialization.
Missing values:
missing_strategy="separate"maps missing values tomissing_label(default:"Missing").missing_strategy="most_frequent"maps missing values to the most frequent fitted output label. The learned replacement is stored on the rule asmissing_label.missing_strategy="propagate"returns the original missing value.missing_strategy="error"raisesMissingValueError.
Numeric boundaries:
boundary_strategy="clip"maps values below/above the fitted range to the first/last label.boundary_strategy="underflow_overflow"maps values below/above the fitted range tounderflow_labelandoverflow_label.boundary_strategy="error"raisesBoundaryError.
Unknown categories:
unknown_category_strategy="other"maps unseen categories toother_label.unknown_category_strategy="missing"maps unseen categories tomissing_label.unknown_category_strategy="keep"returns unseen categories as strings.unknown_category_strategy="error"raisesUnknownCategoryError.
Malformed numeric transform values raise InvalidBucketingError. Data-related
errors include the fitted feature name when one is available.
Every fitted bucketer exposes a rules_ object:
from pickbuckets import EqualWidthBucket
bucket = EqualWidthBucket(n_bins=2).fit([0, 10])
payload = bucket.to_json()
restored = EqualWidthBucket.from_json(payload)
assert restored.transform([1, 9]) == bucket.transform([1, 9])You can also apply a saved rule without constructing the original bucketer:
from pickbuckets import Rule
from pickbuckets.runtime import apply_rule
rule = Rule.from_json(payload)
assert apply_rule(rule, [0, 5, 10, None]) == [0, 1, 1, "Missing"]Rules include:
schema_versionpackage_versionkind- numeric
edgesor categoricalcategory_mapping - output
labels - active missing, boundary, and unknown-category policies
- underflow and overflow labels
fit_stats
Loading a rule with an unsupported major schema version or an unknown policy
value raises RuleSchemaError.
pickbuckets.experimental holds online approximations, kept deliberately
separate from the exact bucketers. StreamingEqualFrequencyBucket fits
approximate quantile edges from data that does not fit in memory, then
materializes a normal portable Rule.
from pickbuckets.experimental import StreamingEqualFrequencyBucket
bucket = StreamingEqualFrequencyBucket(n_bins=10, max_centroids=256)
for chunk in chunks_of_data: # data need not fit in memory
bucket.partial_fit(chunk)
bucket.finalize() # build a standard portable Rule
bucket.transform([1.0, 2.0]) # applies via the normal runtime
print(bucket.to_json())Only fitting is approximate. Error is bounded by max_centroids and the edges
match the exact bucketer once it reaches the number of distinct values. APIs in
experimental may change between minor releases.
Install with pickbuckets[plot]. The helpers in pickbuckets.plotting return a
matplotlib Axes so you can customize and decide when to display.
from pickbuckets import WoEBucket
from pickbuckets.plotting import plot_bucket_counts, plot_target_rate, plot_woe
bucket = WoEBucket(n_bins=4).fit([10, 20, 30, 40, 50, 60], [0, 0, 0, 1, 1, 1])
ax = plot_woe(bucket)
ax.set_title("WoE by score band")
ax.figure.savefig("woe.png")plot_bucket_counts works for any fitted bucketer or Rule; plot_target_rate
and plot_woe require a supervised bucketer. Plotting is never required for the
core package.