75 lines
3.9 KiB
Python
75 lines
3.9 KiB
Python
# Author: Lee Wu Love Lele
|
||
# Datetime: 2024/12/8 12:58
|
||
from config import DATETIME_FORMAT
|
||
from datetime import datetime
|
||
from scipy.signal import savgol_filter
|
||
import numpy as np
|
||
from config import STABILITY_THRESHOLD
|
||
import pandas as pd
|
||
|
||
|
||
def smooth_data(data, window_length=5, polyorder=3):
|
||
"""
|
||
平滑数据降噪,使用Savitzky-Golay滤波
|
||
* 通过算法(Savitzky-Golay滤波),将短期波动平滑化,突出重量变化的主要趋势,从而更接近牛的真实体重曲线。
|
||
* 在牛走上或走下地磅时,体重数据会从小到大(走上)或从大到小(走下)发生变化。平滑处理可以让“从小到大”或“从大到小”的趋势更加清晰,便于后续算法找到牛完整通过地磅的时间段。
|
||
* 如果直接对噪声较大的数据进行分析(如提取最大值、计算均值等),可能需要大量的异常值处理或复杂的算法来排除干扰。
|
||
* 平滑数据可以简化后续计算过程,使得分析模型更加高效。
|
||
* 牛在地磅上可能出现诸如抬腿、摇晃等小幅动作,这些动作会导致短时间内体重数据发生波动。平滑能够消除这些短时间的小波动,使得体重数据更加稳定。
|
||
* 平滑可以让真正的异常值(如多头牛站上地磅)更加显著,便于识别。例如:没有平滑时,普通波动和异常值可能混淆在一起。平滑后,正常数据的波动范围变小,异常值更加容易识别。
|
||
:param data:
|
||
:param window_length: int
|
||
:param polyorder: int
|
||
:return:
|
||
"""
|
||
|
||
"""
|
||
window_length
|
||
* 含义:
|
||
* 表示滑动窗口的长度,即在每次滤波计算中使用的点的数量。
|
||
* 它必须是一个正奇数,如 3、5、7 等,因为 Savitzky-Golay 滤波需要确保窗口中心有一个对称点。
|
||
* 作用:
|
||
* 窗口越大:
|
||
* 数据被平滑得越明显,但可能会导致细节丢失。
|
||
* 更适合处理噪声较多但关注长期趋势的情况。
|
||
* 窗口越小:
|
||
* 保留更多的细节,但对噪声的抑制效果较差。
|
||
* 更适合处理数据变化较快且噪声较少的情况。
|
||
|
||
polyorder
|
||
* 含义:
|
||
* 表示拟合多项式的阶数,用于滑动窗口内的数据拟合。
|
||
* 它必须小于 window_length,因为阶数不能超过拟合点的数量(否则拟合就无意义)。
|
||
* 作用:
|
||
* 多项式阶数越高:
|
||
* 滤波器拟合数据的能力更强,可以保留更多复杂的细节,但同时可能引入噪声(过拟合)。
|
||
* 更适合处理具有快速变化趋势的数据。
|
||
* 多项式阶数越低:
|
||
* 滤波器更倾向于平滑数据,减少噪声,但可能会忽略一些数据细节。
|
||
* 更适合处理平稳的数据或去除高频噪声。
|
||
"""
|
||
print(f'window_len: {window_length}, polyorder: {polyorder}')
|
||
smoothed_data = savgol_filter(data, window_length=window_length, polyorder=polyorder)
|
||
return smoothed_data
|
||
|
||
|
||
def calc_one_cattle(data):
|
||
print('data: ', data)
|
||
# print('data len: ', len(data))
|
||
# 计算变化率
|
||
rate_of_change = np.abs(np.diff(data)) # 一阶差分后的绝对值,比data长度少1
|
||
print('np.diff: ', np.diff(data))
|
||
# print('np.diff len: ', len(np.diff(data)))
|
||
print('rate_of_change: ', rate_of_change)
|
||
# print('rate_of_change len: ', len(rate_of_change))
|
||
# 设置阈值,识别稳定时间段
|
||
stability_threshold = STABILITY_THRESHOLD # 变化率小于此值视为稳定
|
||
stable_indices = np.where(rate_of_change < stability_threshold)[0]
|
||
print('stable_indices: ', stable_indices)
|
||
# 找到稳定时间段的体重中值
|
||
stable_weights = [data[i] for i in stable_indices]
|
||
print('stable_weights: ', stable_weights)
|
||
stable_median_weight = np.median(stable_weights) # TODO:这里取了所有稳定值的中位数,这里包含了异常稳定值形成干扰,如何识别这些异常?
|
||
print('stable_median_weight: ', stable_median_weight)
|
||
return stable_median_weight
|