如何将MLlib矩阵转为Spark DataFrame?Zeppelin中如何美观展示相关系数?
嘿,我来帮你搞定这两个Spark相关的问题:
1. 如何将MLlib矩阵转换为Spark DataFrame?
MLlib里的矩阵主要分稠密矩阵(DenseMatrix)和稀疏矩阵(SparseMatrix),转换方式略有不同,我给你分别举Scala的例子(Spark常用Scala开发,Python思路类似):
对于稠密矩阵(DenseMatrix)
如果你的矩阵是DenseMatrix类型,可以先把它转成二维数组,再映射成Row对象,最后结合自定义Schema创建DataFrame:
import org.apache.spark.ml.linalg.DenseMatrix import org.apache.spark.sql.{Row, SparkSession} import org.apache.spark.sql.types.{DoubleType, StructField, StructType} val spark = SparkSession.builder().appName("MatrixToDF").getOrCreate() // 示例稠密矩阵 val denseMat = new DenseMatrix(3, 2, Array(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)) // 生成Schema:列名可以自定义,比如col_0, col_1... val schema = StructType((0 until denseMat.numCols).map(colIdx => StructField(s"col_$colIdx", DoubleType))) // 将矩阵转为Row集合 val rows = (0 until denseMat.numRows).map(rowIdx => Row.fromSeq(denseMat.row(rowIdx).toArray)) // 创建DataFrame val df = spark.createDataFrame(spark.sparkContext.parallelize(rows), schema) df.show()
对于稀疏矩阵(SparseMatrix)
如果是稀疏矩阵,你可以选择两种转换方式:
- 方式一:转成稠密矩阵再按上面的方法处理(适合小矩阵)
- 方式二:提取非零元素的坐标和值,生成三列(行索引、列索引、值)的长格式DataFrame,更节省空间:
import org.apache.spark.ml.linalg.SparseMatrix import org.apache.spark.sql.Row import org.apache.spark.sql.types.{IntegerType, DoubleType, StructField, StructType} // 示例稀疏矩阵 val sparseMat = new SparseMatrix(3, 2, Array(0, 2, 3), Array(0, 1, 0), Array(1.0, 2.0, 3.0)) // 提取非零元素 val nonZeroRows = sparseMat.nonZero.map { case (i, j, v) => Row(i, j, v) } // 定义Schema val sparseSchema = StructType(Seq( StructField("row_idx", IntegerType), StructField("col_idx", IntegerType), StructField("value", DoubleType) )) val sparseDF = spark.createDataFrame(spark.sparkContext.parallelize(nonZeroRows.toSeq), sparseSchema) sparseDF.show()
2. Zeppelin中美观打印50列的相关系数矩阵
你说转成每值一列的DataFrame用z.show()会被截断,确实50列的宽表在Zeppelin里默认显示会很受限,这里有几个更直接的方法:
方法一:转成“特征对-相关系数”的长格式DataFrame
把宽表转成长表,每一行代表一对特征的相关系数,这样既不会截断,也更方便查看特定特征的相关性:
import org.apache.spark.ml.linalg.Matrix import org.apache.spark.sql.Row import org.apache.spark.sql.types.{StringType, DoubleType, StructField, StructType} // 假设你已经计算得到相关系数矩阵corrMat,以及特征名数组featureNames val corrMat: Matrix = ... val featureNames: Array[String] = ... // 比如Array("feat1", "feat2", ..., "feat50") // 生成所有特征对的相关系数行(可选i<=j避免重复显示对称系数) val corrRows = for { i <- 0 until corrMat.numRows j <- 0 until corrMat.numCols if i <= j } yield Row(featureNames(i), featureNames(j), corrMat(i, j)) // 创建长格式DataFrame val corrDF = spark.createDataFrame(spark.sparkContext.parallelize(corrRows), StructType(Seq( StructField("feature_a", StringType), StructField("feature_b", StringType), StructField("correlation", DoubleType) )) ) // Zeppelin中显示,不会截断 corrDF.show(truncate = false)
方法二:用Zeppelin的%html魔法渲染成完整表格
如果你还是想保留宽表的形式,可以把矩阵转成HTML表格,Zeppelin支持直接渲染HTML,这样能完整显示50列:
// 生成HTML表格字符串 val htmlTable = s""" <table border="1" cellpadding="4" cellspacing="0"> <tr> <th></th> ${featureNames.map(name => s"<th>$name</th>").mkString("")} </tr> ${(0 until corrMat.numRows).map { i => s""" <tr> <td><strong>${featureNames(i)}</strong></td> ${(0 until corrMat.numCols).map(j => s"<td>${corrMat(i, j).formatted("%.4f")}</td>").mkString("")} </tr> """ }.mkString("")} </table> """ // 打印字符串后,在Zeppelin新建单元格用%html渲染 println(htmlTable)
运行上面的代码后,新建一个Zeppelin单元格输入%html ${htmlTable},就能看到格式化后的完整表格了。
方法三:调整Zeppelin的显示参数(临时救急)
如果只是临时想查看完整的宽表,可以调整z.show()的参数,关闭截断并设置最大显示列数:
// 关闭截断,设置显示所有列 corrWideDF.show(truncate = false, numRows = 100, vertical = false)
不过50列的话横向会很长,可能需要拖动滚动条,体验不如前两种方法好。
内容的提问来源于stack exchange,提问作者djWann
相关产品推荐
相关产品推荐

