Example 38
intermediate
38
Preprocessing
Tensors

Feature Engineering & Preprocessing

Advanced preprocessing tools new in v1.0.0: imputation, feature selection, text vectorizers, spline transformers, and advanced splitters. This example uses deepbox/preprocess, deepbox/ndarray and focuses on SimpleImputer, KNNImputer, SelectKBest, VarianceThreshold, TfidfVectorizer, CountVectorizer, SplineTransformer, PolynomialFeatures, RobustScaler, PowerTransformer, KFold, StratifiedKFold, TimeSeriesSplit; tensor.

Deepbox Modules Used

deepbox/preprocessdeepbox/ndarray

What You Will Learn

  • Use deepbox/preprocess for SimpleImputer, KNNImputer, SelectKBest, VarianceThreshold, TfidfVectorizer, CountVectorizer, SplineTransformer, PolynomialFeatures, RobustScaler, PowerTransformer, KFold, StratifiedKFold, TimeSeriesSplit.
  • Use deepbox/ndarray for tensor.
  • Advanced preprocessing tools new in v1.0.0: imputation, feature selection, text vectorizers, spline transformers, and advanced splitters.

Source Files

index.ts
1/**2 * Example 38: Feature Engineering & Preprocessing3 *4 * New in v1.0.0: Imputation (SimpleImputer, KNNImputer), feature selection5 * (SelectKBest, VarianceThreshold), text vectorizers (TF-IDF, Count),6 * SplineTransformer, advanced scalers, and cross-validation splitters.7 */89import { makeClassification } from "deepbox/datasets";10import { tensor } from "deepbox/ndarray";11import {12  CountVectorizer,13  f_classif,14  KFold,15  KNNImputer,16  PolynomialFeatures,17  PowerTransformer,18  RobustScaler,19  SelectKBest,20  SimpleImputer,21  SplineTransformer,22  StratifiedKFold,23  TfidfVectorizer,24  TimeSeriesSplit,25  VarianceThreshold,26} from "deepbox/preprocess";2728console.log("=".repeat(60));29console.log("Example 38: Feature Engineering & Preprocessing");30console.log("=".repeat(60));3132// ============================================================================33// Part 1: SimpleImputer — Fill Missing Values34// ============================================================================35console.log("\n🔧 Part 1: SimpleImputer");36console.log("-".repeat(60));3738// SimpleImputer fills missing values (NaN) with a chosen strategy39const XMissing = tensor([40  [1, 2, NaN],41  [3, NaN, 6],42  [7, 8, 9],43  [NaN, 5, 3],44  [4, 6, 7],45]);4647console.log("Data with missing values:");48console.log(XMissing.toString());4950// Strategy: mean (default)51const impMean = new SimpleImputer({ strategy: "mean" });52const XFilledMean = impMean.fitTransform(XMissing);53console.log("\nSimpleImputer (mean):");54console.log(XFilledMean.toString());5556// Strategy: median57const impMedian = new SimpleImputer({ strategy: "median" });58const XFilledMedian = impMedian.fitTransform(XMissing);59console.log("SimpleImputer (median):");60console.log(XFilledMedian.toString());6162// Strategy: constant63const impConst = new SimpleImputer({ strategy: "constant", fillValue: -1 });64const XFilledConst = impConst.fitTransform(XMissing);65console.log("SimpleImputer (constant=-1):");66console.log(XFilledConst.toString());6768// ============================================================================69// Part 2: KNNImputer — k-Nearest Neighbors Imputation70// ============================================================================71console.log("\n🔍 Part 2: KNNImputer");72console.log("-".repeat(60));7374// KNNImputer fills missing values using the mean of k-nearest neighbors75const knnImp = new KNNImputer({ nNeighbors: 2 });76const XFilledKNN = knnImp.fitTransform(XMissing);77console.log("KNNImputer (k=2):");78console.log(XFilledKNN.toString());7980// ============================================================================81// Part 3: VarianceThreshold — Remove Low-Variance Features82// ============================================================================83console.log("\n📉 Part 3: VarianceThreshold");84console.log("-".repeat(60));8586// VarianceThreshold removes features with variance below a threshold87const XVar = tensor([88  [0, 2, 0.1],89  [0, 4, 0.2],90  [0, 6, 0.1],91  [0, 8, 0.3],92  [0, 10, 0.2],93]);9495console.log("Original data (feature 0 has zero variance):");96console.log(XVar.toString());9798const varThresh = new VarianceThreshold({ threshold: 0.01 });99const XVarReduced = varThresh.fitTransform(XVar);100console.log("\nAfter VarianceThreshold (threshold=0.01):");101console.log(XVarReduced.toString());102console.log(`  Removed ${XVar.shape[1]! - XVarReduced.shape[1]!} low-variance feature(s)`);103104// ============================================================================105// Part 4: SelectKBest — Univariate Feature Selection106// ============================================================================107console.log("\n⭐ Part 4: SelectKBest");108console.log("-".repeat(60));109110// SelectKBest selects the top k features based on a scoring function111const [XFeat, yFeat] = makeClassification({112  nSamples: 100,113  nFeatures: 10,114  nInformative: 3,115  nClasses: 2,116  randomState: 42,117});118119console.log(`Original features: ${XFeat.shape[1]}`);120121const selector = new SelectKBest({ scoreFunc: f_classif, k: 5 });122const XSelected = selector.fitTransform(XFeat, yFeat);123console.log(`After SelectKBest (k=5): ${XSelected.shape[1]} features`);124console.log(`  Selected feature scores: ${selector.scores.toString()}`);125126// ============================================================================127// Part 5: Text Vectorization128// ============================================================================129console.log("\n📝 Part 5: Text Vectorization");130console.log("-".repeat(60));131132// CountVectorizer converts text documents to bag-of-words representation133const documents = ["the cat sat on the mat", "the dog sat on the log", "cats and dogs are friends"];134135console.log("Documents:");136for (const doc of documents) {137  console.log(`  "${doc}"`);138}139140const countVec = new CountVectorizer();141const XCount = countVec.fitTransformText(documents);142console.log(`\nCountVectorizer output shape: ${XCount.shape}`);143console.log(`  Vocabulary size: ${countVec.vocabulary.size}`);144145// TfidfVectorizer weights terms by importance (TF-IDF)146const tfidfVec = new TfidfVectorizer();147const XTfidf = tfidfVec.fitTransformText(documents);148console.log(`\nTfidfVectorizer output shape: ${XTfidf.shape}`);149console.log("  TF-IDF values emphasize unique/important words");150151// ============================================================================152// Part 6: PolynomialFeatures153// ============================================================================154console.log("\n📐 Part 6: PolynomialFeatures");155console.log("-".repeat(60));156157// PolynomialFeatures generates polynomial and interaction features158const XPoly = tensor([159  [1, 2],160  [3, 4],161  [5, 6],162]);163164console.log("Original features:");165console.log(XPoly.toString());166167const poly = new PolynomialFeatures({ degree: 2, includeBias: false });168const XPolyTransformed = poly.fitTransform(XPoly);169console.log("\nPolynomialFeatures (degree=2, no bias):");170console.log(XPolyTransformed.toString());171console.log(172  `  Original: ${XPoly.shape[1]} features → Expanded: ${XPolyTransformed.shape[1]} features`173);174175// ============================================================================176// Part 7: SplineTransformer177// ============================================================================178console.log("\n🌊 Part 7: SplineTransformer");179console.log("-".repeat(60));180181// SplineTransformer generates B-spline basis functions for flexible modeling182const XSpline = tensor([[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]);183184console.log("Original 1D feature:");185console.log(XSpline.toString());186187const spline = new SplineTransformer({ nKnots: 4, degree: 3 });188const XSplineTransformed = spline.fitTransform(XSpline);189console.log(`\nSplineTransformer (4 knots, degree 3):`);190console.log(`  Output shape: ${XSplineTransformed.shape}`);191console.log(`  1 feature → ${XSplineTransformed.shape[1]} spline basis functions`);192193// ============================================================================194// Part 8: RobustScaler & PowerTransformer195// ============================================================================196console.log("\n📊 Part 8: Advanced Scalers");197console.log("-".repeat(60));198199const XSkewed = tensor([200  [1, 100],201  [2, 200],202  [3, 300],203  [100, 400], // outlier in first feature204  [4, 500],205]);206207// RobustScaler is resistant to outliers (uses median and IQR)208const robust = new RobustScaler();209const XRobust = robust.fitTransform(XSkewed);210console.log("RobustScaler (uses median & IQR, robust to outliers):");211console.log(XRobust.toString());212213// PowerTransformer maps data to a Gaussian distribution214const XPositive = tensor([215  [1, 10],216  [2, 20],217  [3, 30],218  [4, 40],219  [5, 50],220]);221222const power = new PowerTransformer({ method: "yeo-johnson" });223const XPower = power.fitTransform(XPositive);224console.log("\nPowerTransformer (Yeo-Johnson):");225console.log(XPower.toString());226227// ============================================================================228// Part 9: Cross-Validation Splitters229// ============================================================================230console.log("\n✂️  Part 9: Cross-Validation Splitters");231console.log("-".repeat(60));232233const [XSplit, ySplit] = makeClassification({234  nSamples: 20,235  nFeatures: 2,236  nInformative: 2,237  nRedundant: 0,238  nClasses: 2,239  randomState: 42,240});241242// KFold — standard k-fold cross-validation243const kfold = new KFold({ nSplits: 5, shuffle: true, randomState: 42 });244console.log("KFold (5 splits, shuffled):");245let foldNum = 1;246for (const { trainIndex, testIndex } of kfold.split(XSplit)) {247  console.log(248    `  Fold ${foldNum}: train=${trainIndex.length} samples, test=${testIndex.length} samples`249  );250  foldNum++;251}252253// StratifiedKFold — preserves class distribution in each fold254const stratKfold = new StratifiedKFold({ nSplits: 5 });255console.log("\nStratifiedKFold (5 splits, preserves class balance):");256foldNum = 1;257for (const { trainIndex, testIndex } of stratKfold.split(XSplit, ySplit)) {258  console.log(`  Fold ${foldNum}: train=${trainIndex.length}, test=${testIndex.length}`);259  foldNum++;260}261262// TimeSeriesSplit — forward-chaining for temporal data263const tsSplit = new TimeSeriesSplit({ nSplits: 4 });264console.log("\nTimeSeriesSplit (4 splits, expanding window):");265foldNum = 1;266for (const { trainIndex, testIndex } of tsSplit.split(XSplit)) {267  console.log(`  Fold ${foldNum}: train=${trainIndex.length}, test=${testIndex.length}`);268  foldNum++;269}270271// ============================================================================272// Summary273// ============================================================================274console.log("\n💡 Key Takeaways");275console.log("-".repeat(60));276console.log("• SimpleImputer: fill NaN with mean, median, most_frequent, or constant");277console.log("• KNNImputer: fill NaN using k-nearest neighbors (context-aware)");278console.log("• VarianceThreshold: remove features with low variance (near-constant)");279console.log("• SelectKBest: select top-k features by statistical test score");280console.log("• CountVectorizer: bag-of-words text representation");281console.log("• TfidfVectorizer: importance-weighted text representation");282console.log("• SplineTransformer: flexible nonlinear feature expansion via B-splines");283console.log("• RobustScaler: outlier-resistant scaling using median and IQR");284console.log("• PowerTransformer: map data to approximate Gaussian distribution");285console.log("• KFold/StratifiedKFold/TimeSeriesSplit: proper CV for different data types");286287console.log("\n✅ Feature Engineering Example Complete!");288console.log("=".repeat(60));289

Console Output

$ npx tsx 38-feature-engineering/index.ts
See the example README for the expected console and artifact output.