数据分箱与标准化:风速分箱、功率归一化、空气密度修正

好,咱们接着聊。前面我们拿到了原始的风速和功率数据,但说实话,这些数据就像刚从矿里挖出来的矿石——杂质多、分布乱、没法直接用。今天我要讲的这三个步骤,就是给数据「洗个澡、分个类、再校准一下」。

我个人习惯把这一章叫做「数据预处理三件套」。你想想看,风功率曲线建模,本质上就是在找风速和功率之间的映射关系。但如果风速数据本身就有偏差,功率值又受环境影响,那模型再漂亮也是白搭。

风速分箱:把连续数据装进「抽屉」里

风速是连续变化的,从0 m/s到25 m/s,中间有无数个可能的值。但实际采集到的数据点,往往在某些风速区间特别密集,某些区间又特别稀疏。直接拿这些不均匀的数据去拟合曲线,结果会偏向数据密集的区域。

怎么办?分箱。

说白了,就是把风速范围切成一个个小段,每个段就是一个「箱子」。比如0.5 m/s一个箱,0~0.5 m/s算第一箱,0.5~1.0 m/s算第二箱,以此类推。然后每个箱子里的功率值取平均,得到一个代表点。

核心原则: 箱宽的选择很关键。太宽了会丢失细节,太窄了箱子里可能没几个数据点。我一般建议用0.5 m/s作为默认箱宽,如果数据量特别大(比如超过10万条),可以缩到0.25 m/s。

我在项目中遇到过这样的情况:某风场的数据采集频率是1 Hz,一年下来几百万条记录。我一开始用了0.1 m/s的箱宽,结果发现高风速区每个箱子里只有两三个点,平均值的波动特别大。后来改成0.5 m/s,曲线就平滑多了。

分箱的代码其实很简单,用pandas的cut函数就能搞定:

import pandas as pd
import numpy as np

# 假设df包含'wind_speed'和'power'两列
bins = np.arange(0, 30, 0.5)  # 从0到30,步长0.5
labels = bins[:-1] + 0.25     # 用箱中心值作为标签

df['bin'] = pd.cut(df['wind_speed'], bins=bins, labels=labels)
binned = df.groupby('bin')['power'].mean().reset_index()
binned.columns = ['wind_speed', 'power_mean']
小技巧: 分箱后别忘了检查每个箱子里的数据量。如果某个箱子的数据点少于10个,我建议直接舍弃,或者跟相邻箱子合并。否则那个点的统计意义不大。

功率归一化:让不同机组的数据能「对话」

不同型号的风机,额定功率不一样。有的2 MW,有的3 MW,甚至还有1.5 MW的老机组。如果你把它们的功率值直接放在一起比较,那肯定乱套。

归一化,就是把功率值映射到0到1之间。最常用的方法是除以额定功率:

P_norm = P / P_rated

这样,不管原始功率是多少,归一化后都在0~1之间。0代表停机,1代表满发。

嗯,这里要注意:归一化不是简单的数学变换,它背后有一个假设——不同机组的功率曲线形状是相似的。实际上这个假设基本成立,因为风机的气动设计原理是相通的。但如果你遇到的是不同技术代际的机组(比如定速定桨和变速变桨),那就要小心了,它们的曲线形状差异可能很大。

我曾经帮一个客户做风场级评估,他们场里有三种不同品牌的风机。我一开始把所有数据归一化后混在一起建模,结果模型在中间风速段偏差很大。后来发现,其中一种机型的控制策略比较激进,在额定风速附近有功率过冲现象。最后我只能按机型分别建模。

归一化的代码:

# 假设已知额定功率P_rated
df['power_norm'] = df['power'] / P_rated

# 如果不知道额定功率,可以用99.5%分位数近似
P_rated_approx = df['power'].quantile(0.995)
df['power_norm'] = df['power'] / P_rated_approx
注意: 不要用最大值作为归一化分母。因为数据中可能有异常尖峰,用最大值会导致大部分数据被压缩到很小的范围。用额定功率或者高分位数更靠谱。

空气密度修正:别让天气「骗」了你

风功率公式里有一个关键参数——空气密度ρ。风功率与空气密度成正比:

P = 0.5 * ρ * A * Cp * v³

但空气密度不是常数。它随海拔、温度、气压变化。海平面标准条件下ρ≈1.225 kg/m³,但在青藏高原,ρ可能只有0.8 kg/m³左右。同一台风机,在高原和在海边,相同风速下发出的功率能差30%以上。

所以,我们需要把实际功率修正到标准空气密度下的等效功率。修正公式很简单:

P_corrected = P_actual * (ρ_standard / ρ_actual)

其中ρ_actual可以根据现场的温度和气压计算:

ρ = P / (R * T)

P是大气压(Pa),R是气体常数(287 J/(kg·K)),T是绝对温度(K)。

你可能会问:如果现场没有测气压怎么办?我一般用海拔高度估算:

海拔 (m) 标准气压 (kPa) 空气密度 (kg/m³)
0 101.3 1.225
500 95.5 1.167
1000 89.9 1.112
1500 84.6 1.058
2000 79.5 1.007

修正代码:

def air_density_correction(power, temp_c, pressure_pa):
    """
    空气密度修正
    temp_c: 温度,摄氏度
    pressure_pa: 大气压,帕斯卡
    """
    R = 287.058  # 气体常数
    temp_k = temp_c + 273.15
    rho_actual = pressure_pa / (R * temp_k)
    rho_standard = 1.225
    return power * (rho_standard / rho_actual)

# 应用修正
df['power_corrected'] = air_density_correction(
    df['power'], 
    df['temperature'], 
    df['pressure']
)
实战经验: 如果现场没有气压数据,可以用温度和海平面气压近似。但要注意,温度每变化10°C,空气密度变化约3.4%。所以如果你在温差大的地区(比如北方冬季和夏季温差能到40°C),不修正的话,功率曲线会明显「漂移」。

本章知识体系

下面这张图,是我自己梳理的本章核心逻辑。你可以把它当作一个检查清单:

数据分箱与标准化流程 原始风速/功率数据 步骤1:风速分箱 将连续风速切分为0.5m/s的区间 步骤2:功率归一化 除以额定功率,映射到0~1区间 步骤3:空气密度修正 修正到标准空气密度下的等效功率 关键参数 箱宽:0.5 m/s(默认) 归一化分母:额定功率 标准密度:1.225 kg/m³ 避坑提醒: • 箱内数据量≥10 • 不用最大值归一化 • 温差大地区必须修正 • 不同机型分开处理

好了,这三个步骤做完,你的数据就「干净」了。风速被规整到统一的箱子里,功率值被归一化到可比较的尺度,空气密度的影响也被消除了。接下来,我们就可以用这些处理好的数据去拟合风功率曲线了。

我个人觉得,数据预处理花的时间,至少应该占到整个建模项目的一半。因为数据质量决定了模型的上限,而模型算法只是在逼近这个上限。你想想看,如果输入的数据都是错的,再牛的算法也救不了。


专注资料整理