我从一本书中读到了一段关于二元决策树的代码。它在原始数据中只有一个分类特征,即字段(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-of-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-grad,Bachelors,Master,PH.D,......
国家:美国,加拿大,英国,澳大利亚......
但我不知道如何将它们合并为一个categoryFeatures ++ numericalFeatures
以放入Vector.dense()
,将一个categoricalFeaturesInfo
合并到DecisionTree.trainRegressor()
答案 0 :(得分:2)
我不清楚你到底在做什么,但从一开始看起来就错了。
忽略通过从头开始实施单热编码重新发明轮子的事实,编码的全部要点是将分类变量转换为数字变量。这对于线性模型是必需的,但可以说在使用决策树时没有意义。
记住这一点,你有两个选择:
categoricalFeaturesInfo
。我认为前一种方法是正确的方法。后者应该在实践中起作用,但它只是人为地增加了维度而没有提供任何好处。它也可能与Spark实现使用的一些启发式方法相冲突。
您应该考虑使用ML Pipelines来提供所有必需的索引,编码和合并工具。