PHP/JS实现价格数组中异常不均匀价格的过滤移除方法
邻域基准法过滤价格异常值方案
问题说明
待处理的价格数组示例如下:
$arr = [200,201,203,205,207,300,209,212,0,211,0,0,301,213,214];
数组中存在0、300、301这类不符合连续波动规律的异常值,已知正常价格区间为200-400,待处理数组长度通常在1000以上,要求仅基于邻域数据完成异常判断。
原有方案问题
最初采用相邻值差值阈值的过滤逻辑:
$fresharray = []; $i = 0; foreach($arr as $lol) { $diff = $lol - $arr[$i-1]; if($diff < -50 || $diff > 50) { // 差值超过阈值则判定为异常,跳过 } else { $fresharray[] = $lol; } $i++; }
该方案存在两个核心问题:
- 未初始化
$i变量,首次循环会取数组最后一个元素做对比,初始判断逻辑完全失效 - 仅以紧邻的上一个值为判断基准,遇到连续异常值场景(如连续两个
0、连续的300和301)时,异常值之间差值仅为0或1,会被误判为正常值加入结果集
优化逻辑
核心是避免单值作为判断基准,采用固定长度的最近正常值队列作为判断基准池,彻底规避连续异常值的干扰:
- 先做区间粗筛,直接排除不在200-400正常区间内的明显异常值
- 维护长度为3的最近正常价格队列,取队列平均值作为当前值的对比基准
- 当前值与基准值差值不超过阈值(默认50)则判定为正常值,加入结果集同时更新基准池
- 差值超阈值则判定为异常跳过,基准池不做更新,避免异常值污染基准
- 全程仅需一次遍历,时间复杂度O(n),可轻松适配万级长度数组
代码实现
PHP版本
/** * 过滤价格数组中的异常值 * @param array $priceArr 原始价格数组 * @param int $threshold 邻域差值阈值,默认50 * @param int $minNormal 正常价格最小值,默认200 * @param int $maxNormal 正常价格最大值,默认400 * @param int $baseWindowSize 基准窗口大小,默认3 * @return array 过滤后的正常价格数组 */ function filterAbnormalPrice(array $priceArr, int $threshold = 50, int $minNormal = 200, int $maxNormal = 400, int $baseWindowSize = 3): array { $result = []; $normalBase = []; // 存储最近确认的正常价格作为基准 foreach ($priceArr as $price) { // 第一步:区间粗筛,直接排除明显超出正常范围的值 if ($price < $minNormal || $price > $maxNormal) { continue; } // 初始阶段基准池未填满时,直接将符合区间的值作为初始基准 if (count($normalBase) < $baseWindowSize) { $result[] = $price; $normalBase[] = $price; continue; } // 计算基准池平均值作为对比基准 $baseAvg = array_sum($normalBase) / count($normalBase); if (abs($price - $baseAvg) <= $threshold) { $result[] = $price; // 更新基准池:移除最早的基准值,加入新的正常值 array_shift($normalBase); $normalBase[] = $price; } } return $result; } // 测试示例 $arr = [200,201,203,205,207,300,209,212,0,211,0,0,301,213,214]; print_r(filterAbnormalPrice($arr)); // 输出结果:Array ( [0] => 200 [1] => 201 [2] => 203 [3] => 205 [4] => 207 [5] => 209 [6] => 212 [7] => 211 [8] => 213 [9] => 214 )
JavaScript版本
/** * 过滤价格数组中的异常值 * @param {number[]} priceArr 原始价格数组 * @param {number} threshold 邻域差值阈值,默认50 * @param {number} minNormal 正常价格最小值,默认200 * @param {number} maxNormal 正常价格最大值,默认400 * @param {number} baseWindowSize 基准窗口大小,默认3 * @returns {number[]} 过滤后的正常价格数组 */ function filterAbnormalPrice(priceArr, threshold = 50, minNormal = 200, maxNormal = 400, baseWindowSize = 3) { const result = []; const normalBase = []; for (const price of priceArr) { // 区间粗筛 if (price < minNormal || price > maxNormal) { continue; } // 初始基准池填充 if (normalBase.length < baseWindowSize) { result.push(price); normalBase.push(price); continue; } // 计算基准平均值 const baseAvg = normalBase.reduce((sum, val) => sum + val, 0) / normalBase.length; if (Math.abs(price - baseAvg) <= threshold) { result.push(price); normalBase.shift(); normalBase.push(price); } } return result; } // 测试示例 const arr = [200,201,203,205,207,300,209,212,0,211,0,0,301,213,214]; console.log(filterAbnormalPrice(arr)); // 输出结果:[200, 201, 203, 205, 207, 209, 212, 211, 213, 214]
可调参数说明:如果价格波动幅度更大,可以适当调大
threshold阈值;如果异常值比例更高,可以适当增大baseWindowSize窗口大小,也可以将基准平均值替换为中位数,进一步提升抗干扰能力。
内容的提问来源于stack exchange,提问作者Gracie williams
相关产品推荐
相关产品推荐

