據(jù)分析實(shí)戰(zhàn):用Python洞察能耗規(guī)律)
簡(jiǎn)介面向制造業(yè)用電量預(yù)測(cè)的 LSTM 項(xiàng)目資源聚焦電力消耗數(shù)據(jù)中典型的周期性、趨勢(shì)性特征并針對(duì)時(shí)間序列建模長(zhǎng)期依賴問(wèn)題給出解決方案資源覆蓋數(shù)據(jù)預(yù)處理、模型構(gòu)建、訓(xùn)練評(píng)估與預(yù)測(cè)結(jié)果對(duì)比等完整流程適合時(shí)間序列分析初學(xué)者、制造業(yè)能源管理從業(yè)者以及正在開(kāi)展電力負(fù)荷預(yù)測(cè)課題或競(jìng)賽的學(xué)生使用。壓縮包共 106 個(gè)文件以 CSV 預(yù)測(cè)結(jié)果文件、Python 腳本和 JPG 可視化圖表為主體另有 XML 工程配置、運(yùn)行日志、PyTorch 模型權(quán)重 pth 及記錄訓(xùn)練損失變化的 Excel 表格總大小僅 4.57MB目錄結(jié)構(gòu)清晰便于按需查閱。資源提供了不同時(shí)間步長(zhǎng)2/4/6與多種損失函數(shù)MSE、SmoothL1Loss下的用電量預(yù)測(cè)結(jié)果并通過(guò)訓(xùn)練損失記錄幫助判斷模型收斂和過(guò)擬合情況讀者可通過(guò)主程序快速?gòu)?fù)現(xiàn)實(shí)驗(yàn)直觀觀察各參數(shù)對(duì)預(yù)測(cè)精度的影響進(jìn)而掌握 LSTM 在能耗數(shù)據(jù)分析中的調(diào)參方法。目前已有 1517 人學(xué)習(xí)下載是一份兼顧代碼、數(shù)據(jù)與訓(xùn)練記錄面向?qū)嶋H問(wèn)題且容易上手的輕量級(jí)參考。 去年夏天我收到電費(fèi)賬單時(shí)愣了一下——比前一個(gè)月多出接近一倍可我明明沒(méi)添什么大功率電器。翻出歷史賬單逐月對(duì)比發(fā)現(xiàn)用電量像坐過(guò)山車(chē)一樣忽上忽下卻又說(shuō)不出具體是哪兒在耗電。后來(lái)我把智能電表里的歷史數(shù)據(jù)導(dǎo)出來(lái)用 Python 做了一遍完整的清洗、分析和可視化才真正看清自己家里的用電規(guī)律。這個(gè)用電量數(shù)據(jù)分享項(xiàng)目本質(zhì)上是教你把手里的用電量數(shù)據(jù)變成能指導(dǎo)行動(dòng)的結(jié)論哪些電器是耗電大頭、峰谷時(shí)段怎么分布、電費(fèi)異常是哪里出了問(wèn)題。我當(dāng)時(shí)做完之后把分析圖表分享到業(yè)主群不少鄰居照著排查還真找到了自家待機(jī)功耗過(guò)高的問(wèn)題。這篇博文會(huì)把整個(gè)項(xiàng)目的思路、數(shù)據(jù)獲取方式、處理代碼、可視化和常見(jiàn)坑都寫(xiě)清楚適合手里有智能電表數(shù)據(jù)或想自己統(tǒng)計(jì)家庭能耗的朋友參考也適合剛接觸 pandas 時(shí)間序列分析的新手拿來(lái)練手。1. 為什么要做用電量數(shù)據(jù)分析1.1 一次電費(fèi)異常引發(fā)的需求數(shù)據(jù)分析最忌諱的就是沒(méi)有明確目的。我這次做用電量分析起因非常樸素——想知道錢(qián)花在哪了。家庭用電不像打車(chē)、網(wǎng)購(gòu)那樣每筆都有明細(xì)賬單它只有一個(gè)總數(shù)字家里電器又多洗衣機(jī)、熱水器、空調(diào)、冰箱各自貢獻(xiàn)了多少基本靠猜。把用電量數(shù)據(jù)從月度粒度細(xì)化到當(dāng)天甚至每15分鐘一個(gè)點(diǎn)之后很多問(wèn)題一眼就能看出答案。比如我分析后發(fā)現(xiàn)自己家的電費(fèi)大頭是電熱水器它在凌晨保溫加熱的耗電量比我白天開(kāi)6小時(shí)電腦還多。這個(gè)結(jié)論在月賬單上根本看不出來(lái)但把逐日數(shù)據(jù)按小時(shí)聚合后凌晨那個(gè)穩(wěn)定存在的小高峰直接暴露了真相。1.2 這個(gè)分析適合誰(shuí)、能解決什么問(wèn)題這套分析思路既適合家庭用戶也適合小區(qū)物業(yè)、小型商戶做能耗摸底。對(duì)于個(gè)人來(lái)說(shuō)能解決三個(gè)具體問(wèn)題找到電費(fèi)異常波動(dòng)的真實(shí)原因而不是靠感覺(jué)猜識(shí)別出峰谷用電時(shí)段配合分時(shí)電價(jià)調(diào)整用電習(xí)慣發(fā)現(xiàn)待機(jī)功耗和老化電器做到有的放矢地更換或斷電如果你是剛學(xué)數(shù)據(jù)分析的開(kāi)發(fā)者這個(gè)項(xiàng)目更是極佳的練手素材。它的數(shù)據(jù)量不大一年也就365條到幾萬(wàn)條不需要分布式計(jì)算一張 Excel 表或 SQLite 數(shù)據(jù)庫(kù)就夠用但完整覆蓋了數(shù)據(jù)清洗、重采樣、聚合、可視化這一套核心流程比看著教程敲示例代碼有感覺(jué)得多。2. 數(shù)據(jù)從哪來(lái)三類(lèi)典型來(lái)源2.1 智能電表的線上查詢現(xiàn)在很多地區(qū)的智能電表已經(jīng)支持高頻采集用電數(shù)據(jù)會(huì)定時(shí)上傳到供電企業(yè)的后臺(tái)。普通用戶最常見(jiàn)的獲取途徑是官方App或小程序一般能查到最近一年到兩年的每日用電量部分區(qū)域甚至支持查詢每15分鐘一個(gè)點(diǎn)的負(fù)荷曲線。我用的就是這類(lèi)渠道導(dǎo)出的 CSV 文件字段通常包含時(shí)間、正向有功電量等需要自己在導(dǎo)出時(shí)留意時(shí)間粒度和單位kWh 還是 Wh。如果你所在區(qū)域的線上渠道查不到日數(shù)據(jù)還有個(gè)笨辦法每天固定時(shí)間抄一次電表讀數(shù)記錄到表格里。雖然麻煩但攢上兩三個(gè)月也能看出規(guī)律。參與過(guò)家庭能源管理系統(tǒng)項(xiàng)目的朋友還可以考慮加裝支持 Modbus 或 Wi-Fi 的智能電表模塊把數(shù)據(jù)自動(dòng)采集到本機(jī)數(shù)據(jù)庫(kù)這樣連手動(dòng)導(dǎo)出都省了。2.2 智能插座與獨(dú)立電表實(shí)測(cè)遇到單個(gè)電器耗電多少這種問(wèn)題時(shí)總表數(shù)據(jù)幫不上忙需要用智能插座做支路測(cè)量。我買(mǎi)了一臺(tái)帶功率統(tǒng)計(jì)的 Wi-Fi 插座接在電熱水器前面用廠商 App 導(dǎo)出它一周的逐小時(shí)功率曲線。這個(gè)數(shù)據(jù)的好處是干凈、和具體設(shè)備強(qiáng)綁定缺點(diǎn)是覆蓋面小不適合做全家用電的整體分析。所以我的做法是總表數(shù)據(jù)負(fù)責(zé)看全貌插座數(shù)據(jù)負(fù)責(zé)定位具體設(shè)備兩者配合使用。2.3 公共數(shù)據(jù)集與工具準(zhǔn)備如果你手上沒(méi)有自己的數(shù)據(jù)想直接練手可以用一些學(xué)術(shù)用途的公開(kāi)數(shù)據(jù)集比如某些機(jī)構(gòu)發(fā)布的住宅用電數(shù)據(jù)這些數(shù)據(jù)通常是 CSV 格式時(shí)間跨度為一年以上字段比家庭導(dǎo)出的更規(guī)范包括時(shí)間戳、總用電量、分項(xiàng)用電量等。不過(guò)要注意這類(lèi)數(shù)據(jù)集大多以教學(xué)和科研為目的分享時(shí)需注明出處。工具方面我的推薦比較固定Python pandas 做數(shù)據(jù)處理matplotlib 做靜態(tài)圖pyecharts 或 Plotly 做交互式圖表。項(xiàng)目做大了想長(zhǎng)期保存數(shù)據(jù)的話可以加一個(gè) SQLite幾百兆以內(nèi)的用電記錄它都能輕松應(yīng)對(duì)。開(kāi)發(fā)環(huán)境裝 Anaconda 或直接pip install pandas matplotlib pyecharts都行不復(fù)雜。3. 數(shù)據(jù)預(yù)處理拿到手先別急著畫(huà)圖3.1 時(shí)間字段的清洗大部分人拿到電表導(dǎo)出的數(shù)據(jù)第一件事就是直接plt.plot結(jié)果畫(huà)出來(lái)往往是一團(tuán)亂麻。原因多半出在時(shí)間字段上時(shí)間列是字符串、時(shí)區(qū)不對(duì)、采樣間隔不均勻、有重復(fù)時(shí)間戳。我處理原始 CSV 的第一步永遠(yuǎn)是統(tǒng)一時(shí)間格式import pandas as pd df pd.read_csv(elec_raw.csv, encodingutf-8-sig) df[time] pd.to_datetime(df[time], format%Y-%m-%d %H:%M:%S) df df.set_index(time).sort_index() print(df.head())需要注意編碼問(wèn)題。供電企業(yè)導(dǎo)出的 CSV 在 Windows 下經(jīng)常是 GBK 編碼直接用 pandas 讀取會(huì)報(bào)錯(cuò)我一般先試utf-8-sig不行就換gbk。時(shí)間格式也要以實(shí)際為準(zhǔn)有的是2023/07/01 08:15有的是2023-07-01 08:15:00最好顯式指定format否則 pandas 自動(dòng)推斷在幾萬(wàn)條數(shù)據(jù)上會(huì)慢得讓人懷疑人生。3.2 缺失值與異常值處理真實(shí)用電數(shù)據(jù)幾乎沒(méi)有完全干凈的。通信故障、電表重啟、偶爾上傳失敗都會(huì)造成數(shù)據(jù)缺失或異常。我最常遇到的異常值有兩種用電量出現(xiàn)負(fù)值以及單點(diǎn)數(shù)值突然跳到平時(shí)的幾十倍。負(fù)值一般是電表重啟或現(xiàn)場(chǎng)校準(zhǔn)造成的處理方式是直接剔除# 剔除負(fù)值和超過(guò)99.9%分位數(shù)的離譜值 df df[df[value] 0] upper_limit df[value].quantile(0.999) df df[df[value] upper_limit]缺失值要不要補(bǔ)、怎么補(bǔ)取決于分析目的。如果是畫(huà)年趨勢(shì)圖少量缺失點(diǎn)完全不影響直接跳過(guò)即可。如果是做日用電量匯總比如把15分鐘數(shù)據(jù)重采樣成每日總量可以先用前向填充把短時(shí)間缺口補(bǔ)上再聚合。我個(gè)人的經(jīng)驗(yàn)是超過(guò)3個(gè)小時(shí)的連續(xù)缺失就不要再?gòu)?qiáng)行插值了寧可讓它空著也不要制造出看似平滑實(shí)則虛構(gòu)的曲線。這個(gè)項(xiàng)目里我試過(guò)用線性插值補(bǔ)一個(gè)長(zhǎng)達(dá)兩天的缺口結(jié)果畫(huà)出來(lái)的曲線在缺口段出現(xiàn)明顯的凹陷極易誤導(dǎo)判斷。3.3 數(shù)據(jù)入庫(kù)與統(tǒng)一單位清洗完的數(shù)據(jù)我會(huì)存一份副本格式統(tǒng)一為 Parquet 或 SQLite字段統(tǒng)一為time、value、unit。單位統(tǒng)一很關(guān)鍵有的電表導(dǎo)出的是 kWh有的是 Wh如果混著計(jì)算差了1000倍后面的分析全白做。我的做法是在讀入時(shí)強(qiáng)制轉(zhuǎn)成 kWhdf[value] df[value] / 1000.0 # 如果原始單位是Wh df df.rename(columns{value: kwh})另外建議建一張meta表記錄數(shù)據(jù)來(lái)源、時(shí)間跨度、單位、異常處理說(shuō)明。這個(gè)小習(xí)慣在數(shù)據(jù)量變大、來(lái)源變多之后特別有用不然過(guò)上兩個(gè)月回頭再看很容易忘記某列到底代表什么。4. 核心分析把用電規(guī)律挖出來(lái)4.1 整體趨勢(shì)與周期規(guī)律分析的第一步是看整體趨勢(shì)。把清洗好的數(shù)據(jù)按天重采樣得到逐日用電量曲線daily df[kwh].resample(D).sum() daily.plot(figsize(12, 4))從這條曲線上能直觀看到季節(jié)變化、周末和工作日的差異以及某個(gè)時(shí)間段的異常抬升。以我的數(shù)據(jù)為例6月到8月的日均用電量明顯高出春秋季一倍多這個(gè)結(jié)論符合直覺(jué)但真正讓我意外的是1月的數(shù)據(jù)——因?yàn)槎扉_(kāi)電暖器用電量竟然和7月開(kāi)空調(diào)時(shí)相差無(wú)幾。有了整體曲線后進(jìn)一步疊加星期維度看周期性。方法很簡(jiǎn)單把數(shù)據(jù)按星期幾分組求平均日用電量daily_avg_by_weekday daily.groupby(daily.index.dayofweek).mean() daily_avg_by_weekday.index [周一, 周二, 周三, 周四, 周五, 周六, 周日] print(daily_avg_by_weekday)我實(shí)測(cè)下來(lái)工作日的用電形態(tài)差異不大但周六會(huì)出現(xiàn)一個(gè)明顯的高峰原因是周末在家時(shí)間長(zhǎng)、做飯次數(shù)多熱水器和抽油煙機(jī)使用頻率上升。而且周日用電量比周六略微回落估計(jì)是不少家庭周日選擇外出。4.2 峰谷時(shí)段拆解比周幾用電多更實(shí)用的是一天之內(nèi)的高峰時(shí)段分布。配合峰谷分時(shí)電價(jià)能直接幫你省錢(qián)。如果電表數(shù)據(jù)是15分鐘或1小時(shí)一個(gè)點(diǎn)就可以按小時(shí)做聚合看一天24小時(shí)的負(fù)荷曲線hourly df[kwh].resample(H).sum() hourly_avg_by_hour hourly.groupby(hourly.index.hour).mean() # 找出最高和最低的三個(gè)時(shí)段 print(hourly_avg_by_hour.sort_values(ascendingFalse).head(3)) print(hourly_avg_by_hour.sort_values(ascendingTrue).head(3))我的分析結(jié)果很清晰晚高峰集中在18點(diǎn)到21點(diǎn)峰值出現(xiàn)在20點(diǎn)左右早高峰則小得多只有晚高峰的一半不到。凌晨1點(diǎn)到5點(diǎn)是用電低谷主要負(fù)荷基本只有冰箱和待機(jī)設(shè)備。知道了這個(gè)規(guī)律之后我把熱水器的加熱時(shí)間調(diào)到凌晨的低谷時(shí)段一個(gè)月電費(fèi)下降了約12%這就是數(shù)據(jù)分析直接兌換成錢(qián)的例子。4.3 電量與外部因素的關(guān)聯(lián)用電量不是孤立的數(shù)據(jù)和溫度、天氣、生活習(xí)慣都有關(guān)系。盡管我沒(méi)有溫度傳感器的數(shù)據(jù)但用本地氣象站的歷史數(shù)據(jù)做了粗略關(guān)聯(lián)分析。做法不復(fù)雜把日用電量和當(dāng)天的最高氣溫放進(jìn)同一個(gè) DataFrame畫(huà)散點(diǎn)圖、計(jì)算相關(guān)系數(shù)。merged pd.merge(daily, temp_daily, ondate) corr merged[kwh].corr(merged[temp_max]) print(corr)在氣溫跨度為-5℃到35℃的全年數(shù)據(jù)里相關(guān)系數(shù)大約是0.73說(shuō)明用電量受氣溫影響非常顯著。而且這個(gè)關(guān)系不是線性的低于5℃和高于28℃后用電量都會(huì)快速上漲中間舒適區(qū)則維持低位。這種非線性特征如果只看月賬單很難察覺(jué)但把逐日數(shù)據(jù)一畫(huà)圖就非常直觀。5. 可視化呈現(xiàn)與分享要點(diǎn)5.1 用 matplotlib 快速出圖分析過(guò)程中我大量依賴 matplotlib因?yàn)樗谔剿麟A段最靈活改圖成本低。常用的圖有三類(lèi):第一類(lèi)是逐日用電量折線圖過(guò)濾掉局部抖動(dòng)后用滾動(dòng)平均rolling(window7).mean()畫(huà)出平滑趨勢(shì)線一眼鎖定季節(jié)變化。第二類(lèi)是24小時(shí)負(fù)荷曲線把全年按工作日和休息日分成兩撥分別畫(huà)平均曲線色差明顯適合放在數(shù)據(jù)分析報(bào)告里說(shuō)明行為差異。第三類(lèi)是月度用電量柱狀圖把所有年份的同一個(gè)月做對(duì)比看是否出現(xiàn)同比異常增長(zhǎng)。貼一段我當(dāng)時(shí)畫(huà)24小時(shí)負(fù)荷曲線的核心代碼包含平滑處理效果比直接畫(huà)原始折線好很多import matplotlib.pyplot as plt fig, ax plt.subplots(figsize(10, 5)) hourly_avg_by_hour.plot(axax, markero, label小時(shí)平均, lw2) ax.fill_between( hourly_avg_by_hour.index, hourly_avg_by_hour - hourly_avg_by_hour.std(), hourly_avg_by_hour hourly_avg_by_hour.std(), alpha0.2, label±1σ ) ax.set_xlabel(小時(shí)) ax.set_ylabel(平均用電量(kWh)) ax.set_title(工作日/休息日 24小時(shí)負(fù)荷曲線) ax.legend()5.2 用 pyecharts 做可交互圖表如果只是在本地自用matplotlib 足夠。但數(shù)據(jù)分享意味著要把結(jié)果發(fā)到群里或嵌到網(wǎng)頁(yè)上靜態(tài)圖不方便讀者自己查看細(xì)節(jié)這時(shí)候我會(huì)用 pyecharts 做可交互的 HTML 圖表鼠標(biāo)懸停能看到具體值還可以縮放平移。pyecharts 的 API 和 ECharts 保持一致畫(huà)一個(gè)交互式折線圖的核心代碼很短from pyecharts.charts import Line from pyecharts import options as opts line ( Line() .add_xaxis([d.strftime(%m-%d) for d in daily.index]) .add_yaxis(日用電量, [round(v, 2) for v in daily.values], is_smoothTrue) .set_global_opts( title_optsopts.TitleOpts(title家庭日用電量趨勢(shì)), datazoom_opts[opts.DataZoomOpts()], ) ) line.render(daily_line.html)這段代碼生成的 HTML 文件可以直接用瀏覽器打開(kāi)也可以嵌入博客或企業(yè)內(nèi)部系統(tǒng)。我在鄰居群分享時(shí)用的就是這種交互圖有幾個(gè)人反饋第一次直觀看到家里每個(gè)小時(shí)耗電這么多比發(fā) PDF 報(bào)告有效得多。5.3 分享數(shù)據(jù)時(shí)的注意事項(xiàng)既然叫數(shù)據(jù)分享脫敏問(wèn)題不能忽視。家庭用電數(shù)據(jù)雖然不像身份證號(hào)那么敏感但高頻用電曲線其實(shí)能反推出家里作息、是否有人、用什么電器私密性并不低。我分享的做法是把時(shí)間精度降到日不分享15分鐘級(jí)別的原始曲線小區(qū)、樓棟、表號(hào)等字段一律不出現(xiàn)別人需要原數(shù)據(jù)時(shí)只提供已經(jīng)聚合好的統(tǒng)計(jì)結(jié)果比如工作日日均8.5kWh、休息日日均10.2kWh。如果是分享代碼也要順手把數(shù)據(jù)路徑、電表表號(hào)、真實(shí)地址等寫(xiě)進(jìn)代碼或 README 的占位符里。這既是保護(hù)自己也是保護(hù)看你代碼的人萬(wàn)一他拿到的是別人的隱私數(shù)據(jù)知道如何處理才正規(guī)。6. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄6.1 時(shí)間序列索引的坑時(shí)間序列分析最常見(jiàn)的報(bào)錯(cuò)就是KeyError或TypeError原因多半是索引沒(méi)有正確設(shè)置。pd.to_datetime之后一定要記得set_index(time)否則后面resample會(huì)直接報(bào)錯(cuò)。另外如果導(dǎo)出的時(shí)間戳帶時(shí)區(qū)后綴比如08:00to_datetime解析出來(lái)的是帶時(shí)區(qū)對(duì)象和本地時(shí)區(qū)的日期做比較時(shí)容易差8個(gè)小時(shí)我習(xí)慣在讀入后直接強(qiáng)制tz_localize(None)去掉時(shí)區(qū)信息。6.2 缺失數(shù)據(jù)怎么補(bǔ)才合理我在第3節(jié)提過(guò)不要強(qiáng)行補(bǔ)長(zhǎng)缺口這里補(bǔ)充一下具體判斷標(biāo)準(zhǔn)。15分鐘粒度的數(shù)據(jù)連續(xù)缺失1-2個(gè)采集點(diǎn)即15-30分鐘用前向填充或者interpolate線性插值都能接受。缺失超過(guò)兩個(gè)小時(shí)最好直接在重采樣時(shí)跳過(guò)因?yàn)檠a(bǔ)出來(lái)的數(shù)據(jù)可能抹掉了一個(gè)真實(shí)的用電行為比如臨時(shí)停電后冰箱重新制冷的高功率段。如果缺失的是整天電表通信完全中斷那天就讓它空著畫(huà)圖時(shí)dropna()掉就行不會(huì)對(duì)月度匯總造成太大偏差。6.3 幾個(gè)能直接抄作業(yè)的小函數(shù)最后分享幾個(gè)幾乎每次分析都會(huì)用的函數(shù)算是這個(gè)項(xiàng)目沉淀下來(lái)的小工具def resample_daily(df): 15分鐘/小時(shí)粒度數(shù)據(jù)轉(zhuǎn)日匯總 return df[kwh].resample(D).sum().dropna() def rolling_padded(series, window7): 滾動(dòng)平均并保留前n-1個(gè)點(diǎn)的原始值 smoothed series.rolling(window).mean() return smoothed.fillna(series) def peak_hours(df, top_n3): 返回一天中用電最高的N個(gè)小時(shí) h df[kwh].resample(H).sum() h h.groupby(h.index.hour).mean() return h.sort_values(ascendingFalse).head(top_n).index.tolist()這些函數(shù)不依賴特定數(shù)據(jù)格式只要你的 DataFrame 有timeDatetimeIndex和kwh兩列就能直接套用。做這個(gè)用電量數(shù)據(jù)分享項(xiàng)目前后花了我大概一周的業(yè)余時(shí)間最花力氣的不是寫(xiě)代碼而是把腦子里那些模糊的感覺(jué)電費(fèi)好高變成清晰的量化結(jié)論。分析做完后我不光找出了電費(fèi)異常的元兇——一臺(tái)老化嚴(yán)重、保溫性能變差的電熱水器還摸清了自己家的用電習(xí)慣順手把熱水器加熱時(shí)段調(diào)整到了低谷電價(jià)區(qū)間。后來(lái)又把這個(gè)過(guò)程寫(xiě)成帶交互圖表的分享帖發(fā)到小區(qū)群不少鄰居照著方法排查出自家待機(jī)功耗異常的設(shè)備。個(gè)人體會(huì)是數(shù)據(jù)分析本身沒(méi)有多玄乎真正有價(jià)值的是那份愿意花時(shí)間把生活細(xì)節(jié)數(shù)字化的耐心。如果你手頭也有一份積灰的用電數(shù)據(jù)不妨按上面的流程走一遍大概率會(huì)收獲幾個(gè)原來(lái)如此的瞬間。本文還有配套的精品資源點(diǎn)擊獲取