让我们暂时忽略 Spark。一般来说,包括逻辑回归在内的线性模型需要数字自变量。它不以任何方式特定于 Spark / MLlib。如果输入包含分类或有序变量,则必须首先对这些变量进行编码。一些语言,比如 R,以透明的方式处理这个问题:
> df <- data.frame(x1 = c("a", "b", "c", "d"), y=c("aa", "aa", "bb", "bb"))
> glm(y ~ x1, df, family="binomial")
Call: glm(formula = y ~ x1, family = "binomial", data = df)
Coefficients:
(Intercept) x1b x1c x1d
-2.357e+01 -4.974e-15 4.713e+01 4.713e+01
...
但真正在幕后使用的是所谓的设计矩阵:
> model.matrix( ~ x1, df)
(Intercept) x1b x1c x1d
1 1 0 0 0
2 1 1 0 0
3 1 0 1 0
4 1 0 0 1
...
跳过细节它与OneHotEncoder
Spark 中执行的转换类型相同。
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer}
val df = sqlContext.createDataFrame(Seq(
Tuple1("a"), Tuple1("b"), Tuple1("c"), Tuple1("d")
)).toDF("x").repartition(1)
val indexer = new StringIndexer()
.setInputCol("x")
.setOutputCol("xIdx")
.fit(df)
val indexed = indexer.transform(df)
val encoder = new OneHotEncoder()
.setInputCol("xIdx")
.setOutputCol("xVec")
val encoded = encoder.transform(indexed)
encoded
.select($"xVec")
.map(_.getAs[Vector]("xVec").toDense)
.foreach(println)
Spark 更进一步,所有特征,即使算法允许名义/有序自变量,也必须Double
使用spark.mllib.linalg.Vector
. 如果spark.ml
它是一DataFrame
列,则spark.mllib
在spark.mllib.regression.LabeledPoint
.
但是,根据模型对特征向量的解释可能会有所不同。如上所述,对于线性模型,这些将被解释为数值变量。因为Naive Bayes
这些被认为是名义上的。如果模型同时接受数值变量和名义变量 Spark 并以不同的方式处理每个组,例如决策/回归树,您可以提供categoricalFeaturesInfo
参数。
值得指出的是,因变量也应进行编码,Double
但与自变量不同,可能需要正确处理额外的元数据。如果您查看indexed
DataFrame,您会发现StringIndexer
不仅 transforms x
,而且还添加了属性:
scala> org.apache.spark.ml.attribute.Attribute.fromStructField(indexed.schema(1))
res12: org.apache.spark.ml.attribute.Attribute = {"vals":["d","a","b","c"],"type":"nominal","name":"xIdx"}
最后,一些Transformers
from ML
,如VectorIndexer,可以根据不同值的数量自动检测和编码分类变量。