使用Scala做特征工程常用方法

写在前面:文章中主要给出了各特征处理所使用的方法和代码例子,并没有详细的解释每个方法的含义是什么、参数是什么、方法背后对应的处理和实现逻辑又是什么。

如果要从更底层的原理上了解这些方法,首先还是建议读你是用的Scala版本的官方文档,如我近期放在手头上会随时查阅的Spark2.3.0 ScalaDoc

或者找其他博主的一些相对详细的文章看看。虽然我还是建议你看官方文档,但前提是你要有代码的基础,要懂一些对象、抽象类、抽象方法类的知识,要有一些设计模式的架构设计知识。

它们会告诉你一些代码里看不到的细节,如StringIndexer将String类型的标签列按照出现频次建立索引:出现次数最多的标签索引值为0。(若输入列为数字类型,先将其转换为String类型再建索引);

再比如,使用向量的时候不使用名称Vector,是因为默认情况下,Scala会导入scala.collection.immutable.Vector。我们要引入org.apache.spark.ml.linalg.Vector的工厂方法对象(实际为一个object)才可以。

等等跟这些方法的实现、源码相关的细节。


1、为后面的代码示例准备下数据

    val modelPath = "/user/gaoToby/model_saved"
    val dataPath = "/user/gaoToby/ml_data"
    //1-1 load data
    // in Scala
    val sales = spark.read.format("csv")
      .option("header", "true")
      .option("inferSchema", "true")
      .load(dataPath + "/retail-data/by-day/*.csv")
      .coalesce(5)
      .where("Description IS NOT NULL")
    val fakeIntDF = spark.read.parquet(dataPath + "/data/simple-ml-integers")
    var simpleDF = spark.read.json(dataPath + "/data/simple-ml")
    val scaleDF = spark.read.parquet(dataPath + "/data/simple-ml-scaling")
    
    sales.cache()
    sales.show()

    fakeIntDF.cache()
    fakeIntDF.show()

    scaleDF.cache()
    scaleDF.show()
    scaleDF.printSchema()

    simpleDF.cache()
    simpleDF.show()
    simpleDF.printSchema()

2、标准化

    //2 - StandardScaler
    import org.apache.spark.ml.feature.StandardScaler
    val ss = new StandardScaler().setInputCol("features")
    ss.fit(scaleDF).transform(scaleDF).show()

    //10 - MaxAbsScaler
    import org.apache.spark.ml.feature.MinMaxScaler
    val minMax = new MinMaxScaler()
      .setMin(5) //自定义最小值
      .setMax(10) //自定义最大值
      .setInputCol("features")
      .setOutputCol("minMax_features")
    val fittedminMax = minMax.fit(scaleDF)
    fittedminMax.transform(scaleDF).show()

    //11 - MaxAbsScaler
    import org.apache.spark.ml.feature.MaxAbsScaler
    val maScaler = new MaxAbsScaler()
      .setInputCol("features")
      .setOutputCol("maxAbs_features")
    val fittedmaScaler = maScaler.fit(scaleDF)
    fittedmaScaler.transform(scaleDF).show()

    //12 - ElementwiseProduct 点积
    import org.apache.spark.ml.feature.ElementwiseProduct
    import org.apache.spark.ml.linalg.Vectors
    val scaleUpVec = Vectors.dense(10.0, 15.0, 20.0)
    val scalingUp = new ElementwiseProduct()
      .setScalingVec(scaleUpVec) //用来对列进行自定义放缩,每列乘不同的倍数放缩。向量和矩阵的点积
      .setInputCol("features")
      .setOutputCol("elementWiseProduct_features")
    scalingUp.transform(scaleDF).show()


    // 13 - 范数规范化
    import org.apache.spark.ml.feature.Normalizer
    val manhattanDistance = new Normalizer()
      .setP(1) //L范数
      .setInputCol("features")
      .setOutputCol("normed_features")
    manhattanDistance.transform(scaleDF).show()

3、离散化

    //7 - Bucketizer
    import org.apache.spark.ml.feature.Bucketizer
    val bucketBorders = Array(-1.0, 5.0, 10.0, 250.0, 600.0) //如果改成等距、等频的分桶呢?
    val bucketerBin = new Bucketizer()
      .setSplits(bucketBorders) //设置分桶方式
      .setInputCol("id") //设置分桶列
      .setOutputCol("binId") //设置输出列名
    bucketerBin.transform(contDF).show()

    //8 - QuantileDiscretizer
    import org.apache.spark.ml.feature.QuantileDiscretizer
    val bucketerQuant = new QuantileDiscretizer()
      .setNumBuckets(5) //等频分桶
      .setInputCol("id")
      .setOutputCol("quantId")
    val fittedBucketer = bucketerQuant.fit(contDF)
    fittedBucketer.transform(contDF).show()

4、特征组装器

    //5 VectorAssembler
    val va = new VectorAssembler()
      .setInputCols(Array("int1", "int2", "int3"))
    va.transform(fakeIntDF).show()

    //3 - RFormula
    import org.apache.spark.ml.feature.RFormula
    val supervised = new RFormula()
      .setFormula("lab ~ . + color:value1 + color:value2")
    supervised.fit(simpleDF).transform(simpleDF).show()

    //.setFormula("y ~ . - w - gmv + x1_times:w + x2_times:w + x3_times:w + x4_times:w") 注意RFormula中符号的含义: . - + :


    //自己使用map进行特征向量的组装。
    //引入org.apache.spark.ml.linalg.Vector的工厂方法对象(实际为一个object)
    //不使用名称Vector,是因为默认情况下,Scala会导入scala.collection.immutable.Vector, 为    了和这个类的伴生对象更好的区分
    import org.apache.spark.ml.linalg.Vectors  
    //稠密型向量
    Vectors.dense(Array(1.2, 2, 3))  // 推荐写法
    Vectors.dense(1.2, 2, 3)         // 写法2

    //稀疏型向量
    Vectors.sparse(10, Array(0, 3), Array(1, 3.1)) // 推荐写法
    Vectors.sparse(10, Seq(0->1.0, 3->3.1)) // 写法2

 注意:

5、编码

 // 14 -1 StringIndexer 目标分类列编码 - 整数编码映射 string to index
    import org.apache.spark.ml.feature.StringIndexer
    val lblIndxr0 = new StringIndexer()
      .setInputCol("lab")
      .setOutputCol("labelInd")
      .setHandleInvalid("skip") //可选项:skip \ keep ,含义是如果在测试的时候发现了一个未曾见过的分类,选择是skip忽略这条数据,还是将其分类统一归为N+1
    val idxRes = lblIndxr0.fit(simpleDF).transform(simpleDF)
    idxRes.show()

    // 14 -2 StringIndexer  特征值列编码
    val valIndexer = new StringIndexer()
      .setInputCol("value1")
      .setOutputCol("valueInd")

    valIndexer.fit(simpleDF).setHandleInvalid("keep").transform(simpleDF).show()

    // 16 - VectorIndexer   对特征向量进行编码。
    import org.apache.spark.ml.feature.VectorIndexer
    import org.apache.spark.ml.linalg.Vectors
    val idxIn = spark.createDataFrame(Seq(
          (Vectors.dense(1, 2, 3),1),
          (Vectors.dense(2, 5, 6),2),
          (Vectors.dense(1, 8, 9),3)
        )).toDF("features", "label")
  
    val indxr = new VectorIndexer()
      .setInputCol("features")
      .setOutputCol("idxed")
      .setMaxCategories(3) //出现大于N个不同值的数值列看作连续型的特征列
    indxr.fit(idxIn).transform(idxIn).show()

   // 17 - OneHotEncoder  ,并比较其与 StringIndexer的异同
    import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer}
    val lblIndxr = new StringIndexer().setInputCol("color").setOutputCol("colorInd")
    val colorLab = lblIndxr.fit(simpleDF).transform(simpleDF.select("color"))
    val ohe = new OneHotEncoder().setInputCol("colorInd")
    ohe.transform(colorLab).show()

6、反编码

    // 15 -  reverse index to string 反编码
    import org.apache.spark.ml.feature.IndexToString
    val labelReverse = new IndexToString().setInputCol("labelInd")
    labelReverse.transform(idxRes).show() //注意这里的DF是有经过编码的

7、特征扩展

    // 27 - PolynomialExpansion
    import org.apache.spark.ml.feature.PolynomialExpansion
    val pe = new PolynomialExpansion()
      .setInputCol("features")
      .setDegree(2)
    pe.transform(scaleDF).show(false)

8、特征选择

    // 26 - PCA
    import org.apache.spark.ml.feature.PCA
    val pca = new PCA()
      .setInputCol("features")
      .setK(2)
    pca.fit(scaleDF).transform(scaleDF).show(false)

   // 28 - ChiSqSelector 特征选择 - 卡方
    import org.apache.spark.ml.feature.{ChiSqSelector, Tokenizer}
    val tkn2 = new Tokenizer().setInputCol("Description").setOutputCol("DescOut")
    val tokenized3 = tkn2
      .transform(sales.select("Description", "CustomerId"))
      .where("CustomerId IS NOT NULL")
    val prechi = fittedCV.transform(tokenized3) 
    val chisq = new ChiSqSelector()
      .setFeaturesCol("countVec")
      .setLabelCol("CustomerId")
      .setNumTopFeatures(2) //保留特征量
    chisq.fit(prechi).transform(prechi)
      .drop("customerId", "Description", "DescOut").show()

9、保存

    // 29 - save
    val fittedPCA = pca.fit(scaleDF)
    fittedPCA.write.overwrite().save(modelPath + "/tmp/fittedPCA")

10、加载

    // 30 - load
    import org.apache.spark.ml.feature.PCAModel
    val loadedPCA = PCAModel.load(modelPath + "/tmp/fittedPCA")
    loadedPCA.transform(scaleDF).show()

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值