2. 测风塔数据质量控制:野点剔除、数据完整率检查、扇区相关性分析
各位好,我是老张。干风电微观选址这行当,少说也有十几年了。今天咱们聊一个最基础、但也最容易翻车的话题——测风塔数据质量控制。
你想想看,一个风电场投资动辄几个亿,微观选址的决策依据是什么?说白了,就是测风塔那几十米高的数据。数据要是脏了、假了、缺了,后面所有分析都是白搭。我见过太多项目,因为数据没处理好,导致发电量估算偏差超过10%,那损失可不是小数目。
所以,这一节咱们就掰开揉碎了讲:野点剔除、数据完整率检查、扇区相关性分析。这三板斧,是数据质量控制的基石。
核心观点:数据质量控制不是“可选项”,而是“必选项”。宁可花一周时间把数据洗干净,也不要省下这一周去赌一个错误的结果。
2.1 野点剔除:别让一个坏数据毁了整个分析
什么是野点?就是那些明显不合理的数据点。比如,10分钟平均风速突然跳到60m/s,或者风向在1秒内转了180度。这些数据,十有八九是仪器故障、结冰、或者信号干扰造成的。
我个人习惯,野点剔除分三步走:
- 物理阈值检查——先砍掉明显超出物理极限的数据。比如风速大于50m/s、小于0m/s的,直接剔除。温度低于-50°C或高于60°C的,也直接干掉。
- 统计阈值检查——用3σ原则或者箱线图法。我一般用箱线图,因为它对极端值更鲁棒。具体做法:计算每个月的风速四分位数,把超出1.5倍IQR范围的数据标记为可疑。
- 时序突变检查——看相邻两个10分钟数据的变化率。如果风速在10分钟内从5m/s跳到25m/s,这基本不可能,除非是台风过境。但台风过境时风速变化也是有规律的,不是这种单点跳变。
我的经验:有一次在内蒙古的项目,测风塔数据里频繁出现风速为0但风向有值的情况。一开始以为是静风,后来发现是风速计结冰了。这种数据如果不剔除,会严重拉低平均风速,导致发电量估算偏低。所以,野点剔除不能只看数值,还要结合气象条件判断。
下面是我常用的野点剔除代码片段,Python写的,简单实用:
import pandas as pd
import numpy as np
def remove_outliers(df, col='ws', method='iqr', factor=1.5):
"""
野点剔除函数
df: 数据框,包含风速列
col: 要检查的列名
method: 'iqr' 或 'sigma'
factor: IQR倍数或sigma倍数
"""
if method == 'iqr':
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - factor * IQR
upper = Q3 + factor * IQR
mask = (df[col] >= lower) & (df[col] <= upper)
elif method == 'sigma':
mean = df[col].mean()
std = df[col].std()
lower = mean - factor * std
upper = mean + factor * std
mask = (df[col] >= lower) & (df[col] <= upper)
return df[mask], df[~mask] # 返回保留的数据和剔除的数据
注意:野点剔除不能太激进。我曾经见过有人把3σ以外的数据全删了,结果把正常的高风速极端天气数据也删掉了,导致发电量估算严重偏低。记住,我们剔除的是“异常值”,不是“极端值”。台风、寒潮等极端天气的数据,恰恰是评估风机安全性的重要依据。
2.2 数据完整率检查:缺多少,补多少,心里要有数
数据完整率,说白了就是有效数据占总时长的比例。行业标准一般要求完整率不低于90%,最好能达到95%以上。但现实往往很骨感——仪器故障、停电、维护,都会导致数据缺失。
我一般这样计算完整率:
def calc_completeness(df, freq='10min', expected_count=None):
"""
计算数据完整率
df: 数据框,索引为时间
freq: 数据频率,默认10分钟
expected_count: 理论数据条数,如果为None则自动计算
"""
total_hours = (df.index[-1] - df.index[0]).total_seconds() / 3600
if expected_count is None:
if freq == '10min':
expected_count = int(total_hours * 6)
elif freq == '1h':
expected_count = int(total_hours)
actual_count = df['ws'].notna().sum()
completeness = actual_count / expected_count * 100
return completeness
完整率检查完了,接下来就是怎么处理缺失数据。这里有几个原则:
- 短期缺失(< 2小时):可以用线性插值。比如10分钟数据,缺了6个点以内,插值问题不大。
- 中期缺失(2小时 - 24小时):建议用相邻测风塔的数据做相关性插补。如果没有相邻塔,可以用该塔前后几天的同一时段数据做平均。
- 长期缺失(> 24小时):别插了,直接标记为无效时段。强行插补只会引入更大的误差。
避坑指南:我曾经在云南的一个山地项目,测风塔因为雷击导致一个月的数据全部丢失。当时有人建议用前后两个月的数据做平均来填补。我坚决反对——山地风况季节性差异极大,这样填补出来的数据毫无意义。最后我们选择用附近另一座塔的数据做相关性分析,才勉强补回了部分信息。
2.3 扇区相关性分析:多塔数据的一致性校验
如果你手头有多个测风塔,扇区相关性分析就是检验数据质量的一把利器。原理很简单:在同一个风电场内,不同位置的测风塔,同一时段、同一风向扇区的风速应该具有较高的相关性。如果某个扇区的相关性突然变差,那八成是其中一座塔的数据出了问题。
具体做法是这样的:
- 把风向分成16个扇区(每个22.5°),或者12个扇区(每个30°)。我个人习惯用16扇区,精度更高。
- 对每个扇区,计算两座塔的风速相关系数(Pearson或Spearman)。
- 设定一个阈值,比如R² < 0.7的扇区标记为可疑。
- 对可疑扇区,逐点检查数据,看看是哪个塔的问题。
下面是一个扇区相关性分析的示例代码:
def sector_correlation(df1, df2, ws_col='ws', wd_col='wd', n_sectors=16):
"""
扇区相关性分析
df1, df2: 两座塔的数据框
ws_col: 风速列名
wd_col: 风向列名
n_sectors: 扇区数量
"""
sector_width = 360 / n_sectors
results = []
for i in range(n_sectors):
lower = i * sector_width
upper = (i + 1) * sector_width
# 筛选该扇区的数据
mask1 = (df1[wd_col] >= lower) & (df1[wd_col] < upper)
mask2 = (df2[wd_col] >= lower) & (df2[wd_col] < upper)
mask = mask1 & mask2
if mask.sum() > 10: # 至少10个数据点才有统计意义
corr = df1.loc[mask, ws_col].corr(df2.loc[mask, ws_col])
results.append({
'sector': f'{lower:.0f}-{upper:.0f}°',
'count': mask.sum(),
'correlation': corr
})
return pd.DataFrame(results)
| 扇区 | 数据点数 | 相关系数 R² | 判定结果 |
|---|---|---|---|
| 0-22.5° | 156 | 0.92 | 正常 |
| 22.5-45° | 132 | 0.88 | 正常 |
| 45-67.5° | 98 | 0.95 | 正常 |
| 67.5-90° | 45 | 0.45 | 可疑 |
| ... | ... | ... | ... |
注意:扇区相关性分析不能只看相关系数,还要看数据点数。如果一个扇区只有几个数据点,相关系数再高也没有统计意义。我一般要求每个扇区至少有30个有效数据点,才做相关性判断。
嗯,说到这里,我想起一个案例。在新疆的一个项目,两座测风塔相距不到3公里,按理说相关性应该很高。但分析发现,90-135°这个扇区的相关系数只有0.3。后来一查,是其中一座塔的风向标卡住了,一直指向120°不动。这种故障,如果不做扇区相关性分析,根本发现不了。
所以,我的建议是:扇区相关性分析要作为数据质量控制的常规动作,每个月跑一次,看看有没有异常。别等到项目做完了,才发现数据有问题,那可就晚了。
小技巧:扇区相关性分析的结果,可以用玫瑰图来可视化。把每个扇区的相关系数用颜色标在玫瑰图上,一眼就能看出哪个扇区有问题。这个可视化方法,我在很多项目里都用,效果非常好。
公众号:蓝海资料掘金营,微信deep3321