如何在Apache Spark

时间:2015-08-07 07:53:37

标签: scala apache-spark apache-spark-mllib apache-spark-1.2

我有一组基于我想要创建分类模型的数据。每行都有以下形式:

user1,class1,product1
user1,class1,product2
user1,class1,product5
user2,class1,product2
user2,class1,product5
user3,class2,product1

大约有1M个用户,2个类和1M个产品。我接下来要做的是创建稀疏向量(MLlib已经支持的东西)但为了应用该函数,我必须首先创建密集向量(使用0)。换句话说,我必须将数据二进制化。什么是最简单(或最优雅)的方式?

鉴于我是MLlib的新手,请问您提供一个具体的例子?我正在使用MLlib 1.2。

修改

我最终得到了以下一段代码,但结果却很慢......还有其他想法,我只能使用MLlib 1.2吗?

val data = test11.map(x=> ((x(0) , x(1)) , x(2))).groupByKey().map(x=> (x._1 , x._2.toArray)).map{x=>
  var lt : Array[Double] = new Array[Double](test12.size)
  val id = x._1._1
  val cl = x._1._2
  val dt = x._2
  var i = -1
  test12.foreach{y => i += 1; lt(i) = if(dt contains y) 1.0 else 0.0}
  val vs = Vectors.dense(lt)
  (id , cl , vs)
}

3 个答案:

答案 0 :(得分:9)

您可以使用spark.ml' s OneHotEncoder

您首先使用:

OneHotEncoder.categories(rdd, categoricalFields)

categoricalFieldRDD包含分类数据的索引序列。 categories,给定数据集和作为分类变量的列索引,返回一个结构,对于每个字段,该结构描述数据集中存在的值。该映射旨在用作编码方法的输入:

OneHotEncoder.encode(rdd, categories)

返回矢量化RDD[Array[T]]

答案 1 :(得分:4)

如果使用内置OneHotEncoder不是一个选项,并且你只有一个变量来实现穷人的单热,那么或多或少是直截了当的。首先,我们创建一个示例数据:

import org.apache.spark.mllib.linalg.{Vector, Vectors}

val rdd = sc.parallelize(List(
    Array("user1", "class1", "product1"),
    Array("user1", "class1", "product2"),
    Array("user1", "class1", "product5"),
    Array("user2", "class1", "product2"),
    Array("user2", "class1", "product5"),
    Array("user3", "class2", "product1")))

接下来,我们必须创建从值到索引的映射:

val prodMap = sc.broadcast(rdd.map(_(2)).distinct.zipWithIndex.collectAsMap)

和一个简单的编码功能:

def encodeProducts(products: Iterable[String]): Vector =  {
    Vectors.sparse(
        prodMap.value.size,
        products.map(product => (prodMap.value(product).toInt, 1.0)).toSeq
    )
}

最后,我们可以将它应用于数据集:

rdd.map(x => ((x(0), x(1)), x(2))).groupByKey.mapValues(encodeProducts)

向上扩展以处理多个变量相对容易。

修改

如果产品数量很大以使广播有用,则应该可以使用join。首先,我们可以创建从产品到索引的类似映射,但将其保留为RDD:

import org.apache.spark.HashPartitioner

val nPartitions = ???

val prodMapRDD = rdd
     .map(_(2))
     .distinct
     .zipWithIndex
     .partitionBy(new HashPartitioner(nPartitions))
     .cache

val nProducts = prodMapRDD.count // Should be < Int.MaxValue

接下来,我们重新设置输入RDD,以便按产品索引PairRDD

val pairs = rdd
    .map(rec => (rec(2), (rec(0), rec(1))))
    .partitionBy(new HashPartitioner(nPartitions))

最后我们可以join两个

def indicesToVec(n: Int)(indices: Iterable[Long]): Vector = {
     Vectors.sparse(n, indices.map(x => (x.toInt, 1.0)).toSeq)
}

pairs.join(prodMapRDD)
   .values
   .groupByKey
   .mapValues(indicesToVec(nProducts.toInt))

答案 2 :(得分:-1)

原始问题要求从非分类中指定分类要素的最简单方法。

在Spark ML中,您可以使用VectorIndexer的setMaxCategories方法,您无需指定字段 - 相反,它将理解为基数低于或等于给定数字的字段(在这种情况下) ,2)。

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

有关详细信息,请参阅this reply