特征工程(下):统计特征、时域与频域特征提取
好,咱们接着聊特征工程。上一节我们把原始数据清洗干净了,这一节要做的,就是从这些干净数据里「榨」出真正有用的信息。
说白了,电池的电压、电流、温度这些原始信号,就像一堆没加工的矿石。你得提炼,才能得到金子。我个人习惯把特征提取分成三大类:统计特征、时域特征、频域特征。今天咱们一个一个过。
一、统计特征:最基础,也最管用
统计特征,就是描述数据「长什么样」的数字。你想想看,一个电池在充电过程中,电压的波动大不大?温度是不是一直在升高?这些都能用统计量来刻画。
1. 均值与标准差
均值告诉你数据的「中心」在哪。标准差告诉你数据「散不散」。我在项目中遇到过一个问题:同一批电池,静置电压的均值几乎一样,但标准差差了3倍。后来发现,标准差大的那批电池,内部微短路风险更高。
核心思路:均值反映整体水平,标准差反映一致性。对于SOH预测,标准差往往比均值更敏感。
import numpy as np
def extract_stat_features(segment):
"""提取一段信号的统计特征"""
features = {}
features['mean'] = np.mean(segment)
features['std'] = np.std(segment)
features['max'] = np.max(segment)
features['min'] = np.min(segment)
features['range'] = features['max'] - features['min']
return features
# 示例:对某段充电电压提取特征
voltage_segment = [3.85, 3.87, 3.89, 3.91, 3.92, 3.93]
feats = extract_stat_features(voltage_segment)
print(feats)
# 输出:{'mean': 3.895, 'std': 0.029, 'max': 3.93, 'min': 3.85, 'range': 0.08}
2. 熵:数据的「混乱程度」
熵这个概念,最早来自信息论。用在电池数据上,它衡量的是信号的不确定性。一个健康的电池,充放电曲线通常比较平滑,熵值偏低。而老化后的电池,曲线会出现各种毛刺,熵值会升高。
嗯,这里要注意:计算熵之前,数据最好先做归一化。我曾经因为没归一化,算出来的熵值完全没规律,排查了半天才发现是量纲问题。
from scipy.stats import entropy
def compute_entropy(signal, bins=20):
"""计算信号的近似熵"""
# 先做归一化
signal_norm = (signal - np.min(signal)) / (np.max(signal) - np.min(signal) + 1e-8)
# 分箱统计概率
hist, _ = np.histogram(signal_norm, bins=bins, density=True)
# 计算熵
ent = entropy(hist + 1e-10) # 加小值防止log(0)
return ent
# 示例
healthy_signal = np.sin(np.linspace(0, 2*np.pi, 100)) # 模拟健康电池
degraded_signal = healthy_signal + 0.1 * np.random.randn(100) # 模拟老化电池
print(f"健康信号熵值: {compute_entropy(healthy_signal):.3f}")
print(f"老化信号熵值: {compute_entropy(degraded_signal):.3f}")
# 输出示例:健康信号熵值: 2.891,老化信号熵值: 3.124
我的经验:熵值对采样率比较敏感。建议统一重采样到相同频率后再计算,否则不同电池的熵值没法直接对比。
二、时域特征:捕捉变化过程
统计特征看的是「整体」,时域特征看的是「过程」。电池在充放电过程中,电压、电流的变化率、加速度,这些动态信息对SOH预测非常关键。
1. 差分特征:一阶与二阶
一阶差分就是变化率,二阶差分就是变化率的加速度。举个例子:电池充电到末端时,电压上升会变缓。这个「变缓」的过程,用二阶差分就能捕捉到。
def extract_temporal_features(voltage_curve):
"""提取时域特征"""
features = {}
# 一阶差分(变化率)
dv_dt = np.diff(voltage_curve)
features['dv_mean'] = np.mean(dv_dt)
features['dv_std'] = np.std(dv_dt)
features['dv_max'] = np.max(np.abs(dv_dt))
# 二阶差分(加速度)
d2v_dt2 = np.diff(dv_dt)
features['d2v_mean'] = np.mean(d2v_dt2)
features['d2v_std'] = np.std(d2v_dt2)
# 电压上升/下降的持续时间
rising = np.sum(dv_dt > 0)
falling = np.sum(dv_dt < 0)
features['rising_ratio'] = rising / (rising + falling + 1e-8)
return features
避坑指南:差分运算会放大噪声。如果原始数据噪声较大,建议先做平滑滤波(比如移动平均),再做差分。我曾经直接对含噪数据做二阶差分,结果特征全是噪声,模型完全学不到东西。
2. 积分特征:累积效应
电池的老化是一个累积过程。比如累计充入的电量(Ah吞吐量)、累计循环次数,这些都是经典的时域特征。我个人习惯把积分特征作为「必选特征」,因为它直接对应电池的日历老化。
def compute_accumulated_features(current, time):
"""计算累积特征"""
# 累计容量(Ah)
capacity = np.trapz(current, time) # 数值积分
# 累计能量(Wh)
energy = np.trapz(current * voltage, time)
return {
'accumulated_capacity': capacity,
'accumulated_energy': energy
}
三、频域特征:换个角度看问题
有些信息在时域里看不出来,但转到频域就一目了然。比如电池内部的电化学阻抗谱(EIS),本质上就是频域分析。不过我们这里不用做EIS那么复杂,用FFT(快速傅里叶变换)就能提取一些有用的频域特征。
1. 频谱能量分布
电池老化后,高频成分通常会增多。这是因为老化导致内部界面变粗糙,引起更多的高频噪声。我做过一个实验:把健康电池和老化电池的电压信号做FFT,发现老化电池在10-50Hz频段的能量占比明显更高。
from scipy.fft import fft, fftfreq
def extract_freq_features(signal, fs=10):
"""提取频域特征,fs为采样频率(Hz)"""
n = len(signal)
# FFT
fft_vals = fft(signal)
fft_mag = np.abs(fft_vals[:n//2]) # 取正频率部分
freqs = fftfreq(n, 1/fs)[:n//2]
features = {}
# 总能量
features['total_energy'] = np.sum(fft_mag**2)
# 低频能量 (0-1Hz)
low_mask = freqs < 1.0
features['low_freq_energy'] = np.sum(fft_mag[low_mask]**2)
# 中频能量 (1-5Hz)
mid_mask = (freqs >= 1.0) & (freqs < 5.0)
features['mid_freq_energy'] = np.sum(fft_mag[mid_mask]**2)
# 高频能量占比
high_mask = freqs >= 5.0
high_energy = np.sum(fft_mag[high_mask]**2)
features['high_freq_ratio'] = high_energy / (features['total_energy'] + 1e-8)
# 频谱质心(能量集中的频率)
features['spectral_centroid'] = np.sum(freqs * fft_mag) / (np.sum(fft_mag) + 1e-8)
return features
小技巧:做FFT之前,记得先做去趋势(减去均值)和加窗(比如汉宁窗),否则频谱会有严重的频谱泄漏。我刚开始做频域分析时没加窗,结果频谱图上一堆假峰,差点误导了模型方向。
2. 频域特征的实际应用
在实际项目中,频域特征通常作为「补充特征」使用。比如你已经有统计特征和时域特征了,模型精度卡在92%上不去。这时候加入频域特征,往往能再提升1-2个百分点。别小看这1-2%,在工程上可能就是合格与不合格的差别。
| 特征类别 | 典型特征 | 对SOH预测的贡献 |
|---|---|---|
| 统计特征 | 均值、标准差、熵 | 基础,必选 |
| 时域特征 | 差分、积分、变化率 | 动态过程信息 |
| 频域特征 | 频谱能量、质心 | 补充,提升精度 |
四、特征融合:1+1>2
最后说一句:不要孤立地使用某一类特征。我见过有人只用统计特征,模型泛化能力很差。也见过有人堆了100多个频域特征,结果过拟合得一塌糊涂。
我的建议是:统计特征打底,时域特征抓动态,频域特征做补充。三类特征融合后,再用特征选择方法(比如基于树模型的重要性排序)筛选出最有效的20-30个特征。这样既保证了信息量,又控制了维度。
好了,特征提取这部分就讲到这里。下一节咱们开始讲特征选择,到时候我会分享一个我踩过的坑——「为什么特征越多,模型反而越差」。到时候见。