基于RDD解决大矩阵问题

本文介绍了一种基于Apache Spark的大规模稀疏矩阵处理方法,包括矩阵的构建、基本操作及高级应用如排序、求和等,并实现了将稀疏矩阵转换为密集矩阵的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

import breeze.linalg.{DenseMatrix, DenseVector}
import org.apache.spark.rdd.RDD




/**
  * Created by legotime
  */
case class MatrixEntry(i: Long, j: Long, value: Double)
case class VectorEntry(i: Long,value: Array[Double])



class bigMatrix(val entries: RDD[MatrixEntry],
                     var nRows: Long,
                     var nCols: Long) extends Serializable{

  def this(entries: RDD[MatrixEntry]) = this(entries, 0L, 0L)

    // 列数
   def numCols(): Long = {
    if (nCols <= 0L) {
      computeSize()
    }
    nCols
  }

    //行数
   def numRows(): Long = {
    if (nRows <= 0L) {
      computeSize()
    }
    nRows
  }

  def apply(row: Long, col: Long) = {
    if(row < - numRows || row >= numRows) throw new IndexOutOfBoundsException((row,col) + " not in [-"+numRows+","+numRows+") x [-"+numCols+"," + numCols+")")
    if(col < - numCols || col >= numCols) throw new IndexOutOfBoundsException((row,col) + " not in [-"+numRows+","+numRows+") x [-"+numCols+"," + numCols+")")
    var tmp = 0.0
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
      if(i==row && j == col){
        tmp = value
      }
    }
    tmp
  }
  def apply(row:Long)={
    if(row < - numRows) throw new IndexOutOfBoundsException((row) + " not in [-"+numRows+","+numRows+") x [-"+numCols+"," + numCols+")")
    getRow(row)
  }

  //和apply一样
  def valueAt(i: Int) = apply(i)
  def valueAt(row: Long, col: Long) = apply(row,col)

  /**
    * 查看一个值出现在矩阵中有多少次
    *
    * @param target 输入的值
    * @return
    */
  def valueCount(target:Double)= entries.filter{element =>
      if(target == element.value){true}else{false}
    }.count()

  /**
    * 查看一个值在矩阵中的坐标值
    *
    * @param target 输入的值
    * @return 得到一个n*2的矩阵,n表示个数
    */

  def valueIndex(target:Double)={
    val mat = DenseMatrix.zeros[Int](valueCount(target).toInt, 2)
    var x = 0
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
      if(target ==value){
        mat(x,0) = i.toInt
        mat(x,1) = j.toInt
        x = x+1
      }
    }
    mat
  }


  /**
    *计算一行中的topN
    *
    * @param i 输入的第几个词语
    * @param n 找出排名前几
    */

  def getRowTopN(i: Long,n:Int): DenseVector[Double] ={
    if( n>numCols){
      require(  n < numCols,
        s"getRowTopN function input arg must < matrix length")
    }
    val vec: DenseVector[Double] = DenseVector.zeros[Double](n)

    val tmp = mergeSort(getColumn(i).toArray.toList)
    for(j <- 0 until n){
        vec(j) = tmp(n-j)
    }
    vec
  }
  /**
    *计算一列中的topN
    *
    * @param j 输入的第几个词语
    * @param n 找出排名前几
    */

  def getColTopN(j:Long,n:Int):DenseVector[Double]={
    if( n>numRows){
      require(  n < numRows,
        s"getColTopN function input arg must < matrix length")
    }
    val vec: DenseVector[Double] = DenseVector.zeros[Double](n)
    val tmp = mergeSort(getRow(j).toArray.toList)
    for(i <- 0 until n){
      vec(i) = tmp(n-i)
    }
    vec
  }

  /**
    * 计算这个矩阵的全部累计值
    *
    * @return
    */
  def sum(): Double =entries.map(part =>part.value).reduce(_+_)

  /**
    * 计算第x行的累计和
    *
    * @param x
    * @return
    */
  def sumRow(x:Long):Double ={
    var result = 0.0
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
      if(i == x){
        result = result+value
      }
    }
    result
  }

  /**
    * 计算第y列的累计和
    *
    * @param y
    * @return
    */
  def sumCol(y:Long):Double={
    var result = 0.0
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
      if(j == y){
        result = result+value
      }
    }
    result
  }

  /**
    * 把其中一个行的数据作为向量提取出来
    *
    * @param x 第几行
    * @return 返回一个向量
    */
  def getColumn(x: Long): DenseVector[Double] ={
    //经过测试,默认JVM参数,发现矩阵存储可以亿级别向量,尝试转换为向量用于排序
    //千万级别的List,可以顶住
    val denseVector = DenseVector.zeros[Double](numCols().toInt)
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
        if(i == x){
          denseVector(j.toInt) = value
        }
    }
    denseVector
  }

  /**
    * 把其中一个列的数据作为向量提取出来
    *
    * @param y 第几列
    * @return 返回一个向量
    */

  def getRow(y:Long): DenseVector[Double] ={
    val denseVector = DenseVector.zeros[Double](numCols().toInt)
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
      if(j == y){
        denseVector(i.toInt) = value
      }
    }
    denseVector
  }
  //转置
  def transpose():bigMatrix={
    new bigMatrix(entries.map(element => MatrixEntry(element.j,element.i,element.value)),numCols(),numRows())
  }
  override def toString = {
    numCols+" colmun "+numRows+ " row " + "big matrix"
  }

  override def equals(obj: scala.Any): Boolean = super.equals(obj)

  /**
    * 转换为breeze 的密集矩阵(直接爆炸,内存不够就别尝试)
    *
    * @return
    */

  def toDenseMatrix: DenseMatrix[Double] = {
    val m = numRows().toInt
    val n = numCols().toInt
    val mat = DenseMatrix.zeros[Double](m, n)
    entries.collect().foreach { case MatrixEntry(i, j, value) =>
      mat(i.toInt, j.toInt) = value
    }
    mat
  }

  /**
    * 归并排序
    *
    * @param SortList 需要排序的list
    * @return 排序好的List
    */

  private def mergeSort(SortList: List[Double]): List[Double] = {
    def merge(a: List[Double], b: List[Double]): List[Double] = (a,b) match {
      case (Nil, _) => b
      case (_, Nil) => a
      case (x::xs, y::ys) =>
        if(x <= y) x :: merge(xs, b)
        else y :: merge(a, ys)
    }
    if(SortList.length == 1) SortList
    else{
      val (first, second) = SortList.splitAt(SortList.length/2)
      merge(mergeSort(first), mergeSort(second))
    }
  }

  private def computeSize()={
    val (m1, n1) = entries.map(entry => (entry.i, entry.j)).reduce { case ((i1, j1), (i2, j2)) =>
      (math.max(i1, i2), math.max(j1, j2))
    }
    nRows = math.max(nRows, m1 + 1L)
    nCols = math.max(nCols, n1 + 1L)

    (nRows,nCols)
  }




}

### 基于 Spark 的词频统计实验常见问题解决方案 #### 1. 数据倾斜 (Data Skew) 数据倾斜是指某些分区的数据量远大于其他分区,从而导致计算资源分配不均衡。这种情况在处理高频次单词时尤为明显。 - **原因**: 如果输入数据集中存在大量重复的高频词,则这些词会被映射到相同的分区上,造成该分区的任务执行时间过长。 - **解决方法**: 使用 `RangePartitioner` 来替代默认的哈希分区器可以有效缓解这一问题[^4]。通过采样来估算键值分布并合理划分范围,使得负载更加均匀分布在各个节点之间。 ```python rdd.repartition(new_num_partitions, partitionFunc=RangePartitioner(numPartitions=new_num_partitions)) ``` --- #### 2. 内存溢出 (Out of Memory Error) 当单个任务所需内存超出分配给它的最大堆大小时会发生 OOM 错误。 - **原因**: 处理大规模文本文件或者复杂操作(如 join 或 groupByKey)可能导致中间结果占用过多内存。 - **解决方法**: 调整垃圾回收机制参数以及增加 executor 和 driver 的内存设置;另外也可以考虑采用广播变量减少 shuffle 过程中的数据传输开销[^2]。 - 设置 spark 配置项: ```bash --conf "spark.executor.memory=8g" ``` --- #### 3. 文件读写失败 提交作业后发现标准输出日志显示正常完成,但实际上生成的结果为空白或者是错误路径下的产物。 - **原因**: 可能是因为程序结束前未等待所有异步 I/O 完成就终止运行,也可能是权限不足无法访问指定目录。 - **解决方法**: 确保最后一步保存动作完成后才退出应用,并检查是否有足够的权限去创建目标位置上的新文件夹结构。 - 修改命令行脚本以同步方式调用 API 方法直到其返回成功状态码为止: ```bash /usr/local/spark/bin/spark-submit \ --class org.apache.spark.examples.SparkPi \ --master yarn-cluster \ ./path/to/jarfile.jar > output.log 2>&1 & wait $! echo Done. ``` --- #### 4. K-Means 文本聚类不稳定 如果尝试利用 k-means 对文档向量化后的特征矩阵做进一步分析,在多次试验间获得差异较大的分组情况。 - **原因**: 如前所述,KMeans 是一种依赖初始质心选取过程的启发式优化算法,因此即使保持其余条件不变也可能因为随机种子不同而得出不一样的结论[^3]。 - **解决方法**: 设定固定随机数发生器种子以便重现相同初始化步骤;同时适当增大迭代次数提高收敛精度。 - Python 示例代码如下所示: ```python from pyspark.ml.clustering import KMeans # 创建模型实例时传入 seed 参数控制伪随机序列生成行为 km_model = KMeans(k=k_value, maxIter=max_iterations, seed=fixed_seed).fit(dataframe_vectorized_features) predictions = km_model.transform(dataframe_vectorized_features) ``` --- #### 5. JSON 解析异常 加载外部源提供的非规范化格式化字符串作为记录字段内容导入过程中抛出了非法字符编码等问题提示信息。 - **原因**: 输入流中包含了不符合预期模式定义的部分片段,比如转义符遗漏、多余逗号等语法瑕疵都会引起解析失败[^1]。 - **解决方法**: 提前预览样本确认整体布局一致后再正式加载整个批次资料集;必要时候编写自定义函数清理脏数据再交给后续阶段继续加工处理。 - 清洗逻辑举例说明: ```python def clean_json_string(s):
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值