【问题标题】:How to convert a maptype into SparkML sparse vector in Spark?如何在 Spark 中将 maptype 转换为 SparkML 稀疏向量?
【发布时间】:2018-07-11 16:37:59
【问题描述】:

我的原始模式包含许多我想在 ML 模型中使用的映射类型,因此我需要将它们转换为 SparkML 稀疏向量。

root
 |-- colA: map (nullable = true)
 |    |-- key: string
 |    |-- value: double (valueContainsNull = true)
 |-- colB: map (nullable = true)
 |    |-- key: string
 |    |-- value: string (valueContainsNull = true)
 |-- colC: map (nullable = true)
 |    |-- key: string
 |    |-- value: string (valueContainsNull = true)

上下文: SparkML 模型要求将数据形成为特征向量。有 一些用于生成特征向量的实用程序,但没有一个支持 maptype 类型。 例如 SparkML VectorAssembler 允许组合多个列(所有数字类型、布尔类型或向量类型)。

编辑

到目前为止,我的解决方案是将地图单独分解为列,然后使用VectorAssembler

val listkeysColA = df.select(explode($"colA"))
  .select($"key").as[Int].distinct.collect.sorted

val exploded= df.select(listkeysColA.map(x => 
  $"colA".getItem(x).alias(x.toString)): _*).na.fill(0) 

val columnNames = exploded.columns

val assembler = new VectorAssembler().setInputCols(columnNames).setOutputCol("features")

编辑2

我应该补充一点,我的地图中的数据非常稀疏,并且事先没有已知的键集。这就是为什么在我当前的解决方案中,我首先传递给数据以收集和排序键。然后我使用 getItem(keyName) 访问这些值。

【问题讨论】:

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


    【解决方案1】:

    据我所知,Spark 中没有内置方法,因此在这种情况下,UDF 将是一个合适的解决方案。这是一个使用Map[String, Double] 的列并返回一个机器学习向量:

    val toVector = udf((m: Map[String, Double]) => Vectors.dense(m.values.toArray).toSparse)
    

    由于 Map 没有顺序,因此也不保证生成的向量具有特定顺序。

    示例输入 (df):

    +---------------------------------+---------------------------------+
    |colA                             |colB                             |
    +---------------------------------+---------------------------------+
    |Map(a -> 1.0, b -> 2.0, c -> 3.0)|Map(a -> 1.0, b -> 2.0, c -> 3.0)|
    +---------------------------------+---------------------------------+
    

    并使用UDF

    val df2 = df.withColumn("colA", toVector($"colA")).withColumn("colB", toVector($"colB"))
    

    给出以下输出:

    +-------------+-------------+
    |colA         |colB         |
    +-------------+-------------+
    |[1.0,2.0,3.0]|[1.0,2.0,3.0]|
    +-------------+-------------+
    

    其中两列都是矢量类型。

    root
     |-- colA: vector (nullable = true)
     |-- colB: vector (nullable = true)
    

    如果您想将所有列合并到一个向量中,这里最好使用VectorAssembler,就像在问题编辑中一样。


    编辑:

    如果您需要保持一定的值顺序,那么您需要先收集所有键,就像您所做的那样。但是,您可以避免使用explode

    val keys = df.select($"colA")
      .flatMap(_.getAs[Map[String, Int]]("colA").keys)
      .distinct
      .collect
      .sorted
    

    然后适当更改UDF 以考虑keys 的顺序,默认值为0.0:

    val toVector = udf((m: Map[String, Double]) => 
      Vectors.dense(keys.map(key => m.getOrElse(key, 0.0))).toSparse
    )
    

    【讨论】:

    • 感谢您提供优雅的解决方案。我试过了,它按说明工作。但是,我需要一个稀疏向量,因为我的 MAP 中的值本身是稀疏的,并且该向量将在 ML 模型中使用,因此特征的顺序很重要。
    • 我确实尝试将您的 udf 更改为 Sparse 以匹配我的数据要求,我认为它需要一些重写以匹配 Vectors.sparse 定义(大小:Int,元素:Iterable [(整数, java.lang.Double)])org.apache.spark.ml.linalg.Vector (size: Int,elements: Seq[(Int, scala.Double)])org.apache.spark.ml.linalg. Vector (size: Int,indices: Array[Int],values: Array[scala.Double])org.apache.spark.ml.linalg.Vector
    • @S.J.Clear:您可以通过在末尾添加.toSparse 将其简单地转换为稀疏。我将它添加到答案中的代码中。它更简单,因为可以使用 Array 创建密集向量,而稀疏计数器部分则无法创建。
    • 感谢您的建议。代码确实运行正确并产生了一个稀疏向量。但是,如果由于我的数据稀疏和地图中值的顺序丢失(如您之前所说,地图中没有定义顺序)而这样做,我将无法将其用作 ML 模型的特征向量。跨度>
    • 这就是为什么在我当前的解决方案中我提取键列表,对它们进行排序,然后以现在定义的键顺序访问值以填充列。
    猜你喜欢
    • 2016-06-02
    • 2017-01-01
    • 2017-07-29
    • 2017-03-26
    • 2017-05-10
    • 1970-01-01
    • 2015-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多