scala-如何将spark中的分类变量 transformation为一组编码为0,1的列?


0

我正在尝试对包含分类变量的 DataSet 执行带有spark mllib(带有scala)的逻辑回归(logisticregressionwithlbfgs)。我发现Spark无法处理这种变量。

在R中,有一种简单的方法来处理这种问题:我 transformation因子(类别)中的变量,因此R创建一组列,这些列编码为0,1指标变量。

我怎样才能用Spark来执行这个操作?

4 答案

0

使用vectorindexer,可以告诉索引器一个字段可能具有的不同值(基数)的数量,以便用setMaxCategories()方法将其视为类别。

val indexer = new VectorIndexer()
.setInputCol("features")
.setOutputCol("indexed")
.setMaxCategories(10)

从scaladocs:

类,用于索引矢量 DataSet 中的分类特征列。

我发现这是提取分类值的一种方便(虽然粗粒度)的方法,但是要注意,在任何情况下,如果您的字段具有较低的实数,您希望保持连续性(例如,大学生的年龄与来源国或美国州的年龄)。


0

如果我理解正确,您不希望在几个虚拟列中 transformation1个分类列。你想让Spark明白这个列是分类的而不是数字的。

我想这取决于你现在想用的算法。例如,Random Forest和GBT都具有categoricalFeaturesInfo作为参数,请在此处检查:

https://spark.apache.org/docs/1.4.0/api/scala/index.html org.apache.spark.mllib.tree.randomforest$

例如:

categoricalFeaturesInfo=map[int,int]((1,2),(2,5))

实际上是说,功能的第二列(索引从0开始,所以1是第二列)是一个具有2个级别的分类功能,第三列也是一个具有5个级别的分类功能。培训RandomForest或GBT时,可以指定这些参数。

您需要确保您的级别 map到0、1、2…因此,如果您有类似的东西(“好”、“中”、“坏”),请将其 map到(0,1,2)。

现在,在您的案例中,您希望使用逻辑回归与lbfgs。在这种情况下,我的建议是将分类列实际 transformation为虚拟列。例如,一个具有3个级别的单列(“好”、“中”、“坏”)变为3个具有1/0的列,具体取决于点击的是哪个列。我没有要使用的示例,因此下面是scala中的示例代码,它应该可以 job:

val dummygen = (data : DataFrame, col:Array[String]) => {
    var temp = data
    for(i <- 0 until col.length) {
      val N = data.select(col(i)).distinct.count.toInt
      for (j<- 0 until N)
      temp = temp.withColumn(col(i) + "_" + j.toString, callUDF(index(j), DoubleType, data(col(i))))
    }
  temp
  }
  val index = (value:Double) => {(a:Double) => {
    if (value==a) {
      1
    } else{
      0
    }
  }}

你可以这样称呼它:

val results = dummygen(data, Array("CategoricalColumn1","CategoricalColumn2"))

在这里,我是为一个分类列列表(以防您的功能列表中有超过1个)而做的。第一个“for loop”遍历每个分类列,第二个“for loop”遍历列中的每个级别,创建的列数等于每个列的级别数。

重要!!!!假设您首先将它们 map到0,1,2…

然后,您可以使用此新功能集使用lbfgs运行您的logisticregression。这种方法也有助于支持向量机。


0

一个vectorindexer将出现在spark 1.4中,它可以帮助您进行这种功能 transformation:http://people.apache.org/~pwindell/spark-1.4.0-rc1-docs/api/scala/index.html org.apache.spark.ml.feature.vectorindexer

但是,它看起来只在spark.ml中可用,而不是mllib

https://issues.apache.org/jira/browse/spark-4081


0

如果这些类别可以放在驾驶员 Memory 中,我的建议是:

import org.apache.spark.ml.feature.StringIndexer
import org.apache.spark.sql.functions._
import org.apache.spark.sql._


val df = Seq((0, "a"),(1, "b"),(2, "c"),(3, "a"),(4, "a"),(5, "c"),(6,"c"),(7,"d"),(8,"b"))
.toDF("id", "category")
val indexer = new StringIndexer()
.setInputCol("category")
.setOutputCol("categoryIndex")
.fit(df)

val indexed = indexer.transform(df)

val categoriesIndecies = indexed.select("category","categoryIndex").distinct
val categoriesMap: scala.collection.Map[String,Double] = categoriesIndecies.map(x=>(x(0).toString,x(1).toString.toDouble)).collectAsMap()

def getCategoryIndex(catMap: scala.collection.Map[String,Double], expectedValue: Double) = udf((columnValue: String) =>
if (catMap(columnValue) == expectedValue) 1 else 0)

val newDf:DataFrame =categoriesMap.keySet.toSeq.foldLeftDataFrame( (acc,c) =>
acc.withColumn(c,getCategoryIndex(categoriesMap,categoriesMap(c))($"category"))
)

newDf.show

+---+--------+-------------+---+---+---+---+
| id|category|categoryIndex| b| d| a| c|
+---+--------+-------------+---+---+---+---+
| 0| a| 0.0| 0| 0| 1| 0|
| 1| b| 2.0| 1| 0| 0| 0|
| 2| c| 1.0| 0| 0| 0| 1|
| 3| a| 0.0| 0| 0| 1| 0|
| 4| a| 0.0| 0| 0| 1| 0|
| 5| c| 1.0| 0| 0| 0| 1|
| 6| c| 1.0| 0| 0| 0| 1|
| 7| d| 3.0| 0| 1| 0| 0|
| 8| b| 2.0| 1| 0| 0| 0|
+---+--------+-------------+---+---+---+---+


我来回答