Scala中如何用MapReduce替换数组中Double.NaN为最近四值平均值?
在Scala中实现NaN替换为最近四个值平均值的方案
当然可以搞定这个需求!咱们结合你的场景,一步步实现符合要求的逻辑:
核心思路拆解
你的需求关键有两点:
- 从左到右顺序处理:替换后的NaN值必须参与后续的计算
- 取最近四个有效数值的平均值:这里的“有效数值”包括原始非NaN值和已经替换好的数值
为了高效跟踪最近的四个值,我们可以用一个队列来维护——每次有新的有效数值(不管是原始的还是替换后的)加入队列,同时保持队列最多只保留最近的4个值,这样遇到NaN时直接取队列内的数值计算平均即可。
完整Scala代码实现
import scala.collection.mutable.Queue def replaceNaNsWithRecentAvg(arr: Array[Double]): Array[Double] = { // 复制原数组,避免修改原始数据 val processedArray = arr.clone() // 用于存储最近的4个有效数值 val recentValidValues = Queue[Double]() // 先遍历一次,填充初始的非NaN值到队列(确保队列先有足够的初始值) processedArray.foreach { num => if (!num.isNaN) { recentValidValues.enqueue(num) // 队列长度超过4时,移除最早加入的元素 if (recentValidValues.size > 4) recentValidValues.dequeue() } } // 再次遍历数组,处理所有NaN for (i <- processedArray.indices) { if (processedArray(i).isNaN) { // 计算最近4个值的平均值(如果队列元素不足4个,可根据需求调整,比如取现有所有值的平均) val avg = recentValidValues.sum / recentValidValues.size processedArray(i) = avg // 将替换后的平均值加入队列,维护最近4个值的长度 recentValidValues.enqueue(avg) if (recentValidValues.size > 4) recentValidValues.dequeue() } else { // 非NaN值直接加入队列,维护长度 recentValidValues.enqueue(processedArray(i)) if (recentValidValues.size > 4) recentValidValues.dequeue() } } processedArray } // 示例使用(模拟你提到的场景) val sampleArray = Array(55.0, 62.0, 65.0, 58.0, Double.NaN, 63.0, Double.NaN, 61.0, 64.0, 60.0, 59.0, 66.0, Double.NaN, 62.0, 67.0) val result = replaceNaNsWithRecentAvg(sampleArray) // 打印结果验证 result.zipWithIndex.foreach { case (num, idx) => println(s"索引$idx: $num") }
代码关键点说明
- 数组副本:用
clone()创建原数组的副本,避免修改原始输入数据,这是函数式编程里的常见做法。 - 队列维护:
recentValidValues队列始终保留最近的4个有效数值,每次新增元素后如果长度超过4,就移除最旧的元素,保证队列里的数值始终是最新的。 - NaN处理逻辑:遇到NaN时,直接用队列内的数值计算平均值替换,替换后的数值会被加入队列,参与后续的NaN计算,完全符合你的需求。
- 边界情况处理:如果数组开头就有NaN(队列元素不足4个),代码会取现有所有元素的平均值替换,你可以根据实际业务需求调整这部分逻辑(比如抛出提示、用默认值等)。
内容的提问来源于stack exchange,提问作者Santosh Bobade




