
Binning Numerical Variables using Custom Cutpoints
Source:R/ob_binning_cutpoints_num.R
ob_cutpoints_num.RdThis function applies user-defined binning to a numerical variable by using specified cutpoints to create intervals and calculates Weight of Evidence (WoE) and Information Value (IV) for each interval bin.
Value
A list containing:
woefeatureNumeric vector of WoE values corresponding to each observation in the input
featurewoebinData frame with one row per bin containing:
id: Sequential bin identifierbin: The bin interval notation (e.g., "(10.00;20.00]")count: Total number of observations in the bincount_pos: Number of positive outcomes (target=1) in the bincount_neg: Number of negative outcomes (target=0) in the binwoe: Weight of Evidence for the biniv: Information Value contribution of the bin
cutpointsThe sorted numeric cutpoints, exposed at the top level so this result can be reassembled into
ob_apply_woe_num'sobresultsargument (see Details)idSequential bin identifier, same as
woebin$id; duplicated at the top level for the same reason ascutpoints
Details
The function takes a numeric vector of cutpoints that define the boundaries
between bins. For n cutpoints, n+1 bins are created, each
right-closed – \((a, b]\) – matching ob_apply_woe_num's
default (include_upper_bound = TRUE) and the convention
obwoe/obwoe_apply use throughout the package:
Bin 1: \((-\infty, cutpoint_1]\)
Bin 2: \((cutpoint_1, cutpoint_2]\)
...
Bin n+1: \((cutpoint_n, +\infty)\)
result does not have the exact shape ob_apply_woe_num
expects as obresults (which needs top-level cutpoints,
woe and id), because woebin also carries count/
iv columns. To round-trip, reassemble it:
list(cutpoints = result$cutpoints, woe = result$woebin$woe, id =
result$id, bin = result$woebin$bin). Before 1.13.1, result carried
no id or top-level cutpoints at all (so a round trip through
ob_apply_woe_num was not even possible without reconstructing
them by hand), and the bin boundaries were left-closed \([a, b)\), the
opposite of ob_apply_woe_num's include_upper_bound =
TRUE default – a value that landed exactly on a cutpoint got a different,
often sign-flipped, WoE depending on whether it went through the fit or the
apply side.
Note
Target variable must contain only 0 and 1 values.
Cutpoints are sorted automatically in ascending order.
Interval notation uses "(" for exclusive and "]" for inclusive bounds – a value exactly equal to a cutpoint falls in the bin that ends at that cutpoint.
Infinite values in feature are handled appropriately.
Examples
# Sample data
feature <- c(5, 15, 25, 35, 45, 55, 65, 75)
target <- c(0, 0, 1, 1, 1, 1, 0, 0)
# Define custom cutpoints
cutpoints <- c(30, 60)
# Apply binning
result <- ob_cutpoints_num(feature, target, cutpoints)
# View bin statistics
print(result$woebin)
#> id bin count count_pos count_neg woe iv
#> 1 1 (-Inf;30.00] 3 1 2 -0.6931472 0.1732868
#> 2 2 (30.00;60.00] 3 3 0 8.9226583 6.6911015
#> 3 3 (60.00;+Inf] 2 0 2 -8.5171932 4.2577449
# View WoE-transformed feature
print(result$woefeature)
#> [1] -0.6931472 -0.6931472 -0.6931472 8.9226583 8.9226583 8.9226583 -8.5171932
#> [8] -8.5171932
# Round-trip through ob_apply_woe_num() with its defaults
apply_obj <- list(
cutpoints = result$cutpoints, woe = result$woebin$woe,
id = result$id, bin = result$woebin$bin
)
woe_new <- ob_apply_woe_num(apply_obj, feature)
stopifnot(isTRUE(all.equal(woe_new$woe, result$woefeature)))