Spark(OneHotEncoder + StringIndexer)= FeatureImportance怎么样?

时间:2016-09-02 09:51:40

标签: apache-spark pyspark apache-spark-mllib

当我使用StringIndexer和OneHot Encoder为我的矩阵做数据准备时,我现在怎样才能知道重要特征的名称/来源?

randomForest分类器只会给我索引,而我看不到原始数据的链接: - (

以下代码: https://github.com/spark-in-action/first-edition/blob/master/ch08/python/ch08-listings.py

在这个数据集上: https://github.com/spark-in-action/first-edition/blob/master/ch08/adult.names

我提取了这个代码子集:

$data.take(1)
[Row(age=39.0, occupation=u' State-gov', capital_gain=77516.0, education=u' Bachelors', marital_status=u' Never-married', workclass=u' Adm-clerical', relationship=u' Not-in-family', race=u' White', sex=u' Male', capital_loss=2174.0, fnlwgt=0.0, hours_per_week=40.0, native_country=u' United-States', income=u' <=50K')]

$data2 = indexStringColumns(data, typeString)
$data2.take(1)
>[Row(age=39.0, capital_gain=77516.0, capital_loss=2174.0, fnlwgt=0.0, hours_per_week=40.0, occupation=4.0, education=2.0, marital_status=1.0, workclass=3.0, relationship=1.0, race=0.0, sex=0.0, native_country=0.0, income=0.0)]

$data3 = oneHotEncodeColumns(data2, colString_without_Y)
$data3.take(1)
>[Row(age=39.0, capital_gain=77516.0, capital_loss=2174.0, fnlwgt=0.0, hours_per_week=40.0, income=0.0, occupation=SparseVector(9, {4: 1.0}), education=SparseVector(16, {2: 1.0}), marital_status=SparseVector(7, {1: 1.0}), workclass=SparseVector(15, {3: 1.0}), relationship=SparseVector(6, {1: 1.0}), race=SparseVector(5, {0: 1.0}), sex=SparseVector(2, {0: 1.0}), native_country=SparseVector(42, {0: 1.0}))]

$# modélisation :

$rf          = RandomForestClassifier(labelCol=colY, numTrees=ntree, maxDepth=depth,)
$model       = rf.fit(trainingData)
$predictions = model.transform(testData)

$model.featureImportances
>SparseVector(107, {0: 0.1016, 1: 0.0302, 2: 0.0995, 3: 0.0207, 4: 0.0517, 5: 0.007, 6: 0.0061, 7: 0.0033, 8: 0.0021, 9: 0.0041, 10: 0.0058, 11: 0.0036, 12: 0.0001, 14: 0.0162, 15: 0.0067, 16: 0.0199, 17: 0.0134, 18: 0.0026, 19: 0.0059, 20: 0.0025, 21: 0.0038, 22: 0.0053, 23: 0.0064, 24: 0.003, 25: 0.0014, 26: 0.007, 27: 0.0023, 28: 0.001, 29: 0.0002, 30: 0.1473, 31: 0.0609, 32: 0.0057, 33: 0.0024, 34: 0.0019, 35: 0.001, 36: 0.0002, 37: 0.0258, 38: 0.0054, 39: 0.0244, 40: 0.0045, 41: 0.0055, 42: 0.0186, 43: 0.0061, 44: 0.0021, 45: 0.0043, 46: 0.0029, 47: 0.0046, 48: 0.0024, 49: 0.0019, 50: 0.0001, 51: 0.0, 52: 0.0786, 53: 0.0354, 54: 0.0169, 55: 0.0117, 56: 0.015, 57: 0.0026, 58: 0.0046, 59: 0.0064, 60: 0.0025, 61: 0.0014, 62: 0.0011, 63: 0.007, 64: 0.0312, 65: 0.0048, 66: 0.005, 67: 0.0022, 68: 0.0008, 69: 0.0008, 70: 0.0006, 71: 0.0006, 72: 0.0003, 73: 0.0013, 74: 0.0006, 75: 0.0012, 76: 0.0004, 77: 0.0003, 78: 0.0002, 79: 0.0005, 80: 0.0002, 81: 0.0003, 82: 0.0002, 83: 0.0003, 84: 0.0004, 85: 0.0002, 86: 0.0001, 87: 0.0003, 88: 0.0004, 89: 0.0001, 90: 0.0, 91: 0.0005, 93: 0.0004, 94: 0.0002, 95: 0.0003, 96: 0.0, 97: 0.0001, 98: 0.0001, 99: 0.0001, 100: 0.0, 101: 0.0, 102: 0.0, 103: 0.0, 104: 0.0, 105: 0.0002})

我怎么知道每个indice在原始数据矩阵中链接回来的类别值?

2 个答案:

答案 0 :(得分:0)

StringIndexerModel.labels就是您所需要的。

例如,

from pyspark.ml.feature import StringIndexer
from pyspark.sql.types import Row

data = sc.parallelize([
  Row(v="A"),
  Row(v="B"), 
]).toDF()

labels = StringIndexer(inputCol="v", outputCol="indexed").fit(data).labels

for idx, v in enumerate(labels):
  print idx, v

OneHotEncoder在这里不是什么大问题,因为它只是将数字转换为索引。注意

  

默认情况下不包括最后一个类别(可通过dropLast配置)

所以你需要确保价值&amp;指数是一致的。

答案 1 :(得分:0)

解决方案是将我从StringIndexer获得的标签映射到模型的特征重要性。标签最终将被映射。 下面的示例是scala,但方法是相同的。

创建DataFrame:

val df = spark.createDataFrame(Seq(
  (0, "a",0),
  (1, "b",1),
  (2, "c",0),
  (3, "a",0),
  (4, "a",1),
 (5, "c",1)
)).toDF("id", "category","y")

将类别编码为类别索引列并获取标签

import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer}
val indexer = new StringIndexer().setInputCol("category").setOutputCol("categoryIndex").fit(df)
val indexed = indexer.transform(df)
val labels = indexer.labels.map("Category=" + _)

这就是labels看起来

的方式
scala> labels
res9: Array[String] = Array(Category=a, Category=c, Category=b)

使用OneHotEncoder将类别索引映射到二进制向量

val encoder = new OneHotEncoder().setInputCol("categoryIndex").setOutputCol("categoryVec")
val encoded = encoder.transform(indexed)
encoded.select("id", "categoryVec").show()

训练RandomForest模型

import org.apache.spark.ml.classification.{RandomForestClassificationModel, RandomForestClassifier}
val rf = new RandomForestClassifier().setLabelCol("y").setFeaturesCol("categoryVec").setNumTrees(10)
val rfmodel = rf.fit(encoded)

现在要将要素重要性映射到标签,我们需要压缩featureImportance索引和值,然后在地图中我们将获得该索引位置labels(x._1)的标签,其中x._1是索引值。

val featureImportance = rfmodel.featureImportances.toSparse
val topFeatures = featureImportance.indices.zip(featureImportance.values).sortBy( - _._2).map(x => (labels(x._1),x._2))

这是最终输出topFeatures的外观:

Array[(String, Double)] = Array((Category=a,0.7998663887552777), (Category=c,0.20013361124472231))