themis (development version)
New steps
step_cluster_centroids()(and its direct-implementation counterpartcluster_centroids()) was added. It under-samples the majority classes by running k-means within each class and replacing the class with one representative per cluster, either the centroid itself (voting = "soft") or the observation closest to it (voting = "hard") (#318).step_cnn()(and its direct-implementation counterpartcnn()) was added. It under-samples the majority classes using Condensed Nearest Neighbors, keeping only a consistent subset of observations that correctly classifies the data using a 1-nearest-neighbor rule (#113).step_enn()(and its direct-implementation counterpartenn()) was added. It cleans the data using the Edited Nearest Neighbors rule, removing observations whose class differs from the majority of their nearest neighbors (#115).step_instance_hardness()(and its direct-implementation counterpartinstance_hardness()) was added. It under-samples the majority classes by removing the observations that are hardest to classify, estimated using the k-Disagreeing Neighbors measure (#172).step_kmeans_smote()(and its direct-implementation counterpartkmeans_smote()) was added. It over-samples the minority classes with KMeans-SMOTE, which clusters the predictor space, generates new examples only inside the clusters where the minority class is dominant, and gives sparser clusters more of the new examples (#317).step_ncl()(and its direct-implementation counterpartncl()) was added. It cleans the data using the Neighborhood Cleaning Rule, removing majority class observations that are noisy or that pollute the neighborhood of minority class observations (#116).step_oss()(and its direct-implementation counterpartoss()) was added. It under-samples the majority classes using One-Sided Selection, combining Condensed Nearest Neighbors to reduce redundant majority class observations with Tomek’s links to remove majority class observations on the decision boundary (#114).step_smogn()(and its direct-implementation counterpartsmogn()) was added. It over-samples rare regions of a numeric outcome for imbalanced regression using a combination of SMOTE-style interpolation and Gaussian noise, while under-sampling common regions (#49).step_smoten()(and its direct-implementation counterpartsmoten()) was added. It over-samples the minority classes for data sets where all predictors are categorical, using the Value Difference Metric to find nearest neighbors and majority voting to generate new examples (#54).step_svmsmote()(and its direct-implementation counterpartsvmsmote()) was added. It over-samples the minority classes near the decision boundary by fitting a support vector machine and generating new examples around the minority class support vectors, interpolating toward minority neighbors where majority neighbors dominate the boundary and extrapolating outward where the support vector sits in a dense minority region (#170).
Improvements
Added a new “Common pitfalls” article on resampling only the training set (
skip = TRUE) and avoiding cross-validation leakage (#320).Added a new “Methods overview” article organizing the sampling steps into a taxonomy and documenting the SMOTE + ENN / SMOTE + Tomek composition (#321, #319).
Added a new article explaining how
over_ratioandunder_ratiowork (#141).The
over_ratioandunder_ratioarguments now accept a named numeric vector in addition to a single number, giving each outcome level its own sampling target. The names are levels of the outcome and the values are ratios of the reference class, exactly as in the single-number case, soover_ratio = c(a = 1, b = 0.5)brings"a"up to the size of the majority level and"b"up to half of it. Levels that are not named are left untouched, as are rows with a missing outcome, and supplying a vector means the argument can no longer be tuned.step_rose()is the exception and still requires a single number, since itsover_ratioscales the size of the total generated sample rather than setting a target per class (#323).The direct-implementation functions
adasyn(),bsmote(),instance_hardness(),nearmiss(),smote(),smoten(),smotenc(), andsvmsmote()now reject a negativeover_ratioorunder_ratio, matching the validation their recipe steps already performed (#323).Added standalone
rose()function as a thin wrapper aroundROSE::ROSE(), making it consistent with the other algorithms in the package that expose a direct implementation alongside their recipe step (#195).All upsampling steps gain an
indicator_columnargument. When set, a logical column is added to the baked data marking rows added by the step (TRUE) vs rows from the original data (FALSE). Forstep_rose(), all rows areTRUEsince ROSE generates a fully synthetic dataset (#58).step_adasyn(),step_cnn(), andstep_oss()(and their direct-implementation counterpartsadasyn(),cnn(), andoss()) are now faster on large data.step_adasyn()computes the full-data nearest neighbors only for the minority observations it needs, andstep_cnn()/step_oss()compute the condensation nearest neighbors for all candidates in one batch per store change instead of once per candidate. Results are unchanged (#257).step_adasyn(),step_bsmote(),step_nearmiss(),step_smote(), andstep_tomek()(and their direct-implementation counterpartsadasyn(),bsmote(),nearmiss(),smote(), andtomek()) gain adistanceargument to control which distance metric is used for nearest neighbor calculations. Supported metrics are"euclidean"(default),"cosine","mahalanobis","manhattan", and"chebyshev"(#171).The
distanceargument of every step that performs nearest neighbor calculations gains four probability-divergence metrics:"squared_chord","matusita","hellinger", and"bhattacharyya". These treat each row as a distribution over the predictors and so require non-negative values, with"hellinger"and"bhattacharyya"further requiring each row to sum to 1. All four run on the fast approximate nearest neighbor path and scale to large data (#234).The
distanceargument of every step that performs nearest neighbor calculations gains nine further probability-divergence metrics, provided by the philentropy package:"canberra","soergel","lorentzian","jeffreys","topsoe","jensen-shannon","jensen_difference","taneja", and"kumar-johnson". philentropy is an optional dependency, and since these metrics compute an exact all-pairs distance matrix they are best suited to smaller datasets (#234).distance = "mahalanobis"now fails with an informative error when the predictors have a singular covariance matrix, instead of a low-level message fromchol()or silently returning distances computed from a numerically unusable inverse. This covers collinear and constant predictors as well as duplicated rows (#246).step_adasyn(),step_bsmote(),step_nearmiss(),step_smote(), andstep_smotenc()now document the minimum number of observations needed to perform the algorithm (#104).step_bsmote()now sets the tuning range of itsneighborsparameter toc(1, 10), matching the other steps that tuneneighbors(#254).step_downsample()gains areplacementargument. When set toTRUEthe under-sample is drawn with replacement, giving a bootstrapped under-sample where the same row can be selected more than once. The defaultFALSEkeeps the current behavior (#325).step_enn()(and its direct-implementation counterpartenn()) gain atimesargument to apply the cleaning repeatedly, stopping early on convergence, which corresponds to Repeated Edited Nearest Neighbors (RENN) (#173).step_enn()(and its direct-implementation counterpartenn()) gain anall_kargument to apply the cleaning with an increasing number of neighbors, from 1 up toneighbors, which corresponds to All k-Nearest Neighbors (AllKNN) (#174).step_enn()now documents thatneighborsdefaults to3(rather than5as in the over-sampling steps) and exposesall_kas a tunable parameter (#254).step_ncl()now documents thatneighborsdefaults to3(rather than5as in the over-sampling steps) and exposesthreshold_cleanas a tunable parameter (#254).step_nearmiss()(and its direct-implementation counterpartnearmiss()) gains aversionargument to select between the NearMiss-1, NearMiss-2, and NearMiss-3 variants of Mani & Zhang (2003), together withn_neighbors_ver3to control the size of the NearMiss-3 candidate pool. The defaultversion = 1preserves the previous behavior (#279).step_nearmiss()andstep_tomek()gain adistance_withargument to control which variables are used for distance calculations. This allows the steps to be used when non-numeric predictor variables are present in the data (#166).step_rose()now validates predictor types duringprep(), giving a clear error for unsupported types consistent with the other sampling steps instead of relying onROSE::ROSE()to fail downstream (#265).step_rose()androse()now have improved documentation forminority_prop, clarifying that it controls the proportion of synthetic observations from the minority class, and how it differs fromover_ratio(#144).step_rose()(and its direct-implementation counterpartrose()) now validate thatminority_propis at most 1, since it is a proportion (#269).step_smogn()now exposesthresholdas a tunable parameter (#254).step_smoten()now gains anindicator_columnargument for parity with the other over-sampling steps. When set, a logical column is added to the baked data marking synthetic rows (#253).step_smotenc()now validates that all predictors are numeric or nominal, erroring on unsupported column types such as dates instead of failing later (#254).step_svmsmote()(and its direct-implementation counterpartsvmsmote()) gainsm_neighborsandout_steparguments, which were previously hard-coded, controlling how many neighbors are used to label support vectors as noise, danger, or safe and how far new examples are extrapolated from safe support vectors.m_neighborsis also tunable, and the fixed choices made when fitting the support vector machine are now documented (#270).step_svmsmote()now sets the tuning range of itsneighborsparameter toc(1, 10), matching the other steps that tuneneighbors(#254).
Bug fixes
Over-sampling steps (
step_smote(),step_adasyn(),step_bsmote(),step_svmsmote(),step_smoten(),step_smotenc(),step_rose(), andstep_smogn()) now error when supplied a case weights column instead of silently filling synthetic rows’ weights withNA. These steps have never supported case weights (#243).All sampling steps now handle an unused (zero-count) factor level in the outcome gracefully, dropping it with a warning before computing sampling targets instead of deleting all rows or erroring (#238).
step_smote(),step_adasyn(),step_bsmote(),step_svmsmote(),step_smoten(), andstep_smotenc()(and their direct-implementation counterparts) now round the fractional oversampling target instead of truncating it, so a fractionalover_ratiolands on the nearest integer count (#248).Nearest-neighbor computations in
step_adasyn(),step_enn(),step_ncl(),step_smote(),step_smotenc(), andstep_tomek()(and their direct-implementation counterparts) now exclude each observation from its own neighbor list by row index rather than assuming it is always the first neighbor returned. Exact-duplicate coordinates could previously leave a point as its own neighbor or make the farthest candidate unreachable (#247).step_adasyn()(and its direct-implementation counterpartadasyn()) now weights minority observations by their exact majority-neighbor count. An off-by-one subtraction previously undercounted majority neighbors, zeroing out the weight of border points with a single majority neighbor and biasing sampling away from the class boundary (#239).step_adasyn()(and its direct-implementation counterpartadasyn()) no longer errors with a cryptic message when a minority class is well separated from the majority classes; it now falls back to uniform sampling and checks the minority class size before sampling (#240).step_bsmote()(and its direct-implementation counterpartbsmote()) now selects the correct “danger” observations on the class border. The danger criterion had inverted the roles of minority and majority neighbors, causing it to oversample safe interior points instead of borderline ones (#235).step_bsmote()(and its direct-implementation counterpartbsmote()) withall_neighbors = TRUEnow seeds synthetic points only from minority-class danger observations and takes a reduced step toward majority-class neighbors, matching borderline-SMOTE2. Previously it could seed from border-adjacent majority rows, generating minority-labeled points around majority centers (#242).step_cnn(),step_oss(), andstep_smogn()(and their direct-implementation counterpartscnn(),oss(), andsmogn()) now sample correctly when only one candidate remains. A length-1 vector passed tosample()was interpreted as a count and sampled from1:n, which could select the wrong observations (#245).step_ncl()(and its direct-implementation counterpartncl()) now treats a majority class whose size is exactlythreshold_cleantimes the minority size as eligible for cleaning, using the>=comparison from Laurikkala (2001) instead of a strict>(#250).step_nearmiss()(and its direct-implementation counterpartnearmiss()) now keeps the majority observations that are genuinely closest to the minority class, rather than selecting rows by their position in the data (#236).step_nearmiss()andstep_smogn()(and their direct-implementation counterpartsnearmiss()andsmogn()) now return true cosine-distance magnitudes withdistance = "cosine". Previously the cosine branch L2-normalized and took Euclidean distances, returningsqrt(2 - 2 * cos_sim)instead of1 - cos_sim. Neighbor ordering was unaffected, but NearMiss neighbor-distance averages and SMOGN’s interpolate-vs-noise threshold used the wrong magnitudes (#244).step_oss()(and its direct-implementation counterpartoss()) now condenses the majority classes with a single pass, matching Kubat & Matwin (1997), instead of iterating Condensed Nearest Neighbors to convergence, which removed more observations than the reference (#250).step_smogn()(and its direct-implementation counterpartsmogn()) now emits a clear early error when automatic relevance is requested for a degenerate outcome (zero interquartile range or heavily tied values), pointing users to therelevanceargument instead of aborting deep inside the boxplot-based computation (#264).step_smogn()(and its direct-implementation counterpartsmogn()) now scales the Gaussian perturbation of unsafe cases by each feature’s standard deviation and caps the perturbation amount at the safe distance, usingsd * min(perturbation, maxD)as in the reference, instead of scaling by a distance-capped standard deviation (#250).step_smotenc()(and its direct-implementation counterpartsmotenc()) now sets each synthetic sample’s nominal features to the majority vote across the seed’s k nearest neighbors, matching the SMOTENC algorithm, rather than voting over the randomly chosen interpolation partners (#241).step_tomek()(and its direct-implementation counterparttomek()) now removes only the majority-class member of each Tomek link, retaining the minority-class member, matching the documented behavior. Previously it removed both members of the pair (#262).step_upsample()now names itself, rather thanstep_downsample(), in the deprecation message shown when the defunctratioargument is supplied (#252).All
step_*()functions now correctly handle 0 and 1 row inputs inbake()(#160).adasyn(),bsmote(),smote(),smoten(),smotenc(), andsvmsmote()now return a proper factor outcome when called with a charactervar, instead of an all-NA, zero-level factor (#261).adasyn(),bsmote(),nearmiss(),smote(), andtomek()now correctly attribute errors from non-numeric columns to the user-facing function (#181).smotenc()now only suppresses the specific benign warning fromgower::gower_topn()about variables with zero range, rather than all warnings (#182).bsmote()now correctly passes theall_neighborsargument to the underlying implementation (#176).step_bsmote()now works correctly when there is only a single predictor (#151).step_downsample()andstep_upsample()now correctly handleNAvalues in the outcome variable instead of erroring (#177).step_upsample()now leaves classes that already meet or exceed the target size untouched instead of resampling them with replacement, and produces the same rows whether or notindicator_columnis set (#263).
themis 1.0.3
CRAN release: 2025-01-22
Improvements
Calling
?tidy.step_*()now sends you to the documentation forstep_*()where the outcome is documented. (#142)Documentation now correctly specifies majority-to-minority and minority-to-majority. (#143, #110)
Documentation for tidy methods for all steps has been improved to describe the return value more accurately. (#148)
All messages, warnings and errors has been translated to use {cli} package (#153, #155).
themis 1.0.1
CRAN release: 2023-04-14
Improvements
Fixed bug where some upsampling functions would error if no upsampling was needed. (#119)
Steps with tunable arguments now have those arguments listed in the documentation.
themis 1.0.0
CRAN release: 2022-07-02
- Added case weights support for
step_upsample()andstep_downsample()
themis 0.2.2
CRAN release: 2022-05-11
-
tomek()has been added, rewritten to apply to multiple classes, removing the need for the unbalanced package, which has been removed as a dependency.
themis 0.2.1
CRAN release: 2022-04-13
- A bug was fixed in
step_downsample()andstep_upsample()that made the steps unable to be tuned. (#90)
themis 0.2.0
CRAN release: 2022-03-30
New steps
-
step_smotenc()have been added to implement SMOTENC which can handle categorical as well as numerical values. Thanks to @RobertGregg (#82)
Improvements and Other Changes
- export
nearmiss()functions to users. - Update examples to no longer use
irisorokcdata sets. - All recipe steps now officially support empty selections to be more aligned with dplyr and other packages that use tidyselect (#55)
Bug fixes
-
step_rose()now correctly allows you to use characters variables. (#26) -
step_tomek()now ignore non-predictor variables when appropriate. (#51) - Fix bug where wrong ordering of columns caused error in
smote(). (#76)
themis 0.1.1
CRAN release: 2020-05-17
-
step_smote()now work regardless of order of classes. Thanks to @sebastien-foulle for point it out #14.
