A fancy version of base::cut().
cut() forces every interval in a vector to use the same open/closed convention. fancycut lifts that restriction: you can mix left-open and right-open intervals, intervals closed on both ends, intervals open on both ends, and even single-point buckets, all in the same call. The range of the input doesn't have to be fully covered by your intervals either — unmatched values (and NAs) can be routed to their own buckets.
install.packages("fancycut")
# or the development version
devtools::install_github("adamleerich/fancycut")
library(fancycut)
There are two functions: fancycut() and wafflecut(). They do the same thing — fancycut() just uses tag = value syntax for convenience, while wafflecut() takes intervals and buckets as separate vectors (handy when they're already stored as vectors, e.g. from a lookup table).
fancycut()Intervals are given as tag = "interval" pairs, where a bare number is treated as a single point.
fancycut(
x = -10:10,
Zero = 0,
Small = '[0,2)',
Medium = '[2,5]',
Large = '(5,10]'
)
# Basic low/high split
x <- seq.int(0, 1, 0.25)
fancycut(x, low = '[0, 0.5]', high = '(0.5, 1]')
# Not all values have to fall in a bucket
x <- seq.int(0, 1, 0.25)
fancycut(x, low = '(0.2, 0.3]', high = '(0.7, 0.8)')
# Route unmatched values with unmatched.bucket
x <- seq.int(0, 1, 0.25)
fancycut(x, low = '(0.2, 0.3]', high = '(0.7, 0.8)', unmatched.bucket = 'other')
# A point value: make the lower and upper bound equal
x <- seq.int(0, 1, 0.25)
fancycut(x, low = '[0, 0.5)', half = '[0.5,0.5]', high = '(0.5, 1]')
# Route NA values with na.bucket
x2 <- c(seq.int(0, 1, 0.25), NA)
fancycut(x2, low = '[0, 0.5)', high = '[0.5, 1]', na.bucket = 'missing')
wafflecut()Same behavior as fancycut(), but intervals and buckets are passed as parallel character vectors instead of named arguments.
wafflecut(-10:10, c('[0,2)', '[2,5)', '[5,10]'), c('Small', 'Medium', 'Large'))
wafflecut(-10:10, c('[0,0]', '(0,2]', '(2,5)', '[5,10]'), c('Zero', 'Small', 'Medium', 'Large'))
# Not all values have to fall in a bucket
x <- seq.int(0, 1, 0.25)
wafflecut(x, c('(0.2, 0.3)', '(0.7, 0.8)'), c('low', 'high'))
# Route unmatched values with unmatched.bucket
x <- seq.int(0, 1, 0.25)
wafflecut(x, c('(0.2, 0.3)', '(0.7, 0.8)'), c('low', 'high'), unmatched.bucket = 'other')
# Route NA values with na.bucket
x2 <- c(seq.int(0, 1, 0.25), NA)
wafflecut(x2, c('[0, 0.5)', '[0.5, 1]'), c('low', 'high'), na.bucket = 'missing')
| Argument | Description |
|---|---|
x |
a numeric vector to cut |
... (fancycut only) |
tag = value pairs — tags become bucket names, values are the interval definitions |
intervals (wafflecut only) |
a character vector of intervals |
buckets (wafflecut only) |
a character vector of level names, matched 1-to-1 with intervals
|
na.bucket |
the level assigned to NA values in x (default NA) |
unmatched.bucket |
the level assigned to values in x not covered by any interval (default NA) |
out.as.factor |
if TRUE (default), returns a factor; if FALSE, returns a character vector |
Intervals are written the same way you'd write them in interval notation: [ / ] for closed (inclusive) ends, ( / ) for open (exclusive) ends — for example '[0, 0.5)' or '(5, 10]'. A bare number (e.g. 0) is treated as a single-point bucket matching that exact value.
Written for a project that needed to cut a numeric vector into buckets, but where the interval boundaries weren't uniformly open or closed — some were even single points. Kept as its own package for CRAN. Interval-parsing improvements contributed by Richie Cotton.
CC0