1. 居民用电行为分析到底在分析什么
1.1 从电表数据到行为画像的转化逻辑
居民用电行为分析这件事,说白了就是从一堆看起来毫无规律的电表读数里,把不同家庭的用电习惯给"扒"出来。智能电表每隔15分钟或者1小时采集一次有功功率、无功功率、电压、电流这些数据,一天下来就是96个或者24个采样点,一个月就是将近3000个数据点。单看一户人家的数据,你只能看到一条上下波动的曲线,但如果你手上有几百户甚至上千户的用电数据,就能发现有些家庭的曲线长得特别像——比如都是早晚两个高峰、白天基本没人、深夜有个小低谷,这类家庭大概率是上班族;还有些家庭白天用电量一直不低,晚上反而降下来,那可能家里有老人或者有人居家办公。
问题在于,靠人眼去比对几百条曲线根本不现实。这时候就需要聚类算法出场了。Kmeans聚类是最常用的手段之一,它的核心思想很简单:先随机选K个中心点,然后计算每个样本到这些中心点的距离,把样本归到最近的那个中心点所在的簇,接着重新计算每个簇的中心,反复迭代直到中心点不再移动或者达到最大迭代次数。听起来很直观对吧?但Kmeans有个致命的短板——初始中心点的选择对最终结果影响极大。选得不好,可能收敛到一个很差的局部最优解,聚类结果完全没有业务解释性。
这就是为什么需要粒子群算法来帮忙。粒子群算法是一种群体智能优化算法,它模拟鸟群觅食的行为:每只鸟(粒子)在搜索空间里飞,记录自己找到过的最好位置,同时也知道整个群体找到过的最好位置,然后根据自己的经验和群体的经验来调整飞行方向和速度。把这个思路套到Kmeans上,就是让粒子群去搜索最优的初始聚类中心组合,避免Kmeans陷入糟糕的局部最优。
1.2 这套方案适合谁参考
如果你手上有居民用电负荷数据,想做用户分群、制定差异化电价套餐、做需求侧响应策略,或者单纯是写毕业论文需要一套完整的聚类分析流程,这套"粒子群优化Kmeans"的方案都值得参考。Matlab作为实现工具,矩阵运算天然适合处理这类数据,而且可视化功能强大,画个三维散点图或者聚类中心雷达图非常方便。代码量不算大,核心逻辑两三百行就能搞定,但里面的参数调优和数据处理细节才是真正花时间的地方。
我见过不少同学拿到数据直接往Kmeans里一扔,跑出来结果发现某个簇只有一两个样本,或者两个簇的中心几乎重合,然后就不知道怎么办了。其实问题往往出在数据预处理和K值选择上,算法本身反而是最不容易出错的一环。接下来的内容我会把整个流程拆开,从数据清洗、特征提取、粒子群参数设置到最终结果解读,一步步说清楚。
2. 核心思路与方案选型:为什么是粒子群加Kmeans
2.1 Kmeans聚类的固有缺陷与改进方向
Kmeans的目标函数是最小化簇内平方误差和,数学上写出来就是每个样本点到其所属簇中心的欧氏距离平方之和。这个目标函数是非凸的,意味着存在多个局部极小值。标准的Kmeans算法采用贪心策略,每次迭代只保证当前步骤最优,不保证全局最优。初始中心点一旦选偏,后面再怎么迭代也跳不出那个"坑"。
举个具体的例子。假设你有三类用电模式:A类是高用电量双峰型,B类是低用电量单峰型,C类是中用电量平坦型。如果初始中心点恰好选了两个都在A类区域附近,一个在B类区域,那么C类的样本可能会被强行分到A类或B类里,最终得到的聚类结果就完全失真了。更糟糕的是,由于Kmeans对异常值非常敏感,一个极端高耗电的样本可能会把整个簇的中心拉偏,导致其他正常样本被错误归类。
改进Kmeans的路径有好几条:一是用多次随机初始化取最优结果,简单但计算量大;二是用层次聚类先做初步划分再 refinement;三是用智能优化算法搜索初始中心。粒子群算法属于第三条路径,它的优势在于搜索能力强、参数少、收敛速度相对较快,而且很容易和Kmeans的迭代过程结合。
2.2 粒子群算法的核心机制拆解
粒子群算法的数学表达其实不复杂。假设搜索空间是D维的,有N个粒子,第i个粒子的位置记作X_i,速度记作V_i。每次迭代时,速度更新公式是:
V_i(t+1) = w * V_i(t) + c1 * r1 * (Pbest_i - X_i(t)) + c2 * r2 * (Gbest - X_i(t))
其中w是惯性权重,c1和c2是学习因子,r1和r2是0到1之间的随机数,Pbest_i是粒子i的历史最优位置,Gbest是整个群体的历史最优位置。位置更新就是X_i(t+1) = X_i(t) + V_i(t+1)。
惯性权重w控制粒子保持原来飞行方向的趋势。w大,全局搜索能力强,但容易飞过最优解;w小,局部搜索精度高,但容易早熟收敛。实践中常用线性递减策略,比如从0.9降到0.4,前期大胆探索,后期精细搜索。学习因子c1调节粒子向自身历史最优学习的程度,c2调节向群体最优学习的程度。通常c1和c2都取2.0左右,也有取1.5到2.5之间的。如果c1太大,粒子太"自我",群体协作效果差;c2太大,粒子容易一窝蜂涌向当前最优,多样性丧失快。
把粒子群和Kmeans结合的方式是这样的:每个粒子的位置编码就是K个聚类中心的坐标拼接。假设你的用电数据提取了M个特征,要聚成K类,那么搜索空间的维度就是K*M。粒子的适应度函数用Kmeans的目标函数值来衡量——把粒子位置解码成K个中心,然后跑一遍Kmeans的分配和更新步骤,计算最终的簇内平方误差和。误差和越小,适应度越好。粒子群迭代若干代后,把找到的最优中心作为Kmeans的初始中心,再跑一次标准Kmeans做精细收敛。
2.3 为什么不用其他优化算法
遗传算法也能做同样的事情,但遗传算法需要编码、交叉、变异,参数更多,而且二进制编码在连续优化问题上精度受限。模拟退火理论上能收敛到全局最优,但降温 schedule 很难调,跑一次时间太长。蚁群算法更适合离散组合优化,用在连续的中心点搜索上不太自然。粒子群算法在连续优化问题上表现稳定,参数少,Matlab实现起来也简洁,对于居民用电行为分析这种中等规模的问题(几百到几千个样本,几个到十几个特征)来说,性价比最高。
当然,粒子群也有自己的问题,比如容易早熟收敛。后面我会讲几个实用的改进技巧,比如自适应惯性权重、变异操作、多种群策略等,都是在实际项目中验证过有效的。
3. 数据预处理与特征工程:决定聚类效果的关键步骤
3.1 原始用电数据的清洗要点
智能电表数据远没有想象中干净。常见的问题包括:缺失值(电表通信中断导致某些时段没有记录)、异常值(传感器故障导致读数突然飙到正常值的几十倍)、时间戳错位(不同电表的时间没有严格对齐)、以及用户档案变更(比如某户人家换了租客,用电模式突变)。这些问题不处理,聚类结果一定是一团糟。
缺失值的处理要看缺失比例。如果某户人家缺失超过30%的数据,建议直接剔除这户,因为剩下的数据不足以代表其用电行为。缺失比例在5%到30%之间的,可以用线性插值或者前向填充。线性插值适合连续缺失不超过3个点的情况,前向填充适合随机单点缺失。如果缺失比例低于5%,用该时刻的历史均值填充也可以,但要注意这可能会平滑掉一些真实的波动。
异常值检测我一般用四分位距法。计算每个时刻所有用户用电量的Q1和Q3,超过Q3+3IQR或者低于Q1-3IQR的值标记为异常。注意这里要分时刻计算,因为不同时刻的用电量基准不一样。比如晚上7点的正常用电量可能是2kW,凌晨3点可能只有0.2kW,如果用全局的IQR去判断,凌晨的数据全都会被误判为异常。检测出来的异常值用该用户相邻时刻的均值替换,而不是直接删除,因为删除会导致时间序列不连续。
注意:数据清洗阶段千万不要用全局统计量去处理分时数据,一定要按时刻分别计算统计量。这个坑我踩过,当时用全局均值做标准化,结果所有夜间数据都被压成了接近零的值,聚类结果完全区分不出夜间用电模式。
3.2 特征提取:从96点曲线到可聚类的向量
原始的一天96点负荷曲线直接拿去做聚类不是不行,但维度太高,而且相邻时刻的数据高度相关,信息冗余严重。更好的做法是提取有物理意义的特征,把维度降下来,同时保留用电行为的核心信息。
我常用的特征集包括这几类:
负荷统计特征:日平均负荷、日最大负荷、日最小负荷、负荷率(平均负荷除以最大负荷)、峰谷差(最大负荷减最小负荷)、峰谷差率(峰谷差除以最大负荷)。这些指标反映了用户的整体用电水平和波动程度。
时间分布特征:高峰时段(比如18:00-22:00)用电量占总用电量的比例、低谷时段(比如00:00-06:00)用电量占比、白天时段(08:00-17:00)用电量占比。这些比例能区分上班族和居家型用户。
负荷曲线形态特征:用主成分分析对标准化后的96点曲线做降维,取前3到5个主成分。主成分分析能把曲线的主要变化模式提取出来,比如第一主成分往往代表整体用电水平,第二主成分代表早晚高峰的对比关系。这样既降了维,又保留了曲线形态信息。
波动性特征:负荷曲线的标准差、变异系数、相邻时刻负荷变化量的均值。这些指标反映用电行为的稳定性。
特征提取完之后,一定要做标准化。因为负荷统计特征的量纲是千瓦,时间分布特征是比例(0到1之间),主成分是无量纲的得分,如果不标准化,量纲大的特征会主导距离计算。标准化用Z-score,即每个特征减去均值再除以标准差。标准化之后所有特征的均值为0,标准差为1,距离计算才公平。
3.3 K值怎么选:肘部法、轮廓系数与业务判断
Kmeans需要预先指定K值,这是它的另一个痛点。选K值不能只看数学指标,还要结合业务解释性。
肘部法是最直观的。计算不同K值下的簇内平方误差和,然后画出来。随着K增大,误差和一定下降,但下降速度会在某个点突然变缓,那个拐点就是推荐的K值。实际操作中,拐点往往不明显,可能需要结合其他指标。
轮廓系数综合考虑了簇内紧密度和簇间分离度,取值范围是-1到1,越接近1越好。计算每个样本的轮廓系数然后取平均,选择平均轮廓系数最大的K值。但轮廓系数在簇形状非球形或者簇大小差异大时会偏高或偏低,不能完全依赖。
我的经验是,先用肘部法和轮廓系数把K值范围缩小到3到6之间,然后逐个跑聚类,看每个簇的用电特征是否能用一两句话解释清楚。比如K=4时,如果四个簇分别对应"高用电双峰型""低用电单峰型""中用电平坦型""高用电单峰型",那K=4就是合理的。如果某个簇的样本特征混杂,说不清楚代表什么行为模式,那说明K值可能偏大或者偏小。
实操心得:居民用电行为分析中,K值通常取3到6。太少区分度不够,太多每个簇的样本量太少,统计意义不强。我做过的一个项目里,K=5时有一个簇只有8个样本,虽然数学上轮廓系数不错,但业务上没法解释,最后降到了K=4。
4. 粒子群优化Kmeans的Matlab实现全流程
4.1 粒子编码与适应度函数设计
在Matlab里实现粒子群优化Kmeans,第一步是确定粒子的编码方式。假设你的特征矩阵是data,大小为N行M列,N是样本数,M是特征数。要聚成K类,那么每个粒子的位置就是一个长度为K*M的向量。解码的时候,把这个向量reshape成K行M列的矩阵,每一行就是一个聚类中心。
适应度函数的设计直接决定了优化效果。我用的适应度函数就是Kmeans的目标函数值,但加了一个惩罚项来避免空簇。具体做法是:把粒子位置解码成K个中心,然后对每个样本计算到K个中心的距离,分配到最近的中心。如果某个中心没有分配到任何样本,给它一个很大的惩罚值。然后计算所有样本到其所属中心的距离平方和,作为适应度值。适应度越小越好。
这里有个细节要注意:粒子群在搜索过程中,粒子的位置可能会超出特征的取值范围。比如某个特征的正常范围是0到1,但粒子飞到了5。这时候要做边界处理,常用的方法是截断到边界值,或者反弹回边界内。我一般用截断,简单有效。
function fitness = pso_kmeans_fitness(position, data, K, M) % position: 1 x (K*M) 的粒子位置向量 % data: N x M 的特征矩阵 % K: 聚类数 % M: 特征数 % 解码粒子位置为聚类中心 centers = reshape(position, K, M); % 计算每个样本到每个中心的距离 N = size(data, 1); distances = zeros(N, K); for k = 1:K diff = data - repmat(centers(k, :), N, 1); distances(:, k) = sum(diff.^2, 2); end % 分配样本到最近的中心 [min_dist, labels] = min(distances, [], 2); % 检查空簇并施加惩罚 penalty = 0; for k = 1:K if sum(labels == k) == 0 penalty = penalty + 1e6; end end % 适应度 = 簇内平方误差和 + 惩罚项 fitness = sum(min_dist) + penalty; end4.2 粒子群参数设置与迭代过程
粒子群的主要参数包括:粒子数量、最大迭代次数、惯性权重、学习因子、速度限制。粒子数量一般取20到50,太少搜索不充分,太多计算量大。对于K*M维度在20到50之间的问题,30个粒子足够了。最大迭代次数取100到200,通常50代左右就能收敛,但为了保险可以设大一点,配合收敛判据提前退出。
惯性权重我用线性递减,从0.9降到0.4。学习因子c1和c2都取2.0。速度限制设为特征范围的一定比例,比如最大速度等于特征范围的一半,防止粒子飞得太猛。
% 粒子群参数 num_particles = 30; max_iter = 100; w_max = 0.9; w_min = 0.4; c1 = 2.0; c2 = 2.0; % 初始化粒子位置和速度 dim = K * M; positions = zeros(num_particles, dim); velocities = zeros(num_particles, dim); for i = 1:num_particles positions(i, :) = rand(1, dim) .* (max_feat - min_feat) + min_feat; velocities(i, :) = rand(1, dim) .* 0.1; end % 初始化个体最优和全局最优 pbest = positions; pbest_fitness = inf(num_particles, 1); gbest = zeros(1, dim); gbest_fitness = inf; % 主循环 for iter = 1:max_iter % 计算适应度 for i = 1:num_particles fit = pso_kmeans_fitness(positions(i, :), data, K, M); if fit < pbest_fitness(i) pbest_fitness(i) = fit; pbest(i, :) = positions(i, :); end if fit < gbest_fitness gbest_fitness = fit; gbest = positions(i, :); end end % 更新惯性权重 w = w_max - (w_max - w_min) * iter / max_iter; % 更新速度和位置 for i = 1:num_particles r1 = rand(1, dim); r2 = rand(1, dim); velocities(i, :) = w * velocities(i, :) ... + c1 * r1 .* (pbest(i, :) - positions(i, :)) ... + c2 * r2 .* (gbest - positions(i, :)); positions(i, :) = positions(i, :) + velocities(i, :); % 边界处理 positions(i, :) = max(positions(i, :), min_feat); positions(i, :) = min(positions(i, :), max_feat); end % 显示进度 if mod(iter, 10) == 0 fprintf('迭代 %d: 最优适应度 = %.4f\n', iter, gbest_fitness); end end4.3 用优化后的中心跑标准Kmeans
粒子群迭代结束后,把gbest解码成K个中心,作为Kmeans的初始中心,然后跑标准Kmeans做精细收敛。Matlab的kmeans函数支持指定初始中心,用'Start'参数传入即可。
% 解码最优粒子位置 optimal_centers = reshape(gbest, K, M); % 用优化后的中心作为初始值跑Kmeans [final_labels, final_centers, sumd] = kmeans(data, K, ... 'Start', optimal_centers, ... 'MaxIter', 500, ... 'Replicates', 1, ... 'Display', 'final'); % 计算最终聚类评价指标 silhouette_vals = silhouette(data, final_labels); avg_silhouette = mean(silhouette_vals); fprintf('最终平均轮廓系数: %.4f\n', avg_silhouette);这里有个细节:kmeans函数的'Replicates'参数设为1,因为我们已经用粒子群找到了好的初始中心,不需要多次随机重启。'MaxIter'设为500足够收敛。跑完之后计算轮廓系数,和随机初始化的Kmeans结果对比,通常能提升5%到15%。
4.4 聚类结果的可视化与业务解读
聚类跑完之后,可视化是必不可少的。对于高维特征,我一般用主成分分析降到二维或三维,然后画散点图,不同簇用不同颜色标记。同时画出每个簇的中心在原始特征上的雷达图,这样能直观看出每个簇的用电行为特点。
% 主成分分析降维可视化 [coeff, score, ~, ~, explained] = pca(data); figure; gscatter(score(:,1), score(:,2), final_labels); xlabel(sprintf('主成分1 (%.1f%%)', explained(1))); ylabel(sprintf('主成分2 (%.1f%%)', explained(2))); title('居民用电行为聚类结果'); grid on; % 聚类中心雷达图 figure; for k = 1:K subplot(2, 3, k); radar_plot(final_centers(k, :), feature_names); title(sprintf('簇 %d (样本数: %d)', k, sum(final_labels == k))); end业务解读的时候,重点看每个簇在关键特征上的表现。比如某个簇在"高峰时段用电占比"上明显高于其他簇,在"日平均负荷"上也偏高,那这个簇就是"高用电双峰型"用户。另一个簇在"白天用电占比"上突出,那就是"居家型"用户。把这些标签和用户档案(比如户型、家庭人口、收入水平)做交叉分析,就能得出有价值的业务结论。
5. 常见问题与排查技巧实录
5.1 粒子群早熟收敛怎么办
早熟收敛是粒子群最常遇到的问题,表现为所有粒子很快聚集到同一个位置,适应度不再下降,但明显不是全局最优。判断方法是看粒子位置的方差,如果方差趋近于零但适应度还比较高,那就是早熟了。
解决办法有几个。一是增大惯性权重或者用自适应惯性权重,在检测到早熟时临时增大w,让粒子重新散开。二是引入变异操作,每隔若干代对部分粒子施加随机扰动,类似遗传算法的变异。三是用多种群策略,把粒子分成几个子群,各自独立搜索,定期交流最优信息。我在实际项目中最常用的是第二种,实现简单,效果稳定。
% 变异操作示例 if std(positions(:)) < 1e-4 && iter > 20 % 检测到早熟,对30%的粒子施加变异 num_mutate = round(0.3 * num_particles); mutate_idx = randperm(num_particles, num_mutate); for i = mutate_idx positions(i, :) = positions(i, :) + randn(1, dim) .* 0.1; positions(i, :) = max(positions(i, :), min_feat); positions(i, :) = min(positions(i, :), max_feat); end end5.2 聚类结果不稳定怎么排查
同样的数据跑两次,聚类标签可能不一样,这是Kmeans的常见现象。但如果差异很大,比如两次的簇中心完全对不上,那就要排查原因了。
首先检查数据预处理是否一致。标准化用的均值和标准差是不是每次重新计算了?如果数据有随机划分训练集和测试集,那结果不一样是正常的。其次检查粒子群的随机种子。Matlab的rand函数默认每次启动时种子不同,导致粒子初始化不同。如果希望结果可复现,在代码开头加rng(42)固定种子。
还有一个容易被忽略的原因:特征之间的相关性太高。如果两个特征几乎完全线性相关,距离计算会被它们主导,聚类结果对微小的数据扰动非常敏感。解决办法是用主成分分析做去相关,或者手动剔除相关性高于0.9的特征。
5.3 某个簇样本量过少怎么处理
如果聚类结果中某个簇只有几个样本,通常有三种处理方式。一是合并到最近的簇,计算该簇中心到其他簇中心的距离,合并到最近的那个。二是把这个簇的样本标记为异常用户,单独分析,不参与后续的业务策略制定。三是调整K值重新聚类。
我倾向于第二种,因为样本量过少的簇往往代表极端用电行为,比如某户人家开了个小作坊,用电模式和普通居民完全不同。这类用户虽然少,但业务价值可能很高,单独拿出来分析反而更有意义。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 粒子群适应度不下降 | 早熟收敛 | 检查粒子位置方差 | 增大惯性权重或加变异 |
| 聚类结果每次不同 | 随机种子未固定 | 检查rng设置 | 固定随机种子 |
| 某簇样本极少 | K值偏大或异常值 | 检查簇中心距离 | 合并簇或标记异常 |
| 轮廓系数很低 | 特征选择不当 | 检查特征相关性 | 主成分分析降维 |
| 运行时间过长 | 粒子数或迭代次数过多 | 检查参数设置 | 减少粒子数或加收敛判据 |
| 空簇出现 | 初始中心重叠 | 检查粒子初始化范围 | 增大初始化范围或加惩罚 |
避坑技巧:在粒子群迭代过程中,每隔10代记录一次全局最优适应度。如果连续30代没有改善,直接退出循环,不用跑满最大迭代次数。这样能节省大量时间,对最终结果影响很小。
6. 参数调优与性能提升的实战经验
6.1 粒子群参数对聚类效果的影响分析
粒子群参数没有一套放之四海而皆准的最优值,但有一些经验规律。粒子数量方面,我做过对比实验,20个粒子和50个粒子的最终适应度差异通常在1%以内,但50个粒子的运行时间是20个粒子的2.5倍。所以对于特征维度在50以下的问题,25到30个粒子是性价比最高的选择。
惯性权重的递减策略对收敛速度影响很大。线性递减从0.9到0.4是最常用的,但在某些数据集上,非线性递减(比如按指数递减)效果更好。我试过用自适应惯性权重,根据粒子的适应度排名动态调整w,排名靠前的粒子用小w精细搜索,排名靠后的用大w大胆探索。这种方法在复杂问题上能提升3%到5%的聚类质量,但实现复杂度也高一些。
学习因子c1和c2的比值影响群体的探索和开发平衡。c1大于c2时,粒子更依赖自身经验,群体多样性保持得好,但收敛慢。c2大于c1时,粒子快速向群体最优靠拢,收敛快但容易早熟。我一般设c1=c2=2.0,如果发现早熟就适当增大c1。
6.2 用并行计算加速粒子群迭代
粒子群每次迭代要计算所有粒子的适应度,而每个粒子的适应度计算是独立的,天然适合并行。Matlab的parfor可以很方便地把这个循环并行化。前提是你有Parallel Computing Toolbox,并且CPU核心数足够。
% 并行计算适应度 parfor i = 1:num_particles fit = pso_kmeans_fitness(positions(i, :), data, K, M); fitness_values(i) = fit; end实测下来,在8核CPU上,30个粒子的问题用parfor能加速3到4倍。但要注意,parfor的启动开销不小,如果单次适应度计算很快(比如数据量小于500),并行反而可能更慢。建议数据量超过1000个样本时再用并行。
6.3 与其他聚类算法的对比测试
为了验证粒子群优化Kmeans的效果,我通常会和标准Kmeans、层次聚类、DBSCAN做对比。对比指标包括轮廓系数、Calinski-Harabasz指数、以及业务解释性。
标准Kmeans用Matlab默认的多次随机重启(Replicates设为10),取最优结果。层次聚类用Ward方法。DBSCAN需要调eps和MinPts两个参数,对密度不均匀的数据集不太友好。
在我的测试中,粒子群优化Kmeans的轮廓系数通常比标准Kmeans高5%到10%,比层次聚类高3%到8%。DBSCAN在某些数据集上表现很好,但参数调优太耗时,而且不能保证每个样本都被分配到一个簇。综合来看,粒子群优化Kmeans在居民用电行为分析这个场景下是最均衡的选择。
6.4 代码优化与运行效率提升
Matlab的循环效率不高,能向量化的地方尽量向量化。比如计算样本到中心的距离,用矩阵运算比双重循环快很多。下面这个向量化版本比逐样本循环快5倍以上:
% 向量化计算距离矩阵 % data: N x M, centers: K x M % 输出: N x K 的距离平方矩阵 data_sq = sum(data.^2, 2); centers_sq = sum(centers.^2, 2); cross_term = data * centers'; distances = data_sq + centers_sq' - 2 * cross_term; distances = max(distances, 0); % 防止数值误差导致负数另外,适应度函数里不需要每次都重新计算所有样本的距离。如果粒子位置变化很小,可以只更新变化的部分。但这种优化实现复杂,对于几千个样本的问题,收益不明显,不建议过度优化。
7. 结果解读与业务应用落地
7.1 从聚类标签到用户画像
聚类跑出来的标签本身没有意义,必须结合特征均值来解读。我一般会生成一张簇特征均值表,每一行是一个簇,每一列是一个特征,单元格里是该簇在该特征上的均值。然后对比全局均值,看哪些特征显著偏高或偏低。
比如某个簇在"日平均负荷"上是全局均值的1.8倍,在"高峰时段用电占比"上是1.3倍,在"负荷率"上是0.7倍,那这个簇就是典型的高用电、强峰谷差用户。这类用户对电价敏感,适合推荐峰谷分时电价套餐。另一个簇在"白天用电占比"上是全局均值的1.5倍,在"日平均负荷"上接近全局均值,那就是居家型用户,适合推荐固定电价套餐或者智能家居节能服务。
7.2 差异化电价套餐设计的参考思路
基于聚类结果设计电价套餐,核心思路是让每个簇的用户都能找到最适合自己的套餐,同时保证供电公司的收益不降低。具体做法是:对每个簇,计算其在现行电价下的电费支出,然后设计一个替代套餐,使得该簇中60%以上的用户在替代套餐下的支出不高于现行支出,同时供电公司的总收入不低于现行水平。
这其实是一个优化问题,可以用线性规划或者遗传算法求解。但套餐数量不宜过多,通常3到5个就够了,太多用户选择困难,管理成本也高。聚类结果正好为套餐数量提供了依据——有几个有业务意义的簇,就设计几个套餐。
7.3 需求侧响应策略的制定
需求侧响应是指通过价格信号或者激励措施,引导用户在高峰时段减少用电。聚类结果能帮助识别哪些用户对价格敏感、哪些用户对激励敏感。高用电双峰型用户通常对价格敏感,因为他们的电费支出高,有动力调整用电行为。低用电平坦型用户对价格不敏感,但可能对一次性激励(比如现金返还)有反应。
制定策略时,可以针对不同簇设计不同的响应方案。对高用电双峰型用户,推峰谷价差更大的套餐;对居家型用户,推智能家居设备补贴,让他们在高峰时段自动降低空调温度或者推迟洗衣机运行。这些策略的效果可以通过后续的用电数据来验证,形成闭环优化。
7.4 模型更新与在线学习
居民用电行为不是一成不变的,季节变化、家庭结构变化、新电器添置都会导致用电模式漂移。聚类模型需要定期更新,比如每季度重新跑一次。但每次都重新聚类会导致标签不一致,上一季度的"簇1"和这一季度的"簇1"可能代表完全不同的行为模式。
解决办法是固定聚类中心,对新数据只做分配不更新中心。当新数据中超过20%的样本到其所属中心的距离超过阈值时,触发重新聚类。重新聚类后,用匈牙利算法或者最近邻匹配把新旧簇对应起来,保持标签的连续性。这样既能适应行为变化,又能保持业务策略的连贯性。
个人体会:在实际项目中,数据预处理和特征工程花的时间往往比算法调参多得多。我做过一个项目,前两周都在清洗数据和试特征,真正跑粒子群优化Kmeans只用了两天。但正是那两周的预处理工作,让最终的聚类结果有了业务解释性。如果直接拿原始数据跑,结果根本没法用。所以如果你刚开始做类似的分析,建议把70%的精力放在数据理解和特征设计上,算法本身反而不是瓶颈。