feiyu02
昨天 e923f06d572c9a0e3b1eb2c54471af02c9d95bcf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.flightfeather.uav.model.epw
 
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.lightshare.bean.DataVo
import com.flightfeather.uav.socket.bean.AirData
import com.flightfeather.uav.socket.eunm.FactorType
 
/**
 * 突变数据预处理
 * 1. 针对单个数据突高或突低,前后两个数据的量级接近的情况,进行处理
 * 2. 针对有关联关系的监测因子,数据量级出现明显错误的情况,进行处理
 * @date 2025/11/20
 * @author feiyu02
 */
class MutationDataPreprocess {
 
    // 保存数据最大记录数
    private val MAX_COUNT = 15
 
    private val lastData = mutableListOf<DataVo>()
 
    // 量级变化阈值倍数(用于判断是否为突变)
    private val MUTATION_THRESHOLD = 5.0
    // 前后数据接近阈值倍数(用于判断前后数据是否量级接近)
    private val SIMILARITY_THRESHOLD = 1.5
 
    /**
     * 数据平滑
     * 解决单个数据相比前后两个数据的量级过大或过小的问题
     * @param data 原始数据
     * @return 预处理后的数据
     */
    fun preprocess(data: List<DataVo>): List<DataVo> {
        // 当新数据与旧数据采样时间差超过1分钟时,认为两组数据已无关联性,清空旧数据
        if (lastData.isNotEmpty() && data.isNotEmpty()) {
            val lastTime = DateUtil.instance.StringToDate(lastData.last().time)
            val thisTime = DateUtil.instance.StringToDate(data.first().time)
            if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) {
                lastData.clear()
            }
        }
        lastData.addAll(data)
        detectAndReplaceMutation(lastData)
        saveHistory(lastData)
        return lastData
    }
 
    /**
     * 检测并替换异常突变数据
     * @param data 原始数据列表
     * @return 处理后的数据列表
     */
    fun detectAndReplaceMutation(data: MutableList<DataVo>){
        if (data.size < 3) return // 数据量不足,无法进行前后对比
 
        // 遍历数据,从第二个开始到倒数第二个结束
        for (i in 1 until data.size - 1) {
            val currentData = data[i]
            val prevData = data[i - 1]
            val nextData = data[i + 1]
 
            // 检查每个监测因子
            if (currentData.values != null && prevData.values != null && nextData.values != null) {
                processEachFactor(currentData, prevData, nextData)
            }
        }
 
    }
 
    /**
     * 处理每个监测因子的数据突变
     */
    private fun processEachFactor(currentData: DataVo, prevData: DataVo, nextData: DataVo) {
        val currentFactors = mutableMapOf<String, AirData>()
        val prevFactors = mutableMapOf<String, AirData>()
        val nextFactors = mutableMapOf<String, AirData>()
 
        // 构建因子名称到AirData的映射
        currentData.values?.forEach { currentFactors[it.factorName ?: ""] = it }
        prevData.values?.forEach { prevFactors[it.factorName ?: ""] = it }
        nextData.values?.forEach { nextFactors[it.factorName ?: ""] = it }
 
        // 遍历当前数据中的所有因子
        currentFactors.forEach { (factorName, currentFactor) ->
            val prevFactor = prevFactors[factorName]
            val nextFactor = nextFactors[factorName]
 
            // 确保三个数据点都有该因子的数据
            if (prevFactor != null && nextFactor != null &&
                currentFactor.factorData != null && prevFactor.factorData != null && nextFactor.factorData != null) {
 
                val currentValue = currentFactor.factorData!!
                val prevValue = prevFactor.factorData!!
                val nextValue = nextFactor.factorData!!
 
                // 跳过0值或负值,避免除零错误
                if (prevValue <= 0 || nextValue <= 0) return@forEach
 
                // 检查是否为异常突变数据
                if (isMutationData(currentValue, prevValue, nextValue)) {
                    // 替换为前一个数据的值
                    currentFactor.factorData = prevValue
                }
            }
        }
    }
 
    /**
     * 判断当前数据是否为突变数据
     * 条件:1. 当前数据与前一个数据的量级变化超过阈值
     *       2. 前一个数据与后一个数据的量级接近
     */
    private fun isMutationData(currentValue: Double, prevValue: Double, nextValue: Double): Boolean {
        // 计算变化率
        val currentToPrevRatio = Math.max(currentValue, prevValue) / Math.min(currentValue, prevValue)
        val prevToNextRatio = Math.max(prevValue, nextValue) / Math.min(prevValue, nextValue)
 
        // 判断是否满足突变条件
        return currentToPrevRatio > MUTATION_THRESHOLD && prevToNextRatio <= SIMILARITY_THRESHOLD
    }
 
    fun saveHistory(data: MutableList<DataVo>) {
//        // 将新数据的至多最后15个保存下来(已经过预处理),用于下一次的判断
//        val newList = mutableListOf<DataVo>()
//        val s = if ((data.lastIndex - MAX_COUNT + 1) < 0) 0 else data.lastIndex - MAX_COUNT + 1
//        data.subList(s, data.lastIndex + 1).forEach {
//            newList.add(it.copy())
//        }
//        // 当新数据与旧数据采样时间差超过1分钟时,认为两组数据已无关联性,清空旧数据
//        if (lastData.isNotEmpty() && newList.isNotEmpty()) {
//            val lastTime = DateUtil.instance.StringToDate(lastData.last().time)
//            val thisTime = DateUtil.instance.StringToDate(newList.first().time)
//            if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) {
//                lastData.clear()
//            }
//        }
//        lastData.addAll(newList)
        // 确保保存的数据最多只有最新的15个
        while (data.size > MAX_COUNT) {
            data.removeAt(0)
        }
    }
}