2016-07-28 67 views
0

我从一本书中读了一段关于二元决策树的代码。它在原始数据中只有一个分类特征,即字段(3),并被转换为一个k(单热编码)。如何处理决策树中的多个分类特征?

def PrepareData(sc: SparkContext): (RDD[LabeledPoint], RDD[LabeledPoint], RDD[LabeledPoint], Map[String, Int]) = { 

    val rawDataWithHeader = sc.textFile("data/train.tsv") 
    val rawData = rawDataWithHeader.mapPartitionsWithIndex { (idx, iter) => if (idx == 0) iter.drop(1) else iter } 
    val lines = rawData.map(_.split("\t")) 


    val categoriesMap = lines.map(fields => fields(3)).distinct.collect.zipWithIndex.toMap 
    val labelpointRDD = lines.map { fields => 
    val trFields = fields.map(_.replaceAll("\"", "")) 
    val categoryFeaturesArray = Array.ofDim[Double](categoriesMap.size) 
    val categoryIdx = categoriesMap(fields(3)) 
    categoryFeaturesArray(categoryIdx) = 1 
    val numericalFeatures = trFields.slice(4, fields.size - 1).map(d => if (d == "?") 0.0 else d.toDouble) 
    val label = trFields(fields.size - 1).toInt 
    LabeledPoint(label, Vectors.dense(categoryFeaturesArray ++ numericalFeatures)) 
    } 

    val Array(trainData, validationData, testData) = labelpointRDD.randomSplit(Array(8, 1, 1)) 
    return (trainData, validationData, testData, categoriesMap) 
} 

不知如何,如果有原始数据几个类别特征修改代码,让我们说现场(3),现场(5),现场(7)全部类别特征。

我修改的第一行:

def PrepareData(sc: SparkContext): (RDD[LabeledPoint], RDD[LabeledPoint], RDD[LabeledPoint], Map[String, Int], Map[String, Int], Map[String, Int], Map[String, Int]) =...... 

然后,我转换另一两个字段成1-的k值编码,因为它是像完成:

val categoriesMap5 = lines.map(fields => fields(5)).distinct.collect.zipWithIndex.toMap 
val categoriesMap7 = lines.map(fields => fields(7)).distinct.collect.zipWithIndex.toMap 
val categoryFeaturesArray5 = Array.ofDim[Double](categoriesMap5.size) 
val categoryFeaturesArray7 = Array.ofDim[Double](categoriesMap7.size) 
val categoryIdx3 = categoriesMap5(fields(5)) 
val categoryIdx5 = categoriesMap7(fields(7)) 
categoryFeaturesArray5(categoryIdx5) = 1 
categoryFeaturesArray7(categoryIdx7) = 1 

最后,我修改LabeledPoint和返回像:

LabeledPoint(label, Vectors.dense(categoryFeaturesArray ++ categoryFeaturesArray5 ++ categoryFeaturesArray7 ++ numericalFeatures)) 
return (trainData, validationData, testData, categoriesMap, categoriesMap5, categoriesMap7) 

它是正确的吗?

============================================== ====

我遇到的第二个问题是:从书下面的代码,在trainModel,它采用

DecisionTree.trainRegressor(trainingData, categoricalFeaturesInfo, impurity, maxDepth, maxBins) 

下面是代码:

def trainModel(trainData: RDD[LabeledPoint], impurity: String, maxDepth: Int, maxBins: Int): (DecisionTreeModel, Double) = { 
    val startTime = new DateTime() 
    val model = DecisionTree.trainClassifier(trainData, 2, Map[Int, Int](), impurity, maxDepth, maxBins) 
    val endTime = new DateTime() 
    val duration = new Duration(startTime, endTime) 
    (model, duration.getMillis()) 
} 

的问题是:如果它具有前面提到的三个分类特征,我该如何将categoricalFeaturesInfo传递到此方法中?

我只想按照书中的步骤通过使用决策树自行建立预测系统。更具体地讲,我选择的是数据集有几个分类的功能,如: 性别:男,女

教育:HS-毕业,学士,硕士,博士,......

国家:美国,加拿大,英国,澳大利亚,......

但我不知道如何将它们合并成一个单一的categoryFeatures ++ numericalFeatures投入Vector.dense(),和一个单一的categoricalFeaturesInfo投入DecisionTree.trainRegressor()

回答

2

我不清楚你到底在做什么,但看起来像从一开始就是错误的。

忽略了一个事实,即通过从零开始实现单热编码重新发明轮子,整个编码点就是将分类变量转换为数字变量。这对于线性模型是必需的,但可以说使用决策树时没有意义。

牢记这一点,你有两个选择:

  • 指数分类字段没有编码,并通过索引功能categoricalFeaturesInfo
  • 一次性编码分类特征并将其视为数值变量。

我相信前一种方法是正确的做法。后者应该在实践中起作用,但它只是人为地增加了维度而没有提供任何好处。它也可能与Spark实现使用的一些启发式相冲突。

您应该考虑使用ML管道来提供所有必需的索引编码和合并工具。

+0

哼哼......我只是想按照书中的步骤,通过使用决策树自己建立预测系统。 更具体地说,我选择的数据集有几个明确的特征,如: 性别:男性,女性; 学历:HS-grad,Bachelors,Master,PH.D,......; 国家:美国,加拿大,英国,澳大利亚......; ...等等。 但我不知道如何将它们合并成一个单独的“categoryFeatures ++ numericalFeatures”放入“Vector.dense()”,并将一个单独的“categoricalFeaturesInfo”放入“DecisionTree.trainRegressor()” –

+0

If您使用ML Pipelines,您将获得所需的所有工具,包括编码器,分度器和组装器。 – zero323

+0

@ C.Y.Wu我赞同zero323,但是如果我问这本书的标题是什么?我想看看它。 – eliasah