Float sampling_strategy truncation raises a spurious ValueError on valid over-sampling requests
Describe the bug
When sampling_strategy is a float, _sampling_strategy_float computes the per-class target count with int(...), which truncates. The majority_count * ratio product carries floating-point error, so a ratio whose exact target is a whole number lands just below it — 100 * 0.29 == 28.999999999999996, not 29.0 — and int() drops a whole sample. In the over-sampling branch the truncated value then trips the n_samples <= 0 guard, so a valid request raises a misleading "required to remove samples" error. The under-sampling branch has the same int(n_sample_minority / sampling_strategy) truncation, which silently produces an off-by-one count.
This is not a one-off. Sweeping majority counts N in [2, 500] against ratios 0.01..1.00, 25 (N, ratio) pairs hit it — e.g. (100, 0.29), (50, 0.58), (90, 0.70), (180, 0.35), (300, 0.41) — each a valid over-sampling request whose exact target is a whole number one above the current minority count, which raises instead of adding one sample.
Steps/Code to Reproduce
import numpy as np
from imblearn.over_sampling import RandomOverSampler
X = np.arange(128).reshape(-1, 1)
y = np.array([0] * 100 + [1] * 28) # majority=100, minority=28
# target minority = 0.29 * 100 = 29, i.e. add one sample — a valid request
RandomOverSampler(sampling_strategy=0.29, random_state=0).fit_resample(X, y)
Expected Results
Over-samples the minority class to 29 → Counter({0: 100, 1: 29}), no error.
Actual Results
Traceback (most recent call last):
File "app.py", line 6, in <module>
RandomOverSampler(sampling_strategy=0.29, random_state=0).fit_resample(X, y)
File "imblearn\base.py", line 204, in fit_resample
return super().fit_resample(X, y, **params)
File "sklearn\base.py", line 1403, in wrapper
return fit_method(estimator, *args, **kwargs)
File "imblearn\base.py", line 103, in fit_resample
self.sampling_strategy_ = check_sampling_strategy(
File "imblearn\\\utils\_validation.py", line 567, in check_sampling_strategy
_sampling_strategy_float(sampling_strategy, y, sampling_type).items()
File "imblearn\\\utils\_validation.py", line 408, in _sampling_strategy_float
raise ValueError(
ValueError: The specified ratio required to remove samples from the minority class while trying to generate new samples. Please increase the ratio.
The truncation is in imblearn/utils/_validation.py, _sampling_strategy_float: int(n_sample_majority * sampling_strategy - value) (over-sampling) and int(n_sample_minority / sampling_strategy) (under-sampling).
Versions
Platform: Windows (win32)
Python: 3.12.10
NumPy: 2.5.3
SciPy: 1.18.1
Scikit-Learn: 1.9.1
Imbalanced-Learn: 0.15.dev0
I'm happy to open a PR. The direct fix is to round the target count instead of truncating, but round vs floor changes behavior for genuinely fractional targets (e.g. 0.35 * 10 = 3.5), so I'd welcome your steer on the intended semantics before I finalize.
Source: scikit-learn-contrib/imbalanced-learn