如何使用Scala添加两列SparseVectors?

时间:2018-09-24 23:04:44

标签: scala apache-spark sparse-matrix

给出SparseVector对象的两个DataFrame列,如何将这两个列加在一起(即矢量加法)以创建新列?

类似

df.columns
df: org.apache.spark.sql.DataFrame = [v1: SparseVector, v2: SparseVector]

df.withColumn("v3", ADD_COL_FUNCTION(col(v1), col(v2)))

2 个答案:

答案 0 :(得分:0)

Spark中的SparseVectors没有内置的附加功能。 DenseVector对象可以通过将它们变成数组来处理,但是对于SparseVector来说,这可能是内存杀手。您可以将SparseVectors解释为地图,然后将地图“添加”在一起。

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

def addVecCols(v1: SparseVector, v2: SparseVector): Vector = {
  val map1: Map[Int, Double] = (v1.indices zip v1.values).toMap

  Vectors.sparse(v1. size, 
    (map1 ++ (v2.indices zip v2.values).toMap)
      .map{ case (k, v) => k -> (v + map1.getOrElse(k, 0d))}
      .toList
  )


val addVecUDF = udf((v1: SparseVector, v2: SparseVector) => addVecCols(v1, v2))

请注意,在Spark 1.6中,Vectors.sparse的返回类型为Vector,而在Spark 2.X中,其返回类型为SparseVector,因此请调整{{1} }。另外,在2.X版本中,您可以使用addVecCols库而不是ml库。

将其用于数据框

mllib

答案 1 :(得分:0)

这是我们解决此问题的最终方法。

首先,我们实现了this post中提供的Spark和Breeze向量之间的隐式转换(请注意注释中的错误修复)。这提供了以下代码中使用的asBreezefromBreeze转换。

然后,我们定义了一个允许添加稀疏向量列的函数:

def addVectors(v1Col: String, v2Col: String, outputCol: String)
            : DataFrame => DataFrame = {
  df: DataFrame => {
    def add(v1: SparkVector, v2: SparkVector): SparkVector =
      (v1.asBreeze + v2.asBreeze).fromBreeze
    val func = udf((v1: SparkVector, v2: SparkVector) => add(v1, v2))
    df.withColumn(outputCol, func(col(v1Col), col(v2Col)))
  }
}

此函数的调用方式为:

 df.transform(addVectors(col1Name, col2name, colOutName))

当然,您可能希望包括一些检查列名是否存在的方法,并确保输出列不会覆盖您不希望的任何内容。