☰
SMILE 数据变换指南:可组合、可序列化的特征预处理管道(Transform / InvertibleTransform / 六种内置缩放器)
2026/10/9 2:07:48 网站建设 项目流程
  • 人工智能
  • 机器学习
  • 深度学习
  • NLP
  • 大模型
  • 模型推理服务
  • 数据可视化

【免费下载链接】smile

Statistical Machine Intelligence & Learning Engine

项目地址:https://gitcode.com/gh_mirrors/smi/smile
点击查看免费下载

本篇技术指南完整讲解 SMILE(Statistical Machine Intelligence & Learning Engine)中smile.data.transform与smile.feature.transform两个包构成的数据变换(Data Transformation)体系:从Transform/InvertibleTransform接口、ColumnTransform/InvertibleColumnTransform实现,到Scaler、Standardizer、RobustStandardizer、WinsorScaler、MaxAbsScaler、Normalizer六种内置预处理算子,再到管道组合、逆变换、可空列处理、序列化与自定义变换。读者学完后,将能够在 SMILE 中搭建"训练集拟合、测试集套用"的标准预处理管道,并把经过变换的预测结果逆变换回原始单位用于业务解读。


1. 架构总览:从单行映射到列并行批量处理

数据变换位于两个层次:

  • 基础设施层smile.data.transform:定义Transform、InvertibleTransform两个接口,以及ColumnTransform、InvertibleColumnTransform两个实现类(源码见 base/src/main/java/smile/data/transform);
  • 业务算子层smile.feature.transform:六个内置统计缩放器全部构建在上述基础设施之上,返回InvertibleColumnTransform或普通Transform(源码见 core/src/main/java/smile/feature/transform)。

整体类型关系如下:

Transform ────────────────────────────────────────────────────────── │ apply(Tuple) → Tuple (逐行) │ apply(DataFrame) → DataFrame (批量,列并行) │ andThen(Transform)→ Transform (正向组合) │ compose(Transform)→ Transform (反向组合) │ ├── ColumnTransform (列名→lambda 映射,按列处理) │ └── InvertibleColumnTransform (额外提供 invert() 正/逆两个映射) │ InvertibleTransform extends Transform │ invert(Tuple) → Tuple │ invert(DataFrame) → DataFrame

所有内置缩放器(Scaler、Standardizer、RobustStandardizer、WinsorScaler、MaxAbsScaler)的fit均返回InvertibleColumnTransform;只有Normalizer返回普通的Transform(它按行归一化,不可逆)。


2. Transform 接口:一切预处理步骤的唯一入口

Transform位于smile.data.transform包,继承自java.util.function.Function<Tuple, Tuple>并实现java.io.Serializable(见 Transform.java)。每个预处理步骤只需实现这一个接口。

2.1 逐行应用

Transform t = /* 任意 transform */; Tuple outRow = t.apply(inRow);

2.2 批量应用

接口的默认apply(DataFrame)实现将所有行stream().map(this)流式逐行变换后重新组装为DataFrame(见 Transform.java)。而ColumnTransform重写了这个默认实现,走列并行的快速路径,避免逐行产生对象分配:

DataFrame out = t.apply(df);

ColumnTransform.apply(DataFrame)内部用IntStream.range(0, schema.length()).parallel()按列并行处理:每个被映射的列先取出doubleStream(),套用 lambda 后重建为DoubleVector或NullableDoubleVector,未映射的列则零拷贝直通(直接复用原ValueVector对象),见 ColumnTransform.java。

2.3 静态工厂方法

// 从已拟合的 transform 构建单步管道 Transform chain = Transform.pipeline(step1, step2, step3); // 依序拟合多个数据依赖的 transform: // 每个后续 trainer 拿到的是经过前面所有步骤变换后的数据 Transform pipeline = Transform.fit(trainDf, Standardizer::fit, data -> WinsorScaler.fit(data, 0.01, 0.99));

pipeline与fit的源码实现值得注意:

  • pipeline(Transform...):从左到右逐级andThen,若传入数组为空或为null则抛出IllegalArgumentException("At least one transform is required");
  • fit(DataFrame, Function<DataFrame,Transform>...):先让第一个 trainer 在原始数据上拟合,然后对每个后续 trainer 执行data = pipeline.apply(data),即trainer 永远看到"已被前序步骤变换过的数据",见 Transform.java。传入空 trainer 列表同样抛IllegalArgumentException。

3. InvertibleTransform 接口:可逆变换

InvertibleTransform在Transform之上增加两个逆操作(见 InvertibleTransform.java):

public interface InvertibleTransform extends Transform { Tuple invert(Tuple x); DataFrame invert(DataFrame data); }

逆变换的价值在于恢复原始特征空间:

  • 解读模型输出:模型在标准化后的目标变量上训练,预测结果必须先反标准化才能呈现给用户;
  • 重建误差度量:自编码器或 PCA 异常检测可以在原始单位上度量重建保真度;
  • 调试自检:验证invert(apply(x)) ≈ x。
InvertibleColumnTransform scaler = Standardizer.fit(train); DataFrame scaled = scaler.apply(test); DataFrame recovered = scaler.invert(scaled); // 回到原始单位

4. ColumnTransform:所有列式变换的实体类

ColumnTransform持有一个Map<String, Function>——列名到smile.util.function.Functionlambda 的映射,构造参数为变换名称与映射表(见 ColumnTransform.java)。

4.1 手动构造

import smile.data.transform.ColumnTransform; import smile.util.function.Function; import java.util.Map; // 对 "income" 做对数变换,其余列原样保留 Map<String, Function> transforms = Map.of("income", Math::log); ColumnTransform ct = new ColumnTransform("log-income", transforms); DataFrame out = ct.apply(df);

4.2 行为特征

  • 映射表中不存在的列直接透传(复用同一ValueVector对象,零拷贝);
  • 被变换的列一律变为DoubleType(输入可空则为NullableDoubleType);
  • apply(DataFrame)使用IntStream.range(...).parallel()按列并行处理;
  • 可空列的空值掩码(BitSet)会被原样复制到输出向量。

4.3 toString 输出

System.out.println(ct); // log-income( // log(income) // )

输出格式为<name>(\n <function_toString()>, ...\n)。内置缩放器为每个Function实现了语义化的toString(),例如Scaler会输出形如(income - 1234.5000) / 5678.9000的公式(见 Scaler.java);而普通 lambda 若不覆写则打印默认的 JVM 引用。


5. InvertibleColumnTransform:正逆双映射

InvertibleColumnTransform extends ColumnTransform implements InvertibleTransform,额外保存一份逆 lambda 映射(见 InvertibleColumnTransform.java)。

import smile.data.transform.InvertibleColumnTransform; Map<String, Function> transforms = Map.of("price", v -> Math.log1p(v)); Map<String, Function> inverses = Map.of("price", v -> Math.expm1(v)); InvertibleColumnTransform ict = new InvertibleColumnTransform("log1p-price", transforms, inverses); DataFrame logPrices = ict.apply(df); DataFrame original = ict.invert(logPrices); // ≈ df

5.1 逆变换行为

  • 出现在inverses 映射中的列用对应 lambda 逆变换;
  • 不在逆映射中的列原样透传;
  • 可空列在正向与逆向两条路径上都保留空值掩码——空值既不会被伪造,也不会丢失。逆变换的invert(DataFrame)同样采用列并行实现,见 InvertibleColumnTransform.java。

6. 六种内置缩放器与标准化器

所有内置变换遵循同一使用模式:

  1. 调用Xxx.fit(trainData)从训练数据学习参数;
  2. 调用.apply(anyData)对训练集和测试集一视同仁地变换;
  3. 可选调用.invert(transformedData)恢复原始尺度。

训练/测试纪律:永远只对训练数据fit,再把拟合好的变换同时应用于训练集与测试集——这是避免数据泄漏(data leakage)的关键。

此外,所有列式fit()方法都接受可变的String... columns参数;省略时自动变换全部数值列(源码中通过schema.fields().stream().filter(StructField::isNumeric)筛选),见 Scaler.java。

6.1 Scaler — 最小-最大缩放

将每个数值列映射到[0, 1]:

x_scaled = (x - min) / (max - min)

训练范围之外的值会被**钳制(clamp)**到[0, 1]。源码实现要点:若列值跨度为零(常数列),缩放因子回退为1.0,逆变换invert为x * scale + lo(见 Scaler.java)。

import smile.feature.transform.Scaler; // 在训练集上拟合,变换全部数值列 InvertibleColumnTransform scaler = Scaler.fit(train); DataFrame trainScaled = scaler.apply(train); DataFrame testScaled = scaler.apply(test); // 只拟合指定列 InvertibleColumnTransform partial = Scaler.fit(train, "age", "income");

注意:Scaler的逆变换对落在训练[min, max]之外的测试值是有损的——因为这些值在正向阶段已被钳制到[0,1](见 Scaler.java 的类注释)。

适用场景:对特征量级敏感的算法(KNN、RBF 核 SVM、神经网络),且数据没有严重离群值。避免场景:存在大离群值——单个极端值会把所有正常值压缩到极小区间,此时改用WinsorScaler。

6.2 Standardizer — z-score 标准化

将每个数值列缩放为零均值、单位方差:

x_std = (x - μ) / σ

实现要点(见 Standardizer.java):标准差采用**样本公式(N−1 分母)**计算(MathEx.stdev);若标准差为零(常数列),缩放因子回退为1.0,只做中心化——训练集上输出全为 0;单行 DataFrame 同样如此处理。

import smile.feature.transform.Standardizer; InvertibleColumnTransform std = Standardizer.fit(train); DataFrame trainStd = std.apply(train); DataFrame testStd = std.apply(test);

适用场景:算法假设特征服从高斯分布,或使用距离/点积计算(线性回归、逻辑回归、线性 SVM、PCA)。避免场景:重尾分布或离群值较多——单个大离群值会抬高 σ,导致主体数据被欠压缩,此时改用RobustStandardizer。

6.3 RobustStandardizer — 中位数 / IQR 稳健标准化

减去中位数并除以四分位距(IQR):

x_robust = (x - median) / IQR

IQR 即第 75 与第 25 百分位之差;IQR 为零时缩放因子回退为1.0。实现要点:分位数通过smile.sort.IQAgent增量计算,是近似结果——在极小数据集(< 20 行)上可能与基于精确排序的分位数略有出入,见 RobustStandardizer.java。

import smile.feature.transform.RobustStandardizer; InvertibleColumnTransform robust = RobustStandardizer.fit(train); DataFrame robustTrain = robust.apply(train);

适用场景:数据含离群值或重尾分布。中位数与 IQR 对极端值的敏感度远低于均值与标准差。

6.4 WinsorScaler — 百分位钳制缩放

两步处理:

  1. 将训练分布[lower, upper]百分位范围之外的值钳制到边界;
  2. 将结果缩放到[0, 1]。
import smile.feature.transform.WinsorScaler; // 默认:5% 下界、95% 上界 InvertibleColumnTransform ws = WinsorScaler.fit(train); // 自定义百分位——在 1% 与 99% 处钳制 InvertibleColumnTransform ws2 = WinsorScaler.fit(train, 0.01, 0.99); // 只变换指定列 InvertibleColumnTransform ws3 = WinsorScaler.fit(train, 0.05, 0.95, "price", "quantity");

参数校验细节(见 WinsorScaler.java):lower < 0抛"Invalid lower: ...",upper > 1抛"Invalid upper: ...",lower >= upper抛"Invalid lower=... >= upper=...";默认fit(data, cols...)等价于fit(data, 0.05, 0.95, cols...)。分位数同样经IQAgent近似计算。

适用场景:训练集中存在有意义但不想让它们扭曲主体数据缩放的离群值,常见于金融与传感器数据。

6.5 MaxAbsScaler — 最大绝对值缩放

每个特征除以自身的最大绝对值,使所有值落在[-1, 1]。该缩放器不做中心化(不减去均值),因此不会破坏稀疏性:

x' = x / max(|x|)

实现要点:最大绝对值累加器初始化为 0.0,因此全负列也能得到正确结果(例如[-3,-2,-1]的缩放因子为 3.0);全零列回退为1.0(见 MaxAbsScaler.java)。

import smile.feature.transform.MaxAbsScaler; InvertibleColumnTransform mas = MaxAbsScaler.fit(train); DataFrame scaled = mas.apply(test);

适用场景:稀疏数据(如 TF-IDF 向量),或当数值符号有意义、且需要精确保留零值时。

6.6 Normalizer — 逐行单位范数

Normalizer与上述列式缩放器不同:它按**行(样本)**缩放,使整行向量的范数为 1,支持三种范数:

范数约束
L1Σ|xᵢ|= 1
L2√(Σxᵢ²)= 1
L_INFmax(|xᵢ|)= 1

实现要点(见 Normalizer.java):构造器要求norm非空、columns非空,否则分别抛NullPointerException/IllegalArgumentException;若行范数为零(零向量),缩放因子回退为1.0防止除零。

import smile.feature.transform.Normalizer; import smile.feature.transform.Normalizer.Norm; // 归一化指定列 String[] features = {"f1", "f2", "f3"}; Normalizer l2 = new Normalizer(Norm.L2, features); DataFrame normed = l2.apply(df); // 对单个 Tuple 逐行应用 Tuple row = df.get(0); Tuple normRow = l2.apply(row);

Normalizer不可逆(不是InvertibleTransform)。

适用场景:文本分类(TF-IDF)、基于余弦相似度的算法,或当"行的方向(特征比例)"比"行的幅值"更有意义时。


7. 组合变换:andThen、compose、pipeline 与 fit

7.1 andThen 与 compose

语义与java.util.function.Function完全一致(见 Transform.java):

// a.andThen(b) ≡ b(a(x)) —— 先 a 后 b Transform aFirst = a.andThen(b); // b.compose(a) ≡ b(a(x)) —— 同结果,不同写法 Transform alsoAFirst = b.compose(a);

两者都返回一个新的Transform(本质是一个 lambda,而非具名类):

InvertibleColumnTransform scaler = Scaler.fit(train); InvertibleColumnTransform std = Standardizer.fit(train); // 先缩放,再标准化 Transform both = scaler.andThen(std); DataFrame out = both.apply(test);

注意:组合结果是普通Transform,即使两个输入都可逆也不再是InvertibleTransform。若需要对组合结果求逆,使用Transform.fit(见 7.3),或按逆序分别对每一步求逆。

7.2 pipeline

Transform.pipeline(t1, t2, ..., tN)等价于连续andThen,三步以上时更易读:

Transform pipeline = Transform.pipeline( imputer, // 填充缺失值 scaler, // [0, 1] 缩放 std // z-score ); DataFrame train_prepped = pipeline.apply(train); DataFrame test_prepped = pipeline.apply(test);

无变换参数调用时抛IllegalArgumentException。

7.3 fit —— 数据依赖管道(核心范式)

Transform.fit(data, trainer1, trainer2, ...)中每个 trainer 都是Function<DataFrame, Transform>,它观察到的是当前已被前序步骤变换过的数据,并返回下一步骤:

Transform pipeline = Transform.fit(train, // 步骤 1:在原始训练数据上拟合 imputer data -> SimpleImputer.fit(data), // 步骤 2:在插补后的训练数据上拟合 scaler data -> Scaler.fit(data), // 步骤 3:在"缩放+插补"后的训练数据上拟合 standardizer data -> Standardizer.fit(data) ); // 对测试数据一次性应用整个管道 DataFrame testOut = pipeline.apply(test);

这是构建多步预处理管道的规范方式:每一步的参数都依赖于前序步骤的输出。trainer 列表为空时抛IllegalArgumentException。


8. 逆变换(Inverting Transforms)

InvertibleTransform.invert()把数据从变换空间映射回原始空间,用途包括:

  • 解读预测:在标准化目标上训练的模型,其输出必须先反标准化再展示给用户;
  • 重建误差:自编码器或 PCA 异常检测可在原始单位上度量重建保真度;
  • 调试:验证invert(apply(x)) ≈ x。
InvertibleColumnTransform std = Standardizer.fit(train); // 正向 DataFrame trainStd = std.apply(train); double[] yStd = model.predict(trainStd); // 恢复原始单位的预测值: // 构造单列 DataFrame 以便通过 invert() DataFrame predDf = DataFrame.of(new DoubleVector("y", yStd)); DataFrame predOriginal = std.invert(predDf);

8.1 逐行逆变换

Tuple row = df.get(0); Tuple scaled = std.apply(row); Tuple backToNormal = std.invert(scaled);

8.2 组合逆变换

多个可逆变换叠加时,求逆必须颠倒顺序:

InvertibleColumnTransform s1 = Scaler.fit(train); InvertibleColumnTransform s2 = Standardizer.fit(s1.apply(train)); // 正向:先 s1 后 s2 DataFrame fwd = s2.apply(s1.apply(test)); // 逆向:先 s2⁻¹ 后 s1⁻¹(顺序颠倒) DataFrame inv = s1.invert(s2.invert(fwd));

9. 可空列处理

SMILE 支持由NullableDoubleVector(及对应的其他基本类型向量)支撑的可空列。本包所有变换都保持可空性:

  • 可空输入列必然产生可空输出列——空值位掩码被完整复制;
  • 变换 lambda 仍会被调用在空单元格对应的原始double值上(约定为Double.NaN),但结果会被忽略,输出向量中以NaN/null 占位;
  • 逆变换同样保留空值掩码。
// 假设 "income" 存在部分空值 InvertibleColumnTransform scaler = Scaler.fit(train); DataFrame scaled = scaler.apply(train); // scaled.column("income").isNullable() → true (与输入一致) // scaled.column("income").isNullAt(k) → 与 train 一致 DataFrame restored = scaler.invert(scaled); // restored.column("income").isNullable() → true (空值掩码被保留)

空值掩码在 ColumnTransform.java 与 InvertibleColumnTransform.java 中均以BitSet显式复制实现。


10. 序列化

Transform继承java.io.Serializable。所有内置实现及其闭包捕获的 lambda 都可序列化,因此拟合好的变换可以保存并重新加载:

// 保存 try (var out = new ObjectOutputStream(new FileOutputStream("scaler.ser"))) { out.writeObject(scaler); } // 加载 InvertibleColumnTransform loaded; try (var in = new ObjectInputStream(new FileInputStream("scaler.ser"))) { loaded = (InvertibleColumnTransform) in.readObject(); } // 用加载的变换处理新数据 DataFrame newData = loaded.apply(incoming);

lambda 陷阱:传给ColumnTransform/InvertibleColumnTransform的 lambda 若引用了一个不可序列化的宿主类,保存时会抛NotSerializableException。因此只在宿主类本身Serializable时使用方法引用或匿名类,或者选用内置缩放器(其内部Function已实现可序列化)。


11. 编写自定义变换

方案 A — 直接实现 Transform

适用于非列式变换(行归一化、降维等):

import smile.data.transform.Transform; import smile.data.Tuple; public class ClipTransform implements Transform { private final double min, max; private final Set<String> columns; public ClipTransform(double min, double max, String... columns) { this.min = min; this.max = max; this.columns = new HashSet<>(Arrays.asList(columns)); } @Override public Tuple apply(Tuple x) { StructType schema = x.schema(); return new smile.data.AbstractTuple(schema) { @Override public Object get(int i) { String name = schema.field(i).name(); if (columns.contains(name)) { double v = x.getDouble(i); return Math.max(min, Math.min(max, v)); } return x.get(i); } }; } }

方案 B — 用 ColumnTransform + lambda

列式数值变换的最简做法:

// 把所有值钳制到 [-3, 3] Map<String, Function> clips = new HashMap<>(); for (String col : numericColumns) { clips.put(col, x -> Math.max(-3.0, Math.min(3.0, x))); } ColumnTransform clipper = new ColumnTransform("clip±3", clips);

方案 C — 用 InvertibleColumnTransform + lambda

需要同时提供正逆变换时:

// Box-Cox 幂变换,λ = 0.5(即平方根变换) double lambda = 0.5; Map<String, Function> fwd = Map.of( "revenue", x -> (Math.pow(x, lambda) - 1.0) / lambda ); Map<String, Function> inv = Map.of( "revenue", y -> Math.pow(y * lambda + 1.0, 1.0 / lambda) ); InvertibleColumnTransform boxCox = new InvertibleColumnTransform("BoxCox(0.5)", fwd, inv);

方案 D — 数据依赖的自定义变换(配合 fit)

// 每个列除以自身训练中位数 static InvertibleColumnTransform fitMedianScaler(DataFrame data, String... cols) { Map<String, Function> transforms = new HashMap<>(); Map<String, Function> inverses = new HashMap<>(); for (String col : cols) { double[] vals = data.column(col).toDoubleArray(); double median = MathEx.median(vals); double scale = MathEx.isZero(median) ? 1.0 : Math.abs(median); transforms.put(col, x -> x / scale); inverses.put(col, y -> y * scale); } return new InvertibleColumnTransform("MedianScaler", transforms, inverses); }

这种fit风格的自定义变换可直接传入Transform.fit(data, ...)组成数据依赖管道。


12. 端到端实战教程:贷款数据集预处理

教程目标:预处理包含loan_amount(double)、annual_income(double,可空)、grade(类别型)、term(int)与标签default(int)的贷款数据集。

步骤 1 — 划分数据

DataFrame data = Read.csv("loans.csv"); // 80/20 训练/测试划分(按索引) int n = data.size(); int trainSize = (int)(n * 0.8); DataFrame train = data.slice(0, trainSize); DataFrame test = data.slice(trainSize, n);

步骤 2 — 插补缺失值

import smile.feature.imputation.SimpleImputer; // 拟合:学习每列填充值(数值列取均值,类别列取众数) Transform imputer = SimpleImputer.fit(train); DataFrame trainImputed = imputer.apply(train); DataFrame testImputed = imputer.apply(test);

步骤 3 — 缩放数值特征

import smile.feature.transform.WinsorScaler; // 收入存在离群值,用 Winsor 提升稳健性 InvertibleColumnTransform scaler = WinsorScaler.fit(trainImputed, 0.01, 0.99, "loan_amount", "annual_income"); DataFrame trainScaled = scaler.apply(trainImputed); DataFrame testScaled = scaler.apply(testImputed);

步骤 4 — 组合为单一可复用管道

Transform fullPipeline = Transform.fit(train, SimpleImputer::fit, data -> WinsorScaler.fit(data, 0.01, 0.99, "loan_amount", "annual_income") ); // 一次调用即可——结果与步骤 2-3 相同 DataFrame trainPrepped = fullPipeline.apply(train); DataFrame testPrepped = fullPipeline.apply(test);

步骤 5 — 用 Formula 训练模型

import smile.data.formula.Formula; Formula formula = Formula.of("default", Terms.$("loan_amount"), Terms.$("annual_income"), Terms.$("term"), Terms.$("grade")); // 提取设计矩阵与响应 var X = formula.matrix(trainPrepped); var y = formula.y(trainPrepped).toIntArray(); // … 用 X 与 y 拟合模型 …

步骤 6 — 逆变换预测结果用于解读

// 假设我们预测了一个连续分数,并希望以原始收入单位理解它: double[] scores = model.predict(formula.matrix(testPrepped)); DataFrame scoreDf = DataFrame.of(new DoubleVector("annual_income", scores)); DataFrame inOriginalScale = scaler.invert(scoreDf); System.out.println(inOriginalScale);

步骤 7 — 检视并保存管道

// 检视每一步学到了什么 System.out.println(fullPipeline); // 序列化用于生产环境复用 try (var out = new ObjectOutputStream(new FileOutputStream("pipeline.ser"))) { out.writeObject(fullPipeline); }

13. 如何选择合适的变换

场景推荐变换
无明显离群值、量级区间重要Scaler
高斯假设、基于距离/点积的算法Standardizer
存在离群值、追求稳健性RobustStandardizer
存在离群值、输出需要有界WinsorScaler
稀疏数据、零必须保持为零MaxAbsScaler
方向(比例)重要、量级无关Normalizer(L2)
文本/计数向量Normalizer(L1 或 L2)
自定义对数/幂变换InvertibleColumnTransform+ lambda
缩放前有缺失值先SimpleImputer,再缩放
多步串联Transform.fit(...)管道

列式缩放器横向对比

给定一列值{1, 2, 3, 100}(其中100是离群值):

变换123100
Scaler0.0000.0100.0201.000
Standardizer(μ≈26.5, σ≈48.0)−0.53−0.51−0.491.53
RobustStandardizer(med=2.5, IQR≈1.5)−1.00−0.330.3365.0
WinsorScaler(5/95 pct)0.0000.5001.0001.000
MaxAbsScaler(max=100)0.0100.0200.0301.000

Scaler因离群值把{1,2,3}压缩到接近 0;WinsorScaler钳制离群值后让{1,2,3}在[0,1]内均匀铺开;RobustStandardizer对离群值稳健,但其输出无界。


14. API 速查表

Transform(接口 —smile.data.transform)

成员说明
apply(Tuple)变换一行(抽象方法)。
apply(DataFrame)变换全部行(默认逐行流式;ColumnTransform覆写为列并行批量)。
andThen(Transform)组合:先this,后after。
compose(Transform)组合:先before,后this。
Transform.pipeline(Transform...)从左到右串联多个变换;空输入抛异常。
Transform.fit(DataFrame, Function<DataFrame,Transform>...)拟合数据依赖管道,每个 trainer 看到已被前序步骤变换的数据;空输入抛异常。

InvertibleTransform(接口 —smile.data.transform)

成员说明
invert(Tuple)逆变换一行。
invert(DataFrame)逆变换全部行;保留可空列。

ColumnTransform(类 —smile.data.transform)

成员说明
ColumnTransform(String name, Map<String,Function> transforms)构造函数。
apply(Tuple)对匹配列套用 lambda,其余列透传。
apply(DataFrame)列并行批量变换;保留空值掩码。
toString()"<name>(\n <fn>, ...\n)"

InvertibleColumnTransform(类 —smile.data.transform)

成员说明
InvertibleColumnTransform(String, Map<String,Function>, Map<String,Function>)构造函数(名称、正向 lambdas、逆 lambdas)。
invert(Tuple)套用逆 lambdas;未匹配列透传。
invert(DataFrame)列并行求逆;保留空值掩码。

内置缩放器(均在smile.feature.transform)

类工厂方法公式可逆
ScalerScaler.fit(data, cols...)(x−min)/(max−min)钳制到[0,1]是
StandardizerStandardizer.fit(data, cols...)(x−μ)/σ是
RobustStandardizerRobustStandardizer.fit(data, cols...)(x−median)/IQR是
WinsorScalerWinsorScaler.fit(data)或fit(data, lo, hi, cols...)先钳制再(x−pLo)/(pHi−pLo)是
MaxAbsScalerMaxAbsScaler.fit(data, cols...)x / max(|x|)是
Normalizernew Normalizer(Norm, cols...)逐行单位范数(L1/L2/L∞)否

所有列式fit()方法均接受可选的可变String... columns参数;省略时自动变换全部数值列。


附:源码级验证线索

  • 接口与实现:Transform/InvertibleTransform/ColumnTransform/InvertibleColumnTransform见 base/src/main/java/smile/data/transform;
  • 内置缩放器:Scaler、Standardizer、RobustStandardizer、WinsorScaler、MaxAbsScaler、Normalizer见 core/src/main/java/smile/feature/transform;
  • 单元测试:基于 ImageSegmentation 数据集的六种变换数值断言见 FeatureTransformTest.java;常数列回退缩放、子集列变换、空 DataFrame 与非数值列异常等边界用例见 FeatureTransformEdgeCaseTest.java,Normalizer专项测试见 NormalizerTest.java。

从上述源码与测试可以确认:所有列式缩放器在常数列/全零列上都会把缩放因子回退为1.0以避免除零;fit在空 DataFrame 上抛IllegalArgumentException("Empty data frame"),指定非数值列抛"<col> is not numeric"——这些边界行为已被 FeatureTransformEdgeCaseTest.java 直接断言,可作为生产代码中的防御性参考。

  • 人工智能
  • 机器学习
  • 深度学习
  • NLP
  • 大模型
  • 模型推理服务
  • 数据可视化

【免费下载链接】smile

Statistical Machine Intelligence & Learning Engine

项目地址:https://gitcode.com/gh_mirrors/smi/smile
点击查看免费下载
上一篇:Visual C++运行库修复:一站式解决Windows软件兼容性问题
下一篇:3步解锁iOS设备:applera1n开源工具实现激活锁完美绕过

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询