资讯动态

电力AI大赛数据挖掘管道:从原始表到可复现提交的完整路径

发布时间:2026/9/26 14:24:16 来源:尧图企业网站定制
简介这份资源围绕大航杯“智造扬中”电力AI大赛提供一套完整的数据挖掘管道搭建示例面向计算机、人工智能、自动化等相关专业的在校学生、教师及企业员工也适合作为毕业设计、课程设计或项目立项的参考案例。压缩包共32个文件约3.72MB以22个ipynb笔记本和4个py脚本为主辅以3个csv数据文件、2个txt说明及1个md文档覆盖数据清洗、特征提取、模型训练与结果预测等环节。内容包含天气与节假日爬取脚本、特征组合与规则处理、样本划分及可视化分析等模块能帮助读者理解从原始数据到预测输出的完整流程。已有44人学习适合在此基础上修改扩展用于竞赛复现、课程作业或算法进阶练习。1. 电力AI大赛数据挖掘管道从原始表到可复现提交的完整路径“大航杯·智造扬中”电力AI大赛给的是典型工业时序数据多张表、字段命名不统一、量测值带噪声、标签分布不均。很多人第一次拿到这类数据第一反应是直接上模型结果在验证集上分数虚高、换到测试集就崩。问题不在模型而在数据挖掘管道没有搭稳。管道搭建的核心目标只有一个让从原始数据到最终提交的每一步都可复现、可回滚、可解释。这篇内容面向已经拿到数据、准备动手的从业者也适合想用AI做电力设计规范查询、数据挖掘实战练手的人。我会按“数据理解→清洗→特征→建模→验证”的顺序把每一步的命令、参数和踩坑点讲清楚源码结构也会给出可抄的骨架。2. 数据挖掘管道的第一段把多源电力表读成统一宽表2.1 先做表结构盘点别急着concat电力大赛的数据通常包含负荷、气象、设备台账、电费等多张表。直接pd.concat会带来两个后果一是时间粒度不一致导致大量NaN二是主键重复让行数膨胀。我一般先写一个盘点脚本输出每张表的行数、列数、时间范围、主键唯一性。这一步不写进模型但能省掉后面80%的返工。import pandas as pd def profile_table(path, time_colNone, key_colsNone): df pd.read_csv(path) info { rows: len(df), cols: list(df.columns), dtypes: df.dtypes.astype(str).to_dict(), null_rate: (df.isnull().mean().round(4)).to_dict(), } if time_col and time_col in df.columns: df[time_col] pd.to_datetime(df[time_col], errorscoerce) info[time_min] str(df[time_col].min()) info[time_max] str(df[time_col].max()) if key_cols: info[key_duplicated] int(df.duplicated(subsetkey_cols).sum()) return info # 示例对负荷表和气象表分别盘点 load_info profile_table(data/load.csv, time_colts, key_cols[ts, meter_id]) weather_info profile_table(data/weather.csv, time_colts, key_cols[ts, station_id]) print(load_info[rows], load_info[key_duplicated]) print(weather_info[time_min], weather_info[time_max])逻辑说明profile_table只做只读盘点不修改原始文件。null_rate帮你判断哪些列需要插补key_duplicated直接暴露主键问题。参数上time_col传时间列名key_cols传业务主键组合。如果key_duplicated大于0先别往下走去查是采集重复还是主键选错。2.2 时间对齐与重采样把15分钟粒度和小时粒度接上电力负荷常见15分钟采样气象常是小时或3小时。对齐策略决定后续特征质量。我的做法是统一到15分钟气象用前向填充加线性插值负荷缺失超过4个连续点就标记为异常段而不是硬插。def align_to_15min(load_df, weather_df): load_df load_df.set_index(ts).sort_index() weather_df weather_df.set_index(ts).sort_index() # 气象重采样到15分钟先线性插值再前向填充 weather_15 weather_df.resample(15min).interpolate(methodlinear).ffill(limit2) # 负荷只做短缺口插值长缺口保留NaN load_15 load_df.resample(15min).mean() load_15[load] load_15[load].interpolate(methodlinear, limit4) merged load_15.join(weather_15, howleft, rsuffix_weather) return merged merged align_to_15min(load_df, weather_df) print(merged.shape, merged[load].isnull().sum())逻辑说明resample(15min).interpolate先升采样再插值ffill(limit2)防止气象缺测被无限拉长。负荷的limit4对应1小时超过就保留NaN后面模型用掩码处理。参数rsuffix避免同名列冲突。这一步做完宽表行数应该等于负荷时间轴长度如果多出来检查气象表是否有重复时间戳。2.3 宽表落盘与版本标记管道必须留痕。每次生成的宽表带日期和行数写入文件名同时记录字段清单到JSON。这样后面特征工程出问题能快速定位是哪一版数据。import json, datetime def save_wide(df, out_dirdata/wide): tag datetime.datetime.now().strftime(%Y%m%d_%H%M) path f{out_dir}/wide_{tag}_{len(df)}.parquet df.to_parquet(path, indexTrue) meta {path: path, rows: len(df), cols: list(df.columns), tag: tag} with open(f{out_dir}/wide_{tag}.json, w) as f: json.dump(meta, f, ensure_asciiFalse, indent2) return path save_wide(merged)逻辑说明用parquet而不是csv保留dtype且读取快。JSON元数据记录列清单方便对比两版差异。参数out_dir按项目习惯改但不要直接覆盖旧文件。3. 特征工程电力负荷的时序特征怎么造才不泄漏3.1 滑窗统计特征与滞后特征电力负荷预测里滞后特征和滑窗统计是最稳的基线。关键是窗口不能跨过预测点。我一般用shift先造滞后再在滞后序列上做滑窗避免把当前时刻的真实值漏进去。def make_lag_features(df, targetload, lags(1, 4, 96), windows(4, 96)): out df.copy() for lag in lags: out[flag_{lag}] out[target].shift(lag) for w in windows: out[froll_mean_{w}] out[target].shift(1).rolling(w).mean() out[froll_std_{w}] out[target].shift(1).rolling(w).std() return out feat_df make_lag_features(merged) print(feat_df[[lag_1, roll_mean_96]].tail())逻辑说明shift(1)保证滑窗只看历史。lags(1,4,96)对应15分钟、1小时、1天。windows(4,96)对应1小时和1天窗口。参数按采样粒度调整如果是小时数据96改成24。注意rolling后前96行会是NaN建模时要么丢弃要么填充。3.2 日历特征与节假日标记电力负荷对星期、节假日极敏感。不要只提取dayofweek还要做“是否周末”“是否节假日”“节前节后”三列。节假日表如果没有可以用公开日历库生成但要注意大赛数据年份范围。def add_calendar(df): df df.copy() df[hour] df.index.hour df[dayofweek] df.index.dayofweek df[is_weekend] (df[dayofweek] 5).astype(int) # 简化版节假日按实际年份补充 holidays {2023-01-01, 2023-01-21, 2023-01-22, 2023-01-23} df[is_holiday] df.index.strftime(%Y-%m-%d).isin(holidays).astype(int) df[is_pre_holiday] df[is_holiday].shift(-1).fillna(0).astype(int) return df feat_df add_calendar(feat_df)逻辑说明is_pre_holiday用shift(-1)看下一天是否节假日注意这是特征不是标签不会泄漏。参数holidays集合按数据实际年份补全不要用固定年份硬套。3.3 特征筛选用相关性加树模型重要性双过滤特征不是越多越好。电力数据里气象特征往往冗余。我一般先算Pearson和Spearman再去掉高相关对最后用LightGBM的重要性做二次筛选。import numpy as np from lightgbm import LGBMRegressor def select_features(df, targetload, corr_th0.95, top_k40): cols [c for c in df.columns if c ! target and df[c].dtype ! object] corr df[cols].corr().abs() upper corr.where(np.triu(np.ones(corr.shape), k1).astype(bool)) drop [c for c in upper.columns if any(upper[c] corr_th)] cols [c for c in cols if c not in drop] X df[cols].fillna(0) y df[target].fillna(0) model LGBMRegressor(n_estimators200, learning_rate0.05) model.fit(X, y) imp pd.Series(model.feature_importances_, indexcols).sort_values(ascendingFalse) return imp.head(top_k).index.tolist() selected select_features(feat_df) print(len(selected), selected[:10])逻辑说明corr_th0.95去掉近乎重复的特征top_k40控制维度。LightGBM参数n_estimators200、learning_rate0.05是保守起点。注意这里用全量数据算重要性只用于筛选最终模型评估必须用时间切分。4. 建模与验证时间序列切分和基线对比4.1 时间切分别用随机KFold电力时序数据用随机KFold会严重高估。正确做法是按时间切前70%训练中间15%验证最后15%测试。如果要做交叉验证用TimeSeriesSplit。from sklearn.model_selection import TimeSeriesSplit def time_split(df, train_ratio0.7, val_ratio0.15): n len(df) train_end int(n * train_ratio) val_end int(n * (train_ratio val_ratio)) train df.iloc[:train_end] val df.iloc[train_end:val_end] test df.iloc[val_end:] return train, val, test train, val, test time_split(feat_df) tscv TimeSeriesSplit(n_splits5) print(len(train), len(val), len(test))逻辑说明time_split按行号切前提是df已按时间排序。TimeSeriesSplit用于训练集内部调参。参数比例按数据量调整数据少时验证集可以到20%。4.2 基线模型先跑通线性回归和LightGBM不要一上来就上深度学习。先跑线性回归和LightGBM拿到基线分数后面所有改进都跟这两个比。from sklearn.linear_model import Ridge from sklearn.metrics import mean_absolute_error import lightgbm as lgb def train_baseline(train, val, features, targetload): X_tr, y_tr train[features].fillna(0), train[target] X_va, y_va val[features].fillna(0), val[target] ridge Ridge(alpha1.0).fit(X_tr, y_tr) pred_ridge ridge.predict(X_va) lgbm lgb.LGBMRegressor(n_estimators500, learning_rate0.03, num_leaves31) lgbm.fit(X_tr, y_tr) pred_lgbm lgbm.predict(X_va) print(Ridge MAE:, mean_absolute_error(y_va, pred_ridge)) print(LGBM MAE:, mean_absolute_error(y_va, pred_lgbm)) return ridge, lgbm ridge, lgbm train_baseline(train, val, selected)逻辑说明Ridge(alpha1.0)是线性基线LGBMRegressor参数n_estimators500、learning_rate0.03、num_leaves31是常用起点。MAE比RMSE更抗离群值电力数据里更稳。如果LGBM比Ridge还差先查特征里有没有未来信息。4.3 误差分析与残差检查模型跑完不看残差等于白跑。按小时、按星期分组看MAE能发现系统性偏差。def residual_report(model, val, features, targetload): pred model.predict(val[features].fillna(0)) val val.copy() val[residual] val[target] - pred by_hour val.groupby(val.index.hour)[residual].apply(lambda x: x.abs().mean()) by_dow val.groupby(val.index.dayofweek)[residual].apply(lambda x: x.abs().mean()) return by_hour, by_dow by_hour, by_dow residual_report(lgbm, val, selected) print(by_hour.round(2))逻辑说明residual是真实减预测取绝对值后按小时聚合。如果某个小时MAE明显高检查该时段是否有特殊用电模式或特征缺失。参数features必须和训练时一致。5. 避坑与排查电力数据挖掘管道里最容易翻车的5个点5.1 现象验证集分数很高测试集直接崩原因特征里混入了未来信息比如用全量数据算的均值编码或者滑窗没有shift。解决所有统计特征必须基于历史窗口均值编码在时间切分后重新计算。检查方法把训练集最后一段当验证如果分数骤降基本就是泄漏。5.2 现象宽表行数比负荷表多出几千行原因气象表或台账表有重复时间戳join后行数膨胀。解决join前先drop_duplicates(subset[ts])或者用groupby(ts).mean()聚合。排查时看merged.index.duplicated().sum()。5.3 现象LightGBM训练报NaN错误原因特征里有inf或object列。解决df.replace([np.inf, -np.inf], np.nan)后统一fillna(0)object列要么编码要么丢弃。注意fillna(0)对某些特征不合理比如温度缺失填0会引入偏差最好用中位数。5.4 现象模型MAE在夜间特别高原因夜间负荷低相对误差大或者夜间气象特征缺失严重。解决分时段建模或者给夜间样本更高权重。也可以加“是否夜间”特征让模型自己学。5.5 现象每次跑结果不一样无法复现原因随机种子没固定或者数据读取顺序依赖文件系统。解决np.random.seed(42)、random.seed(42)LightGBM设random_state42。数据读取用显式排序不要依赖os.listdir顺序。6. 把管道封装成可复用脚本一个技巧和一条习惯走到这里管道已经能跑通。但真正让这套东西值钱的是封装。我一般把整个流程拆成config.yaml加四个脚本01_profile.py、02_align.py、03_features.py、04_train.py。每个脚本只做一件事输入输出都是文件路径。这样换一份新数据只改config里的路径和字段名不用动代码。# config.yaml raw: load: data/load.csv weather: data/weather.csv time: freq: 15min load_limit: 4 features: lags: [1, 4, 96] windows: [4, 96] model: n_estimators: 500 learning_rate: 0.03 num_leaves: 31 random_state: 42参数说明freq控制重采样粒度load_limit控制负荷插值上限lags和windows按采样频率换算。random_state固定后同一份数据两次运行MAE差异应该在0.1%以内超过就说明还有随机源没控住。验证封装是否成功有个简单办法把训练集切出最后一天用前面所有数据训练预测这一天然后跟直接跑全量的结果比。如果封装后分数一致说明管道没有引入额外随机性。这个检查我每次改完脚本都会跑一遍血泪经验是——省这一步后面调参调半天发现是管道在抖。最后一个习惯每次提交前把config.yaml、宽表元数据JSON、验证集MAE一起存档。电力AI大赛的提交次数有限没有后悔药。我一般会在项目根目录建runs/文件夹按时间戳存三样东西配置文件、验证分数、特征列表。这样一周后回头看能清楚知道哪一版最好、为什么好。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑