【Spark ML系列】Spark Matrix DenseMatrix SparseMatrix矩阵原理用法操作示例大全点击这里看全文
一、基本原理
org.apache.spark.ml.linalg.Matrix是 Apache Spark 中用于表示矩阵数据的类。它是一个抽象类,定义了常见的矩阵操作和属性。
Matrix
类的主要设计目标是提供一种通用的、可扩展的方式来处理不同类型的矩阵数据,如密集矩阵和稀疏矩阵。它通过继承 Serializable
接口,使得矩阵对象可以在分布式计算环境中进行序列化和反序列化,以便在集群上进行处理。
Matrix
类的实现涉及到底层的数据结构和算法,这取决于具体的子类。对于密集矩阵,Spark 使用基于列主导(column-major)的存储格式,即将矩阵按列存储为一维数组;对于稀疏矩阵,Spark 使用 Compressed Sparse Column (CSC) 格式或 Compressed Sparse Row (CSR) 格式进行存储,以有效地表示矩阵中的非零元素。
Matrix
类提供了许多常见的矩阵操作方法,如矩阵乘法、转置、求逆等。它还提供了获取矩阵的行数、列数、值等属性的方法,以及转换为不同类型矩阵的方法。
总之,org.apache.spark.ml.linalg.Matrix
类是 Apache Spark 中用于处理矩阵数据的抽象类,提供了常见的矩阵操作和属性,并通过不同子类的实现支持密集矩阵和稀疏矩阵的存储和计算。
二、概念和存储
1.列主序的稠密矩阵
是指矩阵中的元素按列顺序存储在一个双精度数组中 。入口值以列序列存储在一个双精度数组中。
例如,下面的矩阵
1.0 2.0
3.0 4.0
5.0 6.0
存储为[1.0, 3.0, 5.0, 2.0, 4.0, 6.0]
。
列主序的稠密矩阵适用于需要按列遍历或操作矩阵元素的场景。通过使用列主序存储,可以更高效地访问和处理矩阵的列数据。
2.行主序的稠密矩阵
是指矩阵中的元素按行顺序存储在一个双精度数组中。 入口值以列序列存储在一个双精度数组中。
例如,对于下面的矩阵:
1.0 2.0
3.0 4.0
5.0 6.0
按行主序存储为[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
。在数组中,先存储第一行的元素,然后是第二行的元素,依此类推。
行主序的稠密矩阵适用于需要按行遍历或操作矩阵元素的场景。通过使用行主序存储,可以更高效地访问和处理矩阵的行数据。
3.列主序的稀疏矩阵
入口值以压缩稠密列(CSC)格式存储。
例如,下面的矩阵
1.0 0.0 4.0
0.0 3.0 5.0
2.0 0.0 6.0
存储为:
values: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
rowIndices=[0, 2, 1, 0, 1, 2]
colPointers=[0, 2, 3, 6]
4.压缩稠密列(CSC)
压缩稀疏列格式(CSC)是一种列导向的稀疏矩阵表示方法。它使用三个数组:rowIndices、
colPointers和
values。
-
`rowIndices是一个存储行索引的数组,表示矩阵中每个非零值所在的行位置。
-
`values 是对应的非零值数组。
-
colPointers是一个指针数组,指示
rowIndices和values 中每一列的起始位置。它的长度为
n_col + 1
,其中最后一项表示非零值的数量,也是rowIndices
和data
数组的长度
继续使用列主序的稀疏矩阵的矩阵例子说明。
- 首先矩阵的3x3的,所以第一个Array会有4个元素。第一个元素是0。得到Array(0)。
- 矩阵第一列有2个非零元素,所以得到Array的第二个元素为2.得到Array(0, 2)
- 矩阵的第二列有1个非零元素,那么第三个元素的数量为当前Array的最后一个元素加1,也就是2 + 1=3. 得到Array(0,2, 3)
- 矩阵的第三列有3个非零元素,那么Array的最后一个元素的值为 3 + 3 = 6. 得到Array(0, 2, 3, 6)
三、用法和示例
1. Matrix用法
方法 | 描述 |
---|---|
numRows |
返回矩阵的行数。 |
numCols |
返回矩阵的列数。 |
isTransposed |
记录矩阵是否被转置的标志,默认为false。 |
isColMajor |
指示矩阵的值是否按列主序排列。 |
isRowMajor |
指示矩阵的值是否按行主序排列。 |
toArray |
将矩阵转换为列主序的稠密数组。 |
colIter |
返回列向量的迭代器。 |
rowIter |
返回行向量的迭代器,通过转置矩阵实现。 |
asBreeze |
转换为Breeze矩阵。 |
apply(i: Int, j: Int) |
获取第(i, j)个元素。 |
index(i: Int, j: Int) |
返回(i, j)处的元素在底层数组中的索引。 |
update(i: Int, j: Int, v: Double) |
更新(i, j)处的元素。 |
copy |
获取矩阵的深拷贝。 |
transpose |
转置矩阵,返回一个共享相同底层数据的新的Matrix实例。 |
multiply(y: DenseMatrix) |
方便进行Matrix-DenseMatrix乘法运算。 |
multiply(y: DenseVector) |
方便进行Matrix-DenseVector乘法运算。 |
multiply(y: Vector) |
方便进行Matrix-Vector乘法运算。 |
toString |
以人类可读的方式表示矩阵。 |
toString(maxLines: Int, maxLineWidth: Int) |
以指定最大行数和最大宽度的方式表示矩阵。 |
map(f: Double => Double) |
使用函数对矩阵的值进行映射,生成一个新的矩阵。 |
update(f: Double => Double) |
使用函数更新矩阵的所有值。 |
foreachActive(f: (Int, Int, Double) => Unit) |
对稠密和稀疏矩阵的所有非零元素应用函数f。 |
numNonzeros |
查找非零元素的数量。 |
numActives |
查找显式存储的值的数量,这些值可能为零。 |
toSparseMatrix(colMajor: Boolean) |
将矩阵转换为稀疏矩阵。 |
toSparseColMajor |
将矩阵转换为按列主序排列的稀疏矩阵。 |
toSparseRowMajor |
将矩阵转换为按行主序排列的稀疏矩阵。 |
toSparse |
将矩阵转换为保持当前矩阵布局的稀疏矩阵。 |
toDenseMatrix(colMajor: Boolean) |
将矩阵转换为稠密矩阵。 |
toDense |
将矩阵转换为保持当前矩阵布局的稠密矩阵。 |
toDenseRowMajor |
将矩阵转换为按行主序排列的稠密矩阵。 |
toDenseColMajor |
将矩阵转换为按列主序排列的稠密矩阵。 |
compressedColMajor |
返回以较少存储空间表示的按列主序或行主序的稠密或稀疏矩阵。 |
compressedRowMajor |
返回以较少存储空间表示的按行主序或列主序的稠密或稀疏矩阵。 |
compressed |
返回以较少存储空间表示的按列主序、行主序、稠密或稀疏矩阵。 |
getDenseSizeInBytes |
获取矩阵的稠密表示大小(以字节为单位)。 |
getSparseSizeInBytes(colMajor: Boolean) |
获取矩阵的最小稀疏表示大小(以字节为单位)。 |
getSizeInBytes |
获取矩阵当前的大小(以字节为单位)。 |
2. DenseMatrix 用法示例
2.1 DenseMatrix 用法
方法签名 | 描述 |
---|---|
DenseMatrix(numRows: Int, numCols: Int, values: Array[Double], isTransposed: Boolean) |
使用指定的行数、列数、入口值数组和转置标志构造一个列主序的稠密矩阵对象。 |
equals(o: Any): Boolean |
重写equals 方法,判断两个矩阵是否相等。 |
hashCode: Int |
重写hashCode 方法,获取矩阵的哈希值。 |
asBreeze: BM[Double] |
将矩阵转换为Breeze库中的BDM 类型对象。 |
apply(i: Int): Double |
获取指定位置的值。 |
apply(i: Int, j: Int): Double |
获取指定位置的值。 |
index(i: Int, j: Int): Int |
根据行索引和列索引计算对应的索引值。 |
update(i: Int, j: Int, v: Double): Unit |
更新指定位置的值。 |
copy: DenseMatrix |
创建并返回当前矩阵的副本。 |
map(f: Double => Double) |
对矩阵中的每个元素应用给定的函数,并返回新的矩阵。 |
update(f: Double => Double): DenseMatrix |
对矩阵中的每个元素应用给定的函数,并就地更新矩阵。 |
transpose: DenseMatrix |
返回当前矩阵的转置矩阵。 |
foreachActive(f: (Int, Int, Double) => Unit): Unit |
遍历矩阵的非零元素,对每个元素执行指定的操作。 |
numNonzeros: Int |
返回矩阵中非零元素的数量。 |
numActives: Int |
返回矩阵中的元素数量。 |
toSparseMatrix(colMajor: Boolean): SparseMatrix |
将当前稠密矩阵转换为稀疏矩阵。 |
toDenseMatrix(colMajor: Boolean): DenseMatrix |
将当前矩阵转换为稠密矩阵。 |
colIter: Iterator[Vector] |
返回按列迭代的迭代器,每次迭代返回一个列向量。 |
getSizeInBytes: Long |
获取矩阵在内存中占用的字节数。 |
2.2 DenseMatrix 示例
package org.example.scala
import org.apache.spark.ml.linalg.DenseMatrix
object DenseMatrixDemo {
def main(args: Array[String]): Unit = {
// 创建一个3x2的稠密矩阵
val matrix = new DenseMatrix(3, 2, Array(1.0, 3.0, 5.0, 2.0, 4.0, 6.0), isTransposed = false)
// equals(o: Any): Boolean
val anotherMatrix = new DenseMatrix(3, 2, Array(1.0, 3.0, 5.0, 2.0, 4.0, 6.0), isTransposed = false)
println(matrix.equals(anotherMatrix))
// true
// hashCode: Int
println(matrix.hashCode)
// 1693118754
// apply(i: Int, j: Int): Double
val valueAtIndex = matrix.apply(1, 1)
println(valueAtIndex)
// 4.0
// copy: DenseMatrix
val copyMatrix = matrix.copy
println(copyMatrix.equals(matrix))
// true
// transpose: DenseMatrix
val transposedMatrix = matrix.transpose
println(transposedMatrix)
// 1.0 3.0 5.0
// 2.0 4.0 6.0
// foreachActive(f: (Int, Int, Double) => Unit): Unit
matrix.foreachActive((i, j, v) => println(s"($i, $j) -> $v"))
// (0, 0) -> 1.0
// (1, 0) -> 3.0
// (2, 0) -> 5.0
// (0, 1) -> 2.0
// (1, 1) -> 4.0
// (2, 1) -> 6.0
// numNonzeros: Int
val numNonzeros = matrix.numNonzeros
println(numNonzeros)
// 6
// numActives: Int
val numActives = matrix.numActives
println(numActives)
// 6
// colIter: Iterator[Vector]
val columnIterator = matrix.colIter
while (columnIterator.hasNext) {
val columnVector = columnIterator.next()
println(columnVector.toArray.mkString(", "))
}
// 1.0, 3.0, 5.0
// 2.0, 4.0, 6.0
// colIter: Iterator[Vector]
val rowIterator = matrix.rowIter
while (rowIterator.hasNext) {
val rowVector = rowIterator.next()
println(rowVector.toArray.mkString(", "))
}
// 1.0, 2.0
// 3.0, 4.0
// 5.0, 6.0
}
}
2.3 DenseMatrix静态方法
DenseMatrix的伴生对象,提供了一些静态方法来创建和操作密集矩阵。可以使用以下方法来使用这个DenseMatrix
对象
方法名 | 描述 |
---|---|
unapply |
从一个 DenseMatrix 实例中提取元组信息,包含行数、列数、值数组和是否转置的布尔值。 |
fromVectors |
从向量序列生成一个密集矩阵。 |
zeros |
生成一个全零的密集矩阵。 |
ones |
生成一个全一的密集矩阵。 |
eye |
生成一个单位矩阵(对角线上的值为1)。 |
rand |
生成一个由 i.i.d. 均匀分布的随机数组成的密集矩阵。 |
randn |
生成一个由 i.i.d. 高斯分布的随机数组成的密集矩阵。 |
diag |
从提供的值生成一个对角矩阵。 |
2.4 DenseMatrix静态方法示例
package org.example.scala
import org.apache.spark.ml.linalg.{
DenseMatrix, DenseVector, Matrix, Vector}
import java.util.Random
object MatrixExample {
def main(args: Array[String]): Unit = {
// 1. 使用`zeros`方法生成一个全零的密集矩阵。
val matrix0 = DenseMatrix.zeros(3, 3)
println(matrix0)
//0.0 0.0 0.0
//0.0 0.0 0.0
//0.0 0.0 0.0
// 2. 使用`ones`方法生成一个全一的密集矩阵。
val matrix1 = DenseMatrix.ones(3, 3)
println(matrix1)
//1.0 1.0 1.0
//1.0 1.0 1.0
//1.0 1.0 1.0
// 3. 使用`eye`方法生成一个单位矩阵(对角线上的值为1)。
val matrix11 = DenseMatrix.eye(3)
println(matrix11)
//1.0 0.0 0.0
//0.0 1.0 0.0
//0.0 0.0 1.0
// 4. 使用`rand`方法生成一个由`i.i.d.`均匀分布的随机数组成的密集矩阵。
val rng = new Random()
val matrixR = DenseMatrix.rand(3, 3, rng)
println(matrixR)
//0.10056504560115964 0.08144943381588055 0.23630258768260826
//0.3372042400841968 0.06970177174892467 0.536017998050748
//0.1340458736844916 0.39331083016064305 0.8642285352411953
// 5. 使用`randn`方法生成一个由`i.i.d.`高斯分布的随机数组成的密集矩阵。
val rng1 = new Random()
val matrixR1 = DenseMatrix.randn(3, 3, rng)
println(matrixR1)
//-0.19300534303112293 1.4053956759897934 0.9293034206071048
//1.195144434612924 0.25650883752709913 0.32379215164499303
//-0.6590537888544012 -0.43766799612247964 0.11232710742031338
// 6. 使用`diag`方法从提供的值生成一个对角矩阵。
val vector = new DenseVector(Array(1.0, 2.0, 3.0))
val matrix2 = DenseMatrix.diag(vector)
println(matrix2)
//1.0 0.0 0.0
//0.0 2.0 0.0
//0.0 0.0 3.0
}
}
3. SparseMatrix用法示例
3.1 SparseMatrix用法
方法名 | 描述 |
---|---|
SparseMatrix构造函数 | 接受行数、列数、列指针数组、行索引数组和值数组作为参数,并可指定是否转置。 |
hashCode()方法 | 重写了父类的hashCode()方法。 |
equals(o: Any)方法 | 重写了父类的equals()方法,判断两个矩阵是否相等。 |
asBreeze方法 | 将稀疏矩阵转换为Breeze库中的稀疏矩阵表示。 |
apply(i: Int, j: Int)方法 | 获取稀疏矩阵中指定位置(i, j)的元素值。 |
index(i: Int, j: Int)方法 | 根据行索引i和列索引j获取元素在稀疏矩阵中的位置。 |
update(i: Int, j: Int, v: Double)方法 | 更新稀疏矩阵中指定位置(i, j)的元素值。 |
copy方法 | 创建当前稀疏矩阵的副本。 |
map(f: Double => Double)方法 | 对稀疏矩阵的每个非零元素应用函数f。 |
update(f: Double => Double)方法 | 对稀疏矩阵的每个非零元素应用函数f并更新原矩阵。 |
transpose方法 | 返回稀疏矩阵的转置矩阵。 |
foreachActive(f: (Int, Int, Double) => Unit)方法 | 遍历稀疏矩阵的非零元素,并对每个非零元素应用函数f。 |
numNonzeros方法 | 返回稀疏矩阵中的非零元素个数。 |
numActives方法 | 返回稀疏矩阵中的元素总数(包括零元素)。 |
toSparseMatrix(colMajor: Boolean)方法 | 将稀疏矩阵转换为压缩稠密列(CSC)格式的稀疏矩阵,并可以选择是否按列主序排列。 |
toDenseMatrix(colMajor: Boolean)方法 | 将稀疏矩阵转换为密集矩阵,并可以选择是否按列主序排列。 |
colIter方法 | 返回稀疏矩阵的列迭代器,迭代器中的每个元素都是一个稀疏向量。 |
getSizeInBytes方法 | 返回稀疏矩阵占用的内存大小。 |
3.2 SparseMatrix示例
package org.example.scala
import org.apache.spark.ml.linalg.SparseMatrix
object SparseMatrixDemo {
def main(args: Array[String]): Unit = {
// 构造稀疏矩阵
val numRows = 3
val numCols = 3
val colPtrs = Array(0, 2, 3, 6)
val rowIndices = Array(0, 2, 1, 0, 1, 2)
val values = Array(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
val sparseMatrix = new SparseMatrix(numRows, numCols, colPtrs, rowIndices, values)
// hashCode()方法
val hashCode = sparseMatrix.hashCode()
println(s"hashCode: $hashCode")
// hashCode: -1943243555
// equals(o: Any)方法
val otherMatrix = new SparseMatrix(numRows, numCols, colPtrs, rowIndices, values)
val isEqual = sparseMatrix.equals(otherMatrix)
println(s"isEqual: $isEqual")
// isEqual: true
// apply(i: Int, j: Int)方法
val element = sparseMatrix(1, 2)
println(s"element at (1, 2): $element")
// element at (1, 2): 5.0
// copy方法
val copyMatrix = sparseMatrix.copy
println(sparseMatrix.equals(copyMatrix))
// true
// transpose方法
val transposedMatrix = sparseMatrix.transpose
println("Transposed sparse matrix:")
println(transposedMatrix)
// Transposed sparse matrix:
// 3 x 3 CSCMatrix
// (0,0) 1.0
// (2,0) 4.0
// (1,1) 3.0
// (2,1) 5.0
// (0,2) 2.0
// (2,2) 6.0
// foreachActive(f: (Int, Int, Double) => Unit)方法
sparseMatrix.foreachActive {
case (i, j, v) =>
println(s"Element at ($i, $j): $v")
}
// Element at (0, 0): 1.0
// Element at (2, 0): 2.0
// Element at (1, 1): 3.0
// Element at (0, 2): 4.0
// Element at (1, 2): 5.0
// Element at (2, 2): 6.0
// numNonzeros方法
val numNonzeros = sparseMatrix.numNonzeros
println(s"Number of nonzeros: $numNonzeros")
// Number of nonzeros: 6
// numActives方法
val numActives = sparseMatrix.numActives
println(s"Number of actives: $numActives")
// Number of actives: 6
// colIter方法
val colIterator = sparseMatrix.colIter
println("Column iterator:")
colIterator.foreach(println)
// Column iterator:
// (3,[0,2],[1.0,2.0])
// (3,[1],[3.0])
// (3,[0,1,2],[4.0,5.0,6.0])
}
}
3.3 SparseMatrix静态方法
SparseMatrix伴生对象,提供了一些工厂方法来创建稀疏矩阵。可以用于创建不同类型的稀疏矩阵,并根据提供的参数设置其属性。其中,fromCOO
方法是基于COO格式创建稀疏矩阵的常见方式。
方法名 | 描述 |
---|---|
fromVectors |
从向量序列生成稀疏矩阵。 |
fromCOO |
从坐标列表(COO)格式生成稀疏矩阵。 |
speye |
生成一个单位矩阵的稀疏矩阵。 |
sprand |
生成一个由 i.i.d. 均匀分布的随机数组成的稀疏矩阵。 |
sprandn |
生成一个由 i.i.d. 高斯分布的随机数组成的稀疏矩阵。 |
spdiag |
生成一个对角矩阵的稀疏矩阵。 |
3.4 SparseMatrix静态方法示例
package org.example.scala
import org.apache.spark.ml.linalg.{
SparseMatrix, Vectors}
object SparseMatrixTest extends App {
// 从坐标列表(COO)格式生成稀疏矩阵
val numRows = 3
val numCols = 3
val entries = Seq(
(0, 0, 1.0),
(2, 2, 2.0),
(1, 1, 3.0),
(0, 2, 4.0),
(2, 1, 5.0)
)
val sparseMatrix: SparseMatrix = SparseMatrix.fromCOO(numRows, numCols, entries)
println(sparseMatrix)
// 3 x 3 CSCMatrix
// (0,0) 1.0
// (1,1) 3.0
// (2,1) 5.0
// (0,2) 4.0
// (2,2) 2.0
// 生成一个单位矩阵的稀疏矩阵
val size = 3
val matrix3: SparseMatrix = SparseMatrix.speye(size)
println(matrix3)
// 3 x 3 CSCMatrix
// (0,0) 1.0
// (1,1) 1.0
// (2,2) 1.0
// 生成一个由 i.i.d. 均匀分布的随机数组成的稀疏矩阵
val rng = new java.util.Random()
val matrix4: SparseMatrix = SparseMatrix.sprand(size, size, 0.5, rng)
println(matrix4)
// 3 x 3 CSCMatrix
// (0,0) 0.26674460498647046
// (2,0) 0.1582949624866682
// (0,1) 0.6582335262826032
// (1,1) 0.9797496076456758
// (2,2) 0.654539786651401
// 生成一个由 i.i.d. 高斯分布的随机数组成的稀疏矩阵
val matrix5: SparseMatrix = SparseMatrix.sprandn(size, size, 0.5, rng)
println(matrix5)
// 3 x 3 CSCMatrix
// (0,0) -0.025318079602028087
// (2,0) -1.5316431156033294
// (0,1) -0.40409783159156915
// (1,1) 0.0387612497485581
// (2,2) -0.021389697694788348
// 生成一个对角矩阵的稀疏矩阵
val diagonalValues = Array(1.0, 2.0, 3.0)
val matrix6: SparseMatrix = SparseMatrix.spdiag(Vectors.dense(diagonalValues))
println(matrix6)
// 3 x 3 CSCMatrix
// (0,0) 1.0
// (1,1) 2.0
// (2,2) 3.0
}
4. Matrices方法
Matrices的对象提供了一些工厂方法来创建矩阵。这些方法可以用于创建不同类型的矩阵,并根据提供的参数设置其属性。
其中,fromVectors
方法可以根据向量序列自动选择创建密集矩阵还是稀疏矩阵,不过它是private方法。
方法名 | 描述 |
---|---|
fromVectors |
从向量序列生成矩阵。 |
dense |
创建一个密集矩阵。 |
sparse |
创建一个稀疏矩阵。 |
fromBreeze |
从 Breeze 矩阵生成矩阵。 |
zeros |
生成一个全零矩阵。 |
ones |
生成一个全一矩阵。 |
eye |
生成一个单位矩阵。 |
speye |
生成一个稀疏单位矩阵。 |
rand |
生成一个均匀分布的随机矩阵。 |
sprand |
生成一个稀疏均匀分布的随机矩阵。 |
randn |
生成一个高斯分布的随机矩阵。 |
sprandn |
生成一个稀疏高斯分布的随机矩阵。 |
diag |
从提供的值生成对角矩阵。 |
horzcat |
水平连接一系列矩阵。 |
vertcat |
垂直连接一系列矩阵。 |
4.1 Matrices示例
package org.example.scala
import org.apache.spark.ml.linalg.{
DenseMatrix, Matrices, Matrix, SparseMatrix, Vector, Vectors}
object MatricesTest extends App {
// 从向量序列生成矩阵
val vectors: Seq[Vector] = Seq(
Vectors.dense(1.0, 2.0, 3.0),
Vectors.dense(4.0, 5.0, 6.0),
Vectors.dense(7.0, 8.0, 9.0)
)
// 创建一个密集矩阵
val matrix2: DenseMatrix = Matrices.dense(3, 3, Array(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)).toDense
println(matrix2)
// 1.0 4.0 7.0
// 2.0 5.0 8.0
// 3.0 6.0 9.0
// 创建一个稀疏矩阵
val numRows = 3
val numCols = 3
val colPtrs = Array(0, 2, 3, 6)
val rowIndices = Array(0, 2, 1, 0, 1, 2)
val values = Array(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
val matrix3: SparseMatrix = Matrices.sparse(numRows, numCols, colPtrs, rowIndices, values).toSparse
println(matrix3)
// 3 x 3 CSCMatrix
// (0,0) 1.0
// (2,0) 2.0
// (1,1) 3.0
// (0,2) 4.0
// (1,2) 5.0
// (2,2) 6.0
// 生成一个全零矩阵
val matrix5: DenseMatrix = Matrices.zeros(3, 3).toDense
println