ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

光伏曲线聚类:K-means算法MATLAB实现与参数选择全解析

光伏曲线聚类:K-means算法MATLAB实现与参数选择全解析 做光伏功率预测这两年我越来越觉得气象条件对光伏出力曲线的影响是一刀切模型最大的敌人。同样是夏天晴天和暴雨天的出力曲线从幅值到形态都差了一整个量级如果拿一个通用模型去拟合所有天气结果往往是晴天类样本不过拟合、阴雨天类样本欠拟合两头都不讨好。解决这个问题的实用思路就是先把历史日功率曲线聚成几类典型的天气形态再针对每一类单独建模或分配不一样的模型权值。这个分类工作我最终落成了一套基于K-means算法的光伏曲线聚类MATLAB实现方案。这篇文章就把这套方案的完整思路、代码细节、参数选择过程和踩坑记录都摊开来讲希望能给正在做光伏数据分析、功率预测或电站运维分类的朋友省下几个晚上的调试时间。不管你是刚接触聚类的学生还是已经在跑预测模型的研究生和工程师这套实现都可以直接改改数据路径就能用。1. 光伏曲线聚类这件事到底解决什么问题1.1 从一条曲线到一个集群光伏发电功率曲线说白了就是电站一天从早到晚的出功时序。横轴是时间纵轴是功率或者归一化后的出力系数。由于天气系统的随机性这条曲线的形态千变万化晴天是一条接近正弦的光滑拱形多云天是一条剧烈波动的锯齿状折线阴天则整段都被压得很低雨天则可能连拱形都谈不上完全是噪声主导的乱跳。所谓光伏曲线聚类就是由数据自动把历史中每一天的曲线归到少数几个典型形态里。这个过程不需要人工预先给每天打天气标签算法根据曲线之间的距离远近自动划分。我习惯用一个生活里的例子解释这件事把衣柜里所有的鞋按风格分类运动鞋、皮鞋、靴子各自堆成一堆聚类做的就是类似的事情——不过它不看颜色不看品牌只看长得像不像。这里要强调一个关键思路聚类不是分类它不需要先验标签。也就是说你不需要先知道某天是晴还是多云来训练模型算法自己会从数据中找出结构。这在实际工程中太重要了因为光伏电站历史数据里往往只有功率值未必有同步的气象观测记录或者气象数据颗粒度太粗没法直接对应到每一天的形状上。1.2 聚类结果能用在哪些地方把曲线聚类之后衍生应用非常直接。第一个大头是短期功率预测。目前主流的做法叫分类型建模先把历史日曲线聚成K类然后对每一类单独训练预测模型比如BP网络、LSTM或者回归树。做预测当天先根据气象预报判断当日曲线大概率属于哪一类再调用对应模型输出功率。实测下来这种策略比单模型硬扛所有天气要稳得多精度能提升不少。第二个场景是异常数据识别。聚类完成后那些落不进任何一个主流簇的曲线、或者轮廓系数极低的点往往是设备故障、数据采集异常、限电停运导致的坏样本。把这些样本揪出来做标记对后续所有数据驱动的分析工作都非常有价值。第三类是资源评估和电站选址。聚类统计结果可以告诉你某地区一年里晴天形态出现的天数占比这比单纯看年总辐射量要直观得多对估算发电量、测算收益都有用。最后储能配置也能用上不同类别曲线的峰谷差异和波动剧烈程度是不同的聚完类后按类统计出力特征就能更合理地设计储能功率和容量。2. 为什么用K-means算法原理与MATLAB选型逻辑2.1 K-means的核心流程K-means的核心思想简单到可以用四句话讲完先在样本空间里随机放K个质心然后把每个样本划给离它最近的质心接着重新计算每个簇内所有样本的均值作为新质心循环往复直到质心不再变化。用光伏曲线来说就是把成百上千条日功率曲线看作高维空间里的点每条曲线都是这个空间里的一个坐标向量。比如每天取12个小时、15分钟一个采样点那么一天就是48个数值也就是48维空间里的一个点。K-means的目标就是在这个空间里找到K个中心点使得所有样本到各自中心的欧氏距离平方和最小。MATLAB里封装好的kmeans函数一句代码就能跑起来[idx, C] kmeans(X, K);其中X是样本矩阵每行一条日曲线每列是一个时刻的出力值K是设置的聚类数量idx是每个样本所属的类别编号C是K个聚类中心每行就是一个典型日曲线。2.2 三个容易忽略的选型细节为什么偏偏是K-means而不是别的聚类算法我在最初也犹豫过要不要用层次聚类或者DBSCAN。实际对比之后K-means有三个不可替代的优势第一计算效率极高几千条曲线、几百维数据几分钟之内就能跑完而层次聚类一旦样本量过万距离矩阵的内存占用就很头疼了第二MATLAB原生支持kmeans函数参数丰富配合evalclusters还能做自动K值寻优开发效率高第三聚类中心直接可视化成平均曲线工程上解释起来非常方便。当然K-means也有它的硬伤需要预设K值对初始质心敏感而且使用欧氏距离时对曲线形态的相位偏移无能为力。如果你遇到的场景里曲线波形相似但是峰值时刻明显错位比如不同电站因经纬度不同导致日出时间不同那K-means直接跑的聚类结果可能会混乱。这种情况一般需要先做时间对齐或者改用DTW距离搭配k-medoids方法。还有一个容易被新手忽略的点归一化策略。我在多个项目里对比过如果直接拿原始功率数据去算欧氏距离那么幅值大的晴天曲线必然把所有距离主导了最后聚类出来的就只是大曲线、中曲线、小曲线而不是晴天形态、多云形态、阴天形态。想要按形态聚类应该对每条曲线做最大值归一化把每天的曲线都先除以当天的峰值这样晴天多云阴天在绝对大小上的差异就被抹平了剩下的就是纯粹的形态差异。3. 数据预处理决定聚类成败的第一关我做过不止一个项目前期数据清洗仓促后面聚类结果一团糟返工成本极高。光伏数据预处理在我看来是整个流程中最不能省的一步甚至可以说它决定了聚类效果的80%。3.1 光伏数据的三个坑第一是夜间零值段。光伏电站夜里没有出力从晚上七八点到第二天凌晨五六点曲线全是一条直线压在零上。这些零值在欧氏距离计算里会形成一大片无意义的共性把所有曲线都朝着白昼长度的方向推而不是朝天气形态推。实际表现就是冬天的短日照曲线跟夏天的长日照曲线被分成两堆那显然不是我们要的。第二是日出日落时间随季节漂移。同样的晴天曲线冬天早上七点才起功率夏天六点不到就有出力了如果不做对齐同类天气的曲线之间的距离反而比不同类天气的曲线还大。第三是数据缺失和坏值。逆变器通讯中断、传感器故障、限电弃光都会导致曲线局部缺失或者出现离谱的尖峰毛刺。这些噪声样本如果不处理聚类中心就会被带偏。3.2 完整预处理流程我一般按下面这套流程走每一步都有明确的目的剔除无效日曲线。检查每天的完整采样点数如果有效数据不足全天的70%这一整天的曲线直接不要。截取有效日照窗口。根据电站纬度估算全年最早日出和最晚日落时间取一个保守的公共窗口。我这个项目里取的6:00到18:0015分钟一个采样点一共48个点。这一步的目的就是甩掉夜间零值段。缺失点插值。窗口内如果有零散的缺失点用前后时刻线性插值补上。插值只适用于短缺口超过连续2小时丢失的曲线建议舍弃。形态归一化。每条曲线除以当天的最大值让所有曲线的峰值变成1。如果某天曲线最大值太离谱比如超过装机容量1.2倍按坏数据处理。再次筛查。归一化后如果曲线出现连续多个零点的平顶形态多半是限电或设备故障标记出来聚类时去掉。这套流程走完数据基本就干净了。我习惯把最终参与聚类的曲线条数和剔除条数记录下来写进报告里方便后面复现时对比。4. MATLAB代码实现从数据导入到聚类完成我用MATLAB 2026b做这套流程代码量不大但每一段都值得仔细说说参数的含义。4.1 数据加载与结构设计假设数据是Excel表格每一行是一条日曲线每一列是一个采样时刻的出力值。用readmatrix直接导入% 导入原始数据假设已按6:00-18:00截取并归一化 rawData readmatrix(pv_curves_normalized.xlsx); % 检查维度 fprintf(样本数: %d, 维度: %d\n, size(rawData, 1), size(rawData, 2));读进来之后我建议先做一次行方向检查用isnan和isinf函数扫描一遍MATLAB聚类函数对NaN和Inf是零容忍的只要数据里有一个NaN它就直接报错。虽然有点啰嗦但这一步能省下不少排查时间。数据矩阵要求每一行是一个样本每一列是一个特征维。这跟sklearn的习惯一样别把行列搞反了。4.2 kmeans函数参数配置kmeans函数看起来简单但参数怎么配差别很大。我最关心的是四个参数K 4; % 聚类数量 repeatTimes 20; % 重复次数 maxIter 500; % 最大迭代次数 distance sqeuclidean; % 距离度量 [idx, C, sumd] kmeans(rawData, K, ... Distance, distance, ... Replicates, repeatTimes, ... MaxIter, maxIter, ... Display, final);Replicates参数是最容易被忽略的。K-means的初始质心是随机放的一次运行很可能落到局部最优解。设成20的意思是从20组不同的随机初始质心出发各跑一遍完整的迭代最后返回组内平方和最小的那一组结果。这个参数基本不增加调试难度但能显著提高结果稳定性我建议至少设10次以上数据量大时可以适当降一点。MaxIter设成500一般够了K-means收敛很快通常不到几十次迭代就稳定了。如果设置太小算法提前截断返回的质心可能还没稳定。Display final会在命令行输出每次最优收敛信息方便看运行状态。4.3 聚类结果可视化聚类跑完后最有用的可视化是把所有曲线按簇分开叠加画出来同时画上簇中心。% 定义时间轴 t 6:0.25:18; % 6:00 到 18:00步长15分钟 % 分簇画图 figure; hold on; colors lines(K); for i 1:K subplot(2, 2, i); clusterData rawData(idx i, :); plot(t, clusterData, Color, [colors(i, :) 0.15]); % 半透明曲线 hold on; plot(t, C(i, :), Color, colors(i, :), LineWidth, 2.5); title(sprintf(簇 %d (样本数: %d), i, size(clusterData, 1))); xlabel(时刻 (h)); ylabel(归一化出力); xlim([6 18]); ylim([0 1.1]); grid on; end这个图能一眼看出分簇质量好的聚类是每个子图里曲线形态高度一致簇中心曲线平滑有代表性不行的聚类则是子图里曲线形状五花八门说明K值或者预处理流程有问题。另外建议顺便统计每个簇的平均峰值、平均波动率、方差特性这些特征后面写报告时意义很大。比如一个簇的中心曲线峰值稳定在0.9以上且方差极小基本就可以断定是稳定的晴空日。5. K值怎么选肘部法则、轮廓系数与Gap StatisticK值是K-means唯一需要人为设定的超参数也往往是争议最大的一环。我最早做聚类时只会用肘部法则后来发现光看肘部曲线远远不够得几种方法交叉验证再加业务判断一起上。5.1 三种方法的具体实现肘部法则的判断逻辑很直观随着K增大簇内样本到质心的距离平方和SSE会单调下降但当K超过数据本身的真实簇数之后SSE的下降幅度会显著变缓曲线出现一个肘部拐点。MATLAB里手动算SSE很简单Klist 1:8; SSE zeros(size(Klist)); for i 1:length(Klist) [~, ~, sumd] kmeans(rawData, Klist(i), Replicates, 10); SSE(i) sum(sumd); end figure; plot(Klist, SSE, o-); xlabel(K); ylabel(SSE);轮廓系数则是从另一个角度衡量——它同时考察样本与自身簇内样本的紧密度以及样本与最近的其他簇样本的分离度。每个样本的轮廓系数在-1到1之间越接近1越好。MATLAB的evalclusters函数可以一条命令做这个事eva evalclusters(rawData, kmeans, Silhouette, KList, 1:8); plot(eva); bestK eva.OptimalK;Gap Statistic的原理更精细它拿实际数据的SSE曲线跟一组均匀随机数据的SSE曲线做对比找实际数据相对随机数据优势最大的位置。MATLAB里同样可以用evalclusters把评价标准换成gap即可。这个方法说服力更强但计算量大一些。5.2 我实际怎么判断以我手头这个电站数据为例做完预处理之后还剩大约340条曲线。用三种方法跑出来的结果分别是肘部法看起来3到5都说得通轮廓系数最优是4Gap Statistic也指向4。三种方法交叉印证K4基本是稳的。但我还想强调一点统计指标不能替代业务判断。K4对应的四类曲线分别是晴空平滑型、多云波动型、阴天低平型、降雨杂乱型每一类都有清晰可解释的天气意义。如果某个K值下有一类曲线内部形态仍然混杂即使统计指标再好看我也不建议选那个K值。聚类结果最终是要拿到业务里去解释和使用的解释不了的结果就是不可用的。6. 常见问题排查与避坑实录最后这部分我把实际操作中遇到过的典型问题整理成一张排查表这些问题我几乎在每次讲课或者带新人时都会重复一遍每一行都是真金白银换来的经验。问题现象可能原因解决方案每次运行kmeans结果都不一样初始质心随机导致落入不同局部最优增大Replicates建议10-20次并固定数据顺序聚类结果中某条明显异常的曲线独立成一类坏数据占了主导距离预处理阶段严格筛查异常峰值、连续平顶、缺失过多曲线轮廓系数低且为负值的样本很多曲线存在相位偏移欧氏距离失效先做时间对齐或改用DTW距离的k-medoids方案聚类出来的簇就是高幅值/低幅值没有做形态归一化幅值主导距离每条曲线先除自己最大值再做后续分析kmeans报错说数据含NaN或Inf导入数据有缺失或无效值调用前用isnan/isinf全面扫描补值或删行有一部分样本孤立但数量很少可能是极端天气或数据质量问题数据量不足总样本5%的簇要重点核验原始数据除了表里的问题还有一个我特别想单独说说的坑标准化方式的选择。有人贪图方便直接对整个矩阵做一次zscore标准化这在很多聚类场景里是合理的但光伏曲线场景下这种全局标准化会抹掉不同天气之间的幅值层级差异导致聚类时晴天和阴天这两个在工程上必须分开的类别被揉在一起。我在预处理章节已经说过如果目标是按形态聚类就每条曲线除以自身最大值如果目标是按出力大小分层才考虑是否保留绝对幅值信息。这个选择一定要在跑算法之前想清楚不要等出结果了再回头改。还有一个跟可视化相关的经验聚类完成后不要只盯着簇中心曲线看一定要叠加画出簇内所有原始曲线。簇中心毕竟是平均后的结果平滑效应会把很多信息藏掉。只有看到叠加图里每条曲线紧紧地贴着中心才能确认这个簇是干净的。如果叠加图里曲线散得厉害那不管统计指标多好都要重新审视数据或换距离度量。做这套方案最大的体会是聚类本身只占整个工作量的三成数据清洗和特征设计花了七成时间但恰恰是那七成的功夫决定了最后三成的效果上限。一开始我以为K-means就是准备个矩阵调个参数就完事了真正落地才发现对光伏曲线这种带有强烈昼夜周期性和季节漂移的数据预处理阶段每一个细节都直接影响聚类结果的含义。最后再分享一个小技巧聚类完之后把每类的3条典型曲线截图打印出来贴在工位上。后面做功率预测模型时拿到新一天的气象预报看一眼曲线大概形态就能预判它落在哪个簇里这个人肉分类先验在模型冷启动阶段特别管用也方便你跟非技术背景的同事沟通。
返回列表