在Scala中将数据帧转换为Spark mllib矩阵

时间:2018-07-05 06:04:05

标签: scala apache-spark matrix apache-spark-sql apache-spark-mllib

我有一个名为df的Spark数据框作为输入:

+---------------+---+---+---+---+
|Main_CustomerID| A1| A2| A3| A4|
+---------------+---+---+---+---+
|            101|  1|  0|  2|  1|
|            102|  0|  3|  1|  1|
|            103|  2|  1|  0|  0|
+---------------+---+---+---+---+

我需要将A1A2A3A4的值收集到mllib矩阵中,例如,

dm: org.apache.spark.mllib.linalg.Matrix =
1.0  0.0  2.0  1.0
0.0  3.0  1.0  1.0
2.0  1.0  0.0  0.0

如何在Scala中实现这一目标?

1 个答案:

答案 0 :(得分:2)

您可以按照以下步骤进行操作,首先获取应包含在矩阵中的所有列:

import org.apache.spark.sql.functions._

val matrixColumns = df.columns.filter(_.startsWith("A")).map(col(_))

然后将数据帧转换为RDD[Vector]。由于向量需要包含双精度数,因此在此也需要完成转换。

import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.linalg.distributed.{IndexedRow, IndexedRowMatrix}

val rdd = df.select(array(matrixColumns:_*).as("arr")).as[Array[Int]].rdd
  .zipWithIndex()
  .map{ case(arr, index) => IndexedRow(index, Vectors.dense(arr.map(_.toDouble)))}

然后将rdd转换为IndexedRowMatrix,可以根据需要将其转换为本地Matrix:

val dm = new IndexedRowMatrix(rdd).toBlockMatrix().toLocalMatrix()

对于可以收集到驱动程序的较小矩阵,有一个更简单的选择:

val matrixColumns = df.columns.filter(_.startsWith("A")).map(col(_))

val arr = df.select(array(matrixColumns:_*).as("arr")).as[Array[Int]]
  .collect()
  .flatten
  .map(_.toDouble)

val rows = df.count().toInt
val cols = matrixColumns.length

// It's necessary to reverse cols and rows here and then transpose
val dm = Matrices.dense(cols, rows, arr).transpose()