- 人工智能
- 机器学习
- 深度学习
- NLP
- 大模型
- 模型推理服务
- 数据可视化
【免费下载链接】smile
Statistical Machine Intelligence & Learning Engine
本篇技术指南完整讲解 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); // ≈ df5.1 逆变换行为
- 出现在inverses 映射中的列用对应 lambda 逆变换;
- 不在逆映射中的列原样透传;
- 可空列在正向与逆向两条路径上都保留空值掩码——空值既不会被伪造,也不会丢失。逆变换的
invert(DataFrame)同样采用列并行实现,见 InvertibleColumnTransform.java。
6. 六种内置缩放器与标准化器
所有内置变换遵循同一使用模式:
- 调用
Xxx.fit(trainData)从训练数据学习参数; - 调用
.apply(anyData)对训练集和测试集一视同仁地变换; - 可选调用
.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) / IQRIQR 即第 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 — 百分位钳制缩放
两步处理:
- 将训练分布
[lower, upper]百分位范围之外的值钳制到边界; - 将结果缩放到
[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_INF | max(|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是离群值):
| 变换 | 1 | 2 | 3 | 100 |
|---|---|---|---|---|
Scaler | 0.000 | 0.010 | 0.020 | 1.000 |
Standardizer(μ≈26.5, σ≈48.0) | −0.53 | −0.51 | −0.49 | 1.53 |
RobustStandardizer(med=2.5, IQR≈1.5) | −1.00 | −0.33 | 0.33 | 65.0 |
WinsorScaler(5/95 pct) | 0.000 | 0.500 | 1.000 | 1.000 |
MaxAbsScaler(max=100) | 0.010 | 0.020 | 0.030 | 1.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)
| 类 | 工厂方法 | 公式 | 可逆 |
|---|---|---|---|
Scaler | Scaler.fit(data, cols...) | (x−min)/(max−min)钳制到[0,1] | 是 |
Standardizer | Standardizer.fit(data, cols...) | (x−μ)/σ | 是 |
RobustStandardizer | RobustStandardizer.fit(data, cols...) | (x−median)/IQR | 是 |
WinsorScaler | WinsorScaler.fit(data)或fit(data, lo, hi, cols...) | 先钳制再(x−pLo)/(pHi−pLo) | 是 |
MaxAbsScaler | MaxAbsScaler.fit(data, cols...) | x / max(|x|) | 是 |
Normalizer | new 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
相关推荐
sktime数据转换与特征工程:时间序列预处理
sktime数据转换与特征工程:时间序列预处理 本文全面介绍了sktime库在时间序列数据预处理和特征工程方面的强大功能。内容涵盖从基础数据转换(标准化、差分、
机器学习人工智能数据分析Higgs TTS v3-4b vs 竞品:四大权威 benchmark 全面领先的关键解析
Higgs TTS v3 4b vs 竞品:四大权威 benchmark 全面领先的关键解析 在当今快速发展的AI语音合成领域, Higgs TTS v3 4b
requests库文件上传功能:Multipart请求实现详解
requests库文件上传功能:Multipart请求实现详解 在网络开发中,文件上传是常见的功能需求,而Multipart请求是实现这一功能的标准方式。 re
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考