[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EM018GRKZHJ8DSAGAKSBTY":3,"skill-same:01M6EM018GRKZHJ8DSAGAKSBTY":74},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":29,"linkOnly":30,"markdownContent":31,"markdownBody":32,"frontMatter":33,"refName":51,"commitSha":52,"skillDirectory":53,"forksCount":54,"repositoryHtmlUrl":55,"repositoryHomepageUrl":6,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":58,"updatedAt":59,"install":60,"resources":73},"01M6EM018GRKZHJ8DSAGAKSBTY","smote-imbalance-handling",null,"SMOTE applied to training set only; optional ablation Phase A (no SMOTE) vs Phase B (SMOTE) for binary classification.","cgpan\u002Fedm-ars-public","cgpan","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F118746117?v=4","01M6EKT5TG36Q3GH340QN8RH08","Chenguang Pan","01M6EKT5YTQVRARGJF0VPA1SY7",5,"MIT","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Fskills\u002Ftask-type\u002Fsmote-imbalance-handling\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28],"smote imbalance handling","training","SET","classification","ablation","only","task","analyst","binary","type","2026-11-10 10:12:30",false,"---\nname: smote-imbalance-handling\nlayer: task-type\ndescription: SMOTE applied to training set only; optional ablation Phase A (no SMOTE) vs Phase B (SMOTE) for binary classification.\ntrigger_keywords:\n  - smote\n  - imbalance\n  - imbalanced\n  - oversampling\n  - minority\n  - ablation\napplicable_task_types:\n  - prediction\napplicable_datasets: []\napplicable_stages:\n  - Analyst\npriority: 2\nreferences_skills: []\nresources: []\nversion: \"1.0\"\nrule_severity: mandatory\n---\n\n# SMOTE Imbalance Handling (Classification Only)\n\nWhen the binary outcome's minority class proportion drops below the\nconfigured threshold (default 20%), apply SMOTE to the training set.\nOptionally run an ablation that compares pre-SMOTE vs post-SMOTE\nresults so the paper can quantify SMOTE's effect.\n\n## When to apply\n\nRead `data_report.is_imbalanced` (set by the DataEngineer when\n`minority_pct \u003C minority_threshold`), or compute it inline:\n\n```python\nminority_pct = min(np.bincount(y_train)) \u002F len(y_train)\nis_imbalanced = minority_pct \u003C minority_threshold  # default 0.20\n```\n\n## Phase A — Ablation baseline (NO SMOTE)\n\nWhen `ablation_enabled: true` AND imbalance is detected:\n\n1. Train ALL models on the **original** training data (`train_X`,\n   `train_y` as-is).\n2. Evaluate ALL models on the **original test set** (`test_X`,\n   `test_y` — never touched).\n3. Record results in `ablation.all_models` (same schema as\n   primary `all_models`).\n\n## Phase B — SMOTE-augmented training (primary results)\n\n```python\nimport analysis_helpers\n\nX_train_smote, y_train_smote, smote_meta = analysis_helpers.apply_smote(\n    train_X, train_y,\n    minority_threshold=minority_threshold,  # config default 0.20\n    random_state=42,\n    k_neighbors=smote_k_neighbors,          # config default 5\n)\n```\n\n1. Re-train ALL models on the SMOTE-augmented training data.\n2. Evaluate ALL models on the **same original test set** (NEVER SMOTE\n   the test set).\n3. These are the primary `all_models` results in `results.json`.\n\n## When ablation is disabled\n\nWhen `ablation_enabled: false` and imbalance is detected: skip Phase A;\njust apply SMOTE and train once.\n\n## When NOT imbalanced\n\nTrain on the original data as usual. Set `results[\"ablation\"] = None`\nexplicitly so downstream consumers know the absence is intentional.\n\n## Critical rules\n\n1. **NEVER apply SMOTE to the test set** — test set must remain the\n   original distribution for the metrics to reflect deployment\n   conditions.\n2. **NEVER apply SMOTE before the train\u002Ftest split** — the\n   DataEngineer split happens before the Analyst sees the data.\n3. **Use `random_state=42` for SMOTE** so the synthetic minority\n   examples are reproducible.\n4. **Use `analysis_helpers.apply_smote()`**, never `imblearn.SMOTE`\n   directly. The helper handles the imbalance threshold check, the\n   k-neighbor fallback when the minority class is too small for the\n   default `k=5`, and the metadata return.\n5. **Ablation and primary evaluations use the same original test\n   set** — the only thing that changes between Phase A and Phase B is\n   the training data.\n\n## Imbalanced metrics (apply when SMOTE is used)\n\nWhen SMOTE is applied, ADD the following fields to BOTH primary and\nablation `all_models` entries:\n\n- `f2` (`fbeta_score(y_true, y_pred, beta=2)`) — emphasizes recall.\n- `balanced_accuracy` (`balanced_accuracy_score(y_true, y_pred)`).\n\nAccuracy alone is misleading under imbalance. Continue to report it,\nbut emphasize AUC + F2 + balanced accuracy in the discussion.\n\nSee `prediction-evaluation-classification` for the full metric set.\n\n## What goes in `results.json`\n\n```json\n{\n  \"ablation\": {\n    \"description\": \"Pre-SMOTE baseline for comparison\",\n    \"all_models\": {\n      \"LogisticRegression\": {\n        \"auc\": 0.72, \"accuracy\": 0.85, \"precision\": 0.40,\n        \"recall\": 0.18, \"f1\": 0.25, \"f2\": 0.20,\n        \"balanced_accuracy\": 0.55,\n        \"auc_ci_lower\": 0.69, \"auc_ci_upper\": 0.74\n      }\n    },\n    \"smote_applied\": true,\n    \"minority_class_pct_before\": 0.12,\n    \"minority_class_pct_after\": 0.50,\n    \"smote_n_train_before\": 15000,\n    \"smote_n_train_after\": 22000\n  }\n}\n```\n\nWhen no imbalance is detected: `\"ablation\": null`.\n\n## Verification rules (Critic)\n\nThe Critic verifies:\n\n1. If `data_report.is_imbalanced == true` AND classification AND\n   `ablation_enabled: true` → `results.ablation` MUST be present\n   (non-null). Missing → major.\n2. Ablation `n_test` must match primary `n_test`. Mismatch means\n   SMOTE was applied to the test set — **critical** issue.\n3. When SMOTE was applied, F2 and balanced_accuracy must be reported\n   for every model in primary and ablation results. Missing → major.\n\n## MANDATORY RULE — SMOTE and group-aware CV are incompatible\n\nIf a school-aware split or any grouped cross-validator is in use, **do\nnot oversample.** Pass `group_ids=` to `apply_smote` and it will decline,\nreturning the training set unchanged with the reason recorded.\n\n```python\nX_res, y_res, smote_meta = analysis_helpers.apply_smote(\n    X_train, y_train,\n    group_ids=train_school_ids,   # present -> SMOTE declines\n)\n```\n\nThis is conceptual, not a coding slip. At an 11% base rate SMOTE\noversampled X and y to 33,266 rows; the original 18,717 school IDs then\nwent to a grouped splitter:\n\n```\nValueError: Found input variables with inconsistent numbers of samples:\n[33266, 33266, 18717]\n```\n\nPadding the group vector to match would be worse than the crash. A\nsynthetic row interpolated between two students belongs to no school, so\nany label given to it is invented — and it would leak that school across\nthe very train\u002Ftest boundary the grouped split exists to enforce.\n\n**Use instead, when grouping is active:** class weights\n(`class_weight=\"balanced\"`), decision-threshold tuning on the training\nfolds, and PR-AUC alongside ROC-AUC. **State that choice in the generated\nMethods text** — \"SMOTE was not applied because the evaluation uses a\nschool-aware split\" is a methodological decision a reader needs, not an\nimplementation detail.\n\n## Source provenance\n\nCanonical source: `agent_prompts\u002Fanalyst.yaml` §\"Class Imbalance\nHandling\" (L194-L261).\n\nThe metric formulas and the \"accuracy is misleading under imbalance\"\nnarrative also live in `prediction-evaluation-classification`.\n","# SMOTE Imbalance Handling (Classification Only)\n\nWhen the binary outcome's minority class proportion drops below the\nconfigured threshold (default 20%), apply SMOTE to the training set.\nOptionally run an ablation that compares pre-SMOTE vs post-SMOTE\nresults so the paper can quantify SMOTE's effect.\n\n## When to apply\n\nRead `data_report.is_imbalanced` (set by the DataEngineer when\n`minority_pct \u003C minority_threshold`), or compute it inline:\n\n```python\nminority_pct = min(np.bincount(y_train)) \u002F len(y_train)\nis_imbalanced = minority_pct \u003C minority_threshold  # default 0.20\n```\n\n## Phase A — Ablation baseline (NO SMOTE)\n\nWhen `ablation_enabled: true` AND imbalance is detected:\n\n1. Train ALL models on the **original** training data (`train_X`,\n   `train_y` as-is).\n2. Evaluate ALL models on the **original test set** (`test_X`,\n   `test_y` — never touched).\n3. Record results in `ablation.all_models` (same schema as\n   primary `all_models`).\n\n## Phase B — SMOTE-augmented training (primary results)\n\n```python\nimport analysis_helpers\n\nX_train_smote, y_train_smote, smote_meta = analysis_helpers.apply_smote(\n    train_X, train_y,\n    minority_threshold=minority_threshold,  # config default 0.20\n    random_state=42,\n    k_neighbors=smote_k_neighbors,          # config default 5\n)\n```\n\n1. Re-train ALL models on the SMOTE-augmented training data.\n2. Evaluate ALL models on the **same original test set** (NEVER SMOTE\n   the test set).\n3. These are the primary `all_models` results in `results.json`.\n\n## When ablation is disabled\n\nWhen `ablation_enabled: false` and imbalance is detected: skip Phase A;\njust apply SMOTE and train once.\n\n## When NOT imbalanced\n\nTrain on the original data as usual. Set `results[\"ablation\"] = None`\nexplicitly so downstream consumers know the absence is intentional.\n\n## Critical rules\n\n1. **NEVER apply SMOTE to the test set** — test set must remain the\n   original distribution for the metrics to reflect deployment\n   conditions.\n2. **NEVER apply SMOTE before the train\u002Ftest split** — the\n   DataEngineer split happens before the Analyst sees the data.\n3. **Use `random_state=42` for SMOTE** so the synthetic minority\n   examples are reproducible.\n4. **Use `analysis_helpers.apply_smote()`**, never `imblearn.SMOTE`\n   directly. The helper handles the imbalance threshold check, the\n   k-neighbor fallback when the minority class is too small for the\n   default `k=5`, and the metadata return.\n5. **Ablation and primary evaluations use the same original test\n   set** — the only thing that changes between Phase A and Phase B is\n   the training data.\n\n## Imbalanced metrics (apply when SMOTE is used)\n\nWhen SMOTE is applied, ADD the following fields to BOTH primary and\nablation `all_models` entries:\n\n- `f2` (`fbeta_score(y_true, y_pred, beta=2)`) — emphasizes recall.\n- `balanced_accuracy` (`balanced_accuracy_score(y_true, y_pred)`).\n\nAccuracy alone is misleading under imbalance. Continue to report it,\nbut emphasize AUC + F2 + balanced accuracy in the discussion.\n\nSee `prediction-evaluation-classification` for the full metric set.\n\n## What goes in `results.json`\n\n```json\n{\n  \"ablation\": {\n    \"description\": \"Pre-SMOTE baseline for comparison\",\n    \"all_models\": {\n      \"LogisticRegression\": {\n        \"auc\": 0.72, \"accuracy\": 0.85, \"precision\": 0.40,\n        \"recall\": 0.18, \"f1\": 0.25, \"f2\": 0.20,\n        \"balanced_accuracy\": 0.55,\n        \"auc_ci_lower\": 0.69, \"auc_ci_upper\": 0.74\n      }\n    },\n    \"smote_applied\": true,\n    \"minority_class_pct_before\": 0.12,\n    \"minority_class_pct_after\": 0.50,\n    \"smote_n_train_before\": 15000,\n    \"smote_n_train_after\": 22000\n  }\n}\n```\n\nWhen no imbalance is detected: `\"ablation\": null`.\n\n## Verification rules (Critic)\n\nThe Critic verifies:\n\n1. If `data_report.is_imbalanced == true` AND classification AND\n   `ablation_enabled: true` → `results.ablation` MUST be present\n   (non-null). Missing → major.\n2. Ablation `n_test` must match primary `n_test`. Mismatch means\n   SMOTE was applied to the test set — **critical** issue.\n3. When SMOTE was applied, F2 and balanced_accuracy must be reported\n   for every model in primary and ablation results. Missing → major.\n\n## MANDATORY RULE — SMOTE and group-aware CV are incompatible\n\nIf a school-aware split or any grouped cross-validator is in use, **do\nnot oversample.** Pass `group_ids=` to `apply_smote` and it will decline,\nreturning the training set unchanged with the reason recorded.\n\n```python\nX_res, y_res, smote_meta = analysis_helpers.apply_smote(\n    X_train, y_train,\n    group_ids=train_school_ids,   # present -> SMOTE declines\n)\n```\n\nThis is conceptual, not a coding slip. At an 11% base rate SMOTE\noversampled X and y to 33,266 rows; the original 18,717 school IDs then\nwent to a grouped splitter:\n\n```\nValueError: Found input variables with inconsistent numbers of samples:\n[33266, 33266, 18717]\n```\n\nPadding the group vector to match would be worse than the crash. A\nsynthetic row interpolated between two students belongs to no school, so\nany label given to it is invented — and it would leak that school across\nthe very train\u002Ftest boundary the grouped split exists to enforce.\n\n**Use instead, when grouping is active:** class weights\n(`class_weight=\"balanced\"`), decision-threshold tuning on the training\nfolds, and PR-AUC alongside ROC-AUC. **State that choice in the generated\nMethods text** — \"SMOTE was not applied because the evaluation uses a\nschool-aware split\" is a methodological decision a reader needs, not an\nimplementation detail.\n\n## Source provenance\n\nCanonical source: `agent_prompts\u002Fanalyst.yaml` §\"Class Imbalance\nHandling\" (L194-L261).\n\nThe metric formulas and the \"accuracy is misleading under imbalance\"\nnarrative also live in `prediction-evaluation-classification`.\n",{"name":5,"layer":34,"version":35,"priority":36,"resources":37,"description":7,"rule_severity":38,"trigger_keywords":39,"applicable_stages":45,"references_skills":47,"applicable_datasets":48,"applicable_task_types":49},"task-type","1.0",2,[],"mandatory",[40,41,42,43,44,23],"smote","imbalance","imbalanced","oversampling","minority",[46],"Analyst",[],[],[50],"prediction","main","1aeaead66d2bf876109f3b101005d2eed54041d9","skills\u002Ftask-type\u002Fsmote-imbalance-handling",0,"https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public","https:\u002F\u002Fgithub.com\u002Fcgpan","https:\u002F\u002Fcpan.ai","Ph.D. candidate in Measurement, Evaluation, and Statistics.","2026-09-28 20:39:06",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add cgpan\u002Fedm-ars-public --skill smote-imbalance-handling",{"tool":66,"command":67},"bunx","bunx skills add cgpan\u002Fedm-ars-public --skill smote-imbalance-handling",{"tool":69,"command":70},"pnpm","pnpm dlx skills add cgpan\u002Fedm-ars-public --skill smote-imbalance-handling","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Fskills\u002Ftask-type\u002Fsmote-imbalance-handling\u002FSKILL.md",true,[],[75,98,109,118,131,142],{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":91,"publishedAt":29},"01M6EM1ZTQZVAP90DZTVBHCCFB","acm-fixture","Fixture writing skill for ACM acmart sigconf paper output.","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskills\u002Fwriting\u002Facm-fixture\u002FSKILL.md",[81,86],{"categoryNo":82,"categoryName":83,"taxonomyType":84,"taxonomyTypeDesc":85},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[92,93,94,95,96,97],"acm fixture","fixture","writing","LaTeX","writer","paper",{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":104,"publishedAt":29},"01M6EM1XNFETAZFGPHQPAXKBYN","prediction-fixture","Fixture skill for the prediction task type; references shap-fixture.","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskills\u002Ftask-type\u002Fprediction-fixture\u002FSKILL.md",[],[93,105,25,28,106,107,108,26],"prediction fixture","references","fixture skill","shap",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":115,"publishedAt":29},"01M6EM1V4MQXYGQ7K2ZDJRAPSM","causal-fixture","Fixture skill for the causal_inference task type.","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskills\u002Ftask-type\u002Fcausal-fixture\u002FSKILL.md",[],[93,116,28,25,117,107],"causal fixture","inference",{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":124,"publishedAt":29},"01M6EM1SCWFZZ2ACSAP65GNWK9","shap-fixture","Fixture methodology for SHAP feature importance analysis.","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskills\u002Fmethodology\u002Fshap-fixture\u002FSKILL.md",[],[125,93,108,126,127,128,129,130],"shap fixture","feature importance","methodology","analysis","explainer","feature",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":137,"publishedAt":29},"01M6EM1PZC1MK1G0D1YFVYAQ9W","cluster-recon-fixture","Fixture methodology for reconstructing cluster IDs from fingerprint variables.","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskills\u002Fmethodology\u002Fcluster-recon-fixture\u002FSKILL.md",[],[138,139,140,93,141,127],"recon","cluster recon fixture","cluster","ids",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":148,"publishedAt":29},"01M6EM1MZVKMSWEXZ7TJ45DS04","hsls09-fixture","Fixture skill for HSLS:09 with a bundled YAML resource and one cross-reference.","https:\u002F\u002Fgithub.com\u002Fcgpan\u002Fedm-ars-public\u002Fblob\u002Fmain\u002Ftests\u002Ffixtures\u002Fskills\u002Fdataset\u002Fhsls09-fixture\u002FSKILL.md",[],[93,149,150,151,152,107,153,138,140,154],"hsls09 fixture","yaml","public","reference","dataset","registry"]