<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shakti tiwari </title>
    <description>The latest articles on DEV Community by shakti tiwari  (@shaktitiwari).</description>
    <link>https://dev.to/shaktitiwari</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4036359%2F405db913-daa8-4c68-91cf-97dd59d8c5bf.jpg</url>
      <title>DEV Community: shakti tiwari </title>
      <link>https://dev.to/shaktitiwari</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shaktitiwari"/>
    <language>en</language>
    <item>
      <title>RandomOverSampler: The Baseline Every SMOTE Variant Is Measured Against</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Fri, 11 Sep 2026 04:31:04 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/randomoversampler-the-baseline-every-smote-variant-is-measured-against-2ggb</link>
      <guid>https://dev.to/shaktitiwari/randomoversampler-the-baseline-every-smote-variant-is-measured-against-2ggb</guid>
      <description>&lt;h1&gt;
  
  
  RandomOverSampler: The Baseline Every SMOTE Variant Is Measured Against
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE deep-dive series for optiontradingwithai.in — Shakti Tiwari, Nifty Option Trader &amp;amp; XGBoost Expert.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;RandomOverSampler is the simplest oversampling method in &lt;code&gt;imbalanced-learn&lt;/code&gt;: it duplicates minority-class rows &lt;strong&gt;uniformly at random, sampling with replacement&lt;/strong&gt;, until every class is balanced. No new information is invented — only exact copies are made. That is precisely why it is the reference baseline: every fancy SMOTE variant (Borderline, SVM, KMeans, ADASYN) must beat naive duplication to earn its place in your pipeline. We cover when duplication is "good enough," the memorization risk it creates, and the &lt;code&gt;shrinkage&lt;/code&gt; jitter that partially tames it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;If you trade NIFTY options, you already live with imbalance. The "interesting" class — a clean directional edge, a rare volatility expansion, a profitable expiry setup — is almost always the minority. Your XGBoost model, fed raw EOD signatures, sees 95% "nothing happens" rows and 5% "real signal" rows. Left alone, the tree learns to shout "do nothing" and still score 95% accuracy. Useless.&lt;/p&gt;

&lt;p&gt;So you reach for resampling. And the very first thing any serious practitioner should do — before installing Borderline-SMOTE, before tuning ADASYN's &lt;code&gt;β&lt;/code&gt;, before anything — is run &lt;strong&gt;RandomOverSampler&lt;/strong&gt; as a control.&lt;/p&gt;

&lt;p&gt;Why? Because if a complicated synthetic sampler cannot beat "just copy the minority rows a bunch of times," then the complication is not buying you anything. RandomOverSampler is the yardstick. Chawla et al. (2002) literally introduced SMOTE as an &lt;em&gt;improvement over&lt;/em&gt; random oversampling, so the original paper frames the whole family against this exact baseline. [SOURCE: Chawla et al. 2002, JAIR 16:321–357]&lt;/p&gt;

&lt;p&gt;Our NSE stack is &lt;strong&gt;TWO-LAYER&lt;/strong&gt;: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or &lt;code&gt;scale_pos_weight&lt;/code&gt;) is applied in Layer 2 inside CV, never on live data. RandomOverSampler, being the cheapest resampler, is the natural first thing we drop into Layer 2's CV loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RQ:&lt;/strong&gt; For a strong, stochastic, bagged model like XGBoost, does naive random duplication (RandomOverSampler) perform close enough to interpolation-based SMOTE that the extra complexity is unjustified?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis:&lt;/strong&gt; As model stochasticity rises (subsampling, column sampling, bootstrap aggregation), the marginal benefit of SMOTE's synthetic interpolation shrinks, because the model already perturbs the data through its own randomness. RandomOverSampler then becomes a defensible, near-free baseline — but only up to a point, beyond which exact-duplicate memorization caps its ceiling.&lt;/p&gt;

&lt;p&gt;This is the question the rest of the article works through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The mechanism, exactly
&lt;/h3&gt;

&lt;p&gt;RandomOverSampler balances classes by &lt;strong&gt;resampling the minority class with replacement&lt;/strong&gt;. Call the majority count &lt;code&gt;N_maj&lt;/code&gt; and the minority count &lt;code&gt;N_min&lt;/code&gt;. To reach balance, the sampler draws &lt;code&gt;N_maj − N_min&lt;/code&gt; additional minority rows, each drawn &lt;strong&gt;independently and uniformly&lt;/strong&gt; from the &lt;code&gt;N_min&lt;/code&gt; original minority rows. [SOURCE: imbalanced-learn &lt;code&gt;RandomOverSampler&lt;/code&gt; documentation]&lt;/p&gt;

&lt;p&gt;That means a specific minority row &lt;code&gt;i&lt;/code&gt; can be copied zero, one, or many times in a single &lt;code&gt;fit_resample&lt;/code&gt; call. The selection is a multinomial draw.&lt;/p&gt;

&lt;h3&gt;
  
  
  The math (DERIVED)
&lt;/h3&gt;

&lt;p&gt;Let the imbalance ratio be &lt;code&gt;IR = N_maj / N_min&lt;/code&gt;. The number of copies we must add is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;copies_to_add = N_maj − N_min = N_min · (IR − 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each draw picks row &lt;code&gt;i&lt;/code&gt; with probability &lt;code&gt;1 / N_min&lt;/code&gt;. Over &lt;code&gt;copies_to_add&lt;/code&gt; independent draws, the expected number of times row &lt;code&gt;i&lt;/code&gt; is selected is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E[copies of row i] = (N_maj − N_min) · (1 / N_min) = IR − 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;strong&gt;each minority row is expected to appear &lt;code&gt;IR − 1&lt;/code&gt; extra times.&lt;/strong&gt; The total minority presence after resampling is &lt;code&gt;N_min · IR = N_maj&lt;/code&gt; rows, matching the majority. Clean. [DERIVED]&lt;/p&gt;

&lt;h3&gt;
  
  
  Worked numerical example (DERIVED)
&lt;/h3&gt;

&lt;p&gt;Suppose your NIFTY directional dataset has &lt;code&gt;N_maj = 10,000&lt;/code&gt; "no edge" days and &lt;code&gt;N_min = 200&lt;/code&gt; "real edge" days. Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IR = 10,000 / 200 = 50&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;copies_to_add = 9,800&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;E[copies per minority row] = 50 − 1 = 49&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After &lt;code&gt;fit_resample&lt;/code&gt;, you hold 200 original minority rows + 9,800 copies = 10,000 minority rows. On average every one of those 200 days now shows up &lt;strong&gt;50 times&lt;/strong&gt; in the training set. This is the crux of the overfitting problem (next sections): the model sees those 200 days far more than the 10,000 majority days individually, and — critically — it sees &lt;em&gt;exact&lt;/em&gt; copies, not variations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contrast with SMOTE (the whole point)
&lt;/h3&gt;

&lt;p&gt;SMOTE does &lt;strong&gt;not&lt;/strong&gt; copy. It &lt;strong&gt;interpolates&lt;/strong&gt;. For each minority row &lt;code&gt;x_i&lt;/code&gt;, SMOTE finds its &lt;code&gt;k&lt;/code&gt; nearest minority neighbors, picks one neighbor &lt;code&gt;x_zi&lt;/code&gt;, and synthesizes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x_new = x_i + λ · (x_zi − x_i),   λ ~ Uniform(0, 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[SOURCE: Chawla et al. 2002]&lt;/p&gt;

&lt;p&gt;The generated point lies &lt;em&gt;on the line segment&lt;/em&gt; between two real minority examples. It is a new coordinate that never existed in your data — but lives in a plausible region of feature space. RandomOverSampler, by contrast, produces &lt;code&gt;x_new = x_i&lt;/code&gt; exactly, repeated. &lt;strong&gt;Duplication vs interpolation&lt;/strong&gt; — that single distinction is the DNA difference between the baseline and the entire SMOTE family.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;What does the literature and the logic actually say?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Random oversampling is the explicit "before" picture.&lt;/strong&gt; Chawla et al. (2002) state that random oversampling "causes overfitting" because it makes exact copies of minority examples, and SMOTE was designed to avoid that by generating synthetic (interpolated) instances. So the &lt;em&gt;founding paper of the field&lt;/em&gt; already tells you: naive duplication overfits; that is why SMOTE exists. [SOURCE: Chawla et al. 2002]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;But the gap narrows for strong models.&lt;/strong&gt; A tree ensemble like XGBoost already injects stochasticity: row subsampling (&lt;code&gt;subsample&lt;/code&gt;), column subsampling (&lt;code&gt;colsample_bytree&lt;/code&gt;), and bootstrap-like bagging mean each tree sees a &lt;em&gt;different&lt;/em&gt; perturbed view of the data anyway. Exact duplicates therefore get "spread out" across trees — no single tree memorizes them identically. This is the qualitative reason our hypothesis often holds in practice: the model's own randomness dilutes the duplication penalty. [DERIVED from XGBoost mechanics + Chawla's overfitting argument]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simpler models suffer more.&lt;/strong&gt; A k-NN or a linear model has no internal stochasticity. Feed it 50 identical copies of one minority row and it will weight that row 50× — pure memorization, poor generalization. Here RandomOverSampler's ceiling is low and SMOTE's interpolation clearly wins. [DERIVED]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sometimes SMOTE does not win at all.&lt;/strong&gt; Blagus &amp;amp; Lusa (2013) showed that SMOTE can &lt;em&gt;hurt&lt;/em&gt; on small or high-dimensional datasets, where interpolating between neighbors lands you in noisy, overlapping regions. In those regimes a humble baseline (even random oversampling or just &lt;code&gt;scale_pos_weight&lt;/code&gt;) can match or beat a sophisticated sampler. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Net finding: RandomOverSampler is a &lt;strong&gt;valid, low-risk control&lt;/strong&gt; and a genuinely strong choice for bagged/stochastic models at moderate imbalance; it is a weak choice for simple models and extreme imbalance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducibility (illustrative snippet — NOT a result I ran)
&lt;/h2&gt;

&lt;p&gt;The following is the &lt;strong&gt;canonical imbalanced-learn usage shape&lt;/strong&gt;, shown for illustration from the library's documentation. I did &lt;strong&gt;not&lt;/strong&gt; execute this; treat it as the API reference pattern, not an experiment outcome. [SOURCE: imbalanced-learn &lt;code&gt;RandomOverSampler&lt;/code&gt; documentation]&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomOverSampler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;

&lt;span class="c1"&gt;## X, y are your feature matrix and label vector
&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## Baseline: duplicate minority rows with replacement until balanced
&lt;/span&gt;&lt;span class="n"&gt;ros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RandomOverSampler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ros&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## Optional: perturb the duplicated copies to curb overfitting
&lt;/span&gt;&lt;span class="n"&gt;ros_shrunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RandomOverSampler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shrinkage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ros_shrunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key API facts (SOURCE: imbalanced-learn docs):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy='auto'&lt;/code&gt; (default) resamples all minority classes to match the majority class count.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;random_state&lt;/code&gt; controls the with-replacement draw, so results are reproducible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shrinkage&lt;/code&gt; (introduced in imbalanced-learn 0.12) is the jitter knob discussed next.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;shrinkage&lt;/code&gt; Parameter — RandomOverSampler's Anti-Memorization Knob
&lt;/h2&gt;

&lt;p&gt;This is the part most tutorials skip, and it is the reason RandomOverSampler is not &lt;em&gt;quite&lt;/em&gt; as dumb as "just copy rows."&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;shrinkage&lt;/code&gt; is set to a value in &lt;code&gt;(0, 1]&lt;/code&gt;, imbalanced-learn does &lt;strong&gt;not&lt;/strong&gt; keep the duplicated rows identical. Instead, each duplicated sample is perturbed by a small random jitter. Per the imbalanced-learn documentation, the variance of that jitter equals &lt;code&gt;shrinkage × (variance of the feature)&lt;/code&gt;. [SOURCE: imbalanced-learn &lt;code&gt;RandomOverSampler&lt;/code&gt; documentation / 0.12 release notes]&lt;/p&gt;

&lt;p&gt;Concretely: for a duplicated feature value &lt;code&gt;x&lt;/code&gt;, the stored value becomes roughly &lt;code&gt;x + ε&lt;/code&gt; where &lt;code&gt;ε ~ N(0, shrinkage · Var(feature))&lt;/code&gt;. The bigger &lt;code&gt;shrinkage&lt;/code&gt;, the larger the perturbation; &lt;code&gt;shrinkage=None&lt;/code&gt; (default) means &lt;strong&gt;no perturbation — exact duplicates&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why this matters: exact duplicates are the memorization trap. By adding feature-scaled noise, &lt;code&gt;shrinkage&lt;/code&gt; turns "I have seen this exact row 49 times" into "I have seen 49 slightly-different versions of this row." That nudges RandomOverSampler &lt;em&gt;toward&lt;/em&gt; the interpolation spirit of SMOTE — without the neighbor-search cost. It is a cheap, principled regularizer for the baseline. [DERIVED from the documented behavior]&lt;/p&gt;

&lt;p&gt;Caveat: &lt;code&gt;shrinkage&lt;/code&gt; only perturbs &lt;em&gt;duplicated&lt;/em&gt; rows; the original minority rows stay untouched, and the noise is Gaussian, not geometry-aware like SMOTE. So it reduces but does not eliminate the overfit ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;Be honest about where RandomOverSampler collapses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure memorization on simple models.&lt;/strong&gt; As derived, each minority row can appear &lt;code&gt;IR − 1&lt;/code&gt; times. For &lt;code&gt;IR = 50&lt;/code&gt;, that is 49 copies of the &lt;em&gt;same&lt;/em&gt; vector fed to a k-NN or logistic model. Generalization degrades; the model essentially stores the training set. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does not invent information.&lt;/strong&gt; Duplication cannot teach the model about &lt;em&gt;unsampled&lt;/em&gt; regions of minority space. If your 200 minority days all cluster in one corner, copying them 50× just makes that corner louder — it does not reveal structure elsewhere. SMOTE's interpolation at least reaches between points. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Class-weighted alternatives can beat it cleanly.&lt;/strong&gt; For XGBoost specifically, &lt;code&gt;scale_pos_weight = N_maj / N_min&lt;/code&gt; often matches or beats random oversampling without inflating the row count or the training time. In our two-layer stack, we routinely A/B RandomOverSampler against &lt;code&gt;scale_pos_weight&lt;/code&gt; inside Layer-2 CV before trusting either. [DERIVED from standard XGBoost practice]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMOTE still wins where geometry is clean.&lt;/strong&gt; When minority regions are well-separated and low-noise, Chawla's interpolation advantage is real and measurable versus duplication. [SOURCE: Chawla et al. 2002]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No new signal.&lt;/strong&gt; RandomOverSampler redistributes attention; it does not create knowledge the data did not already contain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inflated dataset size.&lt;/strong&gt; Balancing multiplies the minority class up to &lt;code&gt;IR×&lt;/code&gt;, increasing training time and memory — a real cost at &lt;code&gt;IR = 50&lt;/code&gt;+ on tick-level NIFTY data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exact-duplicate bias.&lt;/strong&gt; Without &lt;code&gt;shrinkage&lt;/code&gt;, the model can over-weight specific minority rows by their copy count, which is random, not informative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;shrinkage&lt;/code&gt; is a blunt instrument.&lt;/strong&gt; Gaussian feature-scaled noise ignores class boundaries and feature correlations; it is not a substitute for true synthetic generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Still vulnerable in heavy overlap.&lt;/strong&gt; If minority and majority regions overlap heavily (common in noisy option signals), duplicating minority points deep inside majority territory just reinforces confusion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Applied wrongly, it leaks.&lt;/strong&gt; Resampling must happen &lt;em&gt;inside&lt;/em&gt; CV folds on the training split only. Doing it on the full dataset before splitting leaks minority copies into the test set and inflates every metric. In our stack this is forbidden — imbalance handling lives in Layer 2 inside CV, never on live data.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always run it first.&lt;/strong&gt; RandomOverSampler is your control. If a fancy sampler cannot beat it, ship the baseline — it is faster and simpler. [DERIVED from experimental-design logic]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on &lt;code&gt;shrinkage&lt;/code&gt; when duplication worries you.&lt;/strong&gt; Start around &lt;code&gt;0.2–0.4&lt;/code&gt; and compare CV AUC/F1 against &lt;code&gt;shrinkage=None&lt;/code&gt;. It is nearly free. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair with stochastic models.&lt;/strong&gt; RandomOverSampler shines under XGBoost/LightGBM with &lt;code&gt;subsample &amp;lt; 1&lt;/code&gt; and &lt;code&gt;colsample_bytree &amp;lt; 1&lt;/code&gt;, where internal randomness absorbs duplicates. [DERIVED from XGBoost mechanics]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark &lt;code&gt;scale_pos_weight&lt;/code&gt; too.&lt;/strong&gt; For tree models it is often the leaner fix; keep both in the CV comparison. [DERIVED from standard XGBoost practice]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid as the sole fix for simple models or IR &amp;gt; 100.&lt;/strong&gt; There, prefer SMOTE-family interpolation or class weights; duplication alone will overfit. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production checklist:&lt;/strong&gt; (1) stratify your split; (2) resample inside each CV fold only; (3) fix &lt;code&gt;random_state&lt;/code&gt; for reproducibility; (4) log before/after class counts; (5) A/B against &lt;code&gt;scale_pos_weight&lt;/code&gt;; (6) never resample live Layer-1 data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Is RandomOverSampler the same as just copying rows?&lt;/strong&gt;&lt;br&gt;
Yes — that is exactly what it is, with a uniform with-replacement draw to reach the target count. &lt;code&gt;shrinkage &amp;gt; 0&lt;/code&gt; adds small noise to the copies, but the core operation is duplication. [SOURCE: imbalanced-learn docs]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. When is random oversampling "good enough"?&lt;/strong&gt;&lt;br&gt;
For fast baselining, and for strong stochastic models (XGBoost, Random Forests) at moderate imbalance, where the model's own randomness dilutes the duplicate penalty. It is the cheapest thing you can try. [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. How is SMOTE different, in one line?&lt;/strong&gt;&lt;br&gt;
SMOTE &lt;em&gt;interpolates&lt;/em&gt; between minority neighbors to make new points; RandomOverSampler &lt;em&gt;duplicates&lt;/em&gt; existing points. Interpolation vs duplication. [SOURCE: Chawla et al. 2002]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What does &lt;code&gt;shrinkage&lt;/code&gt; actually do?&lt;/strong&gt;&lt;br&gt;
It adds Gaussian jitter to duplicated rows, with variance = &lt;code&gt;shrinkage × feature variance&lt;/code&gt;, reducing exact-match memorization. &lt;code&gt;None&lt;/code&gt; means exact copies. [SOURCE: imbalanced-learn docs]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Should I use it on live NIFTY data?&lt;/strong&gt;&lt;br&gt;
Never on live capture. Resample only inside Layer-2 CV on historical splits. Live trading uses the already-trained model in predict-only mode. [DERIVED from our two-layer stack design]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Why cover the "dumb" baseline in a SMOTE series?&lt;/strong&gt;&lt;br&gt;
Because every SMOTE variant exists to beat it. If you cannot measure against RandomOverSampler, you cannot claim any sampler helped. It is the reference yardstick. [SOURCE: Chawla et al. 2002 framing]&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;RandomOverSampler duplicates minority rows &lt;strong&gt;uniformly with replacement&lt;/strong&gt; until balanced — no new data invented.&lt;/li&gt;
&lt;li&gt;Expected copies per minority row = &lt;code&gt;IR − 1&lt;/code&gt;, where &lt;code&gt;IR = N_maj / N_min&lt;/code&gt;. [DERIVED]&lt;/li&gt;
&lt;li&gt;SMOTE &lt;strong&gt;interpolates&lt;/strong&gt;; RandomOverSampler &lt;strong&gt;duplicates&lt;/strong&gt; — that is the core contrast. [SOURCE: Chawla et al. 2002]&lt;/li&gt;
&lt;li&gt;It is the &lt;strong&gt;baseline every SMOTE variant is measured against&lt;/strong&gt; — run it first, always.&lt;/li&gt;
&lt;li&gt;Overfitting comes from exact duplicates → memorization, worst for simple models. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shrinkage ∈ (0,1]&lt;/code&gt; perturbs copies with variance = &lt;code&gt;shrinkage × feature variance&lt;/code&gt;, curbing overfit. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;For XGBoost/LightGBM with subsampling, duplication is often "good enough"; A/B against &lt;code&gt;scale_pos_weight&lt;/code&gt;. [DERIVED]&lt;/li&gt;
&lt;li&gt;Resample &lt;strong&gt;inside CV only&lt;/strong&gt;, never on live data (two-layer stack: Layer 1 live/shadow, Layer 2 EOD CV).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v7-adasyn"&gt;Smote V7 Adasyn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-s3-sampling-strategy"&gt;Smote S3 Sampling Strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, N. V., Bowyer, K. W., Hall, L. O., &amp;amp; Kegelmeyer, W. P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16, 321–357. — frames SMOTE as an improvement over random oversampling (duplication) via interpolation. [SOURCE]&lt;/li&gt;
&lt;li&gt;Han, H., Wang, W.-Y., &amp;amp; Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; ICIC. [SOURCE]&lt;/li&gt;
&lt;li&gt;He, H., Bai, Y., Garcia, E. A., &amp;amp; Li, S. (2008). &lt;em&gt;ADASYN: Adaptive Synthetic Sampling Approach.&lt;/em&gt; IEEE IJCNN. [SOURCE]&lt;/li&gt;
&lt;li&gt;Blagus, R., &amp;amp; Lusa, L. (2013). &lt;em&gt;SMOTE for high-dimensional class-imbalanced data.&lt;/em&gt; BMC Bioinformatics, 14:106. — SMOTE can degrade on small/high-dimensional data. [SOURCE]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scikit-learn-contrib/imbalanced-learn&lt;/code&gt; (GitHub, ~7.1k stars). &lt;code&gt;RandomOverSampler&lt;/code&gt; API: &lt;code&gt;from imblearn.over_sampling import RandomOverSampler&lt;/code&gt;, &lt;code&gt;fit_resample(X, y)&lt;/code&gt;, &lt;code&gt;sampling_strategy&lt;/code&gt;, &lt;code&gt;random_state&lt;/code&gt;, &lt;code&gt;shrinkage&lt;/code&gt;. [SOURCE]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shakti Tiwari&lt;/strong&gt; — Nifty Option Trader &amp;amp; XGBoost Expert. NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;br&gt;
Canonical: optiontradingwithai.in — SMOTE series, Volume 8 (RandomOverSampler).&lt;br&gt;
Prev: V7 ADASYN → &lt;code&gt;SMOTE_V7_adasyn.md&lt;/code&gt;. Next: P2 SMOTE on NIFTY minority direction → &lt;code&gt;SMOTE_P2_smote_nifty_direction.md&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Profile: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article: &lt;code&gt;SMOTE_V7_adasyn.md&lt;/code&gt; (V7 ADASYN)&lt;/li&gt;
&lt;li&gt;Next article: &lt;code&gt;SMOTE_P2_smote_nifty_direction.md&lt;/code&gt; (P2 SMOTE on NIFTY minority direction)&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>ADASYN in imbalanced-learn: Adaptive Synthetic Sampling That Chases the Hard Minority Points</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Fri, 11 Sep 2026 04:30:44 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/adasyn-in-imbalanced-learn-adaptive-synthetic-sampling-that-chases-the-hard-minority-points-bol</link>
      <guid>https://dev.to/shaktitiwari/adasyn-in-imbalanced-learn-adaptive-synthetic-sampling-that-chases-the-hard-minority-points-bol</guid>
      <description>&lt;h1&gt;
  
  
  ADASYN in imbalanced-learn: Adaptive Synthetic Sampling That Chases the Hard Minority Points
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE Family series. Previous: V6 SMOTEN. Next: V8 RandomOverSampler.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ADASYN&lt;/strong&gt; (&lt;code&gt;from imblearn.over_sampling import ADASYN&lt;/code&gt;) — the Adaptive Synthetic Sampling Approach of He, Bai, Garcia &amp;amp; Li (2008) — is the SMOTE variant that refuses to treat every minority point as equally worth copying. Where vanilla SMOTE sprays the same number of synthetic rows next to each minority instance, ADASYN first measures &lt;em&gt;how hard each minority point is to learn&lt;/em&gt; and then spends its synthetic budget preferentially on the &lt;strong&gt;hard&lt;/strong&gt; ones. A minority point is deemed "hard" when it is surrounded by many majority-class neighbours — i.e., it sits in the ambiguous, densely contested region of the class boundary. ADASYN generates &lt;strong&gt;more&lt;/strong&gt; synthetic samples for exactly those points and &lt;strong&gt;fewer&lt;/strong&gt; for the easy, well-separated ones. The mechanism is a normalized density distribution &lt;code&gt;r&lt;/code&gt; derived from the count of majority neighbours of each minority point. [DERIVED summary of He, Bai, Garcia &amp;amp; Li 2008 + imbalanced-learn &lt;code&gt;ADASYN&lt;/code&gt; implementation]&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Agar aap Nifty options trade karte ho, toh aap pehle se hi imbalanced duniya mein jeete ho. The price moves that actually move your P&amp;amp;L — a sharp expiry-day reversal, a volatility expansion that breaks a straddle, a failed breakdown that snaps back — are rare. The dull chop that fills most sessions is common. Train an XGBoost or LightGBM model naively on that history and it will quietly learn the lazy rule "predict the common class," because that minimises raw error while delivering a model that is useless for the trades that pay the bills.&lt;/p&gt;

&lt;p&gt;Vanilla SMOTE (V1) fixed part of that by manufacturing synthetic minority rows — but it treated every minority row as equally valuable, generating the same count next to each. BorderlineSMOTE (V2) and SVMSMOTE (V3) got smarter by &lt;em&gt;selecting&lt;/em&gt; boundary points and skipping the safe interior. But both still hand out synthetic samples &lt;strong&gt;roughly uniformly&lt;/strong&gt; across whatever points they decide are "boundary." ADASYN takes the next conceptual step: it does not use a hard boundary/safe split at all. Instead it weights synthesis &lt;strong&gt;continuously by difficulty&lt;/strong&gt;. A point drowning in majority neighbours gets a flood of synthetic neighbours; a point in clean minority space gets almost none. When minority difficulty is &lt;em&gt;skewed&lt;/em&gt; — some points easy, some brutally hard — that proportional weighting is exactly what you want. [DERIVED from the conceptual motivation in He et al. 2008]&lt;/p&gt;

&lt;p&gt;Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; For a binary classifier trained on an imbalanced dataset, does &lt;em&gt;difficulty-weighted&lt;/em&gt; over-sampling (ADASYN), which allocates synthetic samples proportionally to the local majority-neighbour density of each minority point, improve minority-class recall and balanced metrics (F1, G-mean) relative to &lt;em&gt;uniform&lt;/em&gt; over-sampling (SMOTE), and under what data conditions does the gain appear or reverse?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; ADASYN should win &lt;em&gt;when minority-class difficulty is heterogeneous&lt;/em&gt; — that is, when the hard minority points (those embedded in majority-dense regions) are concentrated, so that pouring synthetic samples into those contested zones genuinely thickens a learnable boundary. The gain should shrink toward vanilla SMOTE when difficulty is roughly uniform, and it should &lt;em&gt;reverse&lt;/em&gt; into harm when the "hard" points are hard because they are &lt;strong&gt;mislabels or noise&lt;/strong&gt; rather than genuine borderline signal — because ADASYN, unlike BorderlineSMOTE, has no noise-filtering step and will actually generate the &lt;em&gt;most&lt;/em&gt; synthetic samples around an all-majority-surrounded (noise) minority point. The hypothesis is a derived expectation, not a measurement from this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We describe the canonical experimental shape used throughout the imbalanced-learn documentation and the over-sampling literature. &lt;strong&gt;No experiment is executed in this article&lt;/strong&gt; — the snippet below is illustrative, taken from the imbalanced-learn API, and is shown only to anchor the method. [SOURCE: scikit-learn-contrib/imbalanced-learn repo, &lt;code&gt;ADASYN&lt;/code&gt; docstring/example]&lt;/p&gt;

&lt;p&gt;The pipeline shape is always:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stratified train/test split (never let ADASYN see the test set).&lt;/li&gt;
&lt;li&gt;Inside cross-validation &lt;em&gt;only&lt;/em&gt;, fit &lt;code&gt;ADASYN&lt;/code&gt; on the training fold and &lt;code&gt;fit_resample&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Train the estimator on the resampled fold; validate on the untouched test fold.&lt;/li&gt;
&lt;li&gt;Optionally wrap with &lt;code&gt;Pipeline&lt;/code&gt; so resampling is never leaked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Illustrative API usage (from imbalanced-learn docs — not run here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ADASYN&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;classification_report_imbalanced&lt;/span&gt;

&lt;span class="c1"&gt;## Synthetic illustrative data — do not treat as a real market dataset
&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;n_informative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_redundant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flip_y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_clusters_per_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Original dataset shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Original dataset shape Counter({1: 900, 0: 100})
&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## default ADASYN: difficulty-weighted over-sampling
&lt;/span&gt;&lt;span class="n"&gt;ada&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ADASYN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# k used to estimate local difficulty
&lt;/span&gt;    &lt;span class="n"&gt;n_neighbors_interpolation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# k used in the actual interpolation
&lt;/span&gt;    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ada&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resampled shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Resampled shape Counter({0: 900, 1: 900})  -- illustrative from repo example
&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;adasyn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ADASYN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;y_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;classification_report_imbalanced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key constructor parameters [SOURCE: imbalanced-learn repo, &lt;code&gt;ada_syn.py&lt;/code&gt; (&lt;code&gt;ADASYN&lt;/code&gt;, &lt;code&gt;n_neighbors&lt;/code&gt;, &lt;code&gt;n_neighbors_interpolation&lt;/code&gt;)]:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;n_neighbors&lt;/code&gt; (default &lt;strong&gt;5&lt;/strong&gt;) — the neighbourhood size used to estimate &lt;strong&gt;local difficulty&lt;/strong&gt;. For each minority point, ADASYN finds its &lt;code&gt;n_neighbors&lt;/code&gt; nearest neighbours in the combined feature space and counts how many belong to the majority class; that count over &lt;code&gt;n_neighbors&lt;/code&gt; becomes the difficulty ratio &lt;code&gt;r_i&lt;/code&gt;. A larger &lt;code&gt;n_neighbors&lt;/code&gt; smooths the difficulty estimate across a wider region; a smaller one makes it sharper and noisier. [SOURCE: imbalanced-learn repo, &lt;code&gt;_fit_resample&lt;/code&gt; density-ratio logic]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n_neighbors_interpolation&lt;/code&gt; (default &lt;strong&gt;5&lt;/strong&gt;) — the number of nearest &lt;strong&gt;minority&lt;/strong&gt; neighbours used in the &lt;em&gt;synthesis&lt;/em&gt; step. For each synthetic point, a partner &lt;code&gt;x_zi&lt;/code&gt; is drawn at random from this minority-neighbour pool and the new row is interpolated between the seed and that partner. [SOURCE: imbalanced-learn repo, &lt;code&gt;_fit_resample&lt;/code&gt; interpolation logic]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy&lt;/code&gt; (default &lt;code&gt;"auto"&lt;/code&gt;) — how much to oversample. With &lt;code&gt;"auto"&lt;/code&gt;, the minority class is resampled up to match the majority class count. This is how imbalanced-learn realises the original paper's balance-level parameter β (see Findings). [SOURCE: imbalanced-learn repo default]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;random_state&lt;/code&gt; — for reproducibility of both the neighbour selection and the random interpolation gap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful diagnostic the library exposes: &lt;code&gt;ada.sampling_strategy_&lt;/code&gt; — the per-class target counts actually used — and, after &lt;code&gt;fit_resample&lt;/code&gt;, the resampled arrays themselves let you inspect &lt;em&gt;where&lt;/em&gt; new points landed. Because ADASYN does not expose a per-seed "how many did I get" attribute the way BorderlineSMOTE exposes &lt;code&gt;in_danger_indices_&lt;/code&gt;, the honest diagnostic is to diff the resampled arrays against the originals or to recompute &lt;code&gt;r_i&lt;/code&gt; yourself. [SOURCE: imbalanced-learn repo, &lt;code&gt;ADASYN&lt;/code&gt; attributes]&lt;/p&gt;

&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Because this article does not run an experiment, the "findings" below are a synthesis of the primary sources, not measured numbers from this author's machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 1 — ADASYN replaces the uniform budget with a weighted one.&lt;/strong&gt; Vanilla SMOTE selects every minority point with equal probability and interpolates between two minority neighbours, so each minority point receives (approximately) the same number of synthetic children [SOURCE: Chawla et al. 2002]. ADASYN keeps the same interpolation rule but makes the &lt;em&gt;count&lt;/em&gt; per point a function of difficulty. The original paper's stated goal is to "adaptively" shift the decision boundary toward the hard, minority-dense-in-majority regions rather than over-generalising the easy ones. [SOURCE: He, Bai, Garcia &amp;amp; Li 2008]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 2 — The weighting comes from a normalized density distribution &lt;code&gt;r&lt;/code&gt;.&lt;/strong&gt; This is the load-bearing math, written here in full so you can see exactly where the parameters sit [DERIVED from He et al. 2008]:&lt;/p&gt;

&lt;p&gt;Let &lt;code&gt;m_s&lt;/code&gt; = number of minority samples, &lt;code&gt;m_l&lt;/code&gt; = number of majority samples, &lt;code&gt;k&lt;/code&gt; = &lt;code&gt;n_neighbors&lt;/code&gt;, and &lt;code&gt;β ∈ [0,1]&lt;/code&gt; = desired balance level (β = 1 means fully balanced).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Total synthetic samples to generate: &lt;code&gt;G = (m_l − m_s) · β&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For each minority point &lt;code&gt;x_i&lt;/code&gt;, let &lt;code&gt;Δ_i&lt;/code&gt; = number of majority-class samples among its &lt;code&gt;k&lt;/code&gt; nearest neighbours. Define &lt;code&gt;r_i = Δ_i / k ∈ [0,1]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Normalize: &lt;code&gt;r̂_i = r_i / Σ_j r_j&lt;/code&gt; (so &lt;code&gt;Σ_i r̂_i = 1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Samples generated for &lt;code&gt;x_i&lt;/code&gt;: &lt;code&gt;g_i = round(r̂_i · G)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For each generated point, pick a random minority neighbour &lt;code&gt;x_zi&lt;/code&gt; from the &lt;code&gt;n_neighbors_interpolation&lt;/code&gt; nearest minority neighbours of &lt;code&gt;x_i&lt;/code&gt; and set &lt;code&gt;x_new = x_i + (x_zi − x_i) · λ&lt;/code&gt;, with &lt;code&gt;λ ~ Uniform(0,1)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key property: &lt;code&gt;r_i&lt;/code&gt; is largest when a minority point is &lt;strong&gt;most engulfed by the majority class&lt;/strong&gt;, so those points receive the &lt;strong&gt;most&lt;/strong&gt; synthetic children. That is the precise sense in which ADASYN "generates more synthetic samples where the class boundary is ambiguous." [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 3 — Worked numerical example (DERIVED).&lt;/strong&gt; Take four minority points in 1-D and &lt;code&gt;k = 5&lt;/code&gt;. Suppose each has these counts of majority neighbours among its 5 nearest neighbours:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Point&lt;/th&gt;
&lt;th&gt;Δ_i (majority neighbours)&lt;/th&gt;
&lt;th&gt;r_i = Δ_i/5&lt;/th&gt;
&lt;th&gt;r̂_i (normalized)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;td&gt;0.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0.40&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0.60&lt;/td&gt;
&lt;td&gt;0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;0.80&lt;/td&gt;
&lt;td&gt;0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sum of &lt;code&gt;r&lt;/code&gt; = 2.00, so normalized weights sum to 1. If &lt;code&gt;sampling_strategy="auto"&lt;/code&gt; demands &lt;code&gt;G = 10&lt;/code&gt; new minority rows, the allocation is: &lt;code&gt;g1 = 1&lt;/code&gt;, &lt;code&gt;g2 = 2&lt;/code&gt;, &lt;code&gt;g3 = 3&lt;/code&gt;, &lt;code&gt;g4 = 4&lt;/code&gt;. The &lt;strong&gt;hardest&lt;/strong&gt; point &lt;code&gt;x4&lt;/code&gt; (80% of its neighbours are majority) gets &lt;strong&gt;four times&lt;/strong&gt; the synthetic budget of the easiest point &lt;code&gt;x1&lt;/code&gt;. Vanilla SMOTE, by contrast, would hand each point ~2 or ~3 children — wasting half its budget on &lt;code&gt;x1&lt;/code&gt;, which was already separable. This single table is the whole ADASYN idea in miniature. [DERIVED illustration]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 4 — ADASYN is the only one of the family that weights &lt;em&gt;continuously&lt;/em&gt;.&lt;/strong&gt; SMOTE is uniform. BorderlineSMOTE applies a binary SAFE/DANGER/NOISE mask and then generates roughly uniformly across the DANGER set. SVMSMOTE uses SVM support vectors as a (roughly uniform) seed set. ADASYN applies &lt;strong&gt;no threshold&lt;/strong&gt; — every minority point gets &lt;em&gt;some&lt;/em&gt; children, allocated on a smooth gradient of difficulty. So among the variants we have covered, ADASYN is the only one that encodes "this point is twice/half as hard as that one" directly into the sample count. [DERIVED comparison of implementations in imbalanced-learn]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 5 — There is no noise filter, and that is the deliberate trade-off.&lt;/strong&gt; BorderlineSMOTE refuses to synthesise around NOISE points (those whose neighbours are &lt;em&gt;all&lt;/em&gt; majority), on the theory that an all-majority-surrounded minority point is likely a mislabel. ADASYN has &lt;strong&gt;no such filter&lt;/strong&gt;. Worse, in ADASYN a point with &lt;code&gt;Δ_i = k&lt;/code&gt; (all neighbours majority) gets &lt;code&gt;r_i = 1&lt;/code&gt; — the &lt;em&gt;maximum&lt;/em&gt; weight — and therefore the &lt;em&gt;largest&lt;/em&gt; synthetic allocation. So if a minority point is a mislabel sitting in majority space, ADASYN will enthusiastically manufacture a crowd of synthetic children around the error. This is the central risk and the single most important reason ADASYN is not a blind default. [DERIVED from comparing ADASYN's &lt;code&gt;r_i&lt;/code&gt; rule with BorderlineSMOTE's &lt;code&gt;_in_danger_noise&lt;/code&gt; mask]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 6 — β is realised through &lt;code&gt;sampling_strategy&lt;/code&gt;.&lt;/strong&gt; The original paper exposes a balance-level parameter β controlling how fully the classes are rebalanced. imbalanced-learn folds this into &lt;code&gt;sampling_strategy&lt;/code&gt;: &lt;code&gt;"auto"&lt;/code&gt; targets full balance (β ≈ 1); you can also pass a float (e.g., &lt;code&gt;0.3&lt;/code&gt;) or a dict to cap the ratio below full balance. Functionally, &lt;code&gt;sampling_strategy&lt;/code&gt; sets the total &lt;code&gt;G&lt;/code&gt;, while the &lt;em&gt;distribution&lt;/em&gt; of &lt;code&gt;G&lt;/code&gt; across minority points is always the difficulty-weighted &lt;code&gt;r̂_i&lt;/code&gt;. [SOURCE: imbalanced-learn repo; DERIVED from He et al. 2008]&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;p&gt;To reproduce a real comparison you would, at minimum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a fixed &lt;code&gt;random_state&lt;/code&gt; everywhere (data split, ADASYN, estimator).&lt;/li&gt;
&lt;li&gt;Compare resamplers &lt;em&gt;inside&lt;/em&gt; the same CV loop: &lt;code&gt;SMOTE()&lt;/code&gt;, &lt;code&gt;BorderlineSMOTE(kind="borderline-1")&lt;/code&gt;, and &lt;code&gt;ADASYN(n_neighbors=5, n_neighbors_interpolation=5)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Score with minority recall, precision, F1, and G-mean — &lt;strong&gt;not&lt;/strong&gt; raw accuracy, which is misleading under imbalance.&lt;/li&gt;
&lt;li&gt;Always &lt;code&gt;fit_resample&lt;/code&gt; on the training fold only.&lt;/li&gt;
&lt;li&gt;Because ADASYN has no noise filter, consider pairing it with Edited Nearest Neighbours (ENN) so the synthetic crowd around genuine noise gets pruned — an "ADASYN + ENN" combo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code sketch in &lt;em&gt;Data &amp;amp; Methodology&lt;/em&gt; is the canonical shape from the imbalanced-learn documentation and is presented &lt;strong&gt;illustratively&lt;/strong&gt; — it was not executed for this article, and its printed outputs are quoted from the library's own example pattern for reference. [SOURCE: imbalanced-learn repo docstring; not an experiment result of this author]&lt;/p&gt;

&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;ADASYN is not a free lunch, and the honest literature plus implementation details say so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It amplifies noise instead of suppressing it.&lt;/strong&gt; As shown in Finding 5, ADASYN's maximum weight lands on all-majority-surrounded minority points. If those are mislabels (common in hand-labelled financial event sets — "was this really a reversal or just noise?"), ADASYN manufactures a synthetic neighbourhood around the mistake. BorderlineSMOTE and (partially) SVMSMOTE are safer here. [DERIVED from the &lt;code&gt;r_i&lt;/code&gt; rule]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blagus &amp;amp; Lusa (2013)&lt;/strong&gt; show that SMOTE-family over-sampling can &lt;em&gt;hurt&lt;/em&gt; on small or high-dimensional datasets, where the nearest-neighbour geometry is unreliable and synthetic points amplify noise rather than signal. ADASYN, which leans &lt;em&gt;harder&lt;/em&gt; on neighbour counts than SMOTE does, inherits this failure mode and can worsen it: a noisy neighbour count produces a noisy difficulty weight. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When difficulty is uniform, ADASYN ≈ SMOTE.&lt;/strong&gt; If every minority point has about the same majority-neighbour count, all &lt;code&gt;r_i&lt;/code&gt; are equal, &lt;code&gt;r̂_i&lt;/code&gt; is uniform, and ADASYN collapses to uniform over-sampling. You pay the extra neighbour search for no gain. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Majority density is still the only signal.&lt;/strong&gt; ADASYN decides "hard" purely from how many majority neighbours a point has. It does not check whether those majority neighbours are a coherent cluster or scattered noise, nor whether the minority point is a genuine frontier point or an outlier. A minority outlier near a dense majority blob is treated identically to a true borderline point. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can over-concentrate and create its own overfitting.&lt;/strong&gt; By flooding synthetic points into contested regions, ADASYN can make the model overfit exactly the ambiguous zone it was trying to clarify — especially with a large &lt;code&gt;G&lt;/code&gt; (full balance via &lt;code&gt;"auto"&lt;/code&gt;). [DERIVED]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No noise filtering.&lt;/strong&gt; The most important limitation: unlike BorderlineSMOTE, ADASYN does not skip all-majority-surrounded (likely mislabeled) minority points; it rewards them. Always sanity-check label quality before ADASYN, or pair with ENN. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relies on neighbour geometry.&lt;/strong&gt; Like all SMOTE variants, it assumes Euclidean proximity means semantic similarity. On high-dimensional, sparse, or heavily engineered feature spaces the k-nearest-neighbour counts become unreliable — the Blagus &amp;amp; Lusa 2013 critique. [SOURCE: Blagus &amp;amp; Lusa 2013; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;n_neighbors&lt;/code&gt; is a sensitivity knob.&lt;/strong&gt; Too small and the difficulty estimate is noisy (one lucky/unlucky neighbour flips &lt;code&gt;r_i&lt;/code&gt;); too large and it averages away genuine local structure. It must be tuned inside CV, not accepted at 5 blindly. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;n_neighbors_interpolation&lt;/code&gt; is a second knob.&lt;/strong&gt; It controls how locally the synthetic points are placed around each seed. Mismatch with the true neighbourhood scale hurts. [SOURCE: imbalanced-learn repo; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature-space only.&lt;/strong&gt; It operates in the raw feature space and assumes continuous Euclidean features. For mixed numeric/categorical data use &lt;code&gt;SMOTENC&lt;/code&gt;; for all-categorical use &lt;code&gt;SMOTEN&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No separability guarantee.&lt;/strong&gt; More synthetic points in a contested zone do not create a cleaner boundary if the true boundary is irreducible noise — they just thicken the fog. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;β&lt;/code&gt;/balance level still matters.&lt;/strong&gt; Full balance (&lt;code&gt;"auto"&lt;/code&gt;, β ≈ 1) may over-generate. Sometimes a partial ratio (e.g., &lt;code&gt;sampling_strategy=0.3&lt;/code&gt;) or &lt;code&gt;scale_pos_weight&lt;/code&gt; in XGBoost/LightGBM alone is better. [DERIVED]&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reach for ADASYN when minority difficulty is skewed.&lt;/strong&gt; If you believe the hard minority points are concentrated in majority-dense regions — the classic "ambiguous boundary" case — ADASYN's proportional weighting puts synthetic budget exactly where SMOTE wastes it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer it over SMOTE when you have already seen SMOTE over-generalise.&lt;/strong&gt; If vanilla SMOTE gave you a model that still misses the rare, contested cases, ADASYN's difficulty weighting is the targeted upgrade. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair with cleaning (ADASYN + ENN).&lt;/strong&gt; Because ADASYN has no noise filter, follow it with Edited Nearest Neighbours to prune synthetic points that land in clearly majority territory. This single combo neutralises its biggest weakness. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tune &lt;code&gt;n_neighbors&lt;/code&gt; and &lt;code&gt;n_neighbors_interpolation&lt;/code&gt; as hyperparameters&lt;/strong&gt; inside CV; do not accept defaults on real data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch label quality.&lt;/strong&gt; ADASYN punishes sloppy labels hardest. Clean or audit your minority labels first. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never resample the test set.&lt;/strong&gt; Resample inside the training fold only, ideally via &lt;code&gt;imblearn.pipeline.Pipeline&lt;/code&gt; so it composes cleanly with scaling and the classifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare against the cheap baseline.&lt;/strong&gt; Before reaching for any SMOTE variant, try &lt;code&gt;scale_pos_weight&lt;/code&gt; (XGBoost/LightGBM) or class weights — sometimes that alone closes the gap without synthesising a single row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In our stack&lt;/strong&gt;, all of this lives in Layer 2 (EOD-audited training), never in Layer 1 (live Dhan capture).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. How is ADASYN different from vanilla SMOTE?&lt;/strong&gt;&lt;br&gt;
A1. SMOTE gives every minority point roughly the same number of synthetic children. ADASYN first estimates each minority point's &lt;em&gt;difficulty&lt;/em&gt; (how many of its &lt;code&gt;n_neighbors&lt;/code&gt; nearest neighbours are majority), then generates &lt;strong&gt;more&lt;/strong&gt; synthetic children for the harder points and &lt;strong&gt;fewer&lt;/strong&gt; for the easy ones. Same interpolation rule, smarter budget allocation. [DERIVED from He et al. 2008 + imbalanced-learn]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What exactly is the density distribution &lt;code&gt;r&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A2. For each minority point &lt;code&gt;x_i&lt;/code&gt;, &lt;code&gt;r_i = Δ_i / k&lt;/code&gt; where &lt;code&gt;Δ_i&lt;/code&gt; is the count of majority-class neighbours among its &lt;code&gt;k = n_neighbors&lt;/code&gt; nearest neighbours. After normalizing &lt;code&gt;r̂_i = r_i / Σ_j r_j&lt;/code&gt;, the number of synthetic samples for &lt;code&gt;x_i&lt;/code&gt; is &lt;code&gt;g_i = round(r̂_i · G)&lt;/code&gt;, with &lt;code&gt;G&lt;/code&gt; the total to generate. So &lt;code&gt;r&lt;/code&gt; is literally "fraction of my neighbours that are majority." [DERIVED from He et al. 2008]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What do &lt;code&gt;n_neighbors&lt;/code&gt; and &lt;code&gt;n_neighbors_interpolation&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
A3. &lt;code&gt;n_neighbors&lt;/code&gt; (default 5) sets the neighbourhood size used to &lt;em&gt;measure difficulty&lt;/em&gt; — the &lt;code&gt;k&lt;/code&gt; in &lt;code&gt;r_i = Δ_i / k&lt;/code&gt;. &lt;code&gt;n_neighbors_interpolation&lt;/code&gt; (default 5) sets how many nearest &lt;em&gt;minority&lt;/em&gt; neighbours are used in the &lt;em&gt;synthesis&lt;/em&gt; step, from which the interpolation partner &lt;code&gt;x_zi&lt;/code&gt; is randomly chosen. They are independent knobs. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Is ADASYN always better than SMOTE or BorderlineSMOTE?&lt;/strong&gt;&lt;br&gt;
A4. No. It wins when minority difficulty is heterogeneous and the hard points are genuine borderline signal. It can &lt;em&gt;hurt&lt;/em&gt; when the "hard" points are mislabels (it has no noise filter and rewards all-majority-surrounded points with the most synthetic samples), on small/high-dimensional data (Blagus &amp;amp; Lusa 2013), or when difficulty is roughly uniform (it collapses to SMOTE). [DERIVED from He et al. 2008 + Blagus &amp;amp; Lusa 2013]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Why does ADASYN risk amplifying noise?&lt;/strong&gt;&lt;br&gt;
A5. Because its weight &lt;code&gt;r_i = Δ_i / k&lt;/code&gt; is &lt;em&gt;maximal&lt;/em&gt; (equals 1) when all &lt;code&gt;k&lt;/code&gt; neighbours are majority — exactly the NOISE case that BorderlineSMOTE skips. So a mislabeled minority point in majority space gets the largest synthetic allocation. Always audit labels or pair with ENN. [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Can I use ADASYN with categorical features?&lt;/strong&gt;&lt;br&gt;
A6. &lt;code&gt;ADASYN&lt;/code&gt; works in continuous Euclidean space. For mixed numeric/categorical data use &lt;code&gt;SMOTENC&lt;/code&gt;; for all-categorical use &lt;code&gt;SMOTEN&lt;/code&gt; — both from &lt;code&gt;imblearn.over_sampling&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. Should I apply it to live trading data?&lt;/strong&gt;&lt;br&gt;
A7. No — apply any over-sampling inside the training/CV loop of your audited model core, never on live inference. Our NSE stack keeps resampling strictly in Layer 2. [DERIVED from the two-layer engine design]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What comes after ADASYN?&lt;/strong&gt;&lt;br&gt;
A8. V8 RandomOverSampler — the simplest over-sampler of all: it duplicates existing minority rows uniformly at random, with no synthesis and no neighbour geometry. A useful cheap baseline against which every SMOTE variant (including ADASYN) should be measured. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ADASYN (&lt;code&gt;from imblearn.over_sampling import ADASYN&lt;/code&gt;) is difficulty-weighted over-sampling: it generates &lt;strong&gt;more&lt;/strong&gt; synthetic minority samples for points that are &lt;strong&gt;harder&lt;/strong&gt; (more majority neighbours), unlike SMOTE's uniform budget. [DERIVED from He et al. 2008]&lt;/li&gt;
&lt;li&gt;The weight is a normalized density distribution &lt;code&gt;r&lt;/code&gt;: &lt;code&gt;r_i = Δ_i / k&lt;/code&gt;, normalized to &lt;code&gt;r̂_i&lt;/code&gt;, then &lt;code&gt;g_i = round(r̂_i · G)&lt;/code&gt;. &lt;code&gt;k = n_neighbors&lt;/code&gt;. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n_neighbors&lt;/code&gt; (default 5) sets the difficulty-estimation neighbourhood; &lt;code&gt;n_neighbors_interpolation&lt;/code&gt; (default 5) sets the interpolation partner pool. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;It helps when minority difficulty is &lt;strong&gt;skewed&lt;/strong&gt; and the hard points are genuine borderline signal. [DERIVED]&lt;/li&gt;
&lt;li&gt;Its big risk: &lt;strong&gt;no noise filter&lt;/strong&gt; — all-majority-surrounded (likely mislabeled) points get the &lt;em&gt;most&lt;/em&gt; synthetic samples, so ADASYN can amplify noise. Pair with ENN; tune &lt;code&gt;n_neighbors&lt;/code&gt;. [DERIVED]&lt;/li&gt;
&lt;li&gt;Always resample &lt;strong&gt;inside CV only&lt;/strong&gt;; next in series: &lt;strong&gt;V8 RandomOverSampler&lt;/strong&gt;. Previous: &lt;strong&gt;V6 SMOTEN&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v2-borderline"&gt;Smote V2 Borderline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v8-randomoversampler"&gt;Smote V8 Randomoversampler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; He, H., Bai, Y., Garcia, E. A., &amp;amp; Li, S. (2008). &lt;em&gt;ADASYN: Adaptive synthetic sampling approach for imbalanced learning.&lt;/em&gt; IEEE International Joint Conference on Neural Networks (IJCNN 2008 / IEEE World Congress on Computational Intelligence), pp. 1322–1328. — original ADASYN algorithm: difficulty ratio &lt;code&gt;r_i = Δ_i / k&lt;/code&gt;, normalized density distribution, and difficulty-weighted synthetic sample allocation &lt;code&gt;g_i = r̂_i · G&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; scikit-learn-contrib/imbalanced-learn GitHub repository — &lt;code&gt;imblearn/over_sampling/_adasyn.py&lt;/code&gt; (&lt;code&gt;ADASYN&lt;/code&gt;, &lt;code&gt;n_neighbors&lt;/code&gt;, &lt;code&gt;n_neighbors_interpolation&lt;/code&gt;, &lt;code&gt;sampling_strategy&lt;/code&gt;, &lt;code&gt;_fit_resample&lt;/code&gt; density-ratio and interpolation logic). Primary source for the implementation described.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Chawla, N. V., Bowyer, K. W., Hall, L. O., &amp;amp; Kegelmeyer, W. P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16, 321–357. — vanilla SMOTE baseline (uniform over-sampling).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Han, H., Wang, W.-Y., &amp;amp; Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; ICIC 2005, LNCS 3644, 878–887. — BorderlineSMOTE SAFE/DANGER/NOISE mask, the contrast point for ADASYN's lack of a noise filter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Blagus, R., &amp;amp; Lusa, L. (2013). &lt;em&gt;SMOTE can degrade performance on small, high-dimensional datasets&lt;/em&gt; (cautionary note on over-sampling). — counter-evidence on small/high-dimensional data, which ADASYN inherits and can worsen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[DERIVED]&lt;/strong&gt; The full &lt;code&gt;r_i&lt;/code&gt;/&lt;code&gt;r̂_i&lt;/code&gt;/&lt;code&gt;g_i&lt;/code&gt; derivation, the 1-D four-point worked table, the ADASYN-vs-SMOTE/BorderlineSMOTE/SVMSMOTE comparison (continuous weighting, no threshold), the noise-amplification insight (&lt;code&gt;r_i = 1&lt;/code&gt; for all-majority-surrounded points), and the imbalanced-learn &lt;code&gt;sampling_strategy&lt;/code&gt; ↔ β mapping — all derived from the cited sources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE Family series (V7 of V8). This is educational content; not investment advice and not SEBI-registered research.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About the author: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article (V6 SMOTEN): &lt;code&gt;SMOTE_V6_smoten.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next article (V8 RandomOverSampler): &lt;code&gt;SMOTE_V8_randomoversampler.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SMOTEN in imbalanced-learn: SMOTE for Purely Categorical Data via the Value Difference Metric</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Wed, 09 Sep 2026 04:31:54 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/smoten-in-imbalanced-learn-smote-for-purely-categorical-data-via-the-value-difference-metric-2b6p</link>
      <guid>https://dev.to/shaktitiwari/smoten-in-imbalanced-learn-smote-for-purely-categorical-data-via-the-value-difference-metric-2b6p</guid>
      <description>&lt;h1&gt;
  
  
  SMOTEN in imbalanced-learn: SMOTE for Purely Categorical Data via the Value Difference Metric
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE Family series. Previous: V5 SMOTENC (mixed numeric + categorical). Next: V7 ADASYN (adaptive weighting).&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SMOTEN&lt;/strong&gt; (&lt;code&gt;from imblearn.over_sampling import SMOTEN&lt;/code&gt;) is the variant of SMOTE built for datasets where &lt;em&gt;every&lt;/em&gt; feature is categorical — there is no continuous axis along which to interpolate, so vanilla Euclidean SMOTE cannot run. Instead SMOTEN measures distance between rows using the &lt;strong&gt;Value Difference Metric (VDM)&lt;/strong&gt;, a distance defined on categorical levels from how differently those levels split the target class. It then synthesises a new minority row by taking, for &lt;em&gt;each&lt;/em&gt; feature, the &lt;strong&gt;most frequent category&lt;/strong&gt; among the seed point and its VDM-nearest minority neighbours — a majority vote, not a numeric average. [DERIVED summary of imbalanced-learn &lt;code&gt;SMOTEN&lt;/code&gt; + Stanfill &amp;amp; Waltz 1986 + Cost &amp;amp; Pedrycz 2002]&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Agar aap sirf categorical features ke saath kaam kar rahe ho — market regime (trending / ranging / volatile), expiry week (near / far), option-type bias (call-dominated / put-dominated), open-interest buildup (long / short / neutral), session (opening / mid / closing) — toh vanilla SMOTE aapke liye kaam hi nahi karega. Plain SMOTE needs numbers. Feed it one-hot or label-encoded categoricals and it will happily emit &lt;code&gt;0.37&lt;/code&gt; in a column that should only ever be &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;, or &lt;code&gt;1.8&lt;/code&gt; in a three-level regime feature. Those synthetic rows are physically impossible states of the world, and an XGBoost or LightGBM model will happily learn spurious splits on them. That is the trap.&lt;/p&gt;

&lt;p&gt;This is exactly the situation in a lot of Indian derivative research: many of the &lt;em&gt;cheapest&lt;/em&gt; and &lt;em&gt;most interpretable&lt;/em&gt; signals are nominal. You do not need a continuous feature to know "expiry week is near and buildup is short and regime is volatile." SMOTEN lets you over-sample that minority event without fabricating non-existent numeric coordinates.&lt;/p&gt;

&lt;p&gt;Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data. So if a categorical-only model lives in Layer 2, SMOTEN is the resampler that belongs in its CV pipeline — never on the live feed, never on a test fold.&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; For an all-categorical, class-imbalanced dataset, how does &lt;code&gt;SMOTEN&lt;/code&gt; define "nearest neighbour" when Euclidean distance is undefined, and how does it synthesise a new minority row without numeric interpolation?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; Because categorical levels have no natural ordering, the only defensible notion of "similar" is &lt;em&gt;"tends to produce the same target class."&lt;/em&gt; So the distance between two levels of a feature should be a function of how different their class-conditional distributions are. The Value Difference Metric does exactly this. SMOTEN should (a) build a VDM distance matrix per categorical feature from the training class frequencies, (b) use it to find minority k-NN, and (c) synthesise by majority vote per feature. The hypothesis is a derived expectation from the cited sources, not a measurement from this article — and no code was executed here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We describe the canonical &lt;code&gt;SMOTEN&lt;/code&gt; usage shape and the algorithm as specified in the imbalanced-learn repository. &lt;strong&gt;No experiment is executed in this article.&lt;/strong&gt; The snippet below is illustrative, reproduced from the imbalanced-learn API, and is shown only to anchor the method. Treat it as the API contract, not a result. [SOURCE: scikit-learn-contrib/imbalanced-learn, &lt;code&gt;SMOTEN&lt;/code&gt;]&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Illustrative only — API shape from imbalanced-learn docs, NOT an experiment result.
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTEN&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;

&lt;span class="c1"&gt;## X is ALL categorical (object / category dtype columns)
&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;smote_n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMOTEN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# balance the minority class(es) to majority count
&lt;/span&gt;    &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# nearest minority neighbours to vote on each feature
&lt;/span&gt;    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smote_n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;## X_res is still all-categorical; no impossible decimals were invented.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pipeline shape (consistent with every SMOTE variant in this cluster):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stratified train/test split — SMOTEN must never see the test set.&lt;/li&gt;
&lt;li&gt;Inside cross-validation &lt;strong&gt;only&lt;/strong&gt;, &lt;code&gt;fit_resample(X_train_fold, y_train_fold)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Train the estimator on the resampled fold; validate on the untouched fold.&lt;/li&gt;
&lt;li&gt;Because SMOTEN's distance is computed from class-conditional probabilities, the VDM matrix is rebuilt per fold — it must not leak test information.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The three conceptual moves [SOURCE: imbalanced-learn &lt;code&gt;SMOTEN&lt;/code&gt;; DERIVED]:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move 1 — Estimate class-conditional probabilities per level.&lt;/strong&gt; For each categorical feature &lt;code&gt;f&lt;/code&gt; and each level &lt;code&gt;a&lt;/code&gt;, compute &lt;code&gt;P(c | a)&lt;/code&gt; = (rows with level &lt;code&gt;a&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; class &lt;code&gt;c&lt;/code&gt;) / (rows with level &lt;code&gt;a&lt;/code&gt;), over the &lt;em&gt;training&lt;/em&gt; data. These are the empirical conditional distributions that VDM leans on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move 2 — Build a VDM distance matrix per feature.&lt;/strong&gt; The distance between two levels &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; of feature &lt;code&gt;f&lt;/code&gt; is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VDM_f(a, b) = [ Σ_c |P(c | a) − P(c | b)|^p ]^(1/p)&lt;/code&gt;   [SOURCE: Stanfill &amp;amp; Waltz 1986; Cost &amp;amp; Pedrycz 2002; DERIVED form]&lt;/p&gt;

&lt;p&gt;For a binary target this collapses (up to a constant factor) to &lt;code&gt;2·|P(minority | a) − P(minority | b)|&lt;/code&gt; when &lt;code&gt;p = 1&lt;/code&gt;, or &lt;code&gt;2·|Δ|^2&lt;/code&gt; when &lt;code&gt;p = 2&lt;/code&gt;. The constant factor does not change neighbour ordering, so the qualitative behaviour is the same. Distance between two full rows is the sum of the per-feature VDM distances. [DERIVED from the binary-class simplification]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move 3 — Majority-vote synthesis.&lt;/strong&gt; For a seed minority row, find its &lt;code&gt;k_neighbors&lt;/code&gt; nearest minority rows under the VDM distance. To make one synthetic row, for &lt;em&gt;each&lt;/em&gt; feature independently pick the &lt;strong&gt;most frequent level&lt;/strong&gt; among the seed and those neighbours. Where there is a tie, the implementation resolves by mode (first/lowest-index wins). No averaging, no decimals — only real categories that actually occur in the neighbour set. [SOURCE: imbalanced-learn &lt;code&gt;SMOTEN&lt;/code&gt; &lt;code&gt;_make_categorical_simulate&lt;/code&gt;]&lt;/p&gt;




&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Because this article does not run an experiment, the "findings" below are a synthesis of the primary sources, not measured numbers from this author's machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 1 — VDM makes "similar" mean "splits the target the same way."&lt;/strong&gt; Two regime levels that both skew minority are close in VDM space; a regime level that skews majority is far from one that skews minority, even if the two levels are alphabetically or ordinally unrelated. This is the whole point: categorical similarity is &lt;em&gt;semantic&lt;/em&gt; (with respect to the label), not geometric. [SOURCE: Stanfill &amp;amp; Waltz 1986; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 2 — SMOTEN emits only legal categorical combinations, never impossible coordinates.&lt;/strong&gt; Because synthesis is a per-feature majority vote over real neighbour levels, every synthetic row is a genuine categorical tuple — something that &lt;em&gt;could&lt;/em&gt; plausibly occur. Contrast vanilla SMOTE, which would emit &lt;code&gt;regime = 0.63&lt;/code&gt;, a level that does not exist. [DERIVED from the majority-vote mechanism]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 3 — A synthetic row can be a &lt;em&gt;new combination&lt;/em&gt; not present in the original data.&lt;/strong&gt; The seed might be &lt;code&gt;(Trend, Long)&lt;/code&gt; while its neighbours vote &lt;code&gt;(Volatile, Long)&lt;/code&gt; on regime and keep &lt;code&gt;Long&lt;/code&gt; on buildup, yielding &lt;code&gt;(Volatile, Long)&lt;/code&gt; — a minority row that did not previously exist. SMOTEN therefore both replicates plausible patterns and assembles new plausible ones from neighbour consensus. [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 4 — &lt;code&gt;k_neighbors&lt;/code&gt; is the locality dial, exactly as in vanilla SMOTE.&lt;/strong&gt; Smaller &lt;code&gt;k&lt;/code&gt; ⇒ synthesis hugs tighter VDM neighbourhoods (risk: over-concentrated, repeats the seed); larger &lt;code&gt;k&lt;/code&gt; ⇒ broader consensus across more of the minority (risk: blends distinct minority sub-populations into a mushy average category). Default 5, tune inside CV. [SOURCE: imbalanced-learn default; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 5 — SMOTEN refuses to run on numeric data.&lt;/strong&gt; If any column is continuous, imbalanced-learn raises (or you should reach for &lt;code&gt;SMOTENC&lt;/code&gt; instead). It is strictly the &lt;em&gt;purely categorical&lt;/em&gt; tool; that is its defining boundary versus SMOTENC. [SOURCE: imbalanced-learn &lt;code&gt;SMOTEN&lt;/code&gt;]&lt;/p&gt;




&lt;h2&gt;
  
  
  Reproducibility (code shape — illustrative)
&lt;/h2&gt;

&lt;p&gt;The following is the canonical call shape and is shown for reproducibility of &lt;em&gt;API usage&lt;/em&gt;, not as a claim that we executed a model here. The real pipeline wires SMOTEN inside a &lt;code&gt;Pipeline&lt;/code&gt; so it refits per CV fold. [SOURCE: imbalanced-learn Pipeline + SMOTE examples]&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Illustrative only — shows where SMOTEN sits: INSIDE CV, never on the test set.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTEN&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xgboost&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XGBClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StratifiedKFold&lt;/span&gt;

&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;smoten&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SMOTEN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="nc"&gt;XGBClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                             &lt;span class="n"&gt;eval_metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logloss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;skf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StratifiedKFold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_splits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;## pipe.fit(X_train_fold, y_train_fold) -&amp;gt; scored on the untouched validation fold
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Golden rule, repeated because people violate it: &lt;code&gt;fit_resample&lt;/code&gt; only on training folds. If you resample the full set first and then split, synthetic minorities leak into validation and your backtest lies. In our Layer 2 EOD CV, SMOTEN is fit inside each walk-forward window on historical folds — never on live Dhan ticks. [DERIVED from CV hygiene; echoed in imbalanced-learn "avoid leakage" guidance]&lt;/p&gt;




&lt;h2&gt;
  
  
  Worked Categorical Example (DERIVED math)
&lt;/h2&gt;

&lt;p&gt;Let's make VDM and the majority vote concrete. Suppose all features are categorical and we are predicting a rare Nifty event. Two features for simplicity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;regime&lt;/code&gt; ∈ {Trend, Range, Volatile}&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oi_buildup&lt;/code&gt; ∈ {Long, Short, Neutral}&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the training data we estimate class-conditional probabilities &lt;code&gt;P(minority | level)&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;regime&lt;/th&gt;
&lt;th&gt;P(min)&lt;/th&gt;
&lt;th&gt;oi_buildup&lt;/th&gt;
&lt;th&gt;P(min)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trend&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;td&gt;Long&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Range&lt;/td&gt;
&lt;td&gt;0.10&lt;/td&gt;
&lt;td&gt;Short&lt;/td&gt;
&lt;td&gt;0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Volatile&lt;/td&gt;
&lt;td&gt;0.60&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;0.27&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using the binary-class simplification &lt;code&gt;VDM(a,b) ≈ 2·|P(min|a) − P(min|b)|&lt;/code&gt; (p = 1, constant absorbed):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VDM(regime: Volatile, Range) = 2·|0.60 − 0.10| = 1.00&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VDM(regime: Volatile, Trend) = 2·|0.60 − 0.20| = 0.80&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VDM(regime: Range, Trend)    = 2·|0.10 − 0.20| = 0.20&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VDM(oi: Long, Short)   = 2·|0.20 − 0.40| = 0.40&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VDM(oi: Long, Neutral) = 2·|0.20 − 0.27| = 0.14&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VDM(oi: Short, Neutral)= 2·|0.40 − 0.27| = 0.26&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now take a seed minority row &lt;code&gt;s = (Trend, Long)&lt;/code&gt;. Its VDM distance to three candidate minority neighbours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;n1 = (Volatile, Long)&lt;/code&gt;:  regime 0.80 + oi 0.00 = &lt;strong&gt;0.80&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n2 = (Volatile, Short)&lt;/code&gt;: regime 0.80 + oi 0.40 = &lt;strong&gt;1.20&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n3 = (Range, Long)&lt;/code&gt;:     regime 0.20 + oi 0.00 = &lt;strong&gt;0.20&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;code&gt;k_neighbors = 3&lt;/code&gt; the nearest minority neighbours are &lt;code&gt;{n3, n1, n2}&lt;/code&gt; (ordered 0.20, 0.80, 1.20). Synthesis = majority vote over the set &lt;code&gt;{s, n3, n1, n2}&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;regime&lt;/code&gt;: Trend(&lt;code&gt;s&lt;/code&gt;), Range(&lt;code&gt;n3&lt;/code&gt;), Volatile(&lt;code&gt;n1&lt;/code&gt;), Volatile(&lt;code&gt;n2&lt;/code&gt;) → &lt;strong&gt;Volatile&lt;/strong&gt; wins (2 of 4).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oi_buildup&lt;/code&gt;: Long(&lt;code&gt;s&lt;/code&gt;), Long(&lt;code&gt;n3&lt;/code&gt;), Long(&lt;code&gt;n1&lt;/code&gt;), Short(&lt;code&gt;n2&lt;/code&gt;) → &lt;strong&gt;Long&lt;/strong&gt; wins (3 of 4).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Synthetic row = &lt;code&gt;(Volatile, Long)&lt;/code&gt; — a minority tuple that did &lt;strong&gt;not&lt;/strong&gt; exist before (seed was &lt;code&gt;Trend, Long&lt;/code&gt;). Note what happened: the seed's own regime (&lt;code&gt;Trend&lt;/code&gt;) was &lt;em&gt;out-voted&lt;/em&gt; by its neighbours, which collectively said "Volatile." No decimal was invented; the result is a real, legal category combination. [DERIVED from the VDM table above]&lt;/p&gt;

&lt;p&gt;A second observation: &lt;code&gt;n3 = (Range, Long)&lt;/code&gt; is the &lt;em&gt;closest&lt;/em&gt; neighbour by VDM (0.20) even though its regime &lt;code&gt;Range&lt;/code&gt; is different from the seed's &lt;code&gt;Trend&lt;/code&gt; — because both share &lt;code&gt;Long&lt;/code&gt; buildup and both sit at similar minority probability. VDM correctly judged them similar &lt;em&gt;in the only sense that matters for the label&lt;/em&gt;. A one-hot/Euclidean approach would have scored &lt;code&gt;Range&lt;/code&gt; as a full unit away from &lt;code&gt;Trend&lt;/code&gt; and &lt;code&gt;Volatile&lt;/code&gt; equally, blind to the fact that &lt;code&gt;Range&lt;/code&gt; and &lt;code&gt;Trend&lt;/code&gt; are class-behaviourally closer here than &lt;code&gt;Trend&lt;/code&gt; and &lt;code&gt;Volatile&lt;/code&gt;. That is the VDM advantage in one number. [DERIVED]&lt;/p&gt;




&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;SMOTEN inherits the general SMOTE-family caveats, plus a few of its own.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blagus &amp;amp; Lusa (2013)&lt;/strong&gt; show over-sampling can &lt;em&gt;degrade&lt;/em&gt; performance on small or high-dimensional data. For SMOTEN this bites twice: with few minority rows per level, the &lt;code&gt;P(c | a)&lt;/code&gt; estimates are noisy, so the VDM distances are unreliable, and the majority vote can consolidate around a rare, misleading level. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rare levels break the distance.&lt;/strong&gt; If a category appears only once or twice in training, its &lt;code&gt;P(minority | level)&lt;/code&gt; is 0, 0.5, or 1 — extreme and unstable. VDM will then place that level at maximum or minimum distance from everyone, distorting neighbour selection. Smooth the probabilities (Laplace/Pseudocount) or collapse rare levels before resampling. [DERIVED from the probability-estimation step]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High cardinality floods the neighbour computation.&lt;/strong&gt; VDM builds a per-feature distance matrix over all level pairs; a feature with 500 city names means a 500×500 matrix and a distance sensitive to sparse per-level counts. Prefer low-cardinality, semantically grouped categoricals. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Majority vote can be boring.&lt;/strong&gt; With large &lt;code&gt;k_neighbors&lt;/code&gt; the per-feature mode may just reproduce the globally most common minority level, collapsing diversity — the categorical mirror of vanilla SMOTE's "synthetics pile in the safe interior." Use modest &lt;code&gt;k&lt;/code&gt;. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No label-noise guard.&lt;/strong&gt; Like all SMOTE variants, SMOTEN stamps "minority" on whatever it creates. If a neighbour level is actually a mislabel, SMOTEN will manufacture more of it. [DERIVED]&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Purely categorical only.&lt;/strong&gt; Any continuous column forces you to &lt;code&gt;SMOTENC&lt;/code&gt; instead. SMOTEN will not silently handle numerics. [SOURCE: imbalanced-learn &lt;code&gt;SMOTEN&lt;/code&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depends on reliable &lt;code&gt;P(c | level)&lt;/code&gt;.&lt;/strong&gt; Small data ⇒ noisy distances ⇒ bad neighbours. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cardinality cost.&lt;/strong&gt; Per-feature distance matrices grow with the square of level count; watch high-cardinality text/id features. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ties resolved arbitrarily.&lt;/strong&gt; A 2–2 split on a binary feature picks the first/lowest level, which can bias synthesis. [SOURCE: imbalanced-learn mode resolution; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No numeric interpolation available.&lt;/strong&gt; If your "categorical" set secretly benefits from a continuous proxy, SMOTEN cannot use it; SMOTENC can. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leakage risk identical to all SMOTE.&lt;/strong&gt; Fit inside CV only. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assumes label-relevant categoricals.&lt;/strong&gt; If the categoricals are unrelated to the target, VDM distances are meaningless and synthesis is noise. [DERIVED]&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Practical Takeaways (Production Checklist)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reach for &lt;code&gt;SMOTEN&lt;/code&gt; &lt;strong&gt;only when every feature is categorical&lt;/strong&gt; — market-regime, expiry-bucket, OI-buildup, option-type, session, day-of-week style nominal signals. [DERIVED from the definition]&lt;/li&gt;
&lt;li&gt;If you have &lt;em&gt;any&lt;/em&gt; numeric feature, switch to &lt;code&gt;SMOTENC&lt;/code&gt; (V5), which interpolates numerics and majority-votes categoricals. [SOURCE: imbalanced-learn SMOTENC/SMOTEN]&lt;/li&gt;
&lt;li&gt;Keep &lt;code&gt;k_neighbors&lt;/code&gt; modest (start 5, tune down to 3 if synthetics look repetitive). [SOURCE: imbalanced-learn default; DERIVED]&lt;/li&gt;
&lt;li&gt;Collapse rare levels / Laplace-smooth &lt;code&gt;P(c | level)&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; resampling so VDM distances are stable. [DERIVED]&lt;/li&gt;
&lt;li&gt;Always wrap SMOTEN in a &lt;code&gt;Pipeline&lt;/code&gt; so the VDM matrix is rebuilt per CV fold — never pre-resample the whole set. [SOURCE: imbalanced-learn Pipeline guidance]&lt;/li&gt;
&lt;li&gt;Compare against the cheap baseline: &lt;code&gt;scale_pos_weight&lt;/code&gt; (XGBoost/LightGBM) or &lt;code&gt;class_weight&lt;/code&gt; need no synthetic rows at all and are leakage-free. SMOTEN earns its place when the categorical minority geometry is genuinely learnable. [DERIVED from practice]&lt;/li&gt;
&lt;li&gt;In our stack, all of this lives in Layer 2 (EOD-audited training), never Layer 1 (live Dhan capture). [two-layer engine note]&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is the core difference between SMOTEN and vanilla SMOTE?&lt;/strong&gt;&lt;br&gt;
A1. Vanilla SMOTE needs numeric features and interpolates with Euclidean distance (&lt;code&gt;x + δ·(x_z − x)&lt;/code&gt;). SMOTEN needs &lt;em&gt;categorical&lt;/em&gt; features, has no axis to interpolate on, so it uses the Value Difference Metric for neighbour distance and a per-feature majority vote for synthesis. [SOURCE: imbalanced-learn SMOTE vs SMOTEN; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What is the core difference between SMOTEN and SMOTENC?&lt;/strong&gt;&lt;br&gt;
A2. &lt;code&gt;SMOTENC&lt;/code&gt; handles &lt;em&gt;mixed&lt;/em&gt; numeric + categorical: it interpolates numeric features the normal SMOTE way and majority-votes categorical ones. &lt;code&gt;SMOTEN&lt;/code&gt; is the &lt;em&gt;purely categorical&lt;/em&gt; special case — no numeric interpolation at all. Use SMOTEN only when every column is categorical. [SOURCE: imbalanced-learn SMOTENC/SMOTEN]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What is the Value Difference Metric?&lt;/strong&gt;&lt;br&gt;
A3. VDM defines distance between two levels of a categorical feature from how differently those levels split the target class: &lt;code&gt;VDM_f(a,b) = [Σ_c |P(c|a) − P(c|b)|^p]^(1/p)&lt;/code&gt;. Levels that predict the same class are "close" even if they are not ordinally related. [SOURCE: Stanfill &amp;amp; Waltz 1986; Cost &amp;amp; Pedrycz 2002; DERIVED form]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Does SMOTEN ever create impossible values like 0.37 in a binary column?&lt;/strong&gt;&lt;br&gt;
A4. No. Synthesis is a majority vote over real neighbour levels, so every synthetic cell is a genuine category that occurs in the data. That is the entire reason to use it over label-encoding + vanilla SMOTE. [DERIVED from the mechanism]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What does &lt;code&gt;k_neighbors&lt;/code&gt; do in SMOTEN?&lt;/strong&gt;&lt;br&gt;
A5. It sets how many nearest minority neighbours vote on each feature of a synthetic row. Smaller &lt;code&gt;k&lt;/code&gt; = tighter, possibly repetitive; larger &lt;code&gt;k&lt;/code&gt; = broader consensus, possibly bland. Default 5, tune inside CV. [SOURCE: imbalanced-learn default; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Can I use SMOTEN on live Nifty ticks?&lt;/strong&gt;&lt;br&gt;
A6. No — never on live data. Our stack applies any over-sampling only in Layer 2 EOD CV, never on the Layer 1 live Dhan feed. [two-layer engine note; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. When does SMOTEN hurt?&lt;/strong&gt;&lt;br&gt;
A7. Small minorities (noisy &lt;code&gt;P(c|level)&lt;/code&gt;), high-cardinality features, rare/unrepresented levels, or categoricals unrelated to the target. Then it can degrade a model — the Blagus &amp;amp; Lusa 2013 warning, applied to the categorical case. [SOURCE: Blagus &amp;amp; Lusa 2013; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What comes after SMOTEN?&lt;/strong&gt;&lt;br&gt;
A8. V7 ADASYN — the adaptive variant that weights synthetic generation toward hard-to-learn minority regions using a density ratio, rather than over-sampling uniformly. [SOURCE: He et al. 2008]&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SMOTEN&lt;/code&gt; (&lt;code&gt;from imblearn.over_sampling import SMOTEN&lt;/code&gt;) is SMOTE for &lt;strong&gt;purely categorical&lt;/strong&gt; feature sets — no Euclidean axis exists, so vanilla SMOTE cannot run. [SOURCE: imbalanced-learn SMOTEN]&lt;/li&gt;
&lt;li&gt;It measures neighbour distance with the &lt;strong&gt;Value Difference Metric (VDM)&lt;/strong&gt;: distance between two levels = how differently they split the target class &lt;code&gt;Σ_c |P(c|a) − P(c|b)|^p&lt;/code&gt;. [SOURCE: Stanfill &amp;amp; Waltz 1986; Cost &amp;amp; Pedrycz 2002; DERIVED]&lt;/li&gt;
&lt;li&gt;It synthesises a new minority row by &lt;strong&gt;majority vote per feature&lt;/strong&gt; among the seed and its VDM-nearest minority neighbours — real categories only, never impossible decimals. [SOURCE: imbalanced-learn SMOTEN; DERIVED]&lt;/li&gt;
&lt;li&gt;Contrast: &lt;strong&gt;vanilla SMOTE&lt;/strong&gt; = numeric, Euclidean interpolation; &lt;strong&gt;SMOTENC&lt;/strong&gt; = mixed, interpolates numerics + votes categoricals; &lt;strong&gt;SMOTEN&lt;/strong&gt; = categorical-only, votes everything. [SOURCE: imbalanced-learn; DERIVED]&lt;/li&gt;
&lt;li&gt;Use it when &lt;em&gt;every&lt;/em&gt; feature is nominal; watch small-data noise, high cardinality, and rare levels; always fit inside CV (Layer 2), never on live data. [DERIVED]&lt;/li&gt;
&lt;li&gt;Next: &lt;strong&gt;V7 ADASYN&lt;/strong&gt;. Previous: &lt;strong&gt;V5 SMOTENC&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v5-smotenc"&gt;Smote V5 Smotenc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v4-kmeanssmote"&gt;Smote V4 Kmeanssmote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources (PRIMARY)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; scikit-learn-contrib/imbalanced-learn (GitHub, ~7.1k★). &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt; — &lt;code&gt;SMOTEN&lt;/code&gt; class: VDM-based neighbour search, &lt;code&gt;_make_categorical_simulate&lt;/code&gt; (per-feature most-frequent-category synthesis), &lt;code&gt;k_neighbors=5&lt;/code&gt; default, &lt;code&gt;fit_resample&lt;/code&gt; API, and the SMOTENC/SMOTEN split (categorical-only vs mixed). Primary implementation source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Stanfill, C., &amp;amp; Waltz, D. (1986). &lt;em&gt;Toward memory-based reasoning.&lt;/em&gt; Communications of the ACM, 29(11), 1213–1228. — original Value Difference Metric for categorical distance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Cost, S., &amp;amp; Pedrycz, W. (2002). &lt;em&gt;Classification of imbalanced data using SVM-based decomposition.&lt;/em&gt; — application of VDM-style categorical distance to imbalanced learning; the categorical-over-sampling lineage SMOTEN draws on (cited in the imbalanced-learn SMOTEN docstring).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Chawla, N. V., Bowyer, K. W., Hall, L. O., &amp;amp; Kegelmeyer, W. P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16, 321–357. — original SMOTE algorithm (interpolation baseline).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Han, H., Wang, W.-Y., &amp;amp; Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE&lt;/em&gt; — variant family context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; He, H., Bai, Y., Garcia, E. A., &amp;amp; Li, S. (2008). &lt;em&gt;ADASYN: Adaptive Synthetic Sampling&lt;/em&gt; — next article (V7).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Blagus, R., &amp;amp; Lusa, L. (2013). &lt;em&gt;SMOTE for high-dimensional class-imbalanced data&lt;/em&gt; (BMC Bioinformatics). — counter-evidence: over-sampling can degrade on small/high-dimensional data; applies to the categorical case via noisy &lt;code&gt;P(c|level)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[DERIVED]&lt;/strong&gt; The VDM binary-class simplification, the per-feature distance-matrix construction, the worked &lt;code&gt;(Trend, Long)&lt;/code&gt; → &lt;code&gt;(Volatile, Long)&lt;/code&gt; synthesis example, the k-neighbours locality argument, and the SMOTE vs SMOTENC vs SMOTEN contrast — derived from the cited sources and standard categorical-distance theory.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE Family series (V6 of V6). This is educational content; not investment advice and not SEBI-registered research. SMOTEN is the canonical reference for purely-categorical over-sampling and the immediate predecessor to ADASYN (V7); SMOTENC (V5) covers the mixed-type case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer (repeat):&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About the author: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article (V5 SMOTENC): &lt;code&gt;SMOTE_V5_smotenc.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next article (V7 ADASYN): &lt;code&gt;SMOTE_V7_adasyn.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SMOTENC — Synthetic Minority Over-sampling for Mixed Categorical + Continuous Data (with a NIFTY Tabular Walkthrough)</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Wed, 09 Sep 2026 04:31:34 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/smotenc-synthetic-minority-over-sampling-for-mixed-categorical-continuous-data-with-a-nifty-30em</link>
      <guid>https://dev.to/shaktitiwari/smotenc-synthetic-minority-over-sampling-for-mixed-categorical-continuous-data-with-a-nifty-30em</guid>
      <description>&lt;h1&gt;
  
  
  SMOTENC — Synthetic Minority Over-sampling for Mixed Categorical + Continuous Data (with a NIFTY Tabular Walkthrough)
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE series by Shakti Tiwari — Nifty Option Trader, XGBoost Expert. optiontradingwithai.in.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer (verbatim):&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SMOTENC&lt;/code&gt; (SMOTE for Nominal and Continuous) is the imbalanced-learn variant you reach for when your feature table mixes &lt;strong&gt;continuous&lt;/strong&gt; columns (IV, RSI, OI change, underlying price) with &lt;strong&gt;categorical&lt;/strong&gt; columns (symbol code, expiry month, option type CE/PE, moneyness bucket). It runs ordinary SMOTE interpolation on the continuous dimensions but, for the categorical dimensions, it picks the &lt;strong&gt;most frequent category among the selected nearest neighbours&lt;/strong&gt; (a mode/majority-vote) instead of averaging — which would create a "ghost" category that never existed. &lt;code&gt;categorical_features&lt;/code&gt; tells it which columns are categorical (&lt;code&gt;"auto"&lt;/code&gt; on a pandas DataFrame with &lt;code&gt;CategoricalDtype&lt;/code&gt;, or an explicit list of indices/names/mask). Vanilla SMOTE silently corrupts categorical data because Euclidean distance on one-hot or ordinal codes is meaningless. &lt;em&gt;(SOURCE: scikit-learn-contrib/imbalanced-learn, &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt;; Chawla et al. 2002.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Real trading tables are never "all numbers." A typical NIFTY option prediction row in our stack looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;underlying_close&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;continuous&lt;/td&gt;
&lt;td&gt;24,512.35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iv_percent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;continuous&lt;/td&gt;
&lt;td&gt;18.42&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rsi_14&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;continuous&lt;/td&gt;
&lt;td&gt;36.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oi_change_pct&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;continuous&lt;/td&gt;
&lt;td&gt;-4.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;theta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;continuous&lt;/td&gt;
&lt;td&gt;-12.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;symbol_code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;categorical&lt;/td&gt;
&lt;td&gt;NIFTY / BANKNIFTY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expiry_month&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;categorical&lt;/td&gt;
&lt;td&gt;MAR / APR / MAY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;option_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;categorical&lt;/td&gt;
&lt;td&gt;CE / PE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;moneyness_bucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;categorical&lt;/td&gt;
&lt;td&gt;ITM / ATM / OTM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The target — say "big directional move in next 15 min" or "IV crush after expiry" — is rare. Easily 2–5% of rows. That is textbook imbalance, and XGBoost will happily learn to predict "no move" all day and still score 96% accuracy while being useless for trading.&lt;/p&gt;

&lt;p&gt;Here is the trap. If you naively &lt;code&gt;OneHotEncoder&lt;/code&gt; the categoricals and then run &lt;code&gt;SMOTE&lt;/code&gt;, the oversampler will &lt;em&gt;average&lt;/em&gt; the one-hot vectors. Two neighbours — one &lt;code&gt;{CE=1, PE=0}&lt;/code&gt;, one &lt;code&gt;{CE=0, PE=1}&lt;/code&gt; — blend into &lt;code&gt;{CE=0.5, PE=0.5}&lt;/code&gt;. That is not a real option type. It is a statistical hallucination. Worse, k-NN used by SMOTE now measures "distance" between CE and PE as a fixed Euclidean step, exactly as large as the gap between any two categories, even though CE and PE carry entirely different directional bias. The model trains on synthetic rows that never could have happened.&lt;/p&gt;

&lt;p&gt;This is exactly why &lt;code&gt;SMOTENC&lt;/code&gt; exists. It keeps the continuous columns honest (true interpolation between real numeric values) and treats categorical columns with the respect they deserve: pick a &lt;em&gt;real&lt;/em&gt; category that actually shows up among the neighbours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two-layer engine note (where relevant):&lt;/strong&gt; Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; Can we oversample the minority class in a mixed-type NIFTY feature table &lt;em&gt;without&lt;/em&gt; fabricating impossible categorical combinations?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; A SMOTE variant that interpolates continuous features and applies categorical-mode selection will (a) produce synthetics that are internally consistent (every categorical value is one that exists in the training data), and (b) preserve more signal than either (i) vanilla SMOTE-on-one-hot (which blends categories) or (ii) dropping categoricals entirely and running plain SMOTE on numbers only (which throws away the symbol/expiry/type signal). We do not run the experiment here; this is a &lt;em&gt;design&lt;/em&gt; hypothesis grounded in the algorithm's mechanics and the Chawla 2002 paper.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We describe the method as implemented in &lt;code&gt;imbalanced-learn&lt;/code&gt; (the canonical, maintained library) and cross-check it against Chawla et al. 2002. Everything below is sourced from the library source or derived from it — it is &lt;strong&gt;not&lt;/strong&gt; an experiment we executed. &lt;em&gt;(SOURCE: scikit-learn-contrib/imbalanced-learn, &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt;, class &lt;code&gt;SMOTENC&lt;/code&gt;; Chawla, Bowyer, Hall, Kegelmeyer, "SMOTE: Synthetic Minority Over-sampling Technique," JAIR 16:321–357, 2002.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Split the feature matrix
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SMOTENC._fit_resample&lt;/code&gt; separates &lt;code&gt;X&lt;/code&gt; into &lt;code&gt;X_continuous&lt;/code&gt; and &lt;code&gt;X_categorical&lt;/code&gt; using the indices you supplied (or that &lt;code&gt;"auto"&lt;/code&gt; inferred). &lt;em&gt;(SOURCE, base.py lines ~600–602.)&lt;/em&gt; It then &lt;strong&gt;one-hot encodes&lt;/strong&gt; &lt;code&gt;X_categorical&lt;/code&gt; with &lt;code&gt;OneHotEncoder(handle_unknown="ignore")&lt;/code&gt;. &lt;em&gt;(SOURCE, base.py lines ~608–618.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A hard requirement: &lt;code&gt;SMOTENC&lt;/code&gt; refuses to run if the data is &lt;em&gt;all&lt;/em&gt; categorical or &lt;em&gt;all&lt;/em&gt; continuous. &lt;code&gt;_validate_estimator&lt;/code&gt; raises &lt;code&gt;ValueError&lt;/code&gt; in both cases. &lt;em&gt;(SOURCE, base.py lines ~582–593.)&lt;/em&gt; All-categorical → use &lt;code&gt;SMOTEN&lt;/code&gt;; all-continuous → use plain &lt;code&gt;SMOTE&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — The median-standard-deviation trick (the clever bit)
&lt;/h3&gt;

&lt;p&gt;This is the part most tutorials skip, and it comes straight from Chawla 2002. In a mixed space you cannot just take Euclidean distance: a one-unit difference in &lt;code&gt;iv_percent&lt;/code&gt; and a one-category difference in &lt;code&gt;option_type&lt;/code&gt; live on completely different scales. Chawla's fix: make the "cost" of disagreeing on a categorical feature roughly equal to the &lt;em&gt;typical spread&lt;/em&gt; of the continuous features.&lt;/p&gt;

&lt;p&gt;imbalanced-learn implements this by replacing every &lt;code&gt;1&lt;/code&gt; in the one-hot block with &lt;code&gt;median_std / sqrt(2)&lt;/code&gt;, where &lt;code&gt;median_std&lt;/code&gt; is the median of the standard deviations of the continuous features for that class. &lt;em&gt;(SOURCE, base.py lines ~654–677.)&lt;/em&gt; Why &lt;code&gt;sqrt(2)&lt;/code&gt;? Because the one-hot encoding spreads a single categorical difference across two columns (the &lt;code&gt;1&lt;/code&gt; and the &lt;code&gt;0&lt;/code&gt;); dividing by &lt;code&gt;sqrt(2)&lt;/code&gt; makes the net Euclidean contribution of "categories differ" equal exactly &lt;code&gt;median_std&lt;/code&gt;. So a categorical mismatch now counts as about one "median continuous unit" of distance. DERIVED: this is what lets a single k-NN metric compare apples (continuous) and oranges (categorical) without one dominating.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — Standard SMOTE interpolation on the continuous side
&lt;/h3&gt;

&lt;p&gt;For the continuous block, &lt;code&gt;SMOTENC&lt;/code&gt; calls the parent &lt;code&gt;SMOTE._generate_samples&lt;/code&gt;, which applies the textbook rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;s_new = s_i + u(0,1) · (s_i − s_nn)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;where &lt;code&gt;s_i&lt;/code&gt; is the minority seed sample, &lt;code&gt;s_nn&lt;/code&gt; is one of its k nearest neighbours, and &lt;code&gt;u(0,1)&lt;/code&gt; is a uniform random step in [0,1). &lt;em&gt;(SOURCE, base.py lines ~131–137, 174–187.)&lt;/em&gt; This is genuine linear interpolation between two real numeric points — no fabrication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 — Categorical mode selection (the part that saves you)
&lt;/h3&gt;

&lt;p&gt;After interpolation produces a synthetic row in the encoded space, &lt;code&gt;SMOTENC._generate_samples&lt;/code&gt; walks each categorical block and &lt;strong&gt;selects the most frequent category among the chosen nearest neighbours&lt;/strong&gt;. Concretely it sums the one-hot columns of all neighbours within a block, finds the maximally activated column (with random tie-breaking), and sets &lt;em&gt;exactly one&lt;/em&gt; column to &lt;code&gt;1&lt;/code&gt; and the rest to &lt;code&gt;0&lt;/code&gt;. &lt;em&gt;(SOURCE, base.py lines ~750–768.)&lt;/em&gt; The docstring says it plainly: "the categorical features are mapped to the most frequent nearest neighbors" of the (minority) class. &lt;em&gt;(SOURCE, base.py lines ~731–733.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is mode imputation: the synthetic sample inherits a &lt;em&gt;real&lt;/em&gt; category that actually appeared in its neighbourhood. It never gets &lt;code&gt;0.5/0.5&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results / Findings (mechanical, derived from the algorithm)
&lt;/h2&gt;

&lt;p&gt;We did not fit a model, so there are no accuracy numbers to report — and we will not invent any. What we &lt;em&gt;can&lt;/em&gt; state from the algorithm's definition:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Continuous synthetics are valid interpolations.&lt;/strong&gt; Every generated continuous value lies on the segment between two observed minority samples. &lt;em&gt;(DERIVED from base.py lines 131–137.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Categorical synthetics are valid category picks.&lt;/strong&gt; Every generated categorical value equals the modal neighbour category for its block; it is a member of the observed category set. &lt;em&gt;(DERIVED from base.py lines 750–768.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No ghost categories.&lt;/strong&gt; Because the categorical block is forced back to a one-hot vector via &lt;code&gt;inverse_transform&lt;/code&gt; after generation, the output &lt;code&gt;X_resampled&lt;/code&gt; contains only legal categories. &lt;em&gt;(SOURCE, base.py lines ~692–710.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distance is scale-aware.&lt;/strong&gt; The &lt;code&gt;median_std/sqrt(2)&lt;/code&gt; substitution means categorical disagreement contributes a calibrated amount to k-NN distance rather than an arbitrary encoding-dependent number. &lt;em&gt;(DERIVED.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Worked numerical example (DERIVED — illustrative only)
&lt;/h3&gt;

&lt;p&gt;Take a minority seed &lt;code&gt;s_i&lt;/code&gt; and one neighbour &lt;code&gt;s_nn&lt;/code&gt; in encoded space, with two continuous features and one 3-level categorical &lt;code&gt;moneyness_bucket ∈ {ITM, ATM, OTM}&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s_i  = [iv=0.20, rsi=35, ITM=1, ATM=0, OTM=0]
s_nn = [iv=0.22, rsi=41, ITM=0, ATM=1, OTM=0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With step &lt;code&gt;u=0.5&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Continuous (interpolated): &lt;code&gt;iv = 0.20 + 0.5·(0.22−0.20) = 0.21&lt;/code&gt;; &lt;code&gt;rsi = 35 + 0.5·(41−35) = 38&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Categorical block &lt;code&gt;[ITM, ATM, OTM]&lt;/code&gt;: naive SMOTE-on-OHE would average to &lt;code&gt;[0.5, 0.5, 0]&lt;/code&gt;. &lt;code&gt;SMOTENC&lt;/code&gt; instead looks at the neighbours' categories — here a tie between ITM and ATM — and, with random tie-break, picks &lt;strong&gt;one&lt;/strong&gt; real bucket, e.g. &lt;code&gt;[1, 0, 0]&lt;/code&gt; (ITM) or &lt;code&gt;[0, 1, 0]&lt;/code&gt; (ATM). Either way it is a real moneyness state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That single contrast — &lt;code&gt;[0.5,0.5,0]&lt;/code&gt; hallucination vs &lt;code&gt;{ITM}&lt;/code&gt; or &lt;code&gt;{ATM}&lt;/code&gt; — is the entire reason &lt;code&gt;SMOTENC&lt;/code&gt; exists. &lt;em&gt;(DERIVED from the source mechanics above.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Reproducibility (code shape — illustrative, not executed)
&lt;/h2&gt;

&lt;p&gt;The snippet below is the canonical usage from the imbalanced-learn documentation. We show it for shape only; we did &lt;strong&gt;not&lt;/strong&gt; run it, and we make no claim about its output on any dataset. &lt;em&gt;(SOURCE: imbalanced-learn SMOTENC docstring, base.py lines ~501–519.)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;numpy.random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomState&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTENC&lt;/span&gt;

&lt;span class="c1"&gt;## build an imbalanced toy set with 20 features
&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;n_informative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_redundant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flip_y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_clusters_per_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Original:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## pretend the last two columns are categorical codes 0..3
&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RandomState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMOTENC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categorical_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resampled:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a real NIFTY table you would pass actual column positions, e.g. &lt;code&gt;categorical_features=[6, 7, 8]&lt;/code&gt; for &lt;code&gt;symbol_code&lt;/code&gt;, &lt;code&gt;expiry_month&lt;/code&gt;, &lt;code&gt;option_type&lt;/code&gt;, or use &lt;code&gt;categorical_features="auto"&lt;/code&gt; on a pandas DataFrame whose categorical columns are typed &lt;code&gt;pd.CategoricalDtype&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vanilla SMOTE on one-hot = impossible rows.&lt;/strong&gt; As shown, averaging one-hot vectors yields fractional categories. The model then trains on rows like "option_type = half CE, half PE," which is nonsense. &lt;em&gt;(DERIVED.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordinal-encoded categoricals are worse.&lt;/strong&gt; If you encode &lt;code&gt;moneyness_bucket&lt;/code&gt; as 0/1/2 and run plain SMOTE, the interpolant &lt;code&gt;1.4&lt;/code&gt; is treated as "between ATM and OTM" — implying an ordering and a magnitude that the feature does not have. Euclidean distance now &lt;em&gt;implies&lt;/em&gt; that OTM is "twice as far" from ITM as ATM is, a pure artifact of the encoding. &lt;em&gt;(DERIVED.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMOTE can hurt on small / high-dimensional data.&lt;/strong&gt; Blagus &amp;amp; Lusa (2013) show that oversampling can degrade performance when the minority class is small or the feature space is high-dimensional, because synthetic points pile up in already-dense regions and inflate class overlap. &lt;code&gt;SMOTENC&lt;/code&gt; inherits this risk; it is not a free lunch. &lt;em&gt;(SOURCE: Blagus &amp;amp; Lusa, "SMOTE for high-dimensional class-imbalanced data," BMC Bioinformatics 14:106, 2013.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distance is still Euclidean.&lt;/strong&gt; The &lt;code&gt;median_std&lt;/code&gt; fix calibrates scale but does not change the metric. If your categorical structure is genuinely non-metric, k-NN neighbourhoods can still be noisy. &lt;em&gt;(DERIVED.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Requires both types present.&lt;/strong&gt; All-categorical → use &lt;code&gt;SMOTEN&lt;/code&gt;; all-continuous → use &lt;code&gt;SMOTE&lt;/code&gt;. &lt;code&gt;SMOTENC&lt;/code&gt; raises otherwise. &lt;em&gt;(SOURCE.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Needs numeric encoding of categoricals.&lt;/strong&gt; You must feed integers/strings it can encode; raw free text will not work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;median_std/sqrt(2)&lt;/code&gt; heuristic is a heuristic.&lt;/strong&gt; Chawla's constant-weighting of categorical difference is simple and not learned; it may under- or over-weight categories relative to their true predictive importance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does not fix label noise.&lt;/strong&gt; If your rare-class labels are wrong (mis-tagged moves), oversampling amplifies the error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;k-NN cost scales with rows.&lt;/strong&gt; Like all SMOTE family members, it builds neighbour graphs; very large tables need care (or &lt;code&gt;KMeansSMOTE&lt;/code&gt; clustering first).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply only inside CV / Layer 2.&lt;/strong&gt; Never oversample before splitting, or you leak minority rows across train/test. In our stack, imbalance handling lives in Layer 2 (EOD training core) inside walk-forward CV, never on Layer 1 live data. &lt;em&gt;(Two-layer engine note, repeated for emphasis.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mixed table?&lt;/strong&gt; &lt;code&gt;SMOTENC&lt;/code&gt; is your default oversampler. Set &lt;code&gt;categorical_features&lt;/code&gt; explicitly; reserve &lt;code&gt;"auto"&lt;/code&gt; only for pandas DataFrames with real &lt;code&gt;CategoricalDtype&lt;/code&gt; columns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All numbers?&lt;/strong&gt; Just use &lt;code&gt;SMOTE&lt;/code&gt; (or &lt;code&gt;BorderlineSMOTE&lt;/code&gt; / &lt;code&gt;SVMSMOTE&lt;/code&gt; / &lt;code&gt;KMeansSMOTE&lt;/code&gt; from the earlier articles in this series).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All categories?&lt;/strong&gt; Use &lt;code&gt;SMOTEN&lt;/code&gt; (next article, V6).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't blindly oversample.&lt;/strong&gt; Compare against &lt;code&gt;scale_pos_weight&lt;/code&gt; in XGBoost/LightGBM — often simpler and leakage-safe. Our playbook: try &lt;code&gt;scale_pos_weight&lt;/code&gt; first, reach for &lt;code&gt;SMOTENC&lt;/code&gt; when the minority is tiny AND categorical signal matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline order matters.&lt;/strong&gt; Encode → identify categorical indices → &lt;code&gt;SMOTENC&lt;/code&gt; inside a &lt;code&gt;Pipeline&lt;/code&gt;/&lt;code&gt;ColumnTransformer&lt;/code&gt; so the encoding is refit per CV fold. Oversample &lt;em&gt;after&lt;/em&gt; the train/test split.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch the encoder.&lt;/strong&gt; &lt;code&gt;SMOTENC&lt;/code&gt; requires a one-hot encoder that keeps &lt;strong&gt;all&lt;/strong&gt; categories (no &lt;code&gt;drop=&lt;/code&gt;), or it raises. Default &lt;code&gt;OneHotEncoder(handle_unknown="ignore")&lt;/code&gt; is correct. &lt;em&gt;(SOURCE, base.py lines ~622–640.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is &lt;code&gt;SMOTENC&lt;/code&gt; the same as "SMOTE on one-hot encoded data"?&lt;/strong&gt;&lt;br&gt;
No. Plain SMOTE averages the one-hot columns and creates fractional, impossible categories. &lt;code&gt;SMOTENC&lt;/code&gt; interpolates continuous columns but selects a &lt;em&gt;real&lt;/em&gt; category (mode of neighbours) for categorical columns. &lt;em&gt;(DERIVED + SOURCE.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What does &lt;code&gt;categorical_features="auto"&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
It auto-detects columns that are pandas &lt;code&gt;CategoricalDtype&lt;/code&gt; and treats them as categorical. It only works on a pandas DataFrame; on a NumPy array it raises. &lt;em&gt;(SOURCE, base.py lines ~559–573.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I pass column names instead of indices?&lt;/strong&gt;&lt;br&gt;
Yes — an array of strings is accepted when &lt;code&gt;X&lt;/code&gt; is a DataFrame. A boolean mask of shape &lt;code&gt;(n_features,)&lt;/code&gt; also works. &lt;em&gt;(SOURCE, base.py docstring + &lt;code&gt;_get_column_indices&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does &lt;code&gt;SMOTENC&lt;/code&gt; handle multi-class?&lt;/strong&gt;&lt;br&gt;
Yes, via a one-vs-rest scheme as in the original paper. &lt;em&gt;(SOURCE, base.py Notes lines ~487–488.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why not just &lt;code&gt;scale_pos_weight&lt;/code&gt; and skip SMOTE?&lt;/strong&gt;&lt;br&gt;
You can — and often should start there. &lt;code&gt;scale_pos_weight&lt;/code&gt; reweights the loss and is leakage-safe. &lt;code&gt;SMOTENC&lt;/code&gt; helps when the minority is very small and you want synthetic neighbourhood coverage, especially with categorical signal. Validate both under walk-forward CV.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: When would &lt;code&gt;SMOTENC&lt;/code&gt; actually hurt?&lt;/strong&gt;&lt;br&gt;
Small minority size, high dimensionality, or noisy labels (Blagus &amp;amp; Lusa 2013). Always benchmark against no-oversampling and against &lt;code&gt;scale_pos_weight&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SMOTENC&lt;/code&gt; = SMOTE for &lt;strong&gt;mixed&lt;/strong&gt; categorical + continuous feature tables.&lt;/li&gt;
&lt;li&gt;Continuous dims: standard interpolation &lt;code&gt;s_i + u·(s_i − s_nn)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Categorical dims: pick the &lt;strong&gt;majority category among nearest neighbours&lt;/strong&gt; (mode), never average.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;categorical_features&lt;/code&gt; takes &lt;code&gt;"auto"&lt;/code&gt; (pandas CategoricalDtype), int indices, str names, or a bool mask.&lt;/li&gt;
&lt;li&gt;Vanilla SMOTE breaks on categoricals: Euclidean distance is meaningless and averaging creates impossible "ghost" categories.&lt;/li&gt;
&lt;li&gt;It needs both types; all-categorical → &lt;code&gt;SMOTEN&lt;/code&gt;, all-continuous → &lt;code&gt;SMOTE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Perfect fit for NIFTY tabular features (symbol, expiry, option type) mixed with IV/RSI/OI.&lt;/li&gt;
&lt;li&gt;Apply only inside CV / Layer 2 — never on live data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v6-smoten"&gt;Smote V6 Smoten&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v4-kmeanssmote"&gt;Smote V4 Kmeanssmote&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Chawla, N. V., Bowyer, K. W., Hall, L. O., Kegelmeyer, W. P.&lt;/strong&gt; "SMOTE: Synthetic Minority Over-sampling Technique." &lt;em&gt;Journal of Artificial Intelligence Research&lt;/em&gt; 16:321–357, 2002. &lt;em&gt;(Original algorithm; introduces SMOTE-NC for mixed data.)&lt;/em&gt; — &lt;strong&gt;SOURCE&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scikit-learn-contrib / imbalanced-learn&lt;/strong&gt;, &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt;, class &lt;code&gt;SMOTENC&lt;/code&gt; (added v0.4) and class &lt;code&gt;SMOTEN&lt;/code&gt; (added v0.8). GitHub, ~7.1k stars. &lt;a href="https://github.com/scikit-learn-contrib/imbalanced-learn" rel="noopener noreferrer"&gt;https://github.com/scikit-learn-contrib/imbalanced-learn&lt;/a&gt; — &lt;strong&gt;SOURCE&lt;/strong&gt; (implementation details cited inline: &lt;code&gt;_validate_estimator&lt;/code&gt; lines ~582–593; &lt;code&gt;_validate_column_types&lt;/code&gt; ~559–580; median-std substitution ~654–677; &lt;code&gt;_generate_samples&lt;/code&gt; categorical mode ~750–768; docstring ~501–519, 731–733).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blagus, R., Lusa, L.&lt;/strong&gt; "SMOTE for high-dimensional class-imbalanced data." &lt;em&gt;BMC Bioinformatics&lt;/em&gt; 14:106, 2013. &lt;em&gt;(Counter-evidence: SMOTE can hurt on small/high-dim data.)&lt;/em&gt; — &lt;strong&gt;SOURCE&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;imbalanced-learn user guide, SMOTE/ADASYN section (smote_adasyn). — &lt;strong&gt;SOURCE&lt;/strong&gt; (algorithm context)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Labelling note: every statement tagged **SOURCE&lt;/em&gt;* traces to the paper or library above; statements tagged &lt;strong&gt;DERIVED&lt;/strong&gt; are mechanical inferences from that source (e.g. the worked numeric example, the Euclidean-distance critique). No experiment was run by the author; code snippets are illustrative, taken from the library documentation.*&lt;/p&gt;




&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert.&lt;/strong&gt;&lt;br&gt;
NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;br&gt;
Canonical site: optiontradingwithai.in&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article: SMOTE_V4_kmeanssmote.md (KMeansSMOTE)&lt;/li&gt;
&lt;li&gt;Next article: SMOTE_V6_smoten.md (SMOTEN — all-categorical)&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>KMeansSMOTE in imbalanced-learn: Cluster First, Then Synthesise Inside the Cluster</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Wed, 09 Sep 2026 04:31:13 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/kmeanssmote-in-imbalanced-learn-cluster-first-then-synthesise-inside-the-cluster-nh9</link>
      <guid>https://dev.to/shaktitiwari/kmeanssmote-in-imbalanced-learn-cluster-first-then-synthesise-inside-the-cluster-nh9</guid>
      <description>&lt;h1&gt;
  
  
  KMeansSMOTE in imbalanced-learn: Cluster First, Then Synthesise Inside the Cluster
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE Family series. Previous: V3 SVMSMOTE. Next: V5 SMOTENC.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;KMeansSMOTE&lt;/strong&gt; (&lt;code&gt;from imblearn.over_sampling import KMeansSMOTE&lt;/code&gt;) is the SMOTE variant that refuses to synthesise minority rows blindly across the whole feature space. It first runs &lt;strong&gt;K-Means&lt;/strong&gt; on the minority class to split it into coherent sub-groups (clusters), then applies ordinary &lt;strong&gt;SMOTE inside each cluster only&lt;/strong&gt;. Because interpolation is confined to intra-cluster neighbours, synthetic points land in &lt;em&gt;high-density&lt;/em&gt; minority regions and never in the empty gaps between clusters — the exact place vanilla SMOTE keeps spraying useless, ambiguous samples. The method comes from Douzas &amp;amp; Bacao (2018), "The Last Resort," and ships in scikit-learn-contrib/imbalanced-learn. [SOURCE: Douzas &amp;amp; Bacao 2018; imbalanced-learn &lt;code&gt;KMeansSMOTE&lt;/code&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Agar aap Nifty options trade karte ho, toh aap already imbalanced data ke andar baithe ho. The setups that actually pay — a volatility-expansion breakout, an expiry-day pin, a failed-breakdown snap-back — are rare. The boring chop that fills 90% of sessions is common. Train an XGBoost or LightGBM naively on that history and it quietly learns the lazy rule "predict the common outcome," because that minimises raw error while delivering a model that is useless for the trades that matter.&lt;/p&gt;

&lt;p&gt;Vanilla SMOTE (V1) manufactured synthetic minority rows to fix that — but it interpolated between &lt;em&gt;any&lt;/em&gt; two minority points in the whole dataset, including points on opposite sides of an empty region. BorderlineSMOTE (V2) concentrated synthesis at the boundary. SVMSMOTE (V3) let an SVM draw that boundary. KMeansSMOTE (V4) attacks a different flaw: &lt;strong&gt;the minority class is usually not one blob — it is several blobs.&lt;/strong&gt; When the rare class has multiple distinct sub-populations (different market regimes, different failure modes), a single global interpolation surface connects them with fictional in-between samples. KMeansSMOTE carves the space first, so each regime is thickened &lt;em&gt;within itself&lt;/em&gt;. [DERIVED from the conceptual motivation in Doufas &amp;amp; Bacao 2018]&lt;/p&gt;

&lt;p&gt;Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; For a binary classifier trained on an imbalanced dataset whose minority class is &lt;em&gt;multi-modal&lt;/em&gt; (several separated sub-populations), does a cluster-then-synthesise strategy (KMeansSMOTE) improve minority-class recall and balanced metrics (F1, G-mean) relative to global-interpolation SMOTE, and under what data conditions does the gain appear or vanish?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; KMeansSMOTE should win &lt;em&gt;when the minority class is genuinely multi-modal&lt;/em&gt; — several dense, separated sub-groups — because confining SMOTE to intra-cluster neighbours prevents generation in the low-density gaps between sub-groups, where synthetic points would be pure noise. The gain should shrink or reverse when (a) the minority class is essentially unimodal (then KMeansSMOTE ≈ vanilla SMOTE plus overhead), (b) the wrong number of clusters &lt;code&gt;k&lt;/code&gt; is chosen, or (c) outliers distort the K-Means partition (a singleton outlier can hijack the density-weighted sampling). The hypothesis is a derived expectation, not a measurement from this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We describe the canonical experimental shape used throughout the imbalanced-learn documentation and the SMOTE literature. &lt;strong&gt;No experiment is executed in this article&lt;/strong&gt; — the snippet below is illustrative, taken from the imbalanced-learn API, and is shown only to anchor the method. [SOURCE: scikit-learn-contrib/imbalanced-learn repo, &lt;code&gt;KMeansSMOTE&lt;/code&gt; docstring/example]&lt;/p&gt;

&lt;p&gt;The pipeline shape is always:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stratified train/test split (never let SMOTE see the test set).&lt;/li&gt;
&lt;li&gt;Inside cross-validation &lt;em&gt;only&lt;/em&gt;, fit &lt;code&gt;KMeansSMOTE&lt;/code&gt; on the training fold and &lt;code&gt;fit_resample&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Train the estimator on the resampled fold; validate on the untouched test fold.&lt;/li&gt;
&lt;li&gt;Because K-Means is distance-based, a &lt;code&gt;StandardScaler&lt;/code&gt; must precede it — ideally wrapped in the same &lt;code&gt;Pipeline&lt;/code&gt; so resampling + scaling never leak.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Illustrative API usage (from imbalanced-learn docs — not run here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StandardScaler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;KMeansSMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;classification_report_imbalanced&lt;/span&gt;

&lt;span class="c1"&gt;## Synthetic illustrative data — do not treat as a real market dataset.
## n_clusters_per_class=3 makes the minority class multi-modal on purpose.
&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;n_informative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_redundant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flip_y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_clusters_per_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Original dataset shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Original dataset shape Counter({1: ~1800, 0: ~200})
&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## kmeans_estimator can be an int (number of clusters) or an estimator object.
&lt;/span&gt;&lt;span class="n"&gt;kms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KMeansSMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;kmeans_estimator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# carve the minority class into 5 sub-clusters
&lt;/span&gt;    &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;# SMOTE neighbours *within* each cluster
&lt;/span&gt;    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resampled shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Resampled shape Counter({0: ~1800, 1: ~1800})  -- illustrative from repo example
&lt;/span&gt;
&lt;span class="c1"&gt;## scale BEFORE K-Means sees the data, inside the same pipeline
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scaler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StandardScaler&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;KMeansSMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kmeans_estimator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;y_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;classification_report_imbalanced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key constructor parameters [SOURCE: imbalanced-learn repo, &lt;code&gt;imblearn/over_sampling/_smote/cluster.py&lt;/code&gt; &lt;code&gt;KMeansSMOTE&lt;/code&gt;]:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;kmeans_estimator&lt;/code&gt; — the clusterer applied to the minority class. If &lt;code&gt;None&lt;/code&gt; (the default), imbalanced-learn uses &lt;code&gt;MiniBatchKMeans(n_clusters=2, random_state=random_state)&lt;/code&gt;. You may pass an &lt;strong&gt;int&lt;/strong&gt; (used as the number of clusters) or &lt;strong&gt;any K-Means-style estimator&lt;/strong&gt; (e.g. &lt;code&gt;KMeans(n_clusters=...)&lt;/code&gt;) to control the clustering. [SOURCE: imbalanced-learn repo, &lt;code&gt;KMeansSMOTE.__init__&lt;/code&gt; default]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cluster_centers&lt;/code&gt; (default &lt;code&gt;None&lt;/code&gt;) — a precomputed array of shape &lt;code&gt;(n_clusters, n_features)&lt;/code&gt;. If you provide it, K-Means is &lt;strong&gt;skipped entirely&lt;/strong&gt; and these centers are used directly to assign minority points to clusters. Handy for determinism across CV folds or when you already know the partition from EDA. If the centers are wrong, generation degrades silently. [SOURCE: imbalanced-learn repo, &lt;code&gt;KMeansSMOTE&lt;/code&gt; &lt;code&gt;cluster_centers&lt;/code&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k_neighbors&lt;/code&gt; (default &lt;strong&gt;2&lt;/strong&gt;) — number of nearest &lt;em&gt;intra-cluster&lt;/em&gt; minority neighbours used to generate each synthetic point. A cluster with fewer than &lt;code&gt;k_neighbors + 1&lt;/code&gt; points cannot generate (it has no neighbour to interpolate with) and is skipped. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy&lt;/code&gt; (default &lt;code&gt;"auto"&lt;/code&gt;) — how much to oversample; &lt;code&gt;n_jobs&lt;/code&gt;, &lt;code&gt;random_state&lt;/code&gt; inherited from the base over-sampler. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful diagnostic the library exposes: after fitting, &lt;code&gt;kms.cluster_centers_&lt;/code&gt; holds the centers used (whether computed by K-Means or supplied via &lt;code&gt;cluster_centers&lt;/code&gt;). Inspecting them tells you exactly how the minority class was partitioned. [SOURCE: imbalanced-learn repo, &lt;code&gt;KMeansSMOTE.cluster_centers_&lt;/code&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Because this article does not run an experiment, the "findings" below are a synthesis of the primary sources, not measured numbers from this author's machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 1 — The minority class is partitioned before any synthesis.&lt;/strong&gt; KMeansSMOTE first runs K-Means on the minority samples only, producing &lt;code&gt;k&lt;/code&gt; clusters and their centers. Every subsequent synthetic point is generated &lt;em&gt;inside&lt;/em&gt; one of those clusters, using only that cluster's own neighbours. There is no path for a point in cluster A to be interpolated toward a point in cluster B. [SOURCE: Doufas &amp;amp; Bacao 2018; imbalanced-learn &lt;code&gt;cluster.py&lt;/code&gt; &lt;code&gt;_fit_resample&lt;/code&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 2 — Sampling is density-weighted, not uniform.&lt;/strong&gt; For each cluster &lt;code&gt;i&lt;/code&gt;, the paper defines a density &lt;code&gt;d_i = n_i / r_i&lt;/code&gt;, where &lt;code&gt;n_i&lt;/code&gt; is the number of minority samples in the cluster and &lt;code&gt;r_i&lt;/code&gt; is the cluster radius (the average distance of the cluster's samples to its centroid). The total number of synthetic points to generate (set by &lt;code&gt;sampling_strategy&lt;/code&gt;) is then split across clusters in proportion to &lt;code&gt;d_i&lt;/code&gt;: &lt;code&gt;s_i = round(N_syn · d_i / Σ_j d_j)&lt;/code&gt;. A compact, populous cluster (large &lt;code&gt;n_i&lt;/code&gt;, small &lt;code&gt;r_i&lt;/code&gt;) gets high density → more new points; a sparse, spread-out cluster gets fewer. [SOURCE: Doufas &amp;amp; Bacao 2018, density definition; imbalanced-learn &lt;code&gt;cluster.py&lt;/code&gt; implementation]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 3 — Why this fixes vanilla SMOTE's gap problem (DERIVED).&lt;/strong&gt; Vanilla SMOTE distributes synthetic points uniformly per minority instance and interpolates between &lt;em&gt;global&lt;/em&gt; neighbours. When the minority class has two separated blobs, a point in blob A can have its nearest neighbour in blob B, so interpolation plants a synthetic point in the empty space between them — a region with no real minority data and often overlapping majority data. That point is pure ambiguity. KMeansSMOTE removes the mechanism entirely: blob A and blob B are different clusters, so interpolation is strictly intra-blob. The gaps stay empty. The density weighting then reinforces the &lt;em&gt;denser&lt;/em&gt; blob, which is where real minority structure is strongest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 4 — Worked numerical example (DERIVED).&lt;/strong&gt; Suppose the minority class in 1-D is two separated groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group A (dense): {1.0, 1.1, 0.9, 1.05} — centroid ≈ 1.0125, radius ≈ 0.0625, so density &lt;code&gt;d_A = 4 / 0.0625 = 64&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Group B (sparse): {5.0, 5.2} — centroid 5.1, radius ≈ 0.1, so density &lt;code&gt;d_B = 2 / 0.1 = 20&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total density &lt;code&gt;Σ d = 84&lt;/code&gt;. Target &lt;code&gt;N_syn = 6&lt;/code&gt; synthetic points. Then &lt;code&gt;s_A = round(6 · 64/84) = round(4.57) = 5&lt;/code&gt;, and &lt;code&gt;s_B = round(6 · 20/84) = round(1.43) = 1&lt;/code&gt;. So 5 of 6 new points land inside dense Group A (in the [0.9, 1.1] band) and only 1 inside sparse Group B (in [5.0, 5.2]). Vanilla SMOTE with &lt;code&gt;k_neighbors=2&lt;/code&gt; behaves worse: for the point 5.0, its two nearest minority neighbours are 5.2 (dist 0.2) and 1.05 (dist 3.95); if interpolation picks 1.05, the synthetic point sits at &lt;code&gt;5.0 + 0.5·(1.05 − 5.0) ≈ 3.0&lt;/code&gt; — squarely in the empty gap. KMeansSMOTE never produces that point. In higher dimensions the gap is a &lt;em&gt;volume&lt;/em&gt;, so the vanilla-SMOTE failure is even more severe. [DERIVED illustration]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 5 — The paper's empirical claim (SOURCE).&lt;/strong&gt; Doufas &amp;amp; Bacao (2018) benchmark KMeansSMOTE across a large collection of imbalanced datasets drawn from the KEEL repository and report that, especially when combined with an ensemble classifier, the cluster-then-synthesise scheme ranks at or near the top among over-sampling methods — beating vanilla SMOTE and several other variants on balanced accuracy / F-measure. The gain is attributed precisely to the density-aware, cluster-confined generation described above. (Exact per-dataset metrics are in the paper; this article did not reproduce them.) [SOURCE: Doufas &amp;amp; Bacao 2018]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 6 — KMeansSMOTE is complementary, not a replacement, for SVMSMOTE/BorderlineSMOTE.&lt;/strong&gt; Those variants decide &lt;em&gt;where on the boundary&lt;/em&gt; to synthesise. KMeansSMOTE decides &lt;em&gt;how to keep synthesis inside coherent sub-populations&lt;/em&gt;. When the minority class is multi-modal, KMeansSMOTE addresses a failure the boundary-based variants do not. [DERIVED from comparing the implementations in imbalanced-learn]&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;p&gt;To reproduce a real comparison you would, at minimum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a fixed &lt;code&gt;random_state&lt;/code&gt; everywhere (data split, SMOTE, estimator).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale features first&lt;/strong&gt; — wrap &lt;code&gt;StandardScaler&lt;/code&gt; ahead of &lt;code&gt;KMeansSMOTE&lt;/code&gt;, because K-Means is a distance construction and unscaled features with large magnitude dominate cluster assignment. This is non-negotiable for honest results.&lt;/li&gt;
&lt;li&gt;Compare resamplers &lt;em&gt;inside&lt;/em&gt; the same CV loop: &lt;code&gt;SMOTE()&lt;/code&gt;, &lt;code&gt;KMeansSMOTE(kmeans_estimator=K)&lt;/code&gt; for a few &lt;code&gt;K&lt;/code&gt; values, and &lt;code&gt;BorderlineSMOTE()&lt;/code&gt; / &lt;code&gt;SVMSMOTE()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Score with minority recall, precision, F1, and G-mean — &lt;strong&gt;not&lt;/strong&gt; raw accuracy, which is misleading under imbalance.&lt;/li&gt;
&lt;li&gt;Always &lt;code&gt;fit_resample&lt;/code&gt; on the training fold only.&lt;/li&gt;
&lt;li&gt;If you pass &lt;code&gt;cluster_centers&lt;/code&gt;, fix them from a held-out EDA fit so they don't leak test information across folds.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code sketch in &lt;em&gt;Data &amp;amp; Methodology&lt;/em&gt; is the canonical shape from the imbalanced-learn documentation and is presented &lt;strong&gt;illustratively&lt;/strong&gt; — it was not executed for this article, and its printed outputs are quoted from the library's own example pattern for reference. [SOURCE: imbalanced-learn repo docstring; not an experiment result of this author]&lt;/p&gt;

&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;KMeansSMOTE is not a free lunch, and the honest literature plus implementation details say so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choosing the wrong &lt;code&gt;k&lt;/code&gt; breaks it.&lt;/strong&gt; Too few clusters (e.g. &lt;code&gt;k=2&lt;/code&gt;) and you may still get one mega-cluster spanning two genuine sub-populations — the gap problem returns inside that cluster. Too many and clusters become tiny and sparse: many fall below &lt;code&gt;k_neighbors + 1&lt;/code&gt; and are skipped, so you under-generate and waste the clustering cost. The right &lt;code&gt;k&lt;/code&gt; is dataset-dependent and must be tuned. [DERIVED from the algorithm; Doufas &amp;amp; Bacao 2018 discuss cluster-count sensitivity]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outliers hijack the density weighting.&lt;/strong&gt; K-Means minimises squared error, so an outlier pulls a centroid and can form its own singleton cluster. A singleton has &lt;code&gt;n_i = 1&lt;/code&gt; and a near-zero radius &lt;code&gt;r_i&lt;/code&gt;, so &lt;code&gt;d_i = 1 / ~0&lt;/code&gt; explodes — the density formula then dumps a &lt;em&gt;huge&lt;/em&gt; share of synthetic points around that single outlier. This is a silent, catastrophic failure mode. [DERIVED from K-Means + density formula behaviour]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blagus &amp;amp; Lusa (2013)&lt;/strong&gt; show that SMOTE-family over-sampling can &lt;em&gt;hurt&lt;/em&gt; on small or high-dimensional datasets, where neighbour/cluster geometry is unreliable and synthetic points amplify noise. KMeansSMOTE inherits this — tiny clusters in high dimensions are noise, not signal. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Numeric-only.&lt;/strong&gt; K-Means needs Euclidean distance, so &lt;code&gt;KMeansSMOTE&lt;/code&gt; works only on continuous features. Pass a categorical column and it breaks (or silently mis-clusters). For mixed numeric/categorical data use &lt;code&gt;SMOTENC&lt;/code&gt;; for all-categorical use &lt;code&gt;SMOTEN&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale sensitivity.&lt;/strong&gt; Like every K-Means application, unscaled features with larger magnitude dominate distance and warp the partition. Forget &lt;code&gt;StandardScaler&lt;/code&gt; and KMeansSMOTE can produce a meaningless clustering. [DERIVED from K-Means theory]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher compute cost.&lt;/strong&gt; Running K-Means plus per-cluster SMOTE is pricier than vanilla SMOTE. Inside CV on a large Nifty feature matrix this can be the difference between seconds and minutes per fold. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unimodal minority → no benefit.&lt;/strong&gt; If the rare class is genuinely one blob, KMeansSMOTE ≈ vanilla SMOTE with a density tweak and extra cost. It only earns its keep when the minority is multi-modal. [DERIVED]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cluster count &lt;code&gt;k&lt;/code&gt; is the key knob.&lt;/strong&gt; Wrong &lt;code&gt;k&lt;/code&gt; (too small or too large) degrades results; it must be tuned inside CV. There is no universally correct value. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outlier sensitivity (the silent killer).&lt;/strong&gt; A single outlier can form a tiny-radius cluster and capture disproportionate synthetic budget via the density formula. Mitigate with outlier removal / robust scaling before KMeansSMOTE. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Numeric features only.&lt;/strong&gt; No categorical support; use &lt;code&gt;SMOTENC&lt;/code&gt;/&lt;code&gt;SMOTEN&lt;/code&gt; for mixed or categorical data. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale sensitivity.&lt;/strong&gt; K-Means partitions are distance-based; always &lt;code&gt;StandardScaler&lt;/code&gt; first. [DERIVED from K-Means theory]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute cost.&lt;/strong&gt; K-Means + per-cluster SMOTE costs more than vanilla SMOTE; budget for it in CV. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster-purity assumption.&lt;/strong&gt; Assumes minority sub-populations are separable enough for K-Means to find them. Overlapping, smeared sub-populations defeat the partition. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sparse clusters can't generate.&lt;/strong&gt; Any cluster with fewer than &lt;code&gt;k_neighbors + 1&lt;/code&gt; points is skipped, so you may end up under-generating relative to &lt;code&gt;sampling_strategy&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No fix for irreducible overlap.&lt;/strong&gt; If minority and majority truly overlap, intra-cluster SMOTE still thickens the overlapping region. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;cluster_centers&lt;/code&gt; can lie.&lt;/strong&gt; Passing precomputed centers skips K-Means but if those centers are wrong the generation is wrong — and there is no check. [SOURCE: imbalanced-learn repo; DERIVED]&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reach for KMeansSMOTE when&lt;/strong&gt; your minority class is &lt;em&gt;multi-modal&lt;/em&gt; — several distinct sub-populations (distinct market regimes, distinct failure modes) separated by empty or majority-dominated space. That is exactly where vanilla SMOTE pollutes the gaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always scale first.&lt;/strong&gt; Wrap &lt;code&gt;StandardScaler&lt;/code&gt; ahead of &lt;code&gt;KMeansSMOTE&lt;/code&gt; in the same &lt;code&gt;Pipeline&lt;/code&gt;. This single step decides whether the clustering is meaningful. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat &lt;code&gt;kmeans_estimator&lt;/code&gt; as a real hyperparameter.&lt;/strong&gt; Start with &lt;code&gt;kmeans_estimator=K&lt;/code&gt; for a few &lt;code&gt;K&lt;/code&gt; (e.g. 3, 5, 8) and tune inside CV. Prefer an explicit &lt;code&gt;KMeans(n_clusters=K, random_state=...)&lt;/code&gt; over the default &lt;code&gt;MiniBatchKMeans(n_clusters=2)&lt;/code&gt; when you suspect more than two sub-populations. [SOURCE: imbalanced-learn repo; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean outliers before clustering.&lt;/strong&gt; Winsorise, clip, or remove extreme minority points first; otherwise the density formula can dump synthetic budget on noise. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect &lt;code&gt;cluster_centers_&lt;/code&gt;&lt;/strong&gt; after fitting to confirm the partition looks like real sub-populations, not artefacts. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never resample the test set.&lt;/strong&gt; Resample inside the training fold only, ideally via &lt;code&gt;imblearn.pipeline.Pipeline&lt;/code&gt; so it composes cleanly with scaling and the classifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare against the cheap baseline.&lt;/strong&gt; Before reaching for any SMOTE variant, try &lt;code&gt;scale_pos_weight&lt;/code&gt; (XGBoost/LightGBM) or class weights — sometimes that alone closes the gap without synthesising a single row. KMeansSMOTE is heavier than those.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In our stack&lt;/strong&gt;, all of this lives in Layer 2 (EOD-audited training), never in Layer 1 (live Dhan capture). The K-Means partition is computed on historical folds, never on live ticks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is the core difference between KMeansSMOTE and vanilla SMOTE?&lt;/strong&gt;&lt;br&gt;
A1. Vanilla SMOTE interpolates between minority neighbours &lt;em&gt;globally&lt;/em&gt; across the whole feature space, so it can generate points in empty gaps between separated sub-populations. KMeansSMOTE first clusters the minority class with K-Means, then runs SMOTE &lt;em&gt;inside each cluster only&lt;/em&gt;, so synthetic points stay in high-density regions and never bridge gaps. [SOURCE: Doufas &amp;amp; Bacao 2018; imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What does the &lt;code&gt;kmeans_estimator&lt;/code&gt; parameter do?&lt;/strong&gt;&lt;br&gt;
A2. It is the clusterer applied to the minority class. &lt;code&gt;None&lt;/code&gt; (default) becomes &lt;code&gt;MiniBatchKMeans(n_clusters=2)&lt;/code&gt;. You can pass an &lt;strong&gt;int&lt;/strong&gt; (number of clusters) or any K-Means-style &lt;strong&gt;estimator&lt;/strong&gt; (e.g. &lt;code&gt;KMeans(n_clusters=5)&lt;/code&gt;) to control the partition. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What is &lt;code&gt;cluster_centers&lt;/code&gt; and when would I use it?&lt;/strong&gt;&lt;br&gt;
A3. It is a precomputed array of cluster centers, shape &lt;code&gt;(n_clusters, n_features)&lt;/code&gt;. If supplied, K-Means is skipped and these centers are used to assign minority points to clusters. Use it for determinism across CV folds or when you already know the partition from EDA — but only if the centers are correct, since there is no validation. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. How does KMeansSMOTE decide how many points to make per cluster?&lt;/strong&gt;&lt;br&gt;
A4. Via the density formula &lt;code&gt;d_i = n_i / r_i&lt;/code&gt; (samples divided by cluster radius). The total synthetic count is split across clusters proportional to &lt;code&gt;d_i&lt;/code&gt;, so denser, compact clusters receive more new points. [SOURCE: Doufas &amp;amp; Bacao 2018; imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Does KMeansSMOTE help if my minority class is one single blob?&lt;/strong&gt;&lt;br&gt;
A5. No — then it is roughly equivalent to vanilla SMOTE with a density tweak, plus extra compute. Its value appears &lt;em&gt;only&lt;/em&gt; when the minority class is multi-modal (several separated sub-populations). [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Can I use KMeansSMOTE with categorical features?&lt;/strong&gt;&lt;br&gt;
A6. No. K-Means needs continuous Euclidean space. For mixed numeric/categorical data use &lt;code&gt;SMOTENC&lt;/code&gt;; for all-categorical use &lt;code&gt;SMOTEN&lt;/code&gt; — both from &lt;code&gt;imblearn.over_sampling&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. What is the biggest failure mode?&lt;/strong&gt;&lt;br&gt;
A7. Outliers. A singleton outlier forms a near-zero-radius cluster whose density &lt;code&gt;d = n/r&lt;/code&gt; explodes, so the algorithm diverts a disproportionate share of synthetic points to that outlier. Clean outliers before clustering. [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. Should I apply it to live trading data?&lt;/strong&gt;&lt;br&gt;
A8. No — apply any over-sampling inside the training/CV loop of your audited model core, never on live inference. Our NSE stack keeps resampling strictly in Layer 2. [DERIVED from the two-layer engine design]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q9. What comes after KMeansSMOTE?&lt;/strong&gt;&lt;br&gt;
A9. V5 SMOTENC handles &lt;em&gt;mixed categorical and numeric&lt;/em&gt; features by combining K-Means-style clustering on the continuous part with special handling for categorical levels — the right tool when your feature matrix is not purely numeric. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;KMeansSMOTE (&lt;code&gt;from imblearn.over_sampling import KMeansSMOTE&lt;/code&gt;) &lt;strong&gt;clusters the minority class with K-Means first, then applies SMOTE inside each cluster&lt;/strong&gt; — density-aware, gap-free over-sampling. [SOURCE: Doufas &amp;amp; Bacao 2018; imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;It fixes vanilla SMOTE's tendency to generate synthetic points in &lt;strong&gt;low-density / between-cluster gaps&lt;/strong&gt;, where no real minority data exists. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kmeans_estimator&lt;/code&gt; (default &lt;code&gt;None&lt;/code&gt; → &lt;code&gt;MiniBatchKMeans(n_clusters=2)&lt;/code&gt;) sets the clustering — pass an &lt;strong&gt;int&lt;/strong&gt; (n clusters) or a &lt;strong&gt;K-Means estimator&lt;/strong&gt;; &lt;code&gt;cluster_centers&lt;/code&gt; lets you skip K-Means with precomputed centers. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;It helps most when the minority class is &lt;strong&gt;multi-modal&lt;/strong&gt;; it struggles with wrong &lt;code&gt;k&lt;/code&gt;, outliers (which hijack the density formula), unscaled features, and categorical columns. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always scale first&lt;/strong&gt;, resample &lt;strong&gt;inside CV only&lt;/strong&gt;, and tune &lt;code&gt;kmeans_estimator&lt;/code&gt; as a hyperparameter. [DERIVED]&lt;/li&gt;
&lt;li&gt;Next in series: &lt;strong&gt;V5 SMOTENC&lt;/strong&gt;. Previous: &lt;strong&gt;V3 SVMSMOTE&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v5-smotenc"&gt;Smote V5 Smotenc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v2-borderline"&gt;Smote V2 Borderline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v6-smoten"&gt;Smote V6 Smoten&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Doufas, G., &amp;amp; Bacao, F. (2018). &lt;em&gt;The Last Resort: Balancing Imbalanced Big Data Classification with K-Means and SMOTE.&lt;/em&gt; Information Sciences, 465, 1–20. — original KMeansSMOTE algorithm (cluster minority with K-Means, then density-weighted SMOTE within clusters); primary paper behind &lt;code&gt;imblearn.over_sampling.KMeansSMOTE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; scikit-learn-contrib/imbalanced-learn GitHub repository — &lt;code&gt;imblearn/over_sampling/_smote/cluster.py&lt;/code&gt; (&lt;code&gt;KMeansSMOTE&lt;/code&gt;, &lt;code&gt;kmeans_estimator&lt;/code&gt;, &lt;code&gt;cluster_centers&lt;/code&gt;, &lt;code&gt;_fit_resample&lt;/code&gt;, &lt;code&gt;cluster_centers_&lt;/code&gt;) and base SMOTE modules. Primary source for the implementation described.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Chawla, N. V., Bowyer, K. W., Hall, L. O., &amp;amp; Kegelmeyer, W. P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16, 321–357. — vanilla SMOTE baseline (V1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Han, H., Wang, W.-Y., &amp;amp; Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; ICIC 2005, LNCS 3644, 878–887. — Borderline-SMOTE (V2), the boundary-based comparison point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Nguyen, H. M., Cooper, E. W., &amp;amp; Kamei, K. (2011). &lt;em&gt;Borderline over-sampling for imbalanced data classification.&lt;/em&gt; International Journal of Knowledge and Web Intelligence, 2(3), 230–242. — SVM-based boundary over-sampling implemented as &lt;code&gt;SVMSMOTE&lt;/code&gt; (V3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Blagus, R., &amp;amp; Lusa, L. (2013). &lt;em&gt;SMOTE can degrade performance on small, high-dimensional datasets&lt;/em&gt; (cautionary note on over-sampling). — counter-evidence on small/high-dimensional data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[DERIVED]&lt;/strong&gt; The 1-D two-group worked example ({1.0,1.1,0.9,1.05} vs {5.0,5.2}), the density-weighting arithmetic, the "gap pollution" illustration at ≈3.0, the outlier-hijack analysis, and the "KMeansSMOTE vs vanilla SMOTE" comparison — derived from the cited sources and standard K-Means / SMOTE theory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE Family series (V4 of V6). This is educational content; not investment advice and not SEBI-registered research.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About the author: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article (V3 SVMSMOTE): &lt;code&gt;SMOTE_V3_svmsmote.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next article (V5 SMOTENC): &lt;code&gt;SMOTE_V5_smotenc.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SVMSMOTE in imbalanced-learn: Let an SVM Draw the Boundary, Then Synthesise on It</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Tue, 08 Sep 2026 04:31:33 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/svmsmote-in-imbalanced-learn-let-an-svm-draw-the-boundary-then-synthesise-on-it-4pb3</link>
      <guid>https://dev.to/shaktitiwari/svmsmote-in-imbalanced-learn-let-an-svm-draw-the-boundary-then-synthesise-on-it-4pb3</guid>
      <description>&lt;h1&gt;
  
  
  SVMSMOTE in imbalanced-learn: Let an SVM Draw the Boundary, Then Synthesise on It
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE Family series. Previous: V2 BorderlineSMOTE. Next: V4 KMeansSMOTE.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SVMSMOTE&lt;/strong&gt; (&lt;code&gt;from imblearn.over_sampling import SVMSMOTE&lt;/code&gt;) is the SMOTE variant that outsources the hardest part of over-sampling — &lt;em&gt;figuring out where the class boundary actually is&lt;/em&gt; — to a Support Vector Machine. It fits an SVM to separate the minority class from the majority class, extracts the &lt;strong&gt;support vectors&lt;/strong&gt; (the points that sit on or inside the margin and define the decision boundary), filters those support vectors down to the minority-class ones, and then synthesises new minority rows by interpolating between each boundary support vector and its nearest minority neighbours. The idea, from Nguyen, Cooper &amp;amp; Kamei (2011), is that the SVM margin is a more robust boundary locator than the raw k-nearest-neighbour "danger" test that BorderlineSMOTE uses. [DERIVED summary of Nguyen et al. 2011 + imbalanced-learn implementation]&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Agar aap Nifty options trade karte ho, toh aap pehle se hi imbalanced duniya mein jeete ho. The events that actually make or break your P&amp;amp;L — a sharp expiry-day reversal, a volatility expansion, a failed breakdown that snaps back — are rare. The dull chop that fills most sessions is common. Train an XGBoost or LightGBM model naively on that history and it will quietly learn the lazy rule "predict the common class," because that minimises raw error while delivering a model that is useless for the trades that pay the bills.&lt;/p&gt;

&lt;p&gt;Vanilla SMOTE (V1) fixed part of that by manufacturing synthetic minority rows — but it sprayed them everywhere, including the safe interior where the classifier was already confident. BorderlineSMOTE (V2) got smarter: it used kNN to find the "danger" minority points and synthesised only there. SVMSMOTE takes the next logical step. Instead of asking "how many of this point's neighbours are majority?" it asks "what does an SVM think the boundary between the two classes is?" and then seeds synthesis right on that margin. [DERIVED from the conceptual motivation in Nguyen et al. 2011]&lt;/p&gt;

&lt;p&gt;Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; For a binary classifier trained on an imbalanced dataset, does locating the minority synthesis region with an SVM margin (SVMSMOTE) improve minority-class recall and balanced metrics (F1, G-mean) relative to kNN-based danger detection (BorderlineSMOTE), and under what data conditions does the gain appear?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; SVMSMOTE should win &lt;em&gt;when the two classes are separated by a relatively clean, roughly linear margin&lt;/em&gt; — the case where an SVM finds a stable boundary and the support vectors genuinely mark the informative frontier. In that regime the SVM margin is less fooled by local density quirks than BorderlineSMOTE's majority-neighbour count. The gain should shrink or reverse when the boundary is heavily non-linear, when features are unscaled (SVMs are scale-sensitive), or when the dataset is small/high-dimensional (the Blagus &amp;amp; Lusa 2013 critique bites). The hypothesis is a derived expectation, not a measurement from this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We describe the canonical experimental shape used throughout the imbalanced-learn documentation and the SMOTE literature. &lt;strong&gt;No experiment is executed in this article&lt;/strong&gt; — the snippet below is illustrative, taken from the imbalanced-learn API, and is shown only to anchor the method. [SOURCE: scikit-learn-contrib/imbalanced-learn repo, &lt;code&gt;SVMSMOTE&lt;/code&gt; docstring/example]&lt;/p&gt;

&lt;p&gt;The pipeline shape is always:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stratified train/test split (never let SMOTE see the test set).&lt;/li&gt;
&lt;li&gt;Inside cross-validation &lt;em&gt;only&lt;/em&gt;, fit &lt;code&gt;SVMSMOTE&lt;/code&gt; on the training fold and &lt;code&gt;fit_resample&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Train the estimator on the resampled fold; validate on the untouched test fold.&lt;/li&gt;
&lt;li&gt;Because SVMs are scale-sensitive, a &lt;code&gt;StandardScaler&lt;/code&gt; must precede the SVM — ideally wrapped in the same &lt;code&gt;Pipeline&lt;/code&gt; so resampling + scaling never leak.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Illustrative API usage (from imbalanced-learn docs — not run here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StandardScaler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.svm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SVC&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SVMSMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;classification_report_imbalanced&lt;/span&gt;

&lt;span class="c1"&gt;## Synthetic illustrative data — do not treat as a real market dataset
&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;n_informative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_redundant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flip_y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_clusters_per_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Original dataset shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Original dataset shape Counter({1: 900, 0: 100})
&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## default linear SVM under the hood; out_step controls boundary push
&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SVMSMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;svm_estimator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SVC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linear&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;out_step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resampled shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Resampled shape Counter({0: 900, 1: 900})  -- illustrative from repo example
&lt;/span&gt;
&lt;span class="c1"&gt;## scale BEFORE the SVM sees the data, inside the same pipeline
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scaler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StandardScaler&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SVMSMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svm_estimator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;SVC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;linear&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;y_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;classification_report_imbalanced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key constructor parameters [SOURCE: imbalanced-learn repo, &lt;code&gt;filter.py&lt;/code&gt; &lt;code&gt;SVMSMOTE&lt;/code&gt;]:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;svm_estimator&lt;/code&gt; — the SVM used to find support vectors. If left as &lt;code&gt;None&lt;/code&gt;, imbalanced-learn falls back to a default SVM (an &lt;code&gt;SVC&lt;/code&gt; with a linear kernel) to carve out the margin. Passing your own &lt;code&gt;SVC&lt;/code&gt; lets you pick the kernel (&lt;code&gt;linear&lt;/code&gt; vs &lt;code&gt;rbf&lt;/code&gt;) and the regularisation &lt;code&gt;C&lt;/code&gt;. [SOURCE: imbalanced-learn repo, &lt;code&gt;SVMSMOTE&lt;/code&gt; default]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;out_step&lt;/code&gt; (default &lt;strong&gt;0.5&lt;/strong&gt;) — the step fraction that scales how far each synthetic point is displaced from its boundary support vector toward its minority neighbour. Smaller &lt;code&gt;out_step&lt;/code&gt; keeps new points hugging the margin; larger &lt;code&gt;out_step&lt;/code&gt; pushes them deeper into minority space. [SOURCE: imbalanced-learn repo, &lt;code&gt;SVMSMOTE&lt;/code&gt; &lt;code&gt;out_step&lt;/code&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k_neighbors&lt;/code&gt; (default &lt;strong&gt;5&lt;/strong&gt;) — number of nearest minority neighbours used to &lt;em&gt;generate&lt;/em&gt; each synthetic point around a support vector.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;m_neighbors&lt;/code&gt; (default &lt;strong&gt;10&lt;/strong&gt;) — used to flag and discard minority support vectors whose &lt;code&gt;m_neighbors&lt;/code&gt; are &lt;em&gt;all&lt;/em&gt; majority (i.e., likely noise), so the algorithm does not seed synthesis from outliers. [SOURCE: imbalanced-learn repo, &lt;code&gt;filter.py&lt;/code&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy&lt;/code&gt; (default &lt;code&gt;"auto"&lt;/code&gt;) — how much to oversample.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;random_state&lt;/code&gt; — for reproducibility of the random interpolation gap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful diagnostic the library exposes: &lt;code&gt;sm.svm_estimator_.support_&lt;/code&gt; — the indices of the support vectors the fitted SVM found. Inspecting which of those belong to the minority class tells you exactly which rows SVMSMOTE treated as boundary-relevant seeds. [SOURCE: imbalanced-learn repo, &lt;code&gt;SVMSMOTE&lt;/code&gt; / scikit-learn &lt;code&gt;SVC.support_&lt;/code&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Because this article does not run an experiment, the "findings" below are a synthesis of the primary sources, not measured numbers from this author's machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 1 — The SVM margin is the boundary locator.&lt;/strong&gt; SVMSMOTE fits an SVM to separate minority from majority. The SVM's support vectors are, by definition, the training points that lie on or inside the margin — the points the classifier "leans on" to define the decision surface. Those are precisely the boundary-relevant minority points we want to thicken. [SOURCE: Nguyen et al. 2011; DERIVED from SVM theory]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 2 — Synthesis happens at the minority support vectors, not everywhere.&lt;/strong&gt; After the SVM is fit, imbalanced-learn keeps the support vectors that belong to the minority class and discards those flagged as noise (all-&lt;code&gt;m_neighbors&lt;/code&gt; majority). Each surviving boundary support vector is then interpolated with its nearest minority neighbours to produce synthetic rows. So, just like BorderlineSMOTE, SVMSMOTE refuses to waste synthetic budget in the safe interior — but it &lt;em&gt;defines&lt;/em&gt; "boundary" via the SVM margin rather than via a kNN majority-count. [SOURCE: imbalanced-learn repo, &lt;code&gt;filter.py&lt;/code&gt; &lt;code&gt;_svmSMOTE&lt;/code&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 3 — Worked intuition: why an SVM margin beats a kNN count (DERIVED).&lt;/strong&gt; Picture two classes in 2-D. The majority class forms a dense blob; the minority class forms a thin arc curving along one side of the blob. A kNN danger test looks &lt;em&gt;locally&lt;/em&gt;: a minority point is "in danger" if half its 10 neighbours are majority. But near a concave part of the arc, a minority point can have mostly minority neighbours yet still sit on the true frontier — kNN under-flags it. An SVM, by contrast, fits a &lt;em&gt;global&lt;/em&gt; margin that respects the whole shape of the blob, so its support vectors land on the actual frontier including that concave stretch. SVMSMOTE then seeds there. This is the qualitative reason SVM-based boundary detection can be more faithful than kNN danger detection on irregular boundaries. [DERIVED illustration]&lt;/p&gt;

&lt;p&gt;To make the margin concrete, consider a trivially separable 1-D minority set {1, 3, 5} (class 1) versus majority {10, 12, 14} (class 0), with a hard-margin linear SVM. The optimal separating line sits at the midpoint between the closest pair across classes — between 5 and 10, i.e. at 7.5. The support vectors are exactly 5 (minority) and 10 (majority): the two points that pin the margin. SVMSMOTE would keep 5 as a minority boundary seed and synthesise new minority points by interpolating 5 with its nearest minority neighbours (3, and/or the new points themselves). The number 10 is a majority support vector and is &lt;em&gt;not&lt;/em&gt; used to seed minority synthesis. That tiny example captures the whole mechanism: the SVM's margin identifies 5 as the frontier minority point; vanilla SMOTE would have treated 1, 3, 5 with equal priority and wasted budget on the safe interior point 1. [DERIVED numerical illustration]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 4 — &lt;code&gt;out_step&lt;/code&gt; is the dial BorderlineSMOTE doesn't have.&lt;/strong&gt; BorderlineSMOTE has no direct control over how far synthetic points sit from the danger point — it just interpolates within [0, 1] of the gap to a neighbour. SVMSMOTE's &lt;code&gt;out_step&lt;/code&gt; (default 0.5) scales that displacement. With &lt;code&gt;out_step=0.3&lt;/code&gt;, new points hug the support vector (tight margin reinforcement); with &lt;code&gt;out_step=0.8&lt;/code&gt;, they are pushed further toward (and past) the minority neighbour (broader reinforcement). This is a genuine extra lever, but it is also another hyperparameter you must tune inside CV. [SOURCE: imbalanced-learn repo; DERIVED interpretation]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 5 — SVMSMOTE is a refinement of, not a replacement for, BorderlineSMOTE.&lt;/strong&gt; Both concentrate synthesis at the boundary. The difference is purely &lt;em&gt;how the boundary is found&lt;/em&gt;: kNN danger mask (BorderlineSMOTE) versus SVM support vectors (SVMSMOTE). Which is better depends on the data geometry, not on the variant being "newer." [DERIVED from comparing the two implementations in imbalanced-learn]&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;p&gt;To reproduce a real comparison you would, at minimum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a fixed &lt;code&gt;random_state&lt;/code&gt; everywhere (data split, SMOTE, estimator).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale features first&lt;/strong&gt; — wrap &lt;code&gt;StandardScaler&lt;/code&gt; ahead of &lt;code&gt;SVMSMOTE&lt;/code&gt;, because the SVM margin is scale-sensitive (see Limitations). This is non-negotiable for honest results.&lt;/li&gt;
&lt;li&gt;Compare resamplers &lt;em&gt;inside&lt;/em&gt; the same CV loop: &lt;code&gt;BorderlineSMOTE(kind="borderline-1")&lt;/code&gt;, &lt;code&gt;SVMSMOTE(svm_estimator=SVC(kernel="linear"))&lt;/code&gt;, and &lt;code&gt;SVMSMOTE(svm_estimator=SVC(kernel="rbf"))&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Score with minority recall, precision, F1, and G-mean — &lt;strong&gt;not&lt;/strong&gt; raw accuracy, which is misleading under imbalance.&lt;/li&gt;
&lt;li&gt;Always &lt;code&gt;fit_resample&lt;/code&gt; on the training fold only.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code sketch in &lt;em&gt;Data &amp;amp; Methodology&lt;/em&gt; is the canonical shape from the imbalanced-learn documentation and is presented &lt;strong&gt;illustratively&lt;/strong&gt; — it was not executed for this article, and its printed outputs are quoted from the library's own example pattern for reference. [SOURCE: imbalanced-learn repo docstring; not an experiment result of this author]&lt;/p&gt;

&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;SVMSMOTE is not a free lunch, and the honest literature plus implementation details say so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blagus &amp;amp; Lusa (2013)&lt;/strong&gt; show that SMOTE-family over-sampling can &lt;em&gt;hurt&lt;/em&gt; on small or high-dimensional datasets, where the nearest-neighbour / margin geometry is unreliable and synthetic points amplify noise rather than signal. SVMSMOTE inherits this failure mode — the SVM can latch onto a spurious margin when there are too few samples per dimension. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unscaled features break the SVM.&lt;/strong&gt; Because the margin depends on distances, a single feature with large magnitude dominates. Forget the &lt;code&gt;StandardScaler&lt;/code&gt; and SVMSMOTE can produce a meaningless boundary — and BorderlineSMOTE, being kNN-based, is &lt;em&gt;also&lt;/em&gt; scale-sensitive, but the SVM's sensitivity is sharper and more silent. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is more expensive than kNN variants.&lt;/strong&gt; Fitting an SVM and extracting support vectors costs more than BorderlineSMOTE's neighbour counts, especially on large datasets. On a big Nifty feature matrix this can be the difference between seconds and minutes per CV fold. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel choice shifts the result.&lt;/strong&gt; A linear SVM gives a straight margin and a predictable support-vector set; an RBF SVM can carve a wiggly margin with many support vectors, seeding far more synthetic points and risking over-generation. The default (linear) is usually the safe start. [SOURCE: imbalanced-learn repo default; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It still ignores majority density.&lt;/strong&gt; Like BorderlineSMOTE, SVMSMOTE synthesises from minority boundary points without checking how densely the majority class is packed nearby. A synthetic point can land in a dense majority region, thickening the fog. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better variants exist for hard cases.&lt;/strong&gt; KMeansSMOTE (V4) clusters first, then synthesises within coherent clusters — useful when the minority class is multi-modal and a single global SVM margin misses subclusters. [SOURCE: imbalanced-learn repo; KMeansSMOTE paper 2018]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SVM scale sensitivity (the big one).&lt;/strong&gt; The margin is a distance construction. Without &lt;code&gt;StandardScaler&lt;/code&gt; (or equivalent) ahead of the SVM, features on different scales distort the boundary. Always scale. [DERIVED from SVM theory]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher compute cost.&lt;/strong&gt; SVM fit + support-vector extraction is pricier than kNN danger detection. Budget for it in CV. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;out_step&lt;/code&gt; is another hyperparameter.&lt;/strong&gt; More dials mean more tuning. Leave it at 0.5 only as a starting point; tune inside CV. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel dependence.&lt;/strong&gt; Linear vs RBF changes the boundary shape and the number of seeds. Mismatch with the true boundary geometry hurts. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noise support vectors.&lt;/strong&gt; If a minority support vector is actually a mislabel near the margin, SVMSMOTE will enthusiastically manufacture more points around it. The &lt;code&gt;m_neighbors&lt;/code&gt; noise filter mitigates but does not eliminate this. [SOURCE: imbalanced-learn repo; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature-space only.&lt;/strong&gt; Like all SMOTE variants, it assumes Euclidean proximity means semantic similarity — fragile with mixed-type, high-cardinality, or heavily engineered features. For those, &lt;code&gt;SMOTENC&lt;/code&gt;/&lt;code&gt;SMOTEN&lt;/code&gt; exist. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No guarantee of separability.&lt;/strong&gt; A synthetic margin is not a real one. If the true boundary is irreducible noise, more SVM-boundary points just thicken the fog. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extreme imbalance still needs help.&lt;/strong&gt; At ratios like 1:1000, even SVMSMOTE may need under-sampling of the majority or &lt;code&gt;scale_pos_weight&lt;/code&gt; in the estimator itself. [DERIVED]&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reach for SVMSMOTE when&lt;/strong&gt; your classes are separated by a reasonably clean, near-linear margin and you suspect BorderlineSMOTE's kNN danger mask is under- or over-flagging the boundary on irregular geometry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always scale first.&lt;/strong&gt; Wrap &lt;code&gt;StandardScaler&lt;/code&gt; ahead of &lt;code&gt;SVMSMOTE&lt;/code&gt; in the same &lt;code&gt;Pipeline&lt;/code&gt;. This single step decides whether the SVM margin is meaningful. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with a linear &lt;code&gt;svm_estimator&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;SVC(kernel="linear")&lt;/code&gt;), matching the imbalanced-learn default; try &lt;code&gt;rbf&lt;/code&gt; only if you have evidence of a non-linear frontier, and watch the support-vector count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat &lt;code&gt;out_step&lt;/code&gt; as tunable&lt;/strong&gt;, not fixed at 0.5. Smaller values reinforce the margin tightly; larger values broaden it. Tune inside CV. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never resample the test set.&lt;/strong&gt; Resample inside the training fold only, ideally via &lt;code&gt;imblearn.pipeline.Pipeline&lt;/code&gt; so it composes cleanly with scaling and the classifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare against the cheap baseline.&lt;/strong&gt; Before reaching for any SMOTE variant, try &lt;code&gt;scale_pos_weight&lt;/code&gt; (XGBoost/LightGBM) or class weights — sometimes that alone closes the gap without synthesising a single row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair with cleaning.&lt;/strong&gt; Follow SVMSMOTE with Edited Nearest Neighbours (ENN) to prune clearly misplaced points — a "SVMSMOTE + ENN" combo, just as people do with SMOTE/ENN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In our stack&lt;/strong&gt;, all of this lives in Layer 2 (EOD-audited training), never in Layer 1 (live Dhan capture). The SVM margin is computed on historical folds, never on live ticks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is the core difference between SVMSMOTE and BorderlineSMOTE?&lt;/strong&gt;&lt;br&gt;
A1. Both synthesise minority samples only near the decision boundary. BorderlineSMOTE finds that boundary with a kNN danger mask (counting majority neighbours). SVMSMOTE finds it with an SVM: it fits a support-vector classifier and uses the minority-class support vectors as the synthesis seeds. The "where to synthesise" question is answered by a margin instead of a neighbour count. [SOURCE: imbalanced-learn repo; Nguyen et al. 2011]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. What does the &lt;code&gt;svm_estimator&lt;/code&gt; parameter do?&lt;/strong&gt;&lt;br&gt;
A2. It is the SVM used to locate the boundary. Left as &lt;code&gt;None&lt;/code&gt;, imbalanced-learn uses a default linear-kernel &lt;code&gt;SVC&lt;/code&gt;. Passing your own &lt;code&gt;SVC(kernel="linear")&lt;/code&gt; or &lt;code&gt;SVC(kernel="rbf", C=...)&lt;/code&gt; lets you control the margin shape and regularisation. The support vectors of whatever you pass become the synthesis seeds. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. What is &lt;code&gt;out_step&lt;/code&gt; and how should I set it?&lt;/strong&gt;&lt;br&gt;
A3. &lt;code&gt;out_step&lt;/code&gt; (default 0.5) scales how far each synthetic point is displaced from its boundary support vector toward its minority neighbour. Smaller hugs the margin; larger pushes deeper into minority space. Tune it inside CV; do not accept 0.5 blindly. [SOURCE: imbalanced-learn repo; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Do I need to scale features for SVMSMOTE?&lt;/strong&gt;&lt;br&gt;
A4. Yes — strongly recommended. The SVM margin is a distance construction, so unscaled features with larger magnitude dominate the boundary. Wrap &lt;code&gt;StandardScaler&lt;/code&gt; ahead of &lt;code&gt;SVMSMOTE&lt;/code&gt; in the same pipeline. [DERIVED from SVM theory]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Is SVMSMOTE always better than BorderlineSMOTE?&lt;/strong&gt;&lt;br&gt;
A5. No. It tends to win when the boundary is a clean, near-linear margin; it can cost more compute and even underperform when classes are heavily intermixed, features are unscaled, or the dataset is small/high-dimensional. Validate both inside CV. [DERIVED from Nguyen 2011 + Blagus &amp;amp; Lusa 2013]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Can I use SVMSMOTE with categorical features?&lt;/strong&gt;&lt;br&gt;
A6. &lt;code&gt;SVMSMOTE&lt;/code&gt; works in continuous Euclidean space via an SVM. For mixed numeric/categorical data use &lt;code&gt;SMOTENC&lt;/code&gt;; for all-categorical use &lt;code&gt;SMOTEN&lt;/code&gt; — both from &lt;code&gt;imblearn.over_sampling&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. Should I apply it to live trading data?&lt;/strong&gt;&lt;br&gt;
A7. No — apply any over-sampling inside the training/CV loop of your audited model core, never on live inference. Our NSE stack keeps resampling strictly in Layer 2. [DERIVED from the two-layer engine design]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. What comes after SVMSMOTE?&lt;/strong&gt;&lt;br&gt;
A8. V4 KMeansSMOTE clusters the minority class first (KMeans), then synthesises within each coherent cluster — better when the minority class is multi-modal and a single global SVM margin misses subclusters. [SOURCE: imbalanced-learn repo; KMeansSMOTE paper 2018]&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SVMSMOTE (&lt;code&gt;from imblearn.over_sampling import SVMSMOTE&lt;/code&gt;) fits an &lt;strong&gt;SVM&lt;/strong&gt; to separate the classes, extracts the &lt;strong&gt;support vectors&lt;/strong&gt; (the margin boundary), and synthesises minority points near the &lt;strong&gt;minority-class&lt;/strong&gt; support vectors. [SOURCE: imbalanced-learn repo; Nguyen et al. 2011]&lt;/li&gt;
&lt;li&gt;It is the SVM-margin cousin of BorderlineSMOTE: same goal (synthesize at the boundary), different boundary finder (SVM margin vs kNN danger mask). [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;svm_estimator&lt;/code&gt; (default linear &lt;code&gt;SVC&lt;/code&gt;) chooses the margin shape; &lt;code&gt;out_step&lt;/code&gt; (default 0.5) controls how far synthetic points sit from the support vector. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;It helps most when classes have a &lt;strong&gt;clean, near-linear margin&lt;/strong&gt;; it struggles with unscaled features, small/high-dim data, and costs more than kNN variants. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always scale features&lt;/strong&gt; before the SVM, resample &lt;strong&gt;inside CV only&lt;/strong&gt;, and tune &lt;code&gt;out_step&lt;/code&gt;/&lt;code&gt;svm_estimator&lt;/code&gt; as hyperparameters. [DERIVED]&lt;/li&gt;
&lt;li&gt;Next in series: &lt;strong&gt;V4 KMeansSMOTE&lt;/strong&gt;. Previous: &lt;strong&gt;V2 BorderlineSMOTE&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v2-borderline"&gt;Smote V2 Borderline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v4-kmeanssmote"&gt;Smote V4 Kmeanssmote&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Nguyen, H. M., Cooper, E. W., &amp;amp; Kamei, K. (2011). &lt;em&gt;Borderline over-sampling for imbalanced data classification.&lt;/em&gt; International Journal of Knowledge and Web Intelligence, 2(3), 230–242. — original SVM-balanced SMOTE / support-vector boundary over-sampling algorithm that imbalanced-learn's &lt;code&gt;SVMSMOTE&lt;/code&gt; implements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; scikit-learn-contrib/imbalanced-learn GitHub repository — &lt;code&gt;imblearn/over_sampling/_smote/filter.py&lt;/code&gt; (&lt;code&gt;SVMSMOTE&lt;/code&gt;, &lt;code&gt;svm_estimator&lt;/code&gt;, &lt;code&gt;out_step&lt;/code&gt;, &lt;code&gt;m_neighbors&lt;/code&gt;, &lt;code&gt;_svmSMOTE&lt;/code&gt; support-vector seed logic) and &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt;. Primary source for the implementation described.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Chawla, N. V., Bowyer, K. W., Hall, L. O., &amp;amp; Kegelmeyer, W. P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16, 321–357. — vanilla SMOTE baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Han, H., Wang, W.-Y., &amp;amp; Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; ICIC 2005, LNCS 3644, 878–887. — Borderline-SMOTE (kNN danger mask), the direct comparison point for V3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Blagus, R., &amp;amp; Lusa, L. (2013). &lt;em&gt;SMOTE can degrade performance on small, high-dimensional datasets&lt;/em&gt; (cautionary note on over-sampling). — counter-evidence on small/high-dimensional data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[DERIVED]&lt;/strong&gt; The 1-D margin worked example ({1,3,5} vs {10,12,14}), the concave-arc intuition, the interpolation/displacement interpretation of &lt;code&gt;out_step&lt;/code&gt;, and the "SVM margin vs kNN danger" comparison — derived from the cited sources and standard SVM theory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE Family series (V3 of V6). This is educational content; not investment advice and not SEBI-registered research.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About the author: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article (V2 BorderlineSMOTE): &lt;code&gt;SMOTE_V2_borderline.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next article (V4 KMeansSMOTE): &lt;code&gt;SMOTE_V4_kmeanssmote.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>BorderlineSMOTE in imbalanced-learn: Oversample the Decision Boundary, Not the Safe Interior</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Tue, 08 Sep 2026 04:31:13 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/borderlinesmote-in-imbalanced-learn-oversample-the-decision-boundary-not-the-safe-interior-1ip5</link>
      <guid>https://dev.to/shaktitiwari/borderlinesmote-in-imbalanced-learn-oversample-the-decision-boundary-not-the-safe-interior-1ip5</guid>
      <description>&lt;h1&gt;
  
  
  BorderlineSMOTE in imbalanced-learn: Oversample the Decision Boundary, Not the Safe Interior
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE Family series. Previous: V1 vanilla SMOTE. Next: V3 SVMSMOTE.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BorderlineSMOTE&lt;/strong&gt; (&lt;code&gt;from imblearn.over_sampling import BorderlineSMOTE&lt;/code&gt;) is a variant of SMOTE that refuses to waste synthetic samples in the safe interior of the minority class. It first classifies every minority point as &lt;strong&gt;SAFE&lt;/strong&gt;, &lt;strong&gt;DANGER&lt;/strong&gt;, or &lt;strong&gt;NOISE&lt;/strong&gt; based on how many of its nearest neighbours belong to the majority class, then synthesises new points &lt;strong&gt;only for the DANGER set&lt;/strong&gt; — the minority instances hugging the decision boundary, which are the ones a classifier actually struggles with. When the informative signal lives at the class boundary (the usual case in noisy financial data), BorderlineSMOTE typically beats vanilla SMOTE while adding less redundant noise. [DERIVED summary of Han et al. 2005 + imbalanced-learn implementation]&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;If you trade Nifty options, you already live in an imbalanced world. Big directional moves that you &lt;em&gt;want&lt;/em&gt; to catch — a sharp expiry-day reversal, a volatility expansion — are rare compared to the dull chop that fills most sessions. Train an XGBoost model naively on that history and it will quietly learn the lazy rule "predict the common class," because that minimises raw error while delivering a model that is useless for the trades that pay the bills.&lt;/p&gt;

&lt;p&gt;SMOTE fixed part of that by manufacturing synthetic minority rows. But vanilla SMOTE sprays those synthetic rows everywhere — including deep inside minority territory where the classifier was &lt;em&gt;already&lt;/em&gt; confident. That is wasted effort, and worse, it can blur the very boundary you care about. BorderlineSMOTE is the smarter cousin: it asks &lt;em&gt;where&lt;/em&gt; the hard cases are and spends its synthetic budget there. [DERIVED from the conceptual motivation in Han et al. 2005]&lt;/p&gt;

&lt;p&gt;Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; For a binary classifier trained on an imbalanced dataset, does restricting synthetic over-sampling to the minority "danger" region (BorderlineSMOTE) improve minority-class recall and balanced metrics (F1, G-mean) relative to unrestricted vanilla SMOTE, without inflating training cost?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; Yes — &lt;em&gt;when the classes are separated by a boundary rather than thoroughly intermixed&lt;/em&gt;. The boundary minority points carry the most discriminative information; the interior minority points are already easy; the noise minority points (surrounded by majority) are usually mislabels. Concentrating generation on the danger zone should raise genuine recall while adding less artificial overlap. The gain collapses when the classes are so overlapping that "danger" ≈ "everything," or when the dataset is tiny/high-dimensional (see Limitations).&lt;/p&gt;

&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We describe the canonical experimental shape used throughout the SMOTE literature and the imbalanced-learn documentation. &lt;strong&gt;No experiment is executed in this article&lt;/strong&gt; — the snippet below is illustrative, taken from the imbalanced-learn API, and is shown only to anchor the method. [SOURCE: scikit-learn-contrib/imbalanced-learn repo, &lt;code&gt;BorderlineSMOTE&lt;/code&gt; docstring/example]&lt;/p&gt;

&lt;p&gt;The pipeline shape is always:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stratified train/test split (never let SMOTE see the test set).&lt;/li&gt;
&lt;li&gt;Inside cross-validation &lt;em&gt;only&lt;/em&gt;, fit &lt;code&gt;BorderlineSMOTE&lt;/code&gt; on the training fold and &lt;code&gt;fit_resample&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Train the estimator on the resampled fold; validate on the untouched test fold.&lt;/li&gt;
&lt;li&gt;Optionally wrap with &lt;code&gt;Pipeline&lt;/code&gt; so resampling is never leaked.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Illustrative API usage (from imbalanced-learn docs — not run here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.datasets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BorderlineSMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;classification_report_imbalanced&lt;/span&gt;

&lt;span class="c1"&gt;## Synthetic illustrative data — do not treat as a real market dataset
&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;n_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_sep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;n_informative&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_redundant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flip_y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_clusters_per_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Original dataset shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Original dataset shape Counter({1: 900, 0: 100})
&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;## borderline-1 is the default kind
&lt;/span&gt;&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BorderlineSMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;borderline-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resampled shape&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;## Resampled shape Counter({0: 900, 1: 900})  -- illustrative from repo example
&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BorderlineSMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;y_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;classification_report_imbalanced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_pred&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key constructor parameters [SOURCE: imbalanced-learn repo, &lt;code&gt;filter.py&lt;/code&gt;]:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;k_neighbors&lt;/code&gt; (default &lt;strong&gt;5&lt;/strong&gt;) — number of nearest neighbours used to &lt;em&gt;generate&lt;/em&gt; each synthetic point.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;m_neighbors&lt;/code&gt; (default &lt;strong&gt;10&lt;/strong&gt;) — number of nearest neighbours used to decide whether a minority point is "in danger."&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kind&lt;/code&gt; — &lt;code&gt;"borderline-1"&lt;/code&gt; (default) or &lt;code&gt;"borderline-2"&lt;/code&gt; (see below).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy&lt;/code&gt; (default &lt;code&gt;"auto"&lt;/code&gt;) — how much to oversample.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;random_state&lt;/code&gt; — for reproducibility of the random interpolation gap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful diagnostic the library exposes: &lt;code&gt;sm.in_danger_indices_&lt;/code&gt; — a dict mapping each resampled class to the &lt;em&gt;indices of the original minority rows that were flagged "in danger" and used to seed synthesis&lt;/em&gt;. Inspecting it tells you exactly which rows BorderlineSMOTE considered boundary-relevant. [SOURCE: imbalanced-learn repo, &lt;code&gt;BorderlineSMOTE.in_danger_indices_&lt;/code&gt; attribute]&lt;/p&gt;

&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Because this article does not run an experiment, the "findings" below are a synthesis of the primary sources, not measured numbers from this author's machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 1 — Vanilla SMOTE over-invests in the interior.&lt;/strong&gt; Vanilla SMOTE selects &lt;em&gt;every&lt;/em&gt; minority point with equal probability and interpolates between two minority neighbours [SOURCE: Chawla et al. 2002]. Many of those points sit far from any majority instance, where the classifier already separates cleanly. Generating there adds rows the model did not need and, in the worst case, the interpolated segment between two minority points can cross majority space, fabricating ambiguous examples. [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 2 — The boundary minority points are the informative ones.&lt;/strong&gt; Han, Wang and Mao (2005) argue that a minority instance surrounded mostly by its own class is "safe" and contributes little new separable signal, while one sitting next to majority instances is on the frontier where misclassification actually happens [SOURCE: Han, Wang, Mao 2005]. Concentrating synthesis there strengthens the decision region where it is weakest. [DERIVED interpretation]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 3 — The DANGER mask is the load-bearing mechanism.&lt;/strong&gt; In imbalanced-learn, danger detection lives in &lt;code&gt;_in_danger_noise&lt;/code&gt;: for each minority point, count how many of its &lt;code&gt;m_neighbors&lt;/code&gt; nearest neighbours (default 10) belong to the majority class. A point is flagged &lt;strong&gt;DANGER&lt;/strong&gt; when that count is at least half but not all (&lt;code&gt;m/2 ≤ n_majority &amp;lt; m&lt;/code&gt;); it is flagged &lt;strong&gt;NOISE&lt;/strong&gt; when &lt;em&gt;all&lt;/em&gt; neighbours are majority; otherwise it is left alone (SAFE). Only DANGER points seed new synthetic rows. [SOURCE: imbalanced-learn repo, &lt;code&gt;base.py&lt;/code&gt; &lt;code&gt;_in_danger_noise&lt;/code&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 4 — &lt;code&gt;borderline-1&lt;/code&gt; vs &lt;code&gt;borderline-2&lt;/code&gt; change where synthesis reaches.&lt;/strong&gt; This is the part most practitioners miss.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;borderline-1&lt;/code&gt;&lt;/strong&gt; (default): synthetic samples are generated by interpolating each DANGER minority point &lt;em&gt;with its minority-class neighbours only&lt;/em&gt;. The new points stay on the minority side of the boundary, thickening the edge of the minority region. [SOURCE: imbalanced-learn repo, &lt;code&gt;filter.py&lt;/code&gt; — &lt;code&gt;X_to_sample_from = X_class&lt;/code&gt;]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;borderline-2&lt;/code&gt;&lt;/strong&gt;: the &lt;em&gt;whole&lt;/em&gt; dataset is used as the neighbour pool (&lt;code&gt;X_to_sample_from = X&lt;/code&gt;), so the DANGER point can also be interpolated toward its majority neighbours. This pushes synthetic points across the boundary into majority space, directly contesting the majority region. [SOURCE: imbalanced-learn repo, &lt;code&gt;filter.py&lt;/code&gt; — &lt;code&gt;X_to_sample_from = X&lt;/code&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interpolation itself is the familiar SMOTE rule, here written for a DANGER seed &lt;code&gt;x_i&lt;/code&gt; and a chosen neighbour &lt;code&gt;x_z&lt;/code&gt; [DERIVED]:&lt;br&gt;
&lt;code&gt;x_new = x_i + (x_z − x_i) · δ&lt;/code&gt;, with &lt;code&gt;δ ~ Uniform(0, 1)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;borderline-1&lt;/code&gt;, &lt;code&gt;x_z&lt;/code&gt; is drawn from the minority k-NN of &lt;code&gt;x_i&lt;/code&gt;; for &lt;code&gt;borderline-2&lt;/code&gt;, &lt;code&gt;x_z&lt;/code&gt; may be a minority &lt;em&gt;or&lt;/em&gt; majority k-NN of &lt;code&gt;x_i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding 5 — Empirical direction from the literature.&lt;/strong&gt; The original paper reports Borderline-SMOTE outperforming vanilla SMOTE on a range of imbalanced UCI-style datasets, particularly on F-measure and related balanced metrics, because it stops fabricating easy interior points [SOURCE: Han, Wang, Mao 2005]. We are not quoting exact tables here (this author did not re-run them); the qualitative claim is well established in the over-sampling literature. [DERIVED note on what the paper establishes]&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducibility
&lt;/h2&gt;

&lt;p&gt;To reproduce a real comparison you would, at minimum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick a fixed &lt;code&gt;random_state&lt;/code&gt; everywhere (data split, SMOTE, estimator).&lt;/li&gt;
&lt;li&gt;Compare three resamplers &lt;em&gt;inside&lt;/em&gt; the same CV loop: &lt;code&gt;SMOTE()&lt;/code&gt; (V1), &lt;code&gt;BorderlineSMOTE(kind="borderline-1")&lt;/code&gt;, &lt;code&gt;BorderlineSMOTE(kind="borderline-2")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Score with minority recall, precision, F1, and G-mean — &lt;strong&gt;not&lt;/strong&gt; raw accuracy, which is misleading under imbalance.&lt;/li&gt;
&lt;li&gt;Always &lt;code&gt;fit_resample&lt;/code&gt; on the training fold only.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code sketch in &lt;em&gt;Data &amp;amp; Methodology&lt;/em&gt; is the canonical shape from the imbalanced-learn documentation and is presented &lt;strong&gt;illustratively&lt;/strong&gt; — it was not executed for this article, and its printed outputs are quoted from the library's own example for reference. [SOURCE: imbalanced-learn repo docstring; not an experiment result of this author]&lt;/p&gt;

&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;Borderline-SMOTE is not a free lunch, and the honest literature says so.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blagus &amp;amp; Lusa (2013)&lt;/strong&gt; show that SMOTE-family over-sampling can &lt;em&gt;hurt&lt;/em&gt; on small or high-dimensional datasets, where the nearest-neighbour geometry is unreliable and synthetic points amplify noise rather than signal [SOURCE: Blagus &amp;amp; Lusa 2013]. Borderline-SMOTE narrows the problem but inherits it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When classes are thoroughly intermixed&lt;/strong&gt;, almost every minority point becomes "in danger," so BorderlineSMOTE degenerates toward vanilla SMOTE — you pay the complexity for little gain. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boundary noise is still boundary noise.&lt;/strong&gt; If the minority points near the boundary are actually mislabels (NOISE that escaped the &lt;code&gt;m/2&lt;/code&gt; filter), BorderlineSMOTE will enthusiastically manufacture more of them. Over-focusing on the border can amplify labelling errors. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better variants exist for hard cases.&lt;/strong&gt; SVMSMOTE (V3) uses an SVM to pick support-vector-like border points; KMeansSMOTE clusters first. BorderlineSMOTE is a strong default but not the final word. [SOURCE: imbalanced-learn repo; Nguyen et al. 2011 for SVM-SMOTE]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It still ignores majority density.&lt;/strong&gt; BorderlineSMOTE synthesises minority points based on neighbour &lt;em&gt;labels&lt;/em&gt;, not on how &lt;em&gt;densely&lt;/em&gt; the majority class is packed. It can drop a synthetic minority point into a dense majority cluster, making that region even more ambiguous. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can over-focus on border noise.&lt;/strong&gt; As noted, the DANGER set includes genuinely hard points &lt;em&gt;and&lt;/em&gt; mislabeled ones; the algorithm cannot tell them apart. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold sensitivity.&lt;/strong&gt; Results shift with &lt;code&gt;m_neighbors&lt;/code&gt; and &lt;code&gt;k_neighbors&lt;/code&gt;. Too small an &lt;code&gt;m_neighbors&lt;/code&gt; makes the danger estimate noisy; too large blurs the boundary concept. These are hyperparameters you must tune inside CV, not set once. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature-space only.&lt;/strong&gt; Like all SMOTE variants, it operates in the raw feature space and assumes Euclidean proximity means semantic similarity — fragile with mixed-type, high-cardinality, or heavily engineered features. (For those, SMOTENC/SMOTEN exist.) [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No guarantee of separability.&lt;/strong&gt; Adding boundary minority points does not create a cleaner boundary; if the true boundary is irreducible noise, more synthetic points just thicken the fog. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Class imbalance ratio still matters.&lt;/strong&gt; At extreme ratios (e.g., 1:1000), even BorderlineSMOTE may need to be paired with under-sampling of the majority or with &lt;code&gt;scale_pos_weight&lt;/code&gt; in the estimator itself. [DERIVED]&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reach for BorderlineSMOTE when&lt;/strong&gt; your minority class forms reasonably coherent clusters with a definable boundary and you care about recall at that boundary — the classic imbalanced-classification setup, including many Nifty option signal problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer &lt;code&gt;borderline-1&lt;/code&gt; as the default&lt;/strong&gt;; try &lt;code&gt;borderline-2&lt;/code&gt; only when you suspect the minority region needs to actively push into majority space. Always validate both inside CV.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never resample the test set.&lt;/strong&gt; Resample inside the training fold only, ideally via &lt;code&gt;imblearn.pipeline.Pipeline&lt;/code&gt; so it composes cleanly with scaling and the classifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair with cleaning.&lt;/strong&gt; Because BorderlineSMOTE can still leave noisy majority regions, many practitioners follow it with Edited Nearest Neighbours (ENN) to prune clearly misplaced points — a "SMOTE + ENN" or "BorderlineSMOTE + ENN" combo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tune &lt;code&gt;m_neighbors&lt;/code&gt;/&lt;code&gt;k_neighbors&lt;/code&gt;&lt;/strong&gt; as hyperparameters; don't accept defaults blindly on real data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare against the cheap baseline.&lt;/strong&gt; Before reaching for any SMOTE variant, try &lt;code&gt;scale_pos_weight&lt;/code&gt; (XGBoost/LightGBM) or class weights — sometimes that alone closes the gap without synthesising a single row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In our stack&lt;/strong&gt;, all of this lives in Layer 2 (EOD-audited training), never in Layer 1 (live Dhan capture).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What exactly is the difference between &lt;code&gt;borderline-1&lt;/code&gt; and &lt;code&gt;borderline-2&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A1. Both select the same DANGER minority points. &lt;code&gt;borderline-1&lt;/code&gt; interpolates each DANGER point only with its &lt;em&gt;minority&lt;/em&gt; neighbours, keeping new samples on the minority side. &lt;code&gt;borderline-2&lt;/code&gt; uses the &lt;em&gt;whole&lt;/em&gt; dataset as the neighbour pool, so a DANGER point can also be interpolated toward majority neighbours, pushing synthetic points across the boundary. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. How does BorderlineSMOTE decide a point is "in danger"?&lt;/strong&gt;&lt;br&gt;
A2. For each minority point it counts majority-class neighbours among its &lt;code&gt;m_neighbors&lt;/code&gt; (default 10) nearest neighbours. If at least half but not all are majority → DANGER (used for synthesis). If all are majority → NOISE (skipped). Otherwise → SAFE (skipped). [SOURCE: imbalanced-learn repo, &lt;code&gt;_in_danger_noise&lt;/code&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Is BorderlineSMOTE always better than vanilla SMOTE?&lt;/strong&gt;&lt;br&gt;
A3. No. It wins when the informative minority signal sits at a boundary and the interior is already separable. When classes are heavily intermixed or the dataset is tiny/high-dimensional, it can degenerate toward vanilla SMOTE or even hurt. [DERIVED from Han 2005 + Blagus &amp;amp; Lusa 2013]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Can I use BorderlineSMOTE with categorical features?&lt;/strong&gt;&lt;br&gt;
A4. &lt;code&gt;BorderlineSMOTE&lt;/code&gt; works in continuous Euclidean space. For mixed numeric/categorical data, use &lt;code&gt;SMOTENC&lt;/code&gt;; for all-categorical, use &lt;code&gt;SMOTEN&lt;/code&gt; — both from &lt;code&gt;imblearn.over_sampling&lt;/code&gt;. [SOURCE: imbalanced-learn repo]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Should I apply it to my live trading data?&lt;/strong&gt;&lt;br&gt;
A5. No — apply any over-sampling inside the training/CV loop of your audited model core, never on live inference. Our NSE stack keeps resampling strictly in Layer 2. [DERIVED from the two-layer engine design]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What comes after BorderlineSMOTE?&lt;/strong&gt;&lt;br&gt;
A6. V3 SVMSMOTE uses an SVM to locate border-support points, and KMeansSMOTE clusters before synthesising. They refine the "where to synthesise" question further. [SOURCE: imbalanced-learn repo; Nguyen et al. 2011]&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;BorderlineSMOTE synthesises minority samples &lt;strong&gt;only near the decision boundary&lt;/strong&gt;, not in the safe interior. [DERIVED]&lt;/li&gt;
&lt;li&gt;It tags each minority point &lt;strong&gt;SAFE / DANGER / NOISE&lt;/strong&gt; by counting majority neighbours; only &lt;strong&gt;DANGER&lt;/strong&gt; seeds new rows. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;borderline-1&lt;/code&gt; (default) keeps new points on the minority side; &lt;code&gt;borderline-2&lt;/code&gt; can push them into majority space. [SOURCE: imbalanced-learn repo]&lt;/li&gt;
&lt;li&gt;It usually beats vanilla SMOTE when the boundary carries the signal, but &lt;strong&gt;ignores majority density&lt;/strong&gt; and can amplify border noise. [DERIVED]&lt;/li&gt;
&lt;li&gt;Always resample &lt;strong&gt;inside CV only&lt;/strong&gt;; tune &lt;code&gt;m_neighbors&lt;/code&gt;/&lt;code&gt;k_neighbors&lt;/code&gt;; consider pairing with ENN. [DERIVED]&lt;/li&gt;
&lt;li&gt;Next in series: &lt;strong&gt;V3 SVMSMOTE&lt;/strong&gt;. Previous: &lt;strong&gt;V1 vanilla SMOTE&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v3-svmsmote"&gt;Smote V3 Svmsmote&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v4-kmeanssmote"&gt;Smote V4 Kmeanssmote&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Han, H., Wang, W.-Y., &amp;amp; Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; Advances in Intelligent Computing (ICIC 2005), Lecture Notes in Computer Science, vol. 3644, pp. 878–887. Springer. — original Borderline-SMOTE algorithm, SAFE/BORDERLINE/NOISE classification, borderline-1 vs borderline-2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Chawla, N. V., Bowyer, K. W., Hall, L. O., &amp;amp; Kegelmeyer, W. P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16, 321–357. — vanilla SMOTE baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; scikit-learn-contrib/imbalanced-learn GitHub repository — &lt;code&gt;imblearn/over_sampling/_smote/filter.py&lt;/code&gt; (&lt;code&gt;BorderlineSMOTE&lt;/code&gt;, &lt;code&gt;m_neighbors&lt;/code&gt;, &lt;code&gt;kind&lt;/code&gt;, &lt;code&gt;in_danger_indices_&lt;/code&gt;) and &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt; (&lt;code&gt;_in_danger_noise&lt;/code&gt; danger/noise mask logic). Primary source for the implementation described.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Blagus, R., &amp;amp; Lusa, L. (2013). &lt;em&gt;A note on the estimation of the optimal SMOTE oversampling ratio.&lt;/em&gt; / SMOTE can degrade performance on small, high-dimensional datasets. — counter-evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[SOURCE]&lt;/strong&gt; Nguyen, H. M., Cooper, E. W., &amp;amp; Kamei, K. (2011). &lt;em&gt;Borderline over-sampling for imbalanced data classification.&lt;/em&gt; — basis for SVM-SMOTE (V3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[DERIVED]&lt;/strong&gt; Interpolation formula &lt;code&gt;x_new = x_i + (x_z − x_i)·δ&lt;/code&gt; and the "where to synthesise" interpretation, derived from the cited sources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE Family series (V2 of V6). This is educational content; not investment advice and not SEBI-registered research.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About the author: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Previous article (V1 vanilla SMOTE): &lt;code&gt;SMOTE_V1_smote.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next article (V3 SVMSMOTE): &lt;code&gt;SMOTE_V3_svmsmote.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>Vanilla SMOTE in imbalanced-learn: The kNN Interpolation Engine Behind Balanced XGBoost</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Tue, 08 Sep 2026 04:30:53 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/vanilla-smote-in-imbalanced-learn-the-knn-interpolation-engine-behind-balanced-xgboost-2d49</link>
      <guid>https://dev.to/shaktitiwari/vanilla-smote-in-imbalanced-learn-the-knn-interpolation-engine-behind-balanced-xgboost-2d49</guid>
      <description>&lt;h1&gt;
  
  
  Vanilla SMOTE in imbalanced-learn: The kNN Interpolation Engine Behind Balanced XGBoost
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This is article V1 in the SMOTE cluster. PREV: S1 — The SMOTE Algorithm (intuition). NEXT: V2 — BorderlineSMOTE.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;Vanilla &lt;code&gt;SMOTE&lt;/code&gt; from &lt;code&gt;imbalanced-learn&lt;/code&gt; fixes class imbalance by &lt;strong&gt;synthesizing&lt;/strong&gt; new minority-class samples as random convex combinations between each minority point and one of its k nearest minority neighbors — instead of blindly duplicating rows like random oversampling does (SOURCE: Chawla et al. 2002, JAIR 16:321–357). In &lt;code&gt;imbalanced-learn&lt;/code&gt; the defaults are &lt;code&gt;sampling_strategy='auto'&lt;/code&gt; (balance every minority class up to the majority count), &lt;code&gt;k_neighbors=5&lt;/code&gt;, and an optional &lt;code&gt;random_state&lt;/code&gt; for reproducibility (SOURCE: scikit-learn-contrib/imbalanced-learn, ~7.1k★). It helps most when the minority class forms a connected, dense region; it can hurt on tiny, high-dimensional, or noisy sets where invented points land in the wrong territory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Class imbalance is the silent killer of every Nifty option signal model. Your rare-event labels — a sharp expiry-day reversal, an OTM strangle that actually pays — might be 1–3% of rows. Train XGBoost on that raw and the tree will happily ignore the minority entirely because predicting "no move" already nets a 97% accuracy (DERIVED from the definition of accuracy on a 97/3 split). That 97% is a lie you can trade against at your own peril.&lt;/p&gt;

&lt;p&gt;This is precisely why our resampling discipline lives in Layer 2 of the engine. Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data. You never synthesize imaginary ticks on the live feed — that would be fraud dressed as feature engineering.&lt;/p&gt;

&lt;p&gt;Understanding &lt;em&gt;vanilla&lt;/em&gt; SMOTE — the plain &lt;code&gt;SMOTE&lt;/code&gt; class, no borderline logic, no adaptive weighting — is the foundation. Every variant (BorderlineSMOTE, SVMSMOTE, ADASYN, KMeansSMOTE) is a mutation of this same interpolation core. If you don't understand the base, the variants are just incantations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RQ:&lt;/strong&gt; What exactly distinguishes vanilla &lt;code&gt;SMOTE&lt;/code&gt; from random oversampling, how does its kNN interpolation behave under the default &lt;code&gt;sampling_strategy='auto'&lt;/code&gt; / &lt;code&gt;k_neighbors=5&lt;/code&gt; configuration, and under what data-geometry conditions does it improve a classifier versus degrade it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; Because SMOTE injects &lt;em&gt;new&lt;/em&gt; points along minority–minority segments rather than replaying identical rows, it should (a) reduce the overfitting that duplication causes and (b) smooth the decision boundary — &lt;em&gt;provided&lt;/em&gt; the minority manifold is locally coherent. When that manifold is sparse or the features are noisy/high-dimensional, the synthetic points will be unreliable and can poison the boundary instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We do &lt;strong&gt;not&lt;/strong&gt; run code in this article. The snippet below is &lt;strong&gt;illustrative only&lt;/strong&gt;, reproduced from the canonical &lt;code&gt;imbalanced-learn&lt;/code&gt; usage shape — it is NOT an experiment result (SOURCE: scikit-learn-contrib/imbalanced-learn documentation). Treat it as the API contract, nothing more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Illustrative only — API shape from imbalanced-learn docs, not an experiment result.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;

&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;smote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# balance all minority classes to majority count
&lt;/span&gt;    &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# nearest minority neighbors to interpolate between
&lt;/span&gt;    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;             &lt;span class="c1"&gt;# reproducibility
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The methodology we describe is the &lt;strong&gt;algorithmic specification&lt;/strong&gt; as given by Chawla et al. (2002) and as implemented in the &lt;code&gt;imbalanced-learn&lt;/code&gt; repository (SOURCE for both). We break the vanilla procedure into its atomic steps and then derive the geometry of one synthetic point by hand in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  The four atomic steps (SOURCE: Chawla 2002; SOURCE: imbalanced-learn SMOTE)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Isolate the minority set.&lt;/strong&gt; For each minority class &lt;code&gt;C&lt;/code&gt; (when &lt;code&gt;sampling_strategy='auto'&lt;/code&gt;, every class smaller than the majority), take its instances &lt;code&gt;T_C&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set the target count.&lt;/strong&gt; &lt;code&gt;sampling_strategy&lt;/code&gt; decides how many synthetic samples to make. With &lt;code&gt;'auto'&lt;/code&gt;, the target equals the majority class size, so the number of &lt;em&gt;new&lt;/em&gt; points for class &lt;code&gt;C&lt;/code&gt; is &lt;code&gt;N_majority − N_C&lt;/code&gt; (DERIVED).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find k nearest minority neighbors.&lt;/strong&gt; For each minority point &lt;code&gt;x_i&lt;/code&gt;, compute Euclidean distances to all other points of the &lt;em&gt;same&lt;/em&gt; class and keep the &lt;code&gt;k_neighbors&lt;/code&gt; closest. Default &lt;code&gt;k_neighbors=5&lt;/code&gt; (SOURCE: imbalanced-learn default).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interpolate.&lt;/strong&gt; For each &lt;code&gt;x_i&lt;/code&gt;, draw a random neighbor &lt;code&gt;x_zi&lt;/code&gt; from its k-NN set, draw &lt;code&gt;δ ∼ Uniform(0,1)&lt;/code&gt;, and emit:
&lt;code&gt;x_new = x_i + δ · (x_zi − x_i)&lt;/code&gt;   (SOURCE: Chawla 2002, Equation for synthetic sample generation).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Repeat step 4 until the required count of synthetics is reached. Note what SMOTE does &lt;strong&gt;not&lt;/strong&gt; do: it never duplicates an existing row. Every emitted point is strictly a &lt;em&gt;new&lt;/em&gt; coordinate on a segment between two real minority points (DERIVED from the convex-combination formula with &lt;code&gt;δ∈[0,1]&lt;/code&gt;; at &lt;code&gt;δ=0&lt;/code&gt; it equals &lt;code&gt;x_i&lt;/code&gt; and at &lt;code&gt;δ=1&lt;/code&gt; it equals &lt;code&gt;x_zi&lt;/code&gt;, but &lt;code&gt;δ&lt;/code&gt; is drawn continuously so exact endpoints are measure-zero events).&lt;/p&gt;




&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Below are the concrete behavioral findings, each anchored to a primary source or a derivation from the formula above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F1 — SMOTE differs from random oversampling in mechanism, not just magnitude.&lt;/strong&gt; Random oversampling copies existing minority rows until balance. SMOTE &lt;em&gt;manufactures&lt;/em&gt; points in the convex hull of minority pairs (SOURCE: Chawla 2002 motivation section; DERIVED: copying ⇒ identical rows in training set ⇒ a tree can split on a memorized row, while interpolation ⇒ novel points).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F2 — The default &lt;code&gt;sampling_strategy='auto'&lt;/code&gt; fully balances all minorities to the majority count.&lt;/strong&gt; In &lt;code&gt;imbalanced-learn&lt;/code&gt;, &lt;code&gt;'auto'&lt;/code&gt; is documented to resample all classes except the majority to the majority's cardinality (SOURCE: imbalanced-learn user guide). If you have a 1000/50 split, &lt;code&gt;'auto'&lt;/code&gt; creates 950 synthetic minors. You can also pass a float (e.g. &lt;code&gt;0.2&lt;/code&gt; ⇒ minority becomes 20% of majority) or a dict (per-class overrides) — but the smart default is full balance (SOURCE: imbalanced-learn API).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F3 — &lt;code&gt;k_neighbors=5&lt;/code&gt; sets the locality of invention.&lt;/strong&gt; Smaller &lt;code&gt;k&lt;/code&gt; ⇒ synthetics hug tighter neighborhoods (risk: over-local, can create tiny clusters); larger &lt;code&gt;k&lt;/code&gt; ⇒ synthetics spread toward farther cousins (risk: blend across potentially distinct sub-manifolds). The choice is a bias–variance dial on the minority geometry (DERIVED from k-NN smoothing theory).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F4 — &lt;code&gt;random_state&lt;/code&gt; makes the &lt;em&gt;synthetics&lt;/em&gt; reproducible, not the model.&lt;/strong&gt; Same &lt;code&gt;random_state&lt;/code&gt; ⇒ same synthetic coordinates across runs; the downstream classifier still has its own seed. This is essential for the walk-forward CV in Layer 2 so that two backtests of the same window are comparable (DERIVED from determinism of &lt;code&gt;fit_resample&lt;/code&gt; under fixed seed; SOURCE: imbalanced-learn design).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F5 — SMOTE operates in raw feature space with Euclidean distance.&lt;/strong&gt; It has no notion of feature scale, categorical vs numeric, or label noise. That is both its strength (simplicity, speed) and its trap (DERIVED from the algorithm using raw coordinates).&lt;/p&gt;




&lt;h2&gt;
  
  
  Reproducibility (code shape — illustrative)
&lt;/h2&gt;

&lt;p&gt;As stated up top, the following is the canonical &lt;code&gt;imbalanced-learn&lt;/code&gt; call shape and is shown for reproducibility of &lt;em&gt;API usage&lt;/em&gt;, not as a claim that we executed a model here. The pipeline pattern below is what we actually wire into Layer 2 CV (SOURCE: imbalanced-learn Pipeline + SMOTE examples).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Illustrative only — shows where SMOTE sits: INSIDE CV, never on the test set.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xgboost&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XGBClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StratifiedKFold&lt;/span&gt;

&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;smote&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="nc"&gt;XGBClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;eval_metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logloss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;skf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StratifiedKFold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_splits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;## pipe.fit(X_train_fold, y_train_fold)  -&amp;gt;  scored on the untouched validation fold
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The golden rule, repeated because people violate it constantly: &lt;code&gt;fit_resample&lt;/code&gt; must be called &lt;strong&gt;only on training folds&lt;/strong&gt;, never on validation or test data, or you leak synthetic minorities into your evaluation and your backtest lies (DERIVED from CV hygiene; echoed in every imbalanced-learn tutorial — here SOURCE: imbalanced-learn "avoid leakage" guidance).&lt;/p&gt;




&lt;h2&gt;
  
  
  Worked 2-D Interpolation Example (DERIVED math)
&lt;/h2&gt;

&lt;p&gt;Let's make the formula concrete in two dimensions so you &lt;em&gt;see&lt;/em&gt; what SMOTE emits. Suppose one minority point is &lt;code&gt;A = (2, 3)&lt;/code&gt; and its randomly chosen 5-NN neighbor is &lt;code&gt;B = (4, 7)&lt;/code&gt;. The segment &lt;code&gt;AB&lt;/code&gt; has direction &lt;code&gt;B − A = (2, 4)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;SMOTE draws &lt;code&gt;δ ∼ Uniform(0,1)&lt;/code&gt; and computes &lt;code&gt;A + δ·(B−A)&lt;/code&gt;. Three independent draws:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;δ&lt;/th&gt;
&lt;th&gt;x = 2 + δ·2&lt;/th&gt;
&lt;th&gt;y = 3 + δ·4&lt;/th&gt;
&lt;th&gt;Synthetic point&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.25&lt;/td&gt;
&lt;td&gt;2.50&lt;/td&gt;
&lt;td&gt;4.00&lt;/td&gt;
&lt;td&gt;(2.50, 4.00)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.50&lt;/td&gt;
&lt;td&gt;3.00&lt;/td&gt;
&lt;td&gt;5.00&lt;/td&gt;
&lt;td&gt;(3.00, 5.00)  ← exact midpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.80&lt;/td&gt;
&lt;td&gt;3.60&lt;/td&gt;
&lt;td&gt;6.20&lt;/td&gt;
&lt;td&gt;(3.60, 6.20)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every synthetic lies &lt;strong&gt;on the line segment&lt;/strong&gt; between A and B, never beyond it, because &lt;code&gt;δ∈[0,1]&lt;/code&gt; (DERIVED). Now scale this: with &lt;code&gt;k_neighbors=5&lt;/code&gt;, each minority point gets 5 candidate neighbors, so the &lt;em&gt;set of possible segments&lt;/em&gt; is rich, and SMOTE samples different neighbors and different &lt;code&gt;δ&lt;/code&gt; to fill the minority region with a cloud of plausible in-between points rather than 5 copies of A. This is the entire conceptual difference from random oversampling, which would simply append &lt;code&gt;(2,3)&lt;/code&gt; five more times (DERIVED comparison).&lt;/p&gt;

&lt;p&gt;A second minority point &lt;code&gt;C = (5, 1)&lt;/code&gt; with neighbor &lt;code&gt;D = (6, 4)&lt;/code&gt; (&lt;code&gt;D−C = (1,3)&lt;/code&gt;) and &lt;code&gt;δ=0.6&lt;/code&gt; yields &lt;code&gt;(5.6, 2.8)&lt;/code&gt;. Notice the synthetic cloud now spans two distinct minority sub-regions, and a classifier trained on the augmented set learns a boundary that &lt;em&gt;interpolates&lt;/em&gt; between real minors instead of over-memorizing them (DERIVED; consistent with Chawla 2002's stated intent — SOURCE).&lt;/p&gt;




&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;SMOTE is not a free lunch, and the literature is blunt about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C1 — Blagus &amp;amp; Lusa (2013) show SMOTE can &lt;em&gt;degrade&lt;/em&gt; performance on small or high-dimensional datasets.&lt;/strong&gt; When the minority class has few samples or the feature space is wide, the k-NN neighborhoods become unreliable — synthetic points get placed in regions that don't reflect the true minority distribution, and classifiers trained on them perform worse than on the raw imbalanced data (SOURCE: Blagus &amp;amp; Lusa 2013). This is the single most important caveat for options data, where rare events are genuinely sparse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C2 — Synthetic points can cross the decision boundary into majority territory.&lt;/strong&gt; If a minority point sits near the majority region, its neighbor-based interpolation can emit a point that actually belongs to the majority class. SMOTE has no label-check; it just stamps "minority" on whatever it creates (DERIVED from the algorithm lacking any label-consistency test; SOURCE-adjacent: this limitation motivates BorderlineSMOTE and SVMSMOTE).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C3 — Random oversampling sometimes matches or beats SMOTE.&lt;/strong&gt; On datasets where the minority class is already well-separated, simply duplicating can be sufficient, and the added complexity of SMOTE buys nothing (SOURCE: multiple empirical comparisons in the imbalanced-learning literature; DERIVED: more synthetic noise without benefit when the boundary is already clear).&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scale blindness.&lt;/strong&gt; Euclidean distance on unstandardized features lets large-magnitude columns dominate neighbor selection. Always scale &lt;em&gt;inside&lt;/em&gt; the CV pipeline (DERIVED from distance geometry).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No categorical handling.&lt;/strong&gt; Vanilla SMOTE interpolates numerically; mixing it on one-hot or label-encoded categoricals produces impossible hybrids (e.g. &lt;code&gt;0.4&lt;/code&gt; in a binary column). Use &lt;code&gt;SMOTENC&lt;/code&gt;/&lt;code&gt;SMOTEN&lt;/code&gt; for mixed types (SOURCE: imbalanced-learn SMOTENC/SMOTEN).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curse of dimensionality.&lt;/strong&gt; In high-D spaces, "nearest" neighbors are often far, so synthetic points are wild (DERIVED from concentration of distances; SOURCE-adjacent: Blagus &amp;amp; Lusa 2013).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leakage risk.&lt;/strong&gt; Applied outside CV, it contaminates evaluation (see Reproducibility).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assumes connected manifold.&lt;/strong&gt; Discrete, multi-modal minorities with gaps violate the interpolation assumption (DERIVED).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Practical Takeaways (Production Checklist)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;SMOTE(sampling_strategy='auto', k_neighbors=5, random_state=42)&lt;/code&gt; as the &lt;strong&gt;default baseline&lt;/strong&gt;; tune &lt;code&gt;k_neighbors&lt;/code&gt; only after baseline CV (SOURCE: imbalanced-learn defaults; DERIVED tuning order).&lt;/li&gt;
&lt;li&gt;Always wrap SMOTE in a &lt;code&gt;Pipeline&lt;/code&gt; so it fits &lt;strong&gt;per-fold&lt;/strong&gt;, never on the full set (SOURCE: imbalanced-learn Pipeline guidance).&lt;/li&gt;
&lt;li&gt;Scale features &lt;em&gt;before&lt;/em&gt; SMOTE inside the same pipeline (DERIVED).&lt;/li&gt;
&lt;li&gt;Compare against &lt;code&gt;scale_pos_weight&lt;/code&gt; in XGBoost — often cheaper and leakage-free; SMOTE earns its place when the minority geometry is genuinely learnable (DERIVED from practice; consistent with imbalanced-learn recommendations).&lt;/li&gt;
&lt;li&gt;For Nifty option rare-event labels: start with &lt;code&gt;scale_pos_weight&lt;/code&gt;, add vanilla SMOTE only inside Layer 2 CV, and watch for boundary-crossing degradation on sparse expiries (DERIVED from our two-layer discipline).&lt;/li&gt;
&lt;li&gt;If vanilla SMOTE underperforms, the &lt;em&gt;next&lt;/em&gt; article (V2 BorderlineSMOTE) shows the targeted variant that only synthesizes near the decision border.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is vanilla SMOTE the same as random oversampling?&lt;/strong&gt;&lt;br&gt;
No. Random oversampling duplicates existing minority rows; SMOTE creates &lt;em&gt;new&lt;/em&gt; points interpolated between minority neighbors (SOURCE: Chawla 2002; DERIVED comparison).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is the default &lt;code&gt;sampling_strategy&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;'auto'&lt;/code&gt;, which balances every minority class up to the majority count (SOURCE: imbalanced-learn).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What does &lt;code&gt;k_neighbors&lt;/code&gt; do?&lt;/strong&gt;&lt;br&gt;
It sets how many nearest minority neighbors each point can interpolate with; default 5 (SOURCE: imbalanced-learn default).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does &lt;code&gt;random_state&lt;/code&gt; make my model reproducible?&lt;/strong&gt;&lt;br&gt;
It makes the &lt;em&gt;synthetic samples&lt;/em&gt; reproducible. The classifier needs its own seed (DERIVED).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use SMOTE on live Nifty ticks?&lt;/strong&gt;&lt;br&gt;
No — never on live data. Our stack applies it only in Layer 2 EOD CV (two-layer engine note above; DERIVED from our discipline).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: When does SMOTE hurt?&lt;/strong&gt;&lt;br&gt;
Small/high-dimensional/noisy minorities where neighbors are unreliable (SOURCE: Blagus &amp;amp; Lusa 2013).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What comes after vanilla SMOTE?&lt;/strong&gt;&lt;br&gt;
BorderlineSMOTE (V2), then SVMSMOTE, ADASYN, KMeansSMOTE — the variant family covered in later articles.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Vanilla &lt;code&gt;SMOTE&lt;/code&gt; synthesizes minority samples as convex combinations &lt;code&gt;x_i + δ·(x_zi − x_i)&lt;/code&gt; between a point and one of its &lt;code&gt;k_neighbors&lt;/code&gt; (default 5) minority neighbors, balancing classes via &lt;code&gt;sampling_strategy='auto'&lt;/code&gt;. Unlike random oversampling (which duplicates), it invents plausible in-between points — great on dense, connected minorities, risky on sparse/high-dimensional/noisy ones. Always run it inside CV, never on live or test data.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v2-borderline"&gt;Smote V2 Borderline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v7-adasyn"&gt;Smote V7 Adasyn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v8-randomoversampler"&gt;Smote V8 Randomoversampler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources (PRIMARY)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, N.V., Bowyer, K.W., Hall, L.O., Kegelmeyer, W.P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research (JAIR), 16:321–357. — original algorithm, interpolation formula, motivation vs random oversampling.&lt;/li&gt;
&lt;li&gt;scikit-learn-contrib/imbalanced-learn (GitHub, ~7.1k★). &lt;code&gt;imblearn.over_sampling.SMOTE&lt;/code&gt; — default parameters (&lt;code&gt;sampling_strategy='auto'&lt;/code&gt;, &lt;code&gt;k_neighbors=5&lt;/code&gt;), &lt;code&gt;fit_resample&lt;/code&gt; API, Pipeline/leakage guidance, SMOTENC/SMOTEN for categoricals.&lt;/li&gt;
&lt;li&gt;Blagus, R., Lusa, L. (2013). &lt;em&gt;SMOTE for high-dimensional class-imbalanced data.&lt;/em&gt; BMC Bioinformatics. — evidence SMOTE can degrade on small/high-dimensional data.&lt;/li&gt;
&lt;li&gt;Han, H., Wang, W.-Y., Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE&lt;/em&gt; — previewed variant (V2).&lt;/li&gt;
&lt;li&gt;He, H., Bai, Y., Garcia, E.A., Li, S. (2008). &lt;em&gt;ADASYN&lt;/em&gt; — adaptive variant.&lt;/li&gt;
&lt;li&gt;Nguyen, H.M., Cooper, E.W., Kamei, K. (2011). &lt;em&gt;SVM-SMOTE&lt;/em&gt; — SVM-margin variant.&lt;/li&gt;
&lt;li&gt;KMeansSMOTE (2018, last-resort paper) — clustering-guided variant.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE engineering cluster, sibling to the XGBoost cluster. This article (V1, vanilla SMOTE) is the canonical reference for the base interpolation mechanism; later articles extend it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer (repeat):&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PREV article: S1 — The SMOTE Algorithm (intuition)&lt;/li&gt;
&lt;li&gt;NEXT article: V2 — BorderlineSMOTE&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SMOTE random_state &amp; Reproducibility: Why Seeded Synthetic Samples Matter for Auditable XGBoost Backtests</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Mon, 07 Sep 2026 04:31:20 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/smote-randomstate-reproducibility-why-seeded-synthetic-samples-matter-for-auditable-xgboost-29k0</link>
      <guid>https://dev.to/shaktitiwari/smote-randomstate-reproducibility-why-seeded-synthetic-samples-matter-for-auditable-xgboost-29k0</guid>
      <description>&lt;h1&gt;
  
  
  SMOTE &lt;code&gt;random_state&lt;/code&gt; &amp;amp; Reproducibility: Why Seeded Synthetic Samples Matter for Auditable XGBoost Backtests
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This is article S4 in the SMOTE cluster. PREV: S3 — &lt;code&gt;sampling_strategy&lt;/code&gt;. NEXT: V3 — SVMSMOTE.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;SMOTE is &lt;strong&gt;not deterministic by default&lt;/strong&gt;. For every synthetic minority sample it draws a &lt;em&gt;random&lt;/em&gt; neighbor from the k-NN set and a &lt;em&gt;random&lt;/em&gt; interpolation factor &lt;code&gt;λ ∼ Uniform(0,1)&lt;/code&gt; — both pulled from a pseudo-random number generator (SOURCE: Chawla et al. 2002, JAIR 16:321–357; SOURCE: scikit-learn-contrib/imbalanced-learn, ~7.1k★). If you do &lt;strong&gt;not&lt;/strong&gt; pass &lt;code&gt;random_state&lt;/code&gt;, the two draws come from the global NumPy RNG and change every run. Pass &lt;code&gt;random_state=&amp;lt;int&amp;gt;&lt;/code&gt; and &lt;code&gt;fit_resample&lt;/code&gt; emits the &lt;em&gt;exact same&lt;/em&gt; synthetic coordinates each time. That reproducibility is what makes a backtest auditable — and our Layer-2 training core depends on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Here is the uncomfortable truth about options trading research: a backtest you cannot reproduce is a backtest you cannot trust. Aur bhai, agar kal ki run mein 18% return dikh raha tha aur aaj 11% — and you changed &lt;em&gt;nothing&lt;/em&gt; — then your pipeline has a hidden non-determinism, and your "edge" might just be noise wearing a tuxedo.&lt;/p&gt;

&lt;p&gt;Class imbalance is everywhere in Nifty option signals. Your rare-event label — a sharp expiry-day reversal, an OTM strangle that actually pays, a VIX-spike continuation — might be 1–3% of rows. To teach XGBoost that minority, we often synthesize minority samples with SMOTE inside the training fold. That synthesis is &lt;em&gt;random&lt;/em&gt;. If the random draw is not pinned, two backtests of the &lt;strong&gt;same&lt;/strong&gt; historical window produce &lt;strong&gt;different&lt;/strong&gt; synthetic training sets, &lt;strong&gt;different&lt;/strong&gt; models, and &lt;strong&gt;different&lt;/strong&gt; reported metrics. Now tell me: which number do you show the client? Which number do you defend in an audit?&lt;/p&gt;

&lt;p&gt;This is precisely why our resampling discipline lives in Layer 2 of the engine. &lt;strong&gt;Our NSE stack is TWO-LAYER:&lt;/strong&gt; Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or &lt;code&gt;scale_pos_weight&lt;/code&gt;) is applied in Layer 2 inside CV, never on live data. Within that audited core, every &lt;code&gt;fit_resample&lt;/code&gt; call is seeded so that a reviewer can reconstruct the &lt;em&gt;exact&lt;/em&gt; training set that produced a given model and a given walk-forward metric. Reproducibility is not a nice-to-have here; it is the whole point of "audited."&lt;/p&gt;

&lt;p&gt;Understanding &lt;code&gt;random_state&lt;/code&gt; — what it controls, what it does &lt;em&gt;not&lt;/em&gt; control, and how it tangles with the global NumPy/sklearn RandomState — is the difference between a research notebook that lies silently and one that can be replayed verbatim. Let's pull it apart.&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RQ:&lt;/strong&gt; What exactly does SMOTE's &lt;code&gt;random_state&lt;/code&gt; parameter govern? How does it interact with the global NumPy/&lt;code&gt;sklearn&lt;/code&gt; RandomState? And why is a fixed seed necessary — but not &lt;em&gt;sufficient&lt;/em&gt; — for an auditable, reproducible backtest?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; Because &lt;code&gt;_make_samples&lt;/code&gt; consumes two RNG draws per synthetic point — (a) a random neighbor index and (b) a random &lt;code&gt;λ ∈ [0,1]&lt;/code&gt; — the entire synthetic set is a deterministic function of &lt;code&gt;(X_minority, k_neighbors, sampling_strategy, random_state)&lt;/code&gt;. Seeding it makes the synthetic set reproducible. But the &lt;em&gt;model&lt;/em&gt; needs its own seed, and the &lt;em&gt;global&lt;/em&gt; RNG can still leak into SMOTE if &lt;code&gt;random_state=None&lt;/code&gt;, so a fixed SMOTE seed alone does not guarantee a reproducible end-to-end pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We do &lt;strong&gt;not&lt;/strong&gt; run code in this article. The snippets below are &lt;strong&gt;illustrative only&lt;/strong&gt;, reproduced from the canonical &lt;code&gt;imbalanced-learn&lt;/code&gt; usage shape — they are NOT experiment results (SOURCE: scikit-learn-contrib/imbalanced-learn documentation). Treat them as the API contract, nothing more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Illustrative only — API shape from imbalanced-learn docs, not an experiment result.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;

&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;smote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# set by the PREV article (S3)
&lt;/span&gt;    &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;             &lt;span class="c1"&gt;# &amp;lt;-- the subject of THIS article
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The methodology we describe is the &lt;strong&gt;algorithmic specification&lt;/strong&gt; as given by Chawla et al. (2002) and as implemented in the &lt;code&gt;imbalanced-learn&lt;/code&gt; repository (SOURCE for both). We then derive, from the interpolation formula and from &lt;code&gt;sklearn&lt;/code&gt;'s documented &lt;code&gt;check_random_state&lt;/code&gt; semantics, exactly which RNG draws SMOTE makes and how a seed pins them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the randomness actually lives (SOURCE: Chawla 2002; SOURCE: imbalanced-learn &lt;code&gt;BaseSMOTE._make_samples&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Recall the synthetic-sample formula from the algorithm article (S1):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x_new = x_i + λ · (x_zi − x_i),   λ ~ U(0,1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each minority point &lt;code&gt;x_i&lt;/code&gt; that must be "grown," SMOTE must choose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A random neighbor&lt;/strong&gt; &lt;code&gt;x_zi&lt;/code&gt; — drawn from the &lt;code&gt;k_neighbors&lt;/code&gt; nearest minority neighbors of &lt;code&gt;x_i&lt;/code&gt;. This is one RNG draw (an integer index in &lt;code&gt;[0, k_neighbors)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A random interpolation factor&lt;/strong&gt; &lt;code&gt;λ&lt;/code&gt; — drawn uniformly from &lt;code&gt;[0,1]&lt;/code&gt;. This is a second RNG draw.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both draws are taken from a &lt;code&gt;numpy.random.RandomState&lt;/code&gt; object that SMOTE obtains through &lt;code&gt;sklearn.utils.check_random_state(random_state)&lt;/code&gt; (SOURCE: scikit-learn &lt;code&gt;check_random_state&lt;/code&gt; contract; SOURCE: imbalanced-learn uses this). Therefore the &lt;em&gt;sequence&lt;/em&gt; of neighbor picks and &lt;code&gt;λ&lt;/code&gt; values — and hence every synthetic coordinate — is fully determined by the seed fed into that RandomState. With &lt;code&gt;random_state=None&lt;/code&gt;, the object is the &lt;strong&gt;global&lt;/strong&gt; &lt;code&gt;np.random&lt;/code&gt; singleton, which is mutated by &lt;em&gt;every&lt;/em&gt; random call in your program (DERIVED from &lt;code&gt;check_random_state&lt;/code&gt;'s documented behaviour for the &lt;code&gt;None&lt;/code&gt; case).&lt;/p&gt;




&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;p&gt;Each finding is anchored to a primary source or derived from the formula / RNG contract above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F1 — Without &lt;code&gt;random_state&lt;/code&gt;, the synthetic set differs every run.&lt;/strong&gt; Because neighbor index and &lt;code&gt;λ&lt;/code&gt; are RNG draws, two consecutive &lt;code&gt;fit_resample&lt;/code&gt; calls with &lt;code&gt;random_state=None&lt;/code&gt; (and no intervening global seed reset) produce different synthetic points (DERIVED from the two-draw mechanism). Concretely, the &lt;em&gt;same&lt;/em&gt; minority point can be paired with a different neighbor and a different &lt;code&gt;λ&lt;/code&gt;, landing at a different coordinate (SOURCE: imbalanced-learn design; DERIVED).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F2 — With &lt;code&gt;random_state=&amp;lt;int&amp;gt;&lt;/code&gt;, the synthetic set is bit-for-bit reproducible.&lt;/strong&gt; Passing an integer creates a fresh &lt;code&gt;np.random.RandomState(seed)&lt;/code&gt; that is independent of the global stream, so repeated &lt;code&gt;fit_resample&lt;/code&gt; calls emit identical synthetics (SOURCE: &lt;code&gt;check_random_state&lt;/code&gt; int-branch; DERIVED). This is the property an audited backtest needs: same data + same seed ⇒ same training set ⇒ same model ⇒ same metric.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F3 — &lt;code&gt;random_state=None&lt;/code&gt; silently couples SMOTE to the global NumPy RNG.&lt;/strong&gt; When &lt;code&gt;random_state=None&lt;/code&gt;, &lt;code&gt;check_random_state&lt;/code&gt; returns the global &lt;code&gt;np.random&lt;/code&gt; singleton (SOURCE: sklearn contract). That means &lt;code&gt;np.random.seed(7)&lt;/code&gt; &lt;em&gt;somewhere else&lt;/em&gt; in your script shifts SMOTE's draws, and &lt;em&gt;other&lt;/em&gt; &lt;code&gt;np.random&lt;/code&gt; calls (shuffles, dropouts, initializations) consume from the same stream and displace SMOTE's draws. This is the single most common "why did my synthetic set move?" bug (DERIVED from global-singleton sharing).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F4 — Passing an int &lt;code&gt;random_state&lt;/code&gt; &lt;em&gt;decouples&lt;/em&gt; SMOTE from the global RNG.&lt;/strong&gt; Once you pass &lt;code&gt;42&lt;/code&gt;, SMOTE builds its own &lt;code&gt;RandomState(42)&lt;/code&gt; and ignores the global &lt;code&gt;np.random&lt;/code&gt; state entirely (DERIVED from &lt;code&gt;check_random_state&lt;/code&gt; int-branch). So &lt;code&gt;np.random.seed(999)&lt;/code&gt; no longer moves SMOTE's synthetics — but it &lt;em&gt;does&lt;/em&gt; still affect anything else using the global stream. Mixing the two mental models is where teams get confused (DERIVED).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F5 — &lt;code&gt;random_state&lt;/code&gt; reproduces the &lt;em&gt;synthetics&lt;/em&gt;, not the &lt;em&gt;model&lt;/em&gt;.&lt;/strong&gt; The classifier (XGBoost/LightGBM/RF) has its own seed. A seeded SMOTE gives you a reproducible training set; the model trained on it still needs its own &lt;code&gt;random_state&lt;/code&gt;/&lt;code&gt;seed&lt;/code&gt; for a reproducible model (DERIVED; SOURCE-adjacent: every imbalanced-learn SMOTE+Pipeline example seeds &lt;em&gt;both&lt;/em&gt; SMOTE and the estimator).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;F6 — Reproducibility ≠ validity.&lt;/strong&gt; A perfectly reproducible synthetic set can still be a &lt;em&gt;bad&lt;/em&gt; synthetic set (e.g. boundary-crossing points on sparse expiries). Seeding guarantees you can &lt;em&gt;replay&lt;/em&gt; the mistake, not that the mistake is small (DERIVED; consistent with Blagus &amp;amp; Lusa 2013's caution that SMOTE can hurt on small/high-dimensional data — SOURCE).&lt;/p&gt;




&lt;h2&gt;
  
  
  Reproducibility (code shape — illustrative)
&lt;/h2&gt;

&lt;p&gt;As stated up top, the following is the canonical &lt;code&gt;imbalanced-learn&lt;/code&gt; call shape and is shown for reproducibility of &lt;em&gt;API usage&lt;/em&gt;, not as a claim that we executed a model here. The pattern below is what we actually wire into Layer-2 walk-forward CV (SOURCE: imbalanced-learn Pipeline + SMOTE examples).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## Illustrative only — shows where random_state sits: on BOTH SMOTE and the estimator, INSIDE CV.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xgboost&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XGBClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StratifiedKFold&lt;/span&gt;

&lt;span class="n"&gt;SEED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;   &lt;span class="c1"&gt;# ONE canonical seed, stored in config, version-controlled.
&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;smote&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="nc"&gt;XGBClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;eval_metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;logloss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;skf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StratifiedKFold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_splits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SEED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;## pipe.fit(X_train_fold, y_train_fold) -&amp;gt; scored on the untouched validation fold
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The golden rule, repeated because people violate it constantly: seed &lt;strong&gt;everything&lt;/strong&gt; that touches the random stream — SMOTE, the splitter, &lt;em&gt;and&lt;/em&gt; the estimator — and store that seed in version control next to the data snapshot. A backtest is only auditable if a reviewer can re-run it and land on the same number (DERIVED from CV hygiene; echoed in imbalanced-learn "avoid leakage" guidance — SOURCE).&lt;/p&gt;




&lt;h2&gt;
  
  
  Worked Example: Same Data, Two Seeds, Two Synthetic Sets (DERIVED math)
&lt;/h2&gt;

&lt;p&gt;Let's make the seed-dependence concrete in two dimensions. Take one minority point &lt;code&gt;A = (2, 3)&lt;/code&gt;. Its &lt;code&gt;k_neighbors=2&lt;/code&gt; minority neighbors are &lt;code&gt;B = (4, 7)&lt;/code&gt; and &lt;code&gt;C = (1, 5)&lt;/code&gt;, so the candidate segment set is &lt;code&gt;AB&lt;/code&gt; and &lt;code&gt;AC&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;SMOTE makes two draws per synthetic: (neighbor index ∈ {0,1}, &lt;code&gt;λ ∼ U(0,1)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run 1 — &lt;code&gt;random_state=42&lt;/code&gt;:&lt;/strong&gt; Suppose the RNG yields (neighbor=0 → B, &lt;code&gt;λ=0.25&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x_new = A + 0.25·(B − A) = (2,3) + 0.25·(2,4) = (2.50, 4.00)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run 2 — &lt;code&gt;random_state=7&lt;/code&gt;:&lt;/strong&gt; Suppose the RNG yields (neighbor=1 → C, &lt;code&gt;λ=0.80&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x_new = A + 0.80·(C − A) = (2,3) + 0.80·(−1,2) = (1.20, 4.60)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two different seeds ⇒ two different neighbors ⇒ two different &lt;code&gt;λ&lt;/code&gt; ⇒ two synthetic points that are not even on the same segment. Multiply this by hundreds of minority points and you get &lt;strong&gt;entirely different synthetic clouds&lt;/strong&gt; from the same real minority set (DERIVED). This is exactly why an unseeded SMOTE poisons backtest comparability: you are not comparing models, you are comparing random draws.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key nuance (DERIVED):&lt;/strong&gt; the &lt;em&gt;real&lt;/em&gt; minority points &lt;code&gt;A, B, C&lt;/code&gt; never change — only the invented ones do. So unseeded SMOTE doesn't alter your ground truth; it alters the &lt;em&gt;training fiction&lt;/em&gt; you built on top of it, and that fiction is what your model memorizes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;Seeding SMOTE is necessary, but several things still break end-to-end reproducibility even when &lt;code&gt;random_state&lt;/code&gt; is set. Be honest about these:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C1 — Global &lt;code&gt;np.random&lt;/code&gt; leakage via &lt;code&gt;random_state=None&lt;/code&gt;.&lt;/strong&gt; If any colleague "cleans up" the code and removes &lt;code&gt;random_state&lt;/code&gt; (thinking the global &lt;code&gt;np.random.seed()&lt;/code&gt; call covers it), SMOTE rejoins the global stream and the synthetic set drifts again (DERIVED from F3/F4). We have seen this exact regression in shared notebooks (SOURCE-adjacent: a well-known failure mode in imbalanced-learn GitHub issues).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C2 — The estimator's own non-determinism dominates.&lt;/strong&gt; Even with a seeded SMOTE, XGBoost on GPU or with certain histogram threading can return slightly different trees across runs; LightGBM's feature-parallel splits can, too. So "I seeded SMOTE, why isn't the metric identical?" — because the &lt;em&gt;model&lt;/em&gt; seed or hardware threading moved (DERIVED from XGBoost/LightGBM known non-determinism on parallel/GPU paths). SMOTE seeding is necessary but not sufficient (DERIVED from F5).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C3 — Library version drift changes the RNG stream.&lt;/strong&gt; The exact sequence of draws produced by a given integer seed depends on the implementation of the sampler and the RNG backend. A different &lt;code&gt;imbalanced-learn&lt;/code&gt; / &lt;code&gt;scikit-learn&lt;/code&gt; / &lt;code&gt;numpy&lt;/code&gt; version can emit a &lt;em&gt;different&lt;/em&gt; synthetic set for the &lt;em&gt;same&lt;/em&gt; &lt;code&gt;random_state&lt;/code&gt; (DERIVED from version-sensitive RNG implementations; SOURCE-adjacent: sklearn's documented RNG/Generator migration notes). Pin your versions — reproducibility includes the software, not just the seed (DERIVED engineering principle).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C4 — Different seed &lt;em&gt;per fold&lt;/em&gt; hides instability.&lt;/strong&gt; Some teams reseed SMOTE per CV fold "to get more data." That means each fold trains on a different synthetic cloud, so fold metrics are not comparable across reruns and the variance of the synthetic step is masked (DERIVED). For auditability you want the &lt;em&gt;same&lt;/em&gt; seed every fold, every run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C5 — Pickling SMOTE without recording the seed.&lt;/strong&gt; If you &lt;code&gt;joblib.dump&lt;/code&gt; a fitted SMOTE and later &lt;code&gt;fit_resample&lt;/code&gt; a fresh instance without the seed, you get a new synthetic set (DERIVED from F2). Store the seed alongside the artifact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Seeding controls randomness, not correctness.&lt;/strong&gt; A reproducible bad synthetic set is still bad (see F6; SOURCE: Blagus &amp;amp; Lusa 2013 on SMOTE's limits on small/high-dimensional data).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;random_state=None&lt;/code&gt; is the default trap.&lt;/strong&gt; If you instantiate &lt;code&gt;SMOTE()&lt;/code&gt; with no seed, you opted into global-stream coupling (DERIVED from defaults; SOURCE: imbalanced-learn default &lt;code&gt;random_state=None&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global-singleton sharing is invisible.&lt;/strong&gt; Nothing in the API warns you that &lt;code&gt;random_state=None&lt;/code&gt; shares &lt;code&gt;np.random&lt;/code&gt; with every other random call (DERIVED from &lt;code&gt;check_random_state&lt;/code&gt; semantics).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallelism can reintroduce non-determinism&lt;/strong&gt; in the &lt;em&gt;downstream model&lt;/em&gt;, even when SMOTE is perfectly seeded (see C2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version sensitivity.&lt;/strong&gt; Same seed, different library version ⇒ different synthetics (C3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assumes you actually re-run on the same &lt;code&gt;X&lt;/code&gt;/&lt;code&gt;y&lt;/code&gt; snapshot.&lt;/strong&gt; Reproducibility of synthetics is moot if the input data itself changed between runs (DERIVED — data versioning is part of the audit).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Practical Takeaways (Production Checklist)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always pass &lt;code&gt;random_state=&amp;lt;int&amp;gt;&lt;/code&gt; to SMOTE.&lt;/strong&gt; Never leave it &lt;code&gt;None&lt;/code&gt; in a research or production pipeline (SOURCE: imbalanced-learn API; DERIVED from F1/F3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick ONE canonical seed&lt;/strong&gt; (we use &lt;code&gt;42&lt;/code&gt; as a documented default) and store it in config, version-controlled next to the data snapshot (DERIVED from audit discipline).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seed the estimator too.&lt;/strong&gt; XGBoost &lt;code&gt;random_state&lt;/code&gt;, the &lt;code&gt;StratifiedKFold&lt;/code&gt; &lt;code&gt;random_state&lt;/code&gt;, &lt;em&gt;and&lt;/em&gt; SMOTE's — all three (DERIVED from F5/C2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrap SMOTE in a &lt;code&gt;Pipeline&lt;/code&gt;&lt;/strong&gt; so it fits &lt;strong&gt;per-fold&lt;/strong&gt;, never on the full set, and the same seed applies consistently inside CV (SOURCE: imbalanced-learn Pipeline guidance).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't rely on &lt;code&gt;np.random.seed()&lt;/code&gt; to control SMOTE&lt;/strong&gt; — pass the int. Global seeding is fragile once any other random call exists (DERIVED from F3/F4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin library versions&lt;/strong&gt; (&lt;code&gt;imbalanced-learn&lt;/code&gt;, &lt;code&gt;scikit-learn&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt;, &lt;code&gt;xgboost&lt;/code&gt;) in your environment lockfile; reproducibility includes the software (DERIVED from C3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For Nifty option rare-event labels:&lt;/strong&gt; apply SMOTE only inside Layer-2 EOD walk-forward CV, seed it, and compare against a &lt;code&gt;scale_pos_weight&lt;/code&gt; baseline that is inherently seed-free (DERIVED from our two-layer discipline). When the minority is genuinely sparse, &lt;code&gt;scale_pos_weight&lt;/code&gt; is often the more auditable choice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log the seed with every backtest run.&lt;/strong&gt; An auditable run is (data hash, code hash, library versions, seed) → metric. Miss any one and you can't replay it (DERIVED).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Does SMOTE need &lt;code&gt;random_state&lt;/code&gt; to work?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It works without it — but produces a &lt;em&gt;different&lt;/em&gt; synthetic set every run. For any reproducible research or backtest, set it (SOURCE: imbalanced-learn; DERIVED from F1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What does &lt;code&gt;random_state&lt;/code&gt; actually control in SMOTE?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The random neighbor index and the random &lt;code&gt;λ ∈ [0,1]&lt;/code&gt; used in &lt;code&gt;x_i + λ·(x_zi − x_i)&lt;/code&gt; — i.e. which synthetic points get made (SOURCE: Chawla 2002; DERIVED from &lt;code&gt;_make_samples&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: If I set &lt;code&gt;np.random.seed(42)&lt;/code&gt;, is SMOTE reproducible?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Only if &lt;code&gt;random_state=None&lt;/code&gt; — and even then, any &lt;em&gt;other&lt;/em&gt; &lt;code&gt;np.random&lt;/code&gt; call shifts SMOTE's draws. Pass &lt;code&gt;random_state=42&lt;/code&gt; to SMOTE directly; that decouples it from the global stream (DERIVED from F3/F4).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does seeding SMOTE make my XGBoost model reproducible?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No. It makes the &lt;em&gt;training set&lt;/em&gt; reproducible. The model needs its own seed (F5).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why does reproducibility matter for trading backtests?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Because an auditable backtest must be replayable: same data + same seed + same code + same versions ⇒ same number. Otherwise you can't defend or compare results (DERIVED from our Layer-2 audit discipline).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can two seeds give totally different synthetic clouds?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes — different neighbor picks and &lt;code&gt;λ&lt;/code&gt; values put synthetic points on different segments (see Worked Example). Same real minority, different fiction (DERIVED).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Is &lt;code&gt;random_state&lt;/code&gt; the same across SMOTE variants (SVMSMOTE, Borderline, ADASYN)?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes, they all inherit the same &lt;code&gt;check_random_state&lt;/code&gt; contract, so the seeding principle is identical; only the &lt;em&gt;selection&lt;/em&gt; of points differs (SOURCE: imbalanced-learn; DERIVED). The NEXT article (V3 SVMSMOTE) covers that variant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use SMOTE on live Nifty ticks?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No — never on live data. Our stack applies it only in Layer-2 EOD CV (two-layer engine note above).&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;SMOTE draws a random neighbor and a random &lt;code&gt;λ∼U(0,1)&lt;/code&gt; per synthetic sample, so without &lt;code&gt;random_state&lt;/code&gt; the synthetic set changes every run. Pass &lt;code&gt;random_state=&amp;lt;int&amp;gt;&lt;/code&gt; to make it bit-for-bit reproducible — essential for auditable Layer-2 backtests. But note: &lt;code&gt;random_state=None&lt;/code&gt; silently couples SMOTE to the global NumPy RNG, an int seed decouples it, and the &lt;em&gt;model&lt;/em&gt; still needs its own seed. Seeding is necessary, not sufficient: pin library versions, seed the splitter and estimator too, and log the seed with every run. Source: Chawla et al. 2002 + imbalanced-learn.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-s1-algorithm"&gt;Smote S1 Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-p1-leakage-trap"&gt;Smote P1 Leakage Trap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources (PRIMARY)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, N.V., Bowyer, K.W., Hall, L.O., Kegelmeyer, W.P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research (JAIR), 16:321–357. — original algorithm; interpolation formula &lt;code&gt;x_new = x_i + λ·(x_zi − x_i)&lt;/code&gt; with &lt;code&gt;λ&lt;/code&gt; drawn randomly; motivation vs random oversampling.&lt;/li&gt;
&lt;li&gt;scikit-learn-contrib/imbalanced-learn (GitHub, ~7.1k★). &lt;code&gt;imblearn.over_sampling.SMOTE&lt;/code&gt; — &lt;code&gt;random_state&lt;/code&gt; parameter (default &lt;code&gt;None&lt;/code&gt;), &lt;code&gt;fit_resample&lt;/code&gt; API, &lt;code&gt;BaseSMOTE._make_samples&lt;/code&gt; neighbour/λ draws, Pipeline/leakage guidance, SVMSMOTE/BorderlineSMOTE/ADASYN/KMeansSMOTE variants sharing the same &lt;code&gt;check_random_state&lt;/code&gt; contract.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scikit-learn&lt;/code&gt; &lt;code&gt;sklearn.utils.check_random_state&lt;/code&gt; contract — &lt;code&gt;None&lt;/code&gt; ⇒ global &lt;code&gt;np.random&lt;/code&gt; singleton; &lt;code&gt;int&lt;/code&gt; ⇒ &lt;code&gt;np.random.RandomState(int)&lt;/code&gt;; &lt;code&gt;RandomState&lt;/code&gt; instance ⇒ used as-is. (SOURCE: scikit-learn API docs; DERIVED behaviour described above.)&lt;/li&gt;
&lt;li&gt;Blagus, R., Lusa, L. (2013). &lt;em&gt;SMOTE for high-dimensional class-imbalanced data.&lt;/em&gt; BMC Bioinformatics. — evidence SMOTE can degrade on small/high-dimensional data (reproducibility does not imply validity; F6/C1 context).&lt;/li&gt;
&lt;li&gt;Han, H., Wang, W.-Y., Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE&lt;/em&gt; — boundary-aware variant (previewed; NEXT family).&lt;/li&gt;
&lt;li&gt;He, H., Bai, Y., Garcia, E.A., Li, S. (2008). &lt;em&gt;ADASYN&lt;/em&gt; — adaptive variant sharing the same seeding contract.&lt;/li&gt;
&lt;li&gt;Nguyen, H.M., Cooper, E.W., Kamei, K. (2011). &lt;em&gt;SVM-SMOTE&lt;/em&gt; — SVM-margin variant (NEXT: V3).&lt;/li&gt;
&lt;li&gt;KMeansSMOTE (2018, last-resort paper) — clustering-guided variant.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written for &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt; (optiontradingwithai.in). Part of the SMOTE engineering cluster, sibling to the XGBoost cluster. This article (S4, &lt;code&gt;random_state&lt;/code&gt; &amp;amp; reproducibility) is the canonical reference for seeding SMOTE and for understanding its interaction with the global NumPy/sklearn RandomState inside an auditable Layer-2 backtest. The PREV article (S3, &lt;code&gt;sampling_strategy&lt;/code&gt;) covers &lt;em&gt;how many&lt;/em&gt; synthetics to make; the NEXT article (V3, SVMSMOTE) covers &lt;em&gt;which&lt;/em&gt; points the variant synthesizes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer (repeat):&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PREV article: S3 — &lt;code&gt;sampling_strategy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;NEXT article: V3 — SVMSMOTE&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SMOTE `sampling_strategy`: How to Set the Oversampling Ratio Without Drowning Your Majority Class</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Mon, 07 Sep 2026 04:30:59 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/smote-samplingstrategy-how-to-set-the-oversampling-ratio-without-drowning-your-majority-class-34j7</link>
      <guid>https://dev.to/shaktitiwari/smote-samplingstrategy-how-to-set-the-oversampling-ratio-without-drowning-your-majority-class-34j7</guid>
      <description>&lt;h1&gt;
  
  
  SMOTE &lt;code&gt;sampling_strategy&lt;/code&gt;: How to Set the Oversampling Ratio Without Drowning Your Majority Class
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;A practical guide for Nifty option traders and XGBoost practitioners — by Shakti Tiwari&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;SMOTE's &lt;code&gt;sampling_strategy&lt;/code&gt; decides &lt;strong&gt;how much&lt;/strong&gt; of the minority class gets synthesized. A string like &lt;code&gt;'not majority'&lt;/code&gt; (the older &lt;code&gt;'auto'&lt;/code&gt;) forces a 1:1 balance; a float such as &lt;code&gt;0.5&lt;/code&gt; makes the minority half the size of the majority; a &lt;code&gt;dict&lt;/code&gt; lets you pin exact per-class counts; and &lt;code&gt;'minority'&lt;/code&gt;/&lt;code&gt;'all'&lt;/code&gt; control &lt;em&gt;which&lt;/em&gt; classes get touched. For rare-event models — like Nifty's rare big-move days — a forced 1:1 usually over-injects synthetic noise and hurts the real signal, so a moderate ratio (1:2 or 1:3) typically wins. [SOURCE: imbalanced-learn docs; DERIVED]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer (verbatim):&lt;/strong&gt; &lt;em&gt;Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Class imbalance is the silent killer of trading models. In Nifty option data, the class you actually care about — a genuinely big directional move that makes a straddle/strangle payoff — shows up maybe 3–8% of the time. The "nothing happened" days dominate. [DERIVED from typical Nifty daily-range distributions] If you hand that raw distribution to XGBoost, the tree will happily learn to predict "no move" on every row and still score 94% accuracy. Useless. You've built a coin that's right because the market is boring, not because you're smart.&lt;/p&gt;

&lt;p&gt;SMOTE (Synthetic Minority Over-sampling Technique) fixes this by manufacturing new minority samples in feature space instead of copying existing rows (that would just overfit). [SOURCE: Chawla et al. 2002] But here's the trap most tutorials skip: &lt;strong&gt;SMOTE has a dial, and leaving it at the default is often wrong.&lt;/strong&gt; That dial is &lt;code&gt;sampling_strategy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The default behaviour in imbalanced-learn oversamples the minority class until it &lt;em&gt;matches&lt;/em&gt; the majority class — a perfect 1:1 ratio. That sounds fair. It's usually not. When the true prior is 5% minority, forcing 50% minority teaches your model a world that doesn't exist, and it starts crying wolf on quiet days. For an option seller, that's a margin-eating disaster. [DERIVED]&lt;/p&gt;

&lt;p&gt;This article is the third in our SMOTE deep-dive series. We already covered &lt;code&gt;k_neighbors&lt;/code&gt; (S2). Here we tear apart &lt;code&gt;sampling_strategy&lt;/code&gt; — every option, the math, and how to pick a ratio that survives walk-forward, not just a tidy notebook. [SOURCE: imbalanced-learn docs]&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; For a binary Nifty "big-move vs no-big-move" classifier, what &lt;code&gt;sampling_strategy&lt;/code&gt; value maximizes out-of-sample recall and F1 without inflating false positives?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis:&lt;/strong&gt; A &lt;em&gt;moderate&lt;/em&gt; oversampling ratio (minority-to-majority ≈ 0.3–0.5, i.e. 1:3 to 1:2) will beat forced 1:1 (&lt;code&gt;'not majority'&lt;/code&gt;), because 1:1 (a) drowns the majority signal, (b) stacks synthetic density in borderline regions where classes overlap, and (c) shifts the effective class prior far from reality. [HYPOTHESIS; DERIVED]&lt;/p&gt;

&lt;p&gt;Note: this is a hypothesis framed for walk-forward testing inside our engine. The numeric examples below are &lt;strong&gt;derived illustrations&lt;/strong&gt;, not experiment results — I did not execute code in writing this piece. The canonical snippet in the Reproducibility section is taken from the imbalanced-learn documentation shape and is shown for structure only. [SOURCE: imbalanced-learn docs]&lt;/p&gt;




&lt;h2&gt;
  
  
  Data &amp;amp; Methodology (the Nifty angle)
&lt;/h2&gt;

&lt;p&gt;Picture a realistic daily feature set for Nifty50:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Features (X):&lt;/strong&gt; previous-day range, ATM implied volatility, PCR (put-call ratio), VIX, overnight gap, RSI(14), distance-to-spot of nearest strike, session volume z-score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target (y):&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt; = big move day (|Nifty close − open| &amp;gt; 1.5× trailing 20-day ATR); &lt;code&gt;0&lt;/code&gt; = quiet day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical year gives roughly &lt;strong&gt;9500 quiet days&lt;/strong&gt; (class &lt;code&gt;0&lt;/code&gt;, the majority) and &lt;strong&gt;500 big-move days&lt;/strong&gt; (class &lt;code&gt;1&lt;/code&gt;, the minority) across the historical window we study. That's a ~5% prior. [DERIVED illustration, not a measured dataset]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two-layer engine note (must-read for reproducibility):&lt;/strong&gt; Our NSE stack is &lt;strong&gt;TWO-LAYER&lt;/strong&gt;: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or &lt;code&gt;scale_pos_weight&lt;/code&gt;) is applied in &lt;strong&gt;Layer 2 inside CV&lt;/strong&gt;, never on live data. SMOTE only ever touches the training fold; the validation fold stays raw so we measure honest performance on the true distribution. [ARCHITECTURE NOTE — internal stack]&lt;/p&gt;

&lt;p&gt;This matters enormously for &lt;code&gt;sampling_strategy&lt;/code&gt;: the ratio you choose is a &lt;strong&gt;hyperparameter tuned by cross-validation&lt;/strong&gt;, not a one-time setting. More on that in Practical Takeaways.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;sampling_strategy&lt;/code&gt; Parameter: Every Option Explained
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sampling_strategy&lt;/code&gt; accepts four shapes: &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, or &lt;code&gt;callable&lt;/code&gt;. Let's go through each. [SOURCE: imbalanced-learn docs]&lt;/p&gt;

&lt;h3&gt;
  
  
  1. String options
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'not majority'&lt;/code&gt;&lt;/strong&gt; — resample &lt;em&gt;every class except the majority class&lt;/em&gt; up to the majority's count. For a binary problem this means minority → majority size, i.e. &lt;strong&gt;1:1 balance&lt;/strong&gt;. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'auto'&lt;/code&gt;&lt;/strong&gt; — historically the default; for over-sampling it is equivalent to &lt;code&gt;'not majority'&lt;/code&gt;. &lt;strong&gt;Deprecated since imbalanced-learn 0.9+&lt;/strong&gt; in favour of &lt;code&gt;'not majority'&lt;/code&gt;. If you see &lt;code&gt;'auto'&lt;/code&gt; in old code, it means 1:1. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'minority'&lt;/code&gt;&lt;/strong&gt; — resample &lt;em&gt;only the single minority class&lt;/em&gt; (to majority size). In binary classification this yields the same 1:1 result as &lt;code&gt;'not majority'&lt;/code&gt;, but in &lt;strong&gt;multiclass&lt;/strong&gt; it differs: &lt;code&gt;'minority'&lt;/code&gt; balances only the smallest class, while &lt;code&gt;'not majority'&lt;/code&gt; balances &lt;em&gt;all&lt;/em&gt; minority classes. [SOURCE: imbalanced-learn docs; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'all'&lt;/code&gt;&lt;/strong&gt; — resample all classes. In binary this again collapses to 1:1; it's more meaningful in multiclass setups. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'not minority'&lt;/code&gt;&lt;/strong&gt; — resample everything &lt;em&gt;except&lt;/em&gt; the minority. This is primarily an &lt;strong&gt;under-sampling&lt;/strong&gt; notion; you'll rarely use it with SMOTE over-sampling. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; the string options are coarse. They're either "balance everything to 1:1" or "balance a subset to 1:1." They give you &lt;strong&gt;no control over the degree&lt;/strong&gt; of oversampling. That's why the float and dict forms exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Float ratio (the fine dial)
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;sampling_strategy&lt;/code&gt; is a &lt;strong&gt;float between 0 and 1&lt;/strong&gt;, it specifies the &lt;strong&gt;desired ratio of minority samples to majority samples after resampling&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;α = N_minority(resampled) / N_majority(original)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0.5&lt;/code&gt; → minority becomes &lt;strong&gt;half&lt;/strong&gt; the majority (1:2). [SOURCE: imbalanced-learn docs; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0.33&lt;/code&gt; → minority becomes a third of the majority (1:3).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1.0&lt;/code&gt; → 1:1 (same as &lt;code&gt;'not majority'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0.1&lt;/code&gt; → minority is just 10% of majority (gentle nudge).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical constraint:&lt;/strong&gt; the float form is &lt;strong&gt;only valid for binary classification&lt;/strong&gt;. For multiclass you must use a &lt;code&gt;dict&lt;/code&gt; (next). [SOURCE: imbalanced-learn docs]&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dict (per-class targets — full control)
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;dict&lt;/code&gt; maps each &lt;strong&gt;class label&lt;/strong&gt; to the &lt;strong&gt;exact number of samples you want after resampling&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sampling_strategy = {1: 2000}   # make the big-move class (label 1) have 2000 samples
sampling_strategy = {0: 9500, 1: 3000}  # explicit, both classes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The keys are class labels; the values are desired &lt;em&gt;counts&lt;/em&gt;, not ratios. [SOURCE: imbalanced-learn docs] This is the most explicit form and the one I recommend once you've grid-searched a good target count. It also works for &lt;strong&gt;multiclass&lt;/strong&gt; imbalance, where you might want class A at 4000 and class B at 2500 independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Callable (advanced)
&lt;/h3&gt;

&lt;p&gt;A callable receives the &lt;code&gt;y&lt;/code&gt; array and must return a &lt;code&gt;dict&lt;/code&gt; of target counts. Useful for programmatic, data-dependent logic (e.g. "oversample each minority class to 60% of the largest"). [SOURCE: imbalanced-learn docs] Most traders never need this; the float and dict cover 99% of cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results / Findings (worked numerical example)
&lt;/h2&gt;

&lt;p&gt;Let's make the math concrete with our Nifty illustration: &lt;strong&gt;N_majority = 9500&lt;/strong&gt;, &lt;strong&gt;N_minority = 500&lt;/strong&gt;. Watch how the synthetic count explodes as the ratio approaches 1:1. [DERIVED — illustrative arithmetic, not measured]&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;sampling_strategy&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Minority after&lt;/th&gt;
&lt;th&gt;Synthetic added&lt;/th&gt;
&lt;th&gt;Total rows&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;'not majority'&lt;/code&gt; (1:1)&lt;/td&gt;
&lt;td&gt;9500&lt;/td&gt;
&lt;td&gt;9000&lt;/td&gt;
&lt;td&gt;19000&lt;/td&gt;
&lt;td&gt;1:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4750&lt;/td&gt;
&lt;td&gt;4250&lt;/td&gt;
&lt;td&gt;14250&lt;/td&gt;
&lt;td&gt;1:2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.33&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3135&lt;/td&gt;
&lt;td&gt;2635&lt;/td&gt;
&lt;td&gt;12635&lt;/td&gt;
&lt;td&gt;1:3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1900&lt;/td&gt;
&lt;td&gt;1400&lt;/td&gt;
&lt;td&gt;11400&lt;/td&gt;
&lt;td&gt;1:5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{1: 2000}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2000&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;td&gt;11500&lt;/td&gt;
&lt;td&gt;~1:4.75&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;[DERIVED from α = N_min/N_maj; rounding applied]&lt;/p&gt;

&lt;p&gt;Two things jump out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;1:1 adds 9000 synthetic points — 18× the real minority count.&lt;/strong&gt; You are now trusting generated data more than real data. Chawla et al. (2002) originally demonstrated SMOTE by oversampling the minority to about &lt;strong&gt;2× its original size&lt;/strong&gt; in their experiments — not 18×. Pushing far past that dilutes the genuine minority geometry. [SOURCE: Chawla et al. 2002; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A 1:2 or 1:3 ratio adds a &lt;em&gt;fraction&lt;/em&gt; of that synthetic volume&lt;/strong&gt; while still giving the learner enough minority mass to find a boundary. You get the benefit (the tree actually splits on the rare class) without the cost (a synthetic fog covering half your feature space).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why 1:1 oversampling often hurts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It drowns the majority signal.&lt;/strong&gt; At 1:1 the model's training prior is 50/50, but reality is 5/95. The classifier is implicitly biased toward crying "big move!" — raising false positives. In options, that means legging into trades on quiet days and bleeding theta. [DERIVED from class-prior shift]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It over-packs the borderline region.&lt;/strong&gt; SMOTE creates each synthetic point on the line segment between two real minority points (plus small Gaussian jitter on the &lt;code&gt;k_neighbors&lt;/code&gt;). At 18× density, those segments tile the &lt;em&gt;entire&lt;/em&gt; convex hull of the minority region — including zones that overlap the majority class. The boundary gets fuzzy and overfit. [SOURCE: Chawla et al. 2002; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It wastes the very signal that makes the majority useful.&lt;/strong&gt; The quiet days aren't noise; they carry the "normal regime" shape. Flattening their relative weight teaches the model to under-weight the regime it will actually live in 95% of the time. [DERIVED]&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why moderate ratios (1:2, 1:3) are usually better
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;They preserve skew awareness.&lt;/strong&gt; At 1:3 the model still "knows" big moves are rare, so it doesn't casually flag quiet days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less synthetic density = less boundary overfit.&lt;/strong&gt; Fewer interpolated points means each synthetic sample carries more marginal information. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They're closer to the original SMOTE intent.&lt;/strong&gt; Chawla's experiments oversampled modestly; the technique was never designed for 19× inflation. [SOURCE: Chawla et al. 2002]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Independent critiques agree caution is warranted.&lt;/strong&gt; Blagus &amp;amp; Lusa (2013) showed SMOTE can &lt;em&gt;degrade&lt;/em&gt; performance on small-sample and high-dimensional datasets — exactly the regime where aggressive oversampling amplifies noise. A gentler ratio is a cheaper, safer first move. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to choose (decision flow)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start moderate, not maximal.&lt;/strong&gt; For Nifty big-move models, begin at &lt;code&gt;sampling_strategy = 0.3&lt;/code&gt; to &lt;code&gt;0.5&lt;/code&gt; (1:3 to 1:2). [DERIVED recommendation]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grid-search the ratio inside CV.&lt;/strong&gt; Try &lt;code&gt;{0.1, 0.2, 0.3, 0.5, 1.0}&lt;/code&gt; and rank by &lt;strong&gt;PR-AUC / F1&lt;/strong&gt;, never raw accuracy (accuracy rewards the lazy "always quiet" model). [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider &lt;code&gt;scale_pos_weight&lt;/code&gt; as an alternative.&lt;/strong&gt; XGBoost natively reweights the minority gradient via &lt;code&gt;scale_pos_weight = N_majority/N_minority&lt;/code&gt; (~19 here). SMOTE changes the &lt;em&gt;data&lt;/em&gt;; &lt;code&gt;scale_pos_weight&lt;/code&gt; changes the &lt;em&gt;loss&lt;/em&gt;. They're not identical — SMOTE adds synthetic feature-space points (helping non-tree models and boundary geometry), while &lt;code&gt;scale_pos_weight&lt;/code&gt; only reweights (cleaner, no synthetic leakage risk). Many production stacks use &lt;code&gt;scale_pos_weight&lt;/code&gt; as the primary lever and SMOTE only when the minority is catastrophically small. [DERIVED; SOURCE: XGBoost docs convention]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock the winner as a &lt;code&gt;dict&lt;/code&gt;&lt;/strong&gt; once CV settles, e.g. &lt;code&gt;{1: 3000}&lt;/code&gt;, so the count is explicit and reproducible across retrains. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For multiclass&lt;/strong&gt; (e.g. up-move / down-move / flat), skip the float — use a &lt;code&gt;dict&lt;/code&gt; like &lt;code&gt;{0: 6000, 1: 4000, 2: 5000}&lt;/code&gt;. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Reproducibility (code shape — illustrative only)
&lt;/h2&gt;

&lt;p&gt;The snippet below follows the &lt;strong&gt;imbalanced-learn API exactly&lt;/strong&gt;. It is reproduced from the library's documented usage pattern and is &lt;strong&gt;illustrative — I did not execute it&lt;/strong&gt;, and no experiment results are claimed from it. [SOURCE: imbalanced-learn docs; NOT RUN]&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StratifiedKFold&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xgboost&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XGBClassifier&lt;/span&gt;

&lt;span class="c1"&gt;## Illustrative only — not executed in this article.
## SMOTE must live INSIDE the CV pipeline so the validation fold stays raw.
&lt;/span&gt;&lt;span class="n"&gt;smote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# minority -&amp;gt; half of majority (1:2)
&lt;/span&gt;    &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# see S2: k_neighbors
&lt;/span&gt;    &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;          &lt;span class="c1"&gt;# see S4: random_state
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;smote&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xgb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;XGBClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_estimators&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;scale_pos_weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# or tune jointly
&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;cv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StratifiedKFold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_splits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;## scores = cross_val_score(pipe, X, y, cv=cv, scoring="f1")
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points that keep this honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fit_resample(X_train, y_train)&lt;/code&gt; is the canonical call; here it's wrapped in a &lt;code&gt;Pipeline&lt;/code&gt; so it only fires on training folds. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy=0.5&lt;/code&gt; is the float form (binary only). [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k_neighbors&lt;/code&gt; and &lt;code&gt;random_state&lt;/code&gt; are the neighbouring SMOTE dials (S2 and S4 of this series). [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;Being honest about where this advice bends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1:1 is not &lt;em&gt;always&lt;/em&gt; wrong.&lt;/strong&gt; On very large datasets with strong regularization (shallow &lt;code&gt;max_depth&lt;/code&gt;, high &lt;code&gt;min_child_weight&lt;/code&gt;), a 1:1 balance can be fine because the model can't overfit the synthetic fog. The "1:1 hurts" claim is a &lt;em&gt;default expectation&lt;/em&gt;, not a law. [DERIVED caveat]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMOTE before the train/test split = leakage.&lt;/strong&gt; If you resample the whole dataset and &lt;em&gt;then&lt;/em&gt; split, synthetic points leak into the test set and your metrics lie. Always wrap SMOTE in the CV pipeline (as above). This is the #1 mistake I see in trading notebooks. [SOURCE: imbalanced-learn user guide; DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMOTE can hurt on small/high-dimensional data.&lt;/strong&gt; Blagus &amp;amp; Lusa (2013) document cases where SMOTE degrades generalization — usually when the minority is tiny and features are noisy. In that regime, prefer &lt;code&gt;scale_pos_weight&lt;/code&gt; or ADASYN/Borderline-SMOTE over vanilla SMOTE. [SOURCE: Blagus &amp;amp; Lusa 2013]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Borderline failure mode.&lt;/strong&gt; When minority and majority heavily overlap, vanilla SMOTE will synthesize points &lt;em&gt;inside&lt;/em&gt; the majority region, creating label-noise. That's the motivation for Borderline-SMOTE (Han, Wang &amp;amp; Mao 2005) and ADASYN (He et al. 2008), which focus synthesis near the decision boundary or on hard minority regions. [SOURCE: Han et al. 2005; He et al. 2008]&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Float form is binary-only.&lt;/strong&gt; For multiclass Nifty regimes you must use a &lt;code&gt;dict&lt;/code&gt;. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dict requires you to know target counts&lt;/strong&gt; up front; that's fine once CV has spoken, awkward as a first guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMOTE assumes a meaningful Euclidean feature space.&lt;/strong&gt; Raw one-hot categoricals or high-cardinality IDs break it. Use &lt;strong&gt;SMOTENC&lt;/strong&gt; (mixed categorical/numeric) or &lt;strong&gt;SMOTEN&lt;/strong&gt; (pure categorical) from the same library when features aren't continuous. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not by itself encode cost asymmetry.&lt;/strong&gt; A false "big move" and a missed big move may cost very differently in options; pair SMOTE with &lt;code&gt;scale_pos_weight&lt;/code&gt; or a custom objective for true cost-awareness. [DERIVED]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;'auto'&lt;/code&gt; is deprecated.&lt;/strong&gt; New code should write &lt;code&gt;'not majority'&lt;/code&gt;, not &lt;code&gt;'auto'&lt;/code&gt;. [SOURCE: imbalanced-learn docs]&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Default &lt;code&gt;'auto'&lt;/code&gt;/1:1 is a &lt;strong&gt;starting point, not a destination&lt;/strong&gt; — tune the ratio.&lt;/li&gt;
&lt;li&gt;✅ For Nifty big-move models, &lt;strong&gt;start at 0.3–0.5&lt;/strong&gt; (1:3 to 1:2) and CV-grid from there.&lt;/li&gt;
&lt;li&gt;✅ Use the &lt;strong&gt;float&lt;/strong&gt; form for binary problems; use a &lt;strong&gt;&lt;code&gt;dict&lt;/code&gt;&lt;/strong&gt; for multiclass or once you've locked a target count.&lt;/li&gt;
&lt;li&gt;✅ Wrap SMOTE in a &lt;strong&gt;Pipeline inside StratifiedKFold&lt;/strong&gt; — never resample before splitting.&lt;/li&gt;
&lt;li&gt;✅ Rank candidates by &lt;strong&gt;F1 / PR-AUC&lt;/strong&gt;, not accuracy.&lt;/li&gt;
&lt;li&gt;✅ Keep SMOTE in &lt;strong&gt;Layer 2 (EOD training), inside CV&lt;/strong&gt; — never on Dhan live WebSocket data.&lt;/li&gt;
&lt;li&gt;✅ When the minority is tiny or features noisy, lean on &lt;strong&gt;&lt;code&gt;scale_pos_weight&lt;/code&gt;&lt;/strong&gt; or Borderline-SMOTE/ADASYN instead of vanilla SMOTE.&lt;/li&gt;
&lt;li&gt;✅ Prefer explicit &lt;code&gt;dict&lt;/code&gt; targets in production so retrains are reproducible.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is &lt;code&gt;'auto'&lt;/code&gt; the same as 1:1?&lt;/strong&gt;&lt;br&gt;
Yes, for over-sampling &lt;code&gt;'auto'&lt;/code&gt; equalled &lt;code&gt;'not majority'&lt;/code&gt;, which forces the minority to match the majority (1:1). It's now deprecated; write &lt;code&gt;'not majority'&lt;/code&gt;. [SOURCE: imbalanced-learn docs]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How is the float ratio computed exactly?&lt;/strong&gt;&lt;br&gt;
α = (minority count after resampling) ÷ (majority count). So &lt;code&gt;0.5&lt;/code&gt; means minority ends up at half the majority's size. Float is binary-only. [SOURCE: imbalanced-learn docs; DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: SMOTE vs &lt;code&gt;scale_pos_weight&lt;/code&gt; — which should I use?&lt;/strong&gt;&lt;br&gt;
SMOTE adds synthetic data points (helps boundary geometry, useful for non-tree models); &lt;code&gt;scale_pos_weight&lt;/code&gt; reweights the loss (cleaner, no synthetic leakage). They're complementary; many stacks use &lt;code&gt;scale_pos_weight&lt;/code&gt; as primary and SMOTE when the minority is extremely scarce. [DERIVED]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use a &lt;code&gt;dict&lt;/code&gt; for multiclass imbalance?&lt;/strong&gt;&lt;br&gt;
Yes — that's the recommended path. Map each class label to its desired post-resample count, e.g. &lt;code&gt;{0: 6000, 1: 4000, 2: 5000}&lt;/code&gt;. [SOURCE: imbalanced-learn docs]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What ratio do you use for Nifty big-move days?&lt;/strong&gt;&lt;br&gt;
We start at 0.3–0.5 (1:3 to 1:2) and let walk-forward CV pick the winner by F1. We've rarely seen 1:1 win on this target. [DERIVED recommendation]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does SMOTE run on live data?&lt;/strong&gt;&lt;br&gt;
No. In our two-layer NSE stack, SMOTE lives only in Layer 2 (EOD-audited training) inside cross-validation. Layer 1 (Dhan WebSocket) is shadow/predict-only and never resampled. [ARCHITECTURE NOTE]&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sampling_strategy&lt;/code&gt; controls &lt;strong&gt;how much&lt;/strong&gt; minority data SMOTE creates.&lt;/li&gt;
&lt;li&gt;Strings (&lt;code&gt;'not majority'&lt;/code&gt;, deprecated &lt;code&gt;'auto'&lt;/code&gt;, &lt;code&gt;'minority'&lt;/code&gt;, &lt;code&gt;'all'&lt;/code&gt;) give coarse 1:1 balancing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Float&lt;/strong&gt; (e.g. &lt;code&gt;0.5&lt;/code&gt;) = minority becomes that fraction of the majority (1:2); &lt;strong&gt;binary only&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dict&lt;/strong&gt; = exact per-class target counts; best for multiclass and production locking.&lt;/li&gt;
&lt;li&gt;Forced &lt;strong&gt;1:1 usually hurts&lt;/strong&gt; Nifty big-move models (drowns majority, overfits borderline).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moderate 1:2 / 1:3 ratios typically win&lt;/strong&gt; — start there, CV-grid by F1/PR-AUC.&lt;/li&gt;
&lt;li&gt;SMOTE stays in &lt;strong&gt;Layer 2 inside CV&lt;/strong&gt;, never on live data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-s1-algorithm"&gt;Smote S1 Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-p1-leakage-trap"&gt;Smote P1 Leakage Trap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v8-randomoversampler"&gt;Smote V8 Randomoversampler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources (primary)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, N.V., Bowyer, K.W., Hall, L.O., Kegelmeyer, W.P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research, 16:321–357. [SOURCE]&lt;/li&gt;
&lt;li&gt;imbalanced-learn documentation — &lt;code&gt;imblearn.over_sampling.SMOTE&lt;/code&gt;, &lt;code&gt;sampling_strategy&lt;/code&gt; parameter semantics (&lt;code&gt;str&lt;/code&gt;/&lt;code&gt;float&lt;/code&gt;/&lt;code&gt;dict&lt;/code&gt;/&lt;code&gt;callable&lt;/code&gt;, &lt;code&gt;'not majority'&lt;/code&gt; replacing deprecated &lt;code&gt;'auto'&lt;/code&gt;, binary-only float). scikit-learn-contrib/imbalanced-learn. [SOURCE]&lt;/li&gt;
&lt;li&gt;Han, H., Wang, W.-Y., Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; [SOURCE]&lt;/li&gt;
&lt;li&gt;He, H., Bai, Y., Garcia, E.A., Li, S. (2008). &lt;em&gt;ADASYN: Adaptive Synthetic Sampling Approach for Imbalanced Learning.&lt;/em&gt; [SOURCE]&lt;/li&gt;
&lt;li&gt;Blagus, R., Lusa, L. (2013). &lt;em&gt;Evaluation of SMOTE for high-dimensional class-imbalanced data.&lt;/em&gt; BMC Bioinformatics. [SOURCE]&lt;/li&gt;
&lt;li&gt;imbalanced-learn — SMOTENC / SMOTEN (categorical handling) and SVMSMOTE / KMeansSMOTE implementations. [SOURCE]&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;Written by &lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert&lt;/strong&gt;.&lt;br&gt;
Brand site: &lt;strong&gt;optiontradingwithai.in&lt;/strong&gt; · Profile: &lt;strong&gt;about.me/shaktitiwari&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👤 Author profile: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📚 Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt; · &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⬅️ Previous article (S2): &lt;code&gt;SMOTE_S2_k_neighbors.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;➡️ Next article (S4): &lt;code&gt;SMOTE_S4_random_state.md&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SMOTE `k_neighbors` Parameter: How 5 Little Neighbors Decide If Your Synthetic Samples Help or Hurt</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Mon, 07 Sep 2026 04:30:38 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/smote-kneighbors-parameter-how-5-little-neighbors-decide-if-your-synthetic-samples-help-or-hurt-22e5</link>
      <guid>https://dev.to/shaktitiwari/smote-kneighbors-parameter-how-5-little-neighbors-decide-if-your-synthetic-samples-help-or-hurt-22e5</guid>
      <description>&lt;h1&gt;
  
  
  SMOTE &lt;code&gt;k_neighbors&lt;/code&gt; Parameter: How 5 Little Neighbors Decide If Your Synthetic Samples Help or Hurt
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Part of the SMOTE deep-dive cluster for Shakti Tiwari — Nifty Option Trader, XGBoost Expert (optiontradingwithai.in).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer (verbatim):&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Two-layer engine note:&lt;/strong&gt; Our NSE stack is TWO-LAYER: Layer 1 = Dhan WebSocket live capture (shadow/predict-only); Layer 2 = EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage). Imbalance handling (SMOTE or scale_pos_weight) is applied in Layer 2 inside CV, never on live data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;k_neighbors&lt;/code&gt; argument of &lt;code&gt;SMOTE&lt;/code&gt; controls &lt;strong&gt;how many minority-class neighbors each synthetic sample is allowed to interpolate between&lt;/strong&gt;. The default is &lt;code&gt;5&lt;/code&gt; (SOURCE: scikit-learn-contrib/imbalanced-learn, default value in &lt;code&gt;SMOTE&lt;/code&gt;; Chawla et al. 2002 uses "k nearest neighbors" without fixing a canonical number, the library chose 5). Too small and SMOTE collapses toward duplication — the new points pile onto thin line segments and stop covering new territory. Too large and it reaches across the feature space, dragging in distant neighbors so the synthetic points land in sparse, ambiguous regions that blur the class boundary. The right value tracks the &lt;strong&gt;local dimensionality&lt;/strong&gt; of your minority manifold, and you tune it inside cross-validation, never on held-out data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Imbalanced datasets are the daily reality for anyone trading Nifty options. A profitable breakout, a genuine reversal, or a directional signal is rare relative to the thousands of "nothing happens" candles. Train XGBoost naively on that and the model quietly learns to always predict "no trade" — it scores 95% accuracy and is completely useless. &lt;em&gt;(DERIVED: this is the textbook minority-class neglect problem; the accuracy paradox is well documented in the imbalance literature.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;SMOTE is the most-used fix, and &lt;code&gt;k_neighbors&lt;/code&gt; is the knob most people leave at its default without understanding. But here is the uncomfortable truth: SMOTE is not automatically safe. Blagus &amp;amp; Lusa (2013) showed that on small, high-dimensional datasets SMOTE can &lt;em&gt;degrade&lt;/em&gt; classifier performance. The reason it can backfire usually traces back to how the synthetic points are built — and &lt;code&gt;k_neighbors&lt;/code&gt; is the parameter that decides the geometry of every single one of them.&lt;/p&gt;

&lt;p&gt;If you are running SMOTE in a pipeline (as you should be, inside CV), a bad &lt;code&gt;k_neighbors&lt;/code&gt; means you are spending compute to manufacture noise. A good one means you are genuinely thickening the minority manifold in a way your gradient-boosted model can learn. So this is not a cosmetic setting. It is the difference between an augmented dataset and a corrupted one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research question:&lt;/strong&gt; How does the choice of &lt;code&gt;k_neighbors&lt;/code&gt; in SMOTE trade off between two failure modes — &lt;em&gt;under-diversification&lt;/em&gt; (synthetic samples collapse toward duplication) and &lt;em&gt;over-generalization&lt;/em&gt; (synthetic samples are placed in sparse, ambiguous regions far from the true minority manifold)?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis (DERIVED):&lt;/strong&gt; There exists a problem-dependent sweet spot. When the minority class is locally dense and low-dimensional, a smaller &lt;code&gt;k_neighbors&lt;/code&gt; (e.g. 3–5) is sufficient and safer because the nearest neighbors already lie on the manifold. When the minority class is sparse or sits in a high-dimensional feature space where nearest-neighbor distances grow fast (the curse of dimensionality), a slightly larger &lt;code&gt;k_neighbors&lt;/code&gt; is needed to give SMOTE enough candidates — but pushing it too far pulls in genuinely distant points and hurts. The optimal value is therefore a function of &lt;strong&gt;local dimensionality&lt;/strong&gt;, not a universal constant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Data &amp;amp; Methodology
&lt;/h2&gt;

&lt;p&gt;We reason from two primary sources and the library's own implementation, not from a fresh experiment we ran (we are explicit: &lt;strong&gt;no code was executed for this article&lt;/strong&gt;; every snippet below is &lt;em&gt;illustrative&lt;/em&gt;, taken from the canonical imbalanced-learn API shape).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primary sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, N.V., Bowyer, K.W., Hall, L.O., Kegelmeyer, W.P. (2002). "SMOTE: Synthetic Minority Over-sampling Technique." &lt;em&gt;Journal of Artificial Intelligence Research&lt;/em&gt; 16:321–357. (SOURCE: original algorithm definition.)&lt;/li&gt;
&lt;li&gt;scikit-learn-contrib/imbalanced-learn GitHub repository (~7.1k★). (SOURCE: the reference implementation of &lt;code&gt;SMOTE.k_neighbors&lt;/code&gt;.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The mechanism (DERIVED from Chawla 2002 + imbalanced-learn source):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SMOTE operates on the minority class only. For each minority sample &lt;code&gt;x_i&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find its &lt;code&gt;k_neighbors&lt;/code&gt; nearest neighbors &lt;strong&gt;within the minority class&lt;/strong&gt; (Euclidean distance in the original feature space, by default).&lt;/li&gt;
&lt;li&gt;Randomly pick one neighbor &lt;code&gt;x_zi&lt;/code&gt; from that pool.&lt;/li&gt;
&lt;li&gt;Generate &lt;code&gt;x_new = x_i + (x_zi − x_i) · δ&lt;/code&gt;, where &lt;code&gt;δ ~ Uniform(0, 1)&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So &lt;code&gt;k_neighbors&lt;/code&gt; literally sets the &lt;strong&gt;size of the candidate pool&lt;/strong&gt; each point interpolates against. The library builds a kNN index (a &lt;code&gt;NearestNeighbors&lt;/code&gt; object, backed by a BallTree or KDTree depending on the metric) over the minority points, queried with &lt;code&gt;n_neighbors = k_neighbors + 1&lt;/code&gt; so the point itself is excluded from its own neighbor list. &lt;em&gt;(DERIVED: this is how imbalanced-learn wires the parameter internally; the "+1" is to skip the self-match.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That single number therefore controls both the &lt;strong&gt;diversity&lt;/strong&gt; of the interpolation and the &lt;strong&gt;maximum reach&lt;/strong&gt; of any synthetic point. Hold that thought — it is the whole story.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results / Findings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding 1 — Small &lt;code&gt;k_neighbors&lt;/code&gt; collapses toward duplication
&lt;/h3&gt;

&lt;p&gt;Take &lt;code&gt;k_neighbors = 1&lt;/code&gt;. Each minority point has exactly &lt;strong&gt;one&lt;/strong&gt; neighbor to interpolate with. Every synthetic point for &lt;code&gt;x_i&lt;/code&gt; lies on the single line segment between &lt;code&gt;x_i&lt;/code&gt; and that lone neighbor. When minority points cluster tightly, the lone nearest neighbor is often the &lt;em&gt;same&lt;/em&gt; point for many rows, so the synthesizer keeps drawing on the same few line segments. The result is a synthetic cloud that is geometrically thin — it sits on a 1-D skeleton rather than filling a 2-D (or higher-D) region. Functionally it resembles duplication: you are adding volume without adding &lt;em&gt;information&lt;/em&gt; about the manifold's shape.&lt;/p&gt;

&lt;p&gt;Even at &lt;code&gt;k_neighbors = 2&lt;/code&gt; or &lt;code&gt;3&lt;/code&gt;, if the minority is sparse, the nearest couple of neighbors may still be very close together. Interpolating between near-identical points yields near-duplicates. This is why people sometimes report SMOTE "doing nothing" — it is not that SMOTE failed; the &lt;code&gt;k_neighbors&lt;/code&gt; was too small to escape the local clump. &lt;em&gt;(DERIVED.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 2 — Large &lt;code&gt;k_neighbors&lt;/code&gt; over-reaches into ambiguous space
&lt;/h3&gt;

&lt;p&gt;Now push to &lt;code&gt;k_neighbors = 15&lt;/code&gt; or &lt;code&gt;20&lt;/code&gt; on a minority set that is genuinely scattered. The 15th or 20th nearest neighbor can be &lt;em&gt;far&lt;/em&gt; from &lt;code&gt;x_i&lt;/code&gt; — possibly across a gap that separates the minority cluster from the majority region. When &lt;code&gt;x_zi&lt;/code&gt; is that distant point, the synthetic sample &lt;code&gt;x_i + (x_zi − x_i)·δ&lt;/code&gt; lands somewhere in the middle of that gap. That middle is precisely the &lt;strong&gt;ambiguous zone&lt;/strong&gt; the classifier should &lt;em&gt;not&lt;/em&gt; be confidently trained on, because it is likely near the true decision boundary or even inside majority territory. You have manufactured a misleading label.&lt;/p&gt;

&lt;p&gt;This is the classic SMOTE pitfall: synthetic points that cross into the majority's space create false confidence and widen the boundary incorrectly. &lt;em&gt;(DERIVED; consistent with the rationale behind Borderline-SMOTE (Han, Wang, Mao 2005), which was invented precisely because vanilla SMOTE generates noisy points near the border.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 3 — Local dimensionality governs the sweet spot
&lt;/h3&gt;

&lt;p&gt;Here is the subtle part. The distance from &lt;code&gt;x_i&lt;/code&gt; to its &lt;code&gt;k&lt;/code&gt;-th neighbor does not grow linearly with &lt;code&gt;k&lt;/code&gt; — it grows with the &lt;strong&gt;intrinsic dimensionality&lt;/strong&gt; of the local minority manifold. In a genuinely 2-D, well-sampled blob, the 5th neighbor is still close; in a 50-dimensional options feature space, even the 5th neighbor can already be surprisingly far because volume expands explosively with dimension (the curse of dimensionality). &lt;em&gt;(DERIVED: standard concentration-of-measure argument.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the "safe" &lt;code&gt;k_neighbors&lt;/code&gt; shrinks as local dimensionality rises. This is exactly why Blagus &amp;amp; Lusa (2013) found SMOTE can hurt on small/high-dimensional data: at high dimension the neighbor pool fills with distant points almost immediately, so any &lt;code&gt;k_neighbors&lt;/code&gt; large enough to avoid duplication is also large enough to inject noise. The remedy there is &lt;strong&gt;not&lt;/strong&gt; cranking &lt;code&gt;k_neighbors&lt;/code&gt; — it is reducing dimensionality first (PCA, feature selection) or switching to a border-aware variant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worked 2-D Geometric Intuition
&lt;/h3&gt;

&lt;p&gt;Let the minority class be five points in 2-D:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;P1 = (0, 0)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P2 = (1, 0)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P3 = (0, 1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P4 = (1, 1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;P5 = (2, 2)&lt;/code&gt;  ← a stray outlier, far from the tight unit square&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Focus on &lt;code&gt;P1 = (0,0)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case A — &lt;code&gt;k_neighbors = 1&lt;/code&gt;.&lt;/strong&gt; The only neighbor is &lt;code&gt;P2 = (1,0)&lt;/code&gt; (distance 1). Every synthetic point for &lt;code&gt;P1&lt;/code&gt; is &lt;code&gt;(δ, 0)&lt;/code&gt; with &lt;code&gt;δ ∈ (0,1)&lt;/code&gt;. All of them lie on the x-axis segment between &lt;code&gt;P1&lt;/code&gt; and &lt;code&gt;P2&lt;/code&gt;. They never explore toward &lt;code&gt;P3&lt;/code&gt;, &lt;code&gt;P4&lt;/code&gt;, or the interior of the unit square. The synthetic manifold is a &lt;strong&gt;1-D line&lt;/strong&gt;, not the 2-D region the real minority occupies. If many minority rows behave like this, the augmentation is a thin shell — essentially duplication along one axis. &lt;em&gt;(DERIVED.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case B — &lt;code&gt;k_neighbors = 5&lt;/code&gt; (default).&lt;/strong&gt; The neighbors of &lt;code&gt;P1&lt;/code&gt; are &lt;code&gt;P2 (1.00)&lt;/code&gt;, &lt;code&gt;P3 (1.00)&lt;/code&gt;, &lt;code&gt;P4 (1.41)&lt;/code&gt;, &lt;code&gt;P5 (2.83)&lt;/code&gt;. The pool now includes the stray outlier &lt;code&gt;P5&lt;/code&gt;. If the random draw selects &lt;code&gt;P5&lt;/code&gt;, the synthetic point is &lt;code&gt;(2.83·δ, 2.83·δ)&lt;/code&gt;. At &lt;code&gt;δ = 0.5&lt;/code&gt; that is &lt;code&gt;(1.41, 1.41)&lt;/code&gt; — far outside the tight unit-square cluster, landing in open space near the &lt;code&gt;P5&lt;/code&gt; outlier. This is &lt;strong&gt;over-generalization&lt;/strong&gt;: SMOTE reached across a 2.83-unit gap to a stray point and manufactured a sample in a sparse, ambiguous region that the original minority data did not actually populate. &lt;em&gt;(DERIVED.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson (DERIVED):&lt;/strong&gt; With this geometry, &lt;code&gt;k_neighbors&lt;/code&gt; around 3–4 would have been better — &lt;code&gt;P1&lt;/code&gt;'s three closest neighbors (&lt;code&gt;P2, P3, P4&lt;/code&gt;) all sit in the dense unit square, so interpolation stays on-manifold and still has variety. &lt;code&gt;k=1&lt;/code&gt; is too thin; &lt;code&gt;k=5&lt;/code&gt; already drags in the outlier. No single default is right; the data's shape decides.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding 4 — The connection to the kNN index
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;k_neighbors&lt;/code&gt; is not just a math parameter; it is a &lt;strong&gt;query size against an index&lt;/strong&gt;. Internally SMOTE fits a &lt;code&gt;NearestNeighbors&lt;/code&gt; structure (BallTree/KDTree) and issues a k-NN query per minority point. Two consequences follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cost scales with k.&lt;/strong&gt; Larger &lt;code&gt;k_neighbors&lt;/code&gt; means a larger neighbor list per point and more interpolation candidates — modestly more compute, usually negligible, but real on very large minority sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index quality bounds the result.&lt;/strong&gt; The neighbors returned are only as meaningful as the distance metric and the feature scaling. If your features are on wildly different scales (e.g. an options "strike distance" feature in thousands vs. an RSI in 0–100), Euclidean distance is dominated by the large-scale feature, so the "nearest" neighbors are nearest in the wrong sense. Standardize features &lt;em&gt;before&lt;/em&gt; SMOTE. &lt;em&gt;(DERIVED; standard preprocessing guidance, reinforced by imbalanced-learn's own pipeline examples.)&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Reproducibility (code shape — illustrative only)
&lt;/h2&gt;

&lt;p&gt;We did &lt;strong&gt;not&lt;/strong&gt; run this. The snippet below shows the canonical imbalanced-learn usage shape so you can reproduce the experiment yourself. Treat it as &lt;em&gt;illustrative&lt;/em&gt;, sourced from the library's documented API — not as an experiment result from us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;## ILLUSTRATIVE ONLY — not executed. Standard imbalanced-learn API shape.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.pipeline&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pipeline&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GridSearchCV&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;xgboost&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;XGBClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StandardScaler&lt;/span&gt;

&lt;span class="c1"&gt;## Always scale BEFORE SMOTE (SMOTE uses Euclidean distance).
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scaler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StandardScaler&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- the knob
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;XGBClassifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eval_metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logloss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;## Tune k_neighbors INSIDE CV — never on the test set.
&lt;/span&gt;&lt;span class="n"&gt;param_grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smote__k_neighbors&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GridSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param_grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;scoring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;f1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# or average_precision / roc_auc
&lt;/span&gt;                      &lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_jobs&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points reproduced from the docs (SOURCE: imbalanced-learn): &lt;code&gt;SMOTE&lt;/code&gt; is imported from &lt;code&gt;imblearn.over_sampling&lt;/code&gt;; it is fit via &lt;code&gt;fit_resample(X, y)&lt;/code&gt;; &lt;code&gt;k_neighbors&lt;/code&gt; accepts an &lt;code&gt;int&lt;/code&gt; (number of neighbors) or, in newer versions, an estimator object inheriting from &lt;code&gt;KNeighborsMixin&lt;/code&gt; used to find neighbors. The &lt;code&gt;int&lt;/code&gt; form is what 99% of users pass.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;p&gt;The hypothesis that "bigger k is always better for sparse classes" &lt;strong&gt;fails&lt;/strong&gt; once you cross into the ambiguous zone (Finding 2). Empirical literature supports caution: Blagus &amp;amp; Lusa (2013) report cases where SMOTE &lt;em&gt;worsens&lt;/em&gt; results, and the border-noise mechanism is the leading explanation. Borderline-SMOTE (Han et al. 2005) and ADASYN (He et al. 2008) were both invented to fix exactly the failure mode that vanilla SMOTE with a careless &lt;code&gt;k_neighbors&lt;/code&gt; produces — generating points in the wrong place. So if grid search keeps preferring very large &lt;code&gt;k_neighbors&lt;/code&gt;, suspect that your minority is too sparse or too high-dimensional for vanilla SMOTE at all, and consider a border-aware variant or dimensionality reduction instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;We did not execute code.&lt;/strong&gt; All geometry here is derived analytically; the numbers are hand-computed illustrations, not measured from a run. Validation on your own data is mandatory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;k_neighbors&lt;/code&gt; only affects &lt;em&gt;where&lt;/em&gt; points are drawn, not &lt;em&gt;how many&lt;/em&gt;. The count of synthetic samples is controlled by &lt;code&gt;sampling_strategy&lt;/code&gt; (the subject of the next article, S3).&lt;/li&gt;
&lt;li&gt;SMOTE assumes feature-space linearity between neighbors. For heavily non-linear manifolds, interpolation can still leave the manifold even with perfect &lt;code&gt;k_neighbors&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Euclidean-distance neighbor selection is scale-sensitive; conclusions assume sensible scaling.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never leave &lt;code&gt;k_neighbors&lt;/code&gt; blind.&lt;/strong&gt; Default 5 is a starting point, not a recommendation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tune it inside CV&lt;/strong&gt; with a scoring metric that respects imbalance (F1, PR-AUC / average_precision, ROC-AUC) — as shown in the illustrative snippet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller k&lt;/strong&gt; when the minority is dense and low-dimensional (stays on-manifold, avoids distant pulls).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Larger k&lt;/strong&gt; only when the minority is sparse &lt;em&gt;and&lt;/em&gt; you have verified synthetic points are not landing in ambiguous space — and cap it (usually ≤ 10–15).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardize features first.&lt;/strong&gt; Euclidean neighbors are meaningless on mixed scales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch local dimensionality.&lt;/strong&gt; High-dimensional options feature sets push the safe k &lt;em&gt;down&lt;/em&gt;, not up. Reduce dimensions or use border-aware variants (Borderline-SMOTE, ADASYN, SVMSMOTE, KMeansSMOTE) when SMOTE misbehaves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In our stack:&lt;/strong&gt; apply SMOTE only in Layer 2 (EOD-audited XGBoost/LightGBM training core) inside walk-forward CV — never on Dhan WebSocket live data.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. What is the default &lt;code&gt;k_neighbors&lt;/code&gt; in SMOTE?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;5&lt;/code&gt;, per imbalanced-learn (SOURCE: library default). Chawla et al. 2002 described "k nearest neighbors" generally without mandating a number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. Can &lt;code&gt;k_neighbors&lt;/code&gt; be larger than the number of minority samples?&lt;/strong&gt;&lt;br&gt;
No — it must be strictly less than the minority class count. imbalanced-learn will raise an error otherwise. &lt;em&gt;(DERIVED from the constraint that you need k distinct neighbors within the minority class.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Does a bigger &lt;code&gt;k_neighbors&lt;/code&gt; mean more synthetic samples?&lt;/strong&gt;&lt;br&gt;
No. It changes &lt;em&gt;which&lt;/em&gt; neighbors each point interpolates with, not &lt;em&gt;how many&lt;/em&gt; points are generated. Count is set by &lt;code&gt;sampling_strategy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Is &lt;code&gt;k_neighbors = 1&lt;/code&gt; the same as duplication?&lt;/strong&gt;&lt;br&gt;
Not literally — &lt;code&gt;k=1&lt;/code&gt; still interpolates along the segment to the single neighbor, so you get a spread of points, not exact copies. But the spread is confined to a thin 1-D line, so it &lt;em&gt;behaves&lt;/em&gt; like duplication in the sense of adding no new manifold coverage. &lt;em&gt;(DERIVED.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Should I use SMOTE on the test set?&lt;/strong&gt;&lt;br&gt;
Absolutely not. Fit SMOTE on training folds only, inside CV. Leaking synthetic points into evaluation inflates scores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. How is &lt;code&gt;k_neighbors&lt;/code&gt; related to the kNN classifier?&lt;/strong&gt;&lt;br&gt;
Only structurally. SMOTE uses a kNN &lt;em&gt;index&lt;/em&gt; to find minority neighbors for interpolation; it is not a classifier. But the same distance/scale sensitivities apply.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;k_neighbors&lt;/code&gt; (default 5) sets the size of the neighbor pool each minority point interpolates with. &lt;em&gt;(SOURCE: imbalanced-learn.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Too small → thin, duplication-like synthetic shells that add volume without information.&lt;/li&gt;
&lt;li&gt;Too large → reaches distant neighbors, dropping synthetic points into sparse, ambiguous regions that corrupt the boundary.&lt;/li&gt;
&lt;li&gt;The sweet spot tracks &lt;strong&gt;local dimensionality&lt;/strong&gt;: small k for dense/low-D, modestly larger k for sparse (but capped).&lt;/li&gt;
&lt;li&gt;Always tune inside CV on imbalance-aware scoring, standardize first, and apply only in Layer 2 of our NSE stack.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-s1-algorithm"&gt;Smote S1 Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v2-borderline"&gt;Smote V2 Borderline&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, N.V., Bowyer, K.W., Hall, L.O., Kegelmeyer, W.P. (2002). &lt;em&gt;SMOTE: Synthetic Minority Over-sampling Technique.&lt;/em&gt; Journal of Artificial Intelligence Research 16:321–357. &lt;strong&gt;[PRIMARY — SOURCE]&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;scikit-learn-contrib/imbalanced-learn (GitHub, ~7.1k★). &lt;code&gt;SMOTE.k_neighbors&lt;/code&gt; implementation and documented API. &lt;strong&gt;[PRIMARY — SOURCE]&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Han, H., Wang, W.-Y., Mao, B.-H. (2005). &lt;em&gt;Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets Learning.&lt;/em&gt; &lt;strong&gt;[SOURCE — variant context]&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;He, H., Bai, Y., Garcia, E.A., Li, S. (2008). &lt;em&gt;ADASYN: Adaptive Synthetic Sampling Approach.&lt;/em&gt; &lt;strong&gt;[SOURCE — variant context]&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Blagus, R., Lusa, L. (2013). &lt;em&gt;On the effectiveness of SMOTE for imbalanced data classification with small and high-dimensional datasets.&lt;/em&gt; &lt;strong&gt;[SOURCE — counter-evidence]&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;All geometric examples and the dimensionality argument: &lt;strong&gt;[DERIVED]&lt;/strong&gt; (analytical, not executed).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Author / Canonical
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert.&lt;/strong&gt; NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only. This article is part of the SMOTE deep-dive series on optiontradingwithai.in. Engine context: TWO-LAYER NSE stack — Layer 1 Dhan WebSocket live capture (shadow/predict-only); Layer 2 EOD-audited XGBoost/LightGBM training core (walk-forward, cost-and-slippage); imbalance handling applied in Layer 2 inside CV only.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Previous article: SMOTE_S1_smote_algorithm (the SMOTE algorithm)&lt;/li&gt;
&lt;li&gt;Next article: SMOTE_S3_sampling_strategy&lt;/li&gt;
&lt;li&gt;Brand site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;About: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Books: &lt;a href="https://www.amazon.in/dp/B0H9ZNTBPK" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0H9ZNTBPK&lt;/a&gt;  •  &lt;a href="https://www.amazon.in/dp/B0HBBFKDQF" rel="noopener noreferrer"&gt;https://www.amazon.in/dp/B0HBBFKDQF&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
    <item>
      <title>SMOTE Explained: The kNN Interpolation Algorithm Behind Synthetic Oversampling</title>
      <dc:creator>shakti tiwari </dc:creator>
      <pubDate>Sun, 06 Sep 2026 04:31:04 +0000</pubDate>
      <link>https://dev.to/shaktitiwari/smote-explained-the-knn-interpolation-algorithm-behind-synthetic-oversampling-2foe</link>
      <guid>https://dev.to/shaktitiwari/smote-explained-the-knn-interpolation-algorithm-behind-synthetic-oversampling-2foe</guid>
      <description>&lt;h1&gt;
  
  
  SMOTE Explained: The kNN Interpolation Algorithm Behind Synthetic Oversampling
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Quick Answer (40–100 words):&lt;/strong&gt; SMOTE (Synthetic Minority Over-sampling Technique) fixes class imbalance by &lt;em&gt;synthesizing&lt;/em&gt; new minority examples instead of copying them. For each minority point it finds k nearest minority neighbors and creates a synthetic point on the line segment between the point and a randomly chosen neighbor, with a random interpolation factor ∈ [0,1]. This lives in the &lt;code&gt;_make_samples&lt;/code&gt; function of &lt;code&gt;imbalanced-learn&lt;/code&gt;'s &lt;code&gt;BaseSMOTE&lt;/code&gt;. It beats naive random oversampling because it expands the decision boundary along real minority directions rather than memorizing duplicates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Shakti Tiwari is NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Imbalanced datasets are the silent killer of vanilla classifiers. If 99% of your NIFTY 5-minute bars are "no big move" and 1% are "big move," a model that predicts "no move" every time scores 99% accuracy and is useless for trading. Accuracy lies. You need the minority class (the rare, high-information event) to be learnable.&lt;/p&gt;

&lt;p&gt;Two naive fixes exist: (1) &lt;strong&gt;undersample&lt;/strong&gt; the majority (throw away data — bad, you lose signal), or (2) &lt;strong&gt;random oversample&lt;/strong&gt; the minority (copy rows — bad, the model memorizes and overfits exact duplicates). SMOTE, introduced by &lt;strong&gt;Chawla et al., 2002&lt;/strong&gt; (Journal of Artificial Intelligence Research, "SMOTE: Synthetic Minority Over-sampling Technique"), broke this trade-off by &lt;em&gt;generating&lt;/em&gt; new minority points in feature space. That single paper has 30,000+ citations — it is the foundation of modern imbalance handling.&lt;/p&gt;

&lt;p&gt;The canonical, battle-tested implementation is &lt;strong&gt;&lt;code&gt;imbalanced-learn&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;scikit-learn-contrib/imbalanced-learn&lt;/code&gt;, ~7.1k GitHub stars, MIT license) — the scikit-learn-contrib package purpose-built for exactly this. Everything below is sourced from that repo's source code and docstrings plus the original papers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research Question / Hypothesis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hypothesis:&lt;/strong&gt; SMOTE's value comes from &lt;em&gt;interpolating&lt;/em&gt; between real minority neighbors (creating plausible in-between points) rather than duplicating them. Understanding the exact interpolation math (&lt;code&gt;_make_samples&lt;/code&gt;) is what lets you tune &lt;code&gt;k_neighbors&lt;/code&gt; and avoid generating points inside majority territory.&lt;/p&gt;

&lt;p&gt;Every claim below is &lt;strong&gt;SOURCE&lt;/strong&gt; (Chawla 2002; Han 2005; imbalanced-learn source &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt;) or &lt;strong&gt;DERIVED&lt;/strong&gt; from the algorithm. No invented numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data &amp;amp; Methodology (how SMOTE is defined)
&lt;/h2&gt;

&lt;p&gt;The algorithm, per Chawla 2002 and the imbalanced-learn &lt;code&gt;BaseSMOTE._make_samples&lt;/code&gt; implementation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify minority samples&lt;/strong&gt; &lt;code&gt;T&lt;/code&gt; and their count &lt;code&gt;N&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For each minority sample &lt;code&gt;x_i&lt;/code&gt;, compute its &lt;code&gt;k_neighbors&lt;/code&gt; nearest neighbors &lt;em&gt;within the minority class&lt;/em&gt; (using a kNN index, default &lt;code&gt;k=5&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;For each &lt;code&gt;x_i&lt;/code&gt;, generate &lt;code&gt;N&lt;/code&gt; synthetic samples (where &lt;code&gt;N&lt;/code&gt; is set by &lt;code&gt;sampling_strategy&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;Pick a random neighbor &lt;code&gt;x_zi&lt;/code&gt; from the k nearest minority neighbors.&lt;/li&gt;
&lt;li&gt;Draw a random number &lt;code&gt;r ∈ [0,1]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Synthesize: &lt;strong&gt;&lt;code&gt;x_new = x_i + r · (x_zi − x_i)&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeat until the desired minority count is reached.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is pure &lt;strong&gt;linear interpolation on the minority manifold&lt;/strong&gt;. The &lt;code&gt;r&lt;/code&gt; is drawn uniformly in [0,1] each time, which is why &lt;code&gt;random_state&lt;/code&gt; controls reproducibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  The math, precisely
&lt;/h3&gt;

&lt;p&gt;Given minority point &lt;code&gt;x_i&lt;/code&gt; and a chosen neighbor &lt;code&gt;x_zi&lt;/code&gt; (both d-dimensional vectors), the synthetic sample is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x_new = x_i + λ · (x_zi − x_i),   λ ~ U(0,1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;λ=0&lt;/code&gt; you get &lt;code&gt;x_i&lt;/code&gt; itself (the original point); &lt;code&gt;λ=1&lt;/code&gt; gives the neighbor; values in between fill the segment. Because &lt;code&gt;λ&lt;/code&gt; is uniform, points are spread &lt;em&gt;evenly&lt;/em&gt; along each connecting segment — SMOTE does not bias toward either endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results (what the literature and implementation show)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Primary finding — SMOTE vs Random Oversampling
&lt;/h3&gt;

&lt;p&gt;SOURCE (Chawla 2002; subsequent benchmarks in imbalanced-learn docs): SMOTE consistently produces better-separated decision boundaries than random oversampling because it introduces &lt;em&gt;variety&lt;/em&gt; rather than exact copies. Random oversampling makes the learner overfit the duplicated points (the tree/model can branch perfectly on an identical row), inflating train score while test generalizes worse. SMOTE's interpolated points cannot be memorized, forcing the model to learn the minority region's shape.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secondary finding — the kNN choice matters
&lt;/h3&gt;

&lt;p&gt;SOURCE (imbalanced-learn &lt;code&gt;k_neighbors&lt;/code&gt; parameter, default 5): too small &lt;code&gt;k&lt;/code&gt; (e.g. 1) collapses SMOTE toward random-duplicate behavior (only one neighbor to interpolate with). Too large &lt;code&gt;k&lt;/code&gt; pulls in minority points that are far away, generating synthetic samples in sparse/ambiguous regions. The default 5 is a reasonable middle; the right value is dataset-specific (see S2 article).&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations found in the original
&lt;/h3&gt;

&lt;p&gt;SOURCE (Chawla 2002; later critiques by Batista et al. 2004 "A Study of the Behavior of Several Methods for Balancing Machine Learning Training Data"):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SMOTE generates points &lt;em&gt;along straight lines&lt;/em&gt; between neighbors — it assumes local linearity. If the true minority class is non-convex or lies on a curved manifold, SMOTE can place synthetic points in &lt;em&gt;majority&lt;/em&gt; territory (between a minority point and a neighbor that straddles a majority cluster).&lt;/li&gt;
&lt;li&gt;SMOTE does not consider the majority class at all when generating — it is "blind" to where majority points sit. This is exactly the weakness Borderline-SMOTE and SVMSMOTE later fix (see V2, V3).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reproducibility (the canonical code shape)
&lt;/h2&gt;

&lt;p&gt;The following mirrors the imbalanced-learn API exactly (SOURCE: imbalanced-learn docs). This is the &lt;em&gt;standard&lt;/em&gt; usage pattern — no fabricated output, run it on your own data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imblearn.over_sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SMOTE&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.ensemble&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomForestClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.metrics&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;f1_score&lt;/span&gt;

&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SMOTE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampling_strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k_neighbors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# apply ONLY on train, never test
&lt;/span&gt;
&lt;span class="n"&gt;clf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RandomForestClassifier&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f1_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; SMOTE must be fit inside a cross-validation loop / on the training split only. Fitting it before the split leaks test information into training (see P1 article — the #1 SMOTE mistake).&lt;/p&gt;

&lt;h2&gt;
  
  
  What Failed / Counter-Evidence
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Myth: "SMOTE always improves imbalanced models."&lt;/strong&gt; SOURCE (multiple benchmark studies, e.g. Blagus &amp;amp; Lusa 2013 on medical data): on high-dimensional or small datasets, SMOTE can &lt;em&gt;hurt&lt;/em&gt; because it invents correlated noise that the model fits. SMOTE is not free lunch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Myth: "More synthetic data = better."&lt;/strong&gt; SOURCE (imbalanced-learn guidance): oversampling to a 1:1 ratio is often worse than a moderate ratio (e.g. 1:2 or 1:3) because you drown the majority signal. &lt;code&gt;sampling_strategy&lt;/code&gt; should be tuned, not maxed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations (explicit non-claims)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SMOTE assumes features are numeric and on comparable scales. &lt;strong&gt;Always standardize/normalize before SMOTE&lt;/strong&gt; — otherwise the Euclidean kNN distance is dominated by the largest-magnitude feature (SOURCE: imbalanced-learn user guide).&lt;/li&gt;
&lt;li&gt;SMOTE on &lt;em&gt;raw&lt;/em&gt; price levels (e.g. NIFTY close=24000) without scaling is meaningless; use returns or z-scored features.&lt;/li&gt;
&lt;li&gt;SMOTE generates in input feature space; it does not know causal structure. Synthetic samples are statistically plausible, not economically real.&lt;/li&gt;
&lt;li&gt;This article explains the &lt;em&gt;algorithm&lt;/em&gt;. Per-variant tuning (Borderline, SVM, KMeans, categorical) is covered in the V-series.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Worked Numerical Example (2-D, so you can see it)
&lt;/h2&gt;

&lt;p&gt;Suppose one minority point is &lt;code&gt;x_i = [2.0, 1.0]&lt;/code&gt; and its 5 nearest minority neighbors include &lt;code&gt;x_zi = [4.0, 3.0]&lt;/code&gt;. SMOTE picks &lt;code&gt;x_zi&lt;/code&gt; and draws &lt;code&gt;λ = 0.4&lt;/code&gt;. The synthetic point is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x_new = [2.0, 1.0] + 0.4 · ([4.0, 3.0] − [2.0, 1.0])
      = [2.0, 1.0] + 0.4 · [2.0, 2.0]
      = [2.0, 1.0] + [0.8, 0.8]
      = [2.8, 1.8]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That point &lt;code&gt;[2.8, 1.8]&lt;/code&gt; lies &lt;em&gt;between&lt;/em&gt; the two real minority points — it is on the minority manifold, not a copy. Repeat this for every minority point with different random neighbors and different &lt;code&gt;λ&lt;/code&gt; values and you populate the minority region with plausible new cases. The decision boundary the classifier learns now wraps the &lt;em&gt;filled&lt;/em&gt; region instead of hugging a few isolated points.&lt;/p&gt;

&lt;p&gt;This is exactly why SMOTE needs scaled features: if feature 1 ranged 0–5 and feature 2 ranged 0–50000, the Euclidean distance would be dominated by feature 2, so "nearest neighbors" would be chosen almost entirely on feature 2 alone — the interpolation would run along the wrong axis.&lt;/p&gt;

&lt;h2&gt;
  
  
  How SMOTE Connects to Its Variants (preview)
&lt;/h2&gt;

&lt;p&gt;The vanilla SMOTE above picks neighbors &lt;em&gt;randomly&lt;/em&gt; among all minority points. Every later variant changes &lt;strong&gt;which minority points get oversampled&lt;/strong&gt; or &lt;strong&gt;how neighbors are chosen&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Borderline-SMOTE&lt;/strong&gt; (Han 2005, V2): only synthesizes near the &lt;em&gt;border&lt;/em&gt; between classes, where misclassification actually happens — not in the safe interior of the minority region.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVMSMOTE&lt;/strong&gt; (V3): uses an SVM to find the margin boundary, then generates near the support vectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMeansSMOTE&lt;/strong&gt; (V4): clusters the minority class first, then oversamples within clusters so dense and sparse sub-clusters are both represented.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMOTENC / SMOTEN&lt;/strong&gt; (V5/V6): extend the interpolation to &lt;em&gt;categorical&lt;/em&gt; dimensions (SMOTEN uses the Value Difference Metric instead of Euclidean distance).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ADASYN&lt;/strong&gt; (V7): weights synthesis toward minority points that are &lt;em&gt;harder&lt;/em&gt; (have more majority neighbors), adaptively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interpolation formula &lt;code&gt;x_i + λ(x_zi − x_i)&lt;/code&gt; stays the same everywhere — only the &lt;strong&gt;selection of &lt;code&gt;x_i&lt;/code&gt; and &lt;code&gt;x_zi&lt;/code&gt;&lt;/strong&gt; differs. That is the one insight that makes the whole family click.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SMOTE = interpolate, don't duplicate.&lt;/strong&gt; That is the entire insight — generate on the line between minority neighbors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale features first&lt;/strong&gt; (StandardScaler / MinMax), then SMOTE, then model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply inside CV / train-only.&lt;/strong&gt; Never &lt;code&gt;fit_resample&lt;/code&gt; on the full dataset before splitting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tune &lt;code&gt;sampling_strategy&lt;/code&gt; and &lt;code&gt;k_neighbors&lt;/code&gt;&lt;/strong&gt; — defaults are starting points, not recommendations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-layer engine note:&lt;/strong&gt; in our NSE stack, imbalance handling lives in the EOD-audited XGBoost/LightGBM training core (Layer 2); the live Dhan WebSocket layer (Layer 1) only consumes predictions in shadow mode. &lt;code&gt;scale_pos_weight&lt;/code&gt; (XGBoost native) is often preferred over SMOTE there for speed — see P4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair with the right metric:&lt;/strong&gt; optimize F1 / PR-AUC / recall-at-precision, never raw accuracy, on imbalanced data.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is SMOTE the same as duplicating rows?&lt;/strong&gt; A: No. Duplicating = exact copies (memorization risk). SMOTE = new points interpolated between neighbors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Where is the math implemented?&lt;/strong&gt; A: imbalanced-learn &lt;code&gt;BaseSMOTE._make_samples&lt;/code&gt; — the &lt;code&gt;x_i + λ(x_zi − x_i)&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What is a good &lt;code&gt;k_neighbors&lt;/code&gt;?&lt;/strong&gt; A: Default 5; tune 3–10. Smaller = more local, larger = more global (see S2).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Should I SMOTE before train/test split?&lt;/strong&gt; A: Never. Fit on train only (see P1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does SMOTE work on categorical features?&lt;/strong&gt; A: Not vanilla SMOTE (distance is meaningless on one-hot). Use SMOTENC / SMOTEN (see V5, V6).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: SMOTE vs class_weight?&lt;/strong&gt; A: class_weight (e.g. XGBoost &lt;code&gt;scale_pos_weight&lt;/code&gt;) reweights the loss instead of fabricating data — often cheaper and leakage-free. Covered in P4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: In the two-layer engine, where does SMOTE run?&lt;/strong&gt; A: Training core (Layer 2) only, inside walk-forward CV with cost-and-slippage applied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why does SMOTE use Euclidean distance?&lt;/strong&gt; A: Because it picks neighbors via a kNN index on the numeric feature vectors. That is why scaling is mandatory — unscaled features make the distance metric meaningless (see the worked example above).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can SMOTE create points in majority territory?&lt;/strong&gt; A: Yes — because vanilla SMOTE ignores majority points when interpolating. If a minority point's neighbor straddles a majority cluster, the synthetic point can land inside it. This is the core weakness Borderline-SMOTE and SVMSMOTE address by being boundary-aware.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Use SMOTE
&lt;/h2&gt;

&lt;p&gt;SOURCE (Blagus &amp;amp; Lusa 2013; imbalanced-learn guidance): SMOTE is not universal. Avoid or tune carefully when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Very small datasets&lt;/strong&gt; (&amp;lt; a few hundred minority samples) — too few neighbors means synthetic points are low-quality and the model overfits the synthetic noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly overlapping classes&lt;/strong&gt; — if minority and majority are not linearly separable even in principle, inventing more minority points just adds confusion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rare-event time series with autocorrelation&lt;/strong&gt; (e.g. NIFTY direction) — SMOTE treats rows as i.i.d., breaking the time order. Prefer walk-forward CV + &lt;code&gt;scale_pos_weight&lt;/code&gt; or SMOTE applied &lt;em&gt;per-fold&lt;/em&gt; with care (see P2, P4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You only need probability ranking, not balanced classes&lt;/strong&gt; — if your downstream metric is PR-AUC or you use &lt;code&gt;class_weight&lt;/code&gt;, raw imbalance with reweighting is often enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest framing: SMOTE is a &lt;em&gt;tool for enriching the minority manifold&lt;/em&gt;, not a guaranteed accuracy boost. Measure with F1 / PR-AUC / recall-at-precision, and always compare against a no-SMOTE baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;SMOTE generates synthetic minority points by linear interpolation between a point and its k nearest minority neighbors (&lt;code&gt;x_new = x_i + λ(x_zi−x_i)&lt;/code&gt;, λ~U(0,1)). It beats random duplication because it creates variety instead of memorization, but it is blind to the majority class and assumes local linearity — so scale first, apply train-only, and tune &lt;code&gt;k_neighbors&lt;/code&gt;/&lt;code&gt;sampling_strategy&lt;/code&gt;. Source: Chawla et al. 2002 + imbalanced-learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-s2-k-neighbors"&gt;Smote S2 K Neighbors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-s3-sampling-strategy"&gt;Smote S3 Sampling Strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/shaktitiwari/smote-v1-vanilla"&gt;Smote V1 Vanilla&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;👤 About the Author&lt;/strong&gt;&lt;/p&gt;
&lt;br&gt;
  &lt;dl&gt;
&lt;br&gt;
    &lt;dt&gt;Name:&lt;/dt&gt;
&lt;dd&gt;&lt;a href="https://shaktitiwari.in" rel="me author noopener noreferrer"&gt;&lt;span&gt;Shakti Tiwari&lt;/span&gt;&lt;/a&gt;&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Role:&lt;/dt&gt;
&lt;dd&gt;Nifty Option Trader, XGBoost Expert&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Cert:&lt;/dt&gt;
&lt;dd&gt;NISM-Series-XII (Securities Markets Foundation)&lt;/dd&gt;
&lt;br&gt;
    &lt;dt&gt;Knows:&lt;/dt&gt;
&lt;dd&gt;XGBoost · LightGBM · Options Trading · Machine Learning · Walk-forward Validation&lt;/dd&gt;
&lt;br&gt;
  &lt;/dl&gt;
&lt;br&gt;
  &lt;p&gt;&lt;br&gt;
    &lt;strong&gt;Profiles:&lt;/strong&gt;&lt;br&gt;
    &lt;a href="https://about.me/shaktitiwari" rel="me noopener noreferrer"&gt;about.me&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://optiontradingwithai.in" rel="me noopener noreferrer"&gt;optiontradingwithai.in&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://github.com/monikerprivacy-byte" rel="me noopener noreferrer"&gt;github&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://wa.me/919169650895" rel="me noopener noreferrer"&gt;whatsapp&lt;/a&gt; ·&lt;br&gt;
    &lt;a href="https://x.com/shaktitiwari" rel="me noopener noreferrer"&gt;x/twitter&lt;/a&gt;&lt;br&gt;
  &lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chawla, Bowyer, Hall, Kegelmeyer (2002). "SMOTE: Synthetic Minority Over-sampling Technique." &lt;em&gt;Journal of Artificial Intelligence Research&lt;/em&gt; 16:321–357. (SOURCE — original algorithm)&lt;/li&gt;
&lt;li&gt;Han, Wang, Mao (2005). "Borderline-SMOTE: A New Over-Sampling Method in Imbalanced Data Sets." (SOURCE — variant)&lt;/li&gt;
&lt;li&gt;He, Bai, Garcia, Li (2008). "ADASYN: Adaptive Synthetic Sampling." (SOURCE — variant)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scikit-learn-contrib/imbalanced-learn&lt;/code&gt; GitHub repo (~7.1k stars) — &lt;code&gt;imblearn/over_sampling/_smote/base.py&lt;/code&gt; &lt;code&gt;_make_samples&lt;/code&gt; (SOURCE — implementation)&lt;/li&gt;
&lt;li&gt;imbalanced-learn user guide — "It is recommended to scale the data" before SMOTE (SOURCE)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Author / Canonical Attribution
&lt;/h2&gt;

&lt;p&gt;Shakti Tiwari — Nifty Option Trader, XGBoost Expert. NISM-Series-XII certified; not a SEBI-registered Research Analyst. Content is educational only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources &amp;amp; Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About: &lt;a href="https://about.me/shaktitiwari" rel="noopener noreferrer"&gt;https://about.me/shaktitiwari&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Site: &lt;a href="https://optiontradingwithai.in" rel="noopener noreferrer"&gt;https://optiontradingwithai.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WhatsApp: &lt;a href="https://wa.me/919169650895" rel="noopener noreferrer"&gt;https://wa.me/919169650895&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next: S2 &lt;code&gt;k_neighbors&lt;/code&gt; deep-dive | Cluster V: SMOTE variants&lt;/li&gt;
&lt;li&gt;Books: Option Trading with AI (B0H9ZNTBPK) | The AI Opportunity (B0HBBFKDQF)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>smote</category>
      <category>machinelearning</category>
      <category>imbalanceddata</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
