據(jù)可視化與分析系統(tǒng))
每年到了畢設(shè)選題季我后臺(tái)收到的私信里出現(xiàn)頻率最高的一句話就是老師我想做個(gè) Python 大數(shù)據(jù)相關(guān)的畢設(shè)要盡量用上 Hadoop 和 Spark最好還能有可視化大屏和機(jī)器學(xué)習(xí)模型。今天我就拿「基于 HadoopSpark 的高頻電力消耗數(shù)據(jù)可視化與分析系統(tǒng)」這個(gè)題當(dāng)樣板把從選題邏輯、集群搭建、數(shù)據(jù)處理、模型訓(xùn)練到可視化落地再到答辯準(zhǔn)備的完整實(shí)操細(xì)節(jié)全部拆給你。這個(gè)題目不是空架子它能把大數(shù)據(jù)課程的核心知識(shí)點(diǎn)全部串起來而且電力數(shù)據(jù)天然具有高頻、周期性強(qiáng)、業(yè)務(wù)含義清晰的特點(diǎn)非常適合作為畢業(yè)設(shè)計(jì)的主場景。如果你還在糾結(jié)題目怎么定、技術(shù)方案怎么落地這篇可以直接幫你把框架定下來照著做就能少走很多彎路。1. 這個(gè)選題真的安全嗎拆開看它的三個(gè)底氣1.1 技術(shù)棧覆蓋度一門畢設(shè)把課程重點(diǎn)全串起來選畢設(shè)題目最重要的標(biāo)準(zhǔn)不是聽起來高大上而是能讓評(píng)委在十分鐘內(nèi)快速理解你在做什么并且相信它是有技術(shù)含量的。HadoopSpark 這個(gè)組合幾乎覆蓋了高校大數(shù)據(jù)方向課程的所有核心模塊分布式存儲(chǔ)HDFS、分布式計(jì)算MapReduce/Spark、數(shù)據(jù)清洗與 ETL、數(shù)據(jù)可視化、機(jī)器學(xué)習(xí)建模。你在做這個(gè)題目的過程中等于把三年學(xué)的重點(diǎn)知識(shí)重新過了一遍。很多同學(xué)擔(dān)心我用 Hadoop 處理一個(gè)幾百萬行的 CSV是不是殺雞用牛刀。這個(gè)擔(dān)心在答辯的時(shí)候一定要能正面回應(yīng)畢設(shè)展示的不是數(shù)據(jù)量有多大而是你掌握了用分布式手段處理超大規(guī)模數(shù)據(jù)的思路和方法。哪怕實(shí)際跑的數(shù)據(jù)量只有幾 GB但你的代碼是跑在分布式集群上的你的存儲(chǔ)是落在 HDFS 上的你的任務(wù)是通過 YARN 調(diào)度執(zhí)行的——這就夠了。評(píng)委不會(huì)因?yàn)槟愕臄?shù)據(jù)集不夠大而扣分會(huì)因?yàn)槟阒v不清分布式原理而扣分。1.2 高頻電力數(shù)據(jù)為什么是好材料題目里的高頻兩個(gè)字是關(guān)鍵。如果是日度或月度電量數(shù)據(jù)比如一年 365 條記錄那用 Excel 就能完成所有分析評(píng)委第一句話就會(huì)問你為什么要用 Hadoop但一旦數(shù)據(jù)變成分鐘級(jí)采樣情況就完全不同了。以公開的電力數(shù)據(jù)集為例一臺(tái)家用電表的分鐘級(jí)采樣數(shù)據(jù)4 年下來就有兩百多萬行。兩百多萬行對(duì) Hadoop 來說完全不是壓力但它仍然遠(yuǎn)超單機(jī) Excel 的處理舒適區(qū)。更重要的是電力消耗數(shù)據(jù)有很強(qiáng)的規(guī)律性每天有明顯的早高峰和晚高峰、周末和工作日模式不同、不同季節(jié)耗電量差異顯著。這些規(guī)律讓后續(xù)的探索性分析、特征工程和機(jī)器學(xué)習(xí)預(yù)測都有話可說不會(huì)出現(xiàn)分析完了但不知道在講什么的尷尬局面。另外電力數(shù)據(jù)的業(yè)務(wù)含義非常直觀不用給評(píng)委科普行業(yè)背景。你可以指著屏幕說這是晚上八點(diǎn)的負(fù)荷尖峰對(duì)應(yīng)的是居民下班后的用電高峰任何人都能聽明白。這種數(shù)據(jù)自己會(huì)講故事的特性在答辯時(shí)是巨大的優(yōu)勢。1.3 數(shù)據(jù)從哪來別卡在第一步不少同學(xué)選完題才發(fā)現(xiàn)沒有數(shù)據(jù)這是最坑的情況。電力消耗數(shù)據(jù)其實(shí)有非常成熟的公開來源我建議優(yōu)先用下面這幾個(gè)UCI Machine Learning Repository 的 Individual Household Electric Power Consumption 數(shù)據(jù)集。這是經(jīng)典的家用電表分鐘級(jí)采樣數(shù)據(jù)集時(shí)間跨度為 2006 年 12 月到 2010 年 11 月約 207 萬行記錄包含有功功率、無功功率、電壓、電流以及廚房、洗衣房、熱水器空調(diào)三個(gè)子回路的能耗數(shù)據(jù)。字段完整質(zhì)量不錯(cuò)適合做預(yù)處理、時(shí)間序列分析和預(yù)測。Kaggle 上的智能電表數(shù)據(jù)集。有一些來自歐洲國家的家庭/商業(yè)電表數(shù)據(jù)細(xì)粒度各不相同有的甚至到 15 分鐘一條適合做多用戶、多維度分析。本地化替代方案。如果老師希望你用本地真實(shí)數(shù)據(jù)可以聯(lián)系學(xué)校后勤或當(dāng)?shù)仉娋W(wǎng)的公開年報(bào)但要注意數(shù)據(jù)的授權(quán)問題別因此給自己惹麻煩。拿到原始數(shù)據(jù)之后千萬不要直接在本地 Excel 里開始分析那會(huì)讓整篇論文失去立足點(diǎn)。正確的做法是把數(shù)據(jù)先導(dǎo)入 HDFS后續(xù)所有處理都通過 Spark 完成讓大數(shù)據(jù)技術(shù)棧真正跑起來。這一步是很多同學(xué)會(huì)忽略的但恰恰是體現(xiàn)工程完整性的關(guān)鍵環(huán)節(jié)。2. 集群環(huán)境搭建從偽分布式起步到能講清原理2.1 單機(jī)偽分布式夠不夠先算筆資源賬很多同學(xué)一上來就糾結(jié)要不要租三臺(tái)云服務(wù)器搭真集群。我的建議很直接如果你的畢設(shè)目標(biāo)是把系統(tǒng)跑通、把原理講清偽分布式完全夠用。所謂偽分布式就是在一臺(tái)機(jī)器上同時(shí)啟動(dòng) NameNode、DataNode、ResourceManager、NodeManager 等所有守護(hù)進(jìn)程它們各自用獨(dú)立進(jìn)程運(yùn)行HDFS 和 YARN 的調(diào)度邏輯與真實(shí)集群完全一致只是數(shù)據(jù)副本數(shù)只能設(shè)為 1。資源方面本地虛擬機(jī)推薦給 4 核 CPU、8GB 內(nèi)存磁盤 50GB 以上。操作系統(tǒng)用 Ubuntu 22.04 或 CentOS 7 都可以。如果你手上的電腦配置不夠也可以買一臺(tái) 2 核 4G 的云服務(wù)器做驗(yàn)證但體驗(yàn)會(huì)比較煎熬因?yàn)橥瑫r(shí)跑 Hadoop 和 Spark 時(shí)內(nèi)存會(huì)很緊張。這里有一個(gè)論文里很好用的表述你的實(shí)驗(yàn)環(huán)境是單節(jié)點(diǎn)偽分布式集群并明確指出它與生產(chǎn)環(huán)境的區(qū)別在于——所有角色進(jìn)程部署在同一臺(tái)物理機(jī)上、副本數(shù)為 1、無數(shù)據(jù)節(jié)點(diǎn)間的網(wǎng)絡(luò)傳輸開銷。這樣既誠實(shí)又展示了你知道真實(shí)集群長什么樣。2.2 Hadoop 三件套配置與常見的坑Hadoop 跑起來需要改三個(gè)核心配置文件core-site.xml、hdfs-site.xml、yarn-site.xml。先說我的推薦配置!-- core-site.xml -- configuration property namefs.defaultFS/name valuehdfs://hadoop-master:9000/value /property property namehadoop.tmp.dir/name value/data/hadoop/tmp/value /property /configuration!-- hdfs-site.xml -- configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/data/hadoop/namenode/value /property property namedfs.datanode.data.dir/name value/data/hadoop/datanode/value /property /configuration!-- yarn-site.xml -- configuration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property /configuration這幾個(gè)配置里最容易踩的坑有三個(gè)。第一hostname 不要帶下劃線我見過很多人把主機(jī)名設(shè)成 hadoop_master結(jié)果 NameNode 怎么都起不來換成 hadoop-master 就好了。第二JAVA_HOME 必須明確指到 JDK 安裝目錄不要依賴系統(tǒng)默認(rèn)環(huán)境變量否則 start-dfs.sh 能執(zhí)行但進(jìn)程啟動(dòng)后秒掛。第三首次啟動(dòng)之前一定要格式化 NameNode運(yùn)行 hdfs namenode -format不格式化的話 NameNode 會(huì)一直報(bào)錯(cuò)找不到存儲(chǔ)目錄。啟動(dòng)順序也有講究先 start-dfs.sh再 start-yarn.sh。啟動(dòng)完用 jps 命令檢查能看到 NameNode、DataNode、ResourceManager、NodeManager 四個(gè)關(guān)鍵進(jìn)程就說明基本正常。之后可以訪問 HDFS Web UI默認(rèn) 9870 端口和 YARN 資源管理界面默認(rèn) 8088 端口確認(rèn)集群狀態(tài)為 LIVE。2.3 Spark on YARN統(tǒng)一資源管理才是答辯加分點(diǎn)Spark 有兩種常見的部署方式Standalone 模式和 on YARN 模式。畢設(shè)我強(qiáng)烈建議你用 Spark on YARN理由很簡單資源統(tǒng)一交給 YARN 調(diào)度和 Hadoop 共用一套資源管理框架這就不是一個(gè)玩具項(xiàng)目了而是貼合生產(chǎn)環(huán)境的架構(gòu)。spark-env.sh 里面重點(diǎn)配置兩塊export JAVA_HOME/usr/local/jdk export HADOOP_CONF_DIR/usr/local/hadoop/etc/hadoop export SPARK_MASTER_HOSTlocalhost提交任務(wù)的時(shí)候使用這樣的命令/usr/local/spark/bin/spark-submit \ --master yarn \ --deploy-mode client \ --num-executors 2 \ --executor-memory 2g \ --executor-cores 2 \ power_analysis.py注意--num-executors 的數(shù)量不能超過 YARN 能分配的容器上限否則任務(wù)會(huì)一直卡在 ACCEPTED 狀態(tài)。偽分布式環(huán)境下executor 內(nèi)存建議控制在 2G 以內(nèi)給系統(tǒng)留足余量不然 NodeManager 會(huì)因?yàn)閮?nèi)存不足直接殺掉你的容器任務(wù)報(bào)錯(cuò)信息還特別難查。2.4 驗(yàn)證環(huán)境跑通第一條數(shù)據(jù)流正式寫業(yè)務(wù)代碼之前先跑兩條最基礎(chǔ)的驗(yàn)證任務(wù)能省下后面一大半排查時(shí)間。第一在 HDFS 上建目錄、傳文件、查文件hdfs dfs -mkdir -p /data/power/raw hdfs dfs -put household_power_consumption.txt /data/power/raw/ hdfs dfs -ls /data/power/raw/第二跑 Spark 自帶的 Pi 計(jì)算樣例/usr/local/spark/bin/spark-submit \ --master yarn \ --class org.apache.spark.examples.SparkPi \ /usr/local/spark/examples/jars/spark-examples_2.12-3.3.0.jar 10這一步能同時(shí)驗(yàn)證 Spark 源碼包、YARN 調(diào)度、日志聚合是否正常。如果 Pi 任務(wù)能跑通你的大數(shù)據(jù)環(huán)境就準(zhǔn)備好迎接正式數(shù)據(jù)了。3. 數(shù)據(jù)鏈路設(shè)計(jì)從原始電表記錄到規(guī)整分析表3.1 數(shù)據(jù)落 HDFS目錄怎么設(shè)計(jì)拿到原始數(shù)據(jù)之后不要一股腦丟進(jìn)去。HDFS 目錄設(shè)計(jì)直接決定了后續(xù)處理的效率和論文里數(shù)據(jù)管理這一章的含金量。我的建議按層級(jí)劃分/data/power/raw/ -- 原始數(shù)據(jù)按日期分目錄 /data/power/clean/ -- 清洗后的數(shù)據(jù) /data/power/features/ -- 特征工程產(chǎn)出的訓(xùn)練表 /data/power/results/ -- 模型預(yù)測結(jié)果原始數(shù)據(jù)按日期分目錄的好處是Spark 讀取時(shí)可以只掃某個(gè)時(shí)間段而不是每次全表掃描。處理邏輯上也更貼近真實(shí)的離線數(shù)倉分層原始層ODS、明細(xì)層DWD、匯總層DWS、應(yīng)用層ADS。論文里用這套分層邏輯來描述評(píng)委一眼就知道你懂?dāng)?shù)據(jù)倉庫的基本理念。對(duì)于 UCI 那個(gè)單文件數(shù)據(jù)集你可以寫一個(gè)簡單的 Python 腳本按天切分成多個(gè) CSV 文件模擬每天采集產(chǎn)生一個(gè)新文件的場景再依次上傳到 HDFS。雖然數(shù)據(jù)本身是歷史的但這種處理方式能體現(xiàn)你對(duì)真實(shí)數(shù)據(jù)產(chǎn)生節(jié)奏的理解。3.2 清洗規(guī)則的取舍電力數(shù)據(jù)看著干凈實(shí)際清洗起來問題不少。UCI 數(shù)據(jù)集中有大約 1.25% 的記錄存在缺失值常見處理方式有三種直接刪除、前向填充、線性插值。我的經(jīng)驗(yàn)是分鐘級(jí)數(shù)據(jù)如果某一行缺失直接刪除或者用前后值的均值填充差別不大但要注意不要在整個(gè)時(shí)間段都連續(xù)缺失的情況下用前向填充那會(huì)產(chǎn)生一段平的假數(shù)據(jù)后續(xù)模型會(huì)被誤導(dǎo)。異常值處理更重要我建議先用規(guī)則過濾明顯的物理不可能值比如有功功率出現(xiàn)負(fù)數(shù)、電壓低于 100V 或高于 300V這些基本都是采集異常直接剔除。然后再用箱線圖或者 IQR 方法識(shí)別統(tǒng)計(jì)離群點(diǎn)比如某幾分鐘的功率突然飆到正常值的十倍這種通常不是真實(shí)用電行為而是設(shè)備噪聲。下面是 Spark 清洗的簡版示例from pyspark.sql import functions as F df spark.read.option(header, true) \ .option(delimiter, ;) \ .csv(hdfs://hadoop-master:9000/data/power/raw/*.csv) df_clean ( df.withColumn(timestamp, F.to_timestamp(F.concat_ws( , F.col(Date), F.col(Time)), dd/MM/yyyy HH:mm:ss)) .filter(F.col(Global_active_power) 0) .filter((F.col(Voltage) 100) (F.col(Voltage) 300)) .dropDuplicates([timestamp]) .select( timestamp, F.col(Global_active_power).alias(active_power), F.col(Global_reactive_power).alias(reactive_power), F.col(Voltage).alias(voltage), F.col(Global_intensity).alias(current), F.col(Sub_metering_1).alias(kitchen_power), F.col(Sub_metering_2).alias(laundry_power), F.col(Sub_metering_3).alias(hvac_power) ) )清洗后的數(shù)據(jù)寫回 HDFS 的 clean 路徑建議保存成 Parquet 格式不要繼續(xù)用 CSV。Parquet 是列式存儲(chǔ)后續(xù)按時(shí)間過濾只需要讀取相關(guān)列IO 開銷小很多而且 Spark 對(duì) Parquet 有原生優(yōu)化。這個(gè)細(xì)節(jié)在論文里寫出來也很加分。3.3 時(shí)間窗口聚合讓分鐘級(jí)數(shù)據(jù)更可用原始數(shù)據(jù)是分鐘級(jí)的207 萬行做探索性分析沒問題但做機(jī)器學(xué)習(xí)預(yù)測時(shí)噪聲較大。我建議按小時(shí)聚合這樣既能保留一天 24 小時(shí)的峰谷形態(tài)又能大幅降低數(shù)據(jù)規(guī)模、提高模型穩(wěn)定性。聚合規(guī)則很簡單對(duì)每小時(shí)內(nèi)的有功功率取均值電壓取均值子回路能耗取求和。聚合后大約有 3.4 萬小時(shí)級(jí)記錄訓(xùn)練和預(yù)測的速度會(huì)非??爝€方便和后面的特征工程銜接。如果你的畢設(shè)題目里強(qiáng)調(diào)高頻那你可以在論文里這樣處理原始數(shù)據(jù)是分鐘級(jí)高頻采樣清洗后保留分鐘級(jí)數(shù)據(jù)用于實(shí)時(shí)頻域分析模型訓(xùn)練時(shí)聚合成小時(shí)級(jí)降低噪聲影響。這樣既扣住了高頻二字又讓模型效果更容易做得好。4. Spark 分析模塊探索性分析到負(fù)荷預(yù)測模型4.1 EDA 看什么先建立業(yè)務(wù)直覺拿到規(guī)整表之后不要急著建模先做探索性分析。這一步的目的有兩個(gè)一是讓你自己熟悉數(shù)據(jù)二是給論文的分析章節(jié)積累素材。電力消耗數(shù)據(jù)的探索性分析通常從這三個(gè)維度入手。時(shí)間維度方面看一天 24 小時(shí)的負(fù)荷曲線你會(huì)很直觀地看到早晚兩個(gè)高峰。工作日和周末的曲線形態(tài)有明顯差異周末的早高峰會(huì)推遲晚高峰的峰值可能更高。季節(jié)維度方面夏季和冬季的用電量明顯大于春秋兩季但驅(qū)動(dòng)因素不同——夏季是空調(diào)冬季是取暖設(shè)備。把春節(jié)等長假效應(yīng)一并分析論文的分析章節(jié)就有血有肉了。粒度交叉方面按季節(jié)工作日/周末做交叉分析觀察不同組合下的負(fù)荷曲線偏移這是電力負(fù)荷分析中最經(jīng)典的視角。用 Spark DataFrame 做這些分析非常方便比如計(jì)算小時(shí)平均負(fù)荷df_hourly df_clean.groupBy( F.hour(timestamp).alias(hour), F.date_format(timestamp, EEEE).alias(weekday) ).agg(F.avg(active_power).alias(avg_power)).orderBy(hour)4.2 特征工程滯后特征為什么比你想象的重要電力負(fù)荷預(yù)測本質(zhì)上是時(shí)間序列預(yù)測特征工程的核心思路是把時(shí)間信息變成模型能理解的特征。我常用的特征分四類時(shí)間戳特征小時(shí)、星期幾、是否周末、是否節(jié)假日。這些特征能幫助模型區(qū)分工作日和周末的不同用電模式。滯后特征預(yù)測當(dāng)前小時(shí)的負(fù)荷上一小時(shí)的負(fù)荷一定是最強(qiáng)相關(guān)特征再往前推 24 小時(shí)昨天同一時(shí)刻和 168 小時(shí)上周同一時(shí)刻也很有價(jià)值因?yàn)樗鼈兛坍嬃巳罩芷谛院椭苤芷谛??;瑒?dòng)窗口特征過去 3 小時(shí)的平均負(fù)荷、過去 6 小時(shí)的最大負(fù)荷等。這類特征相當(dāng)于給模型提供一段短期趨勢信息。外部特征如果有氣溫?cái)?shù)據(jù)可以加入溫度與電力負(fù)荷之間存在很強(qiáng)的非線性關(guān)系。沒有的話不強(qiáng)求。滯后特征用 Spark 的窗口函數(shù)實(shí)現(xiàn)代碼非常簡潔from pyspark.sql.window import Window w Window.orderBy(timestamp) df_feature ( df_hourly .withColumn(target, F.col(avg_power)) .withColumn(lag_1h, F.lag(avg_power, 1).over(w)) .withColumn(lag_24h, F.lag(avg_power, 24).over(w)) .withColumn(lag_168h, F.lag(avg_power, 168).over(w)) .withColumn(rolling_3h_avg, F.avg(avg_power).over(w.rowsBetween(-3, -1))) )注意滯后特征會(huì)讓數(shù)據(jù)前面若干行為空比如 lag_168h 會(huì)讓前 168 行全部為空需要在訓(xùn)練前 dropna。很多同學(xué)做完特征工程不檢查空值直接灌進(jìn)模型訓(xùn)練結(jié)果報(bào)錯(cuò)或者結(jié)果大幅偏差這個(gè)務(wù)必留意。4.3 模型訓(xùn)練與評(píng)估時(shí)間序列切分是硬約束特征準(zhǔn)備好之后就可以建模了。第一梯隊(duì)推薦用 Spark MLlib 里的 RandomForestRegressor 和 GBTRegressor梯度提升樹。它們對(duì)非線性關(guān)系擬合能力好不需要做大規(guī)模特征標(biāo)準(zhǔn)化而且能直接訓(xùn)練在分布式數(shù)據(jù)上。線性回歸可以作為基線模型用來做效果對(duì)比。這里有一個(gè)幾乎所有初學(xué)者都會(huì)犯的錯(cuò)誤用隨機(jī)劃分的方式分割訓(xùn)練集和測試集。時(shí)間序列數(shù)據(jù)的切分必須按時(shí)間順序用前 80% 的數(shù)據(jù)做訓(xùn)練用后 20% 的數(shù)據(jù)做驗(yàn)證。隨機(jī)切分會(huì)讓模型偷看到未來的數(shù)據(jù)導(dǎo)致評(píng)估指標(biāo)虛高答辯時(shí)評(píng)委一問就露餡。完整的建模流程用 Pipeline 組織from pyspark.ml.feature import VectorAssembler from pyspark.ml.regression import RandomForestRegressor from pyspark.ml.evaluation import RegressionEvaluator from pyspark.ml import Pipeline feature_cols [hour, dayofweek, is_weekend, lag_1h, lag_24h, lag_168h, rolling_3h_avg] assembler VectorAssembler(inputColsfeature_cols, outputColfeatures) rf RandomForestRegressor(featuresColfeatures, labelColtarget, numTrees100) pipeline Pipeline(stages[assembler, rf]) train_df df_feature.filter(F.col(timestamp) split_point) test_df df_feature.filter(F.col(timestamp) split_point) model pipeline.fit(train_df) predictions model.transform(test_df) evaluator RegressionEvaluator(labelColtarget, predictionColprediction, metricNamemae) mae evaluator.evaluate(predictions)評(píng)估指標(biāo)建議用 MAE、RMSE、MAPE 三個(gè)一起報(bào)。MAE 給出平均絕對(duì)誤差RMSE 對(duì)大的誤差更敏感MAPE 給出相對(duì)誤差百分比尤其適合電力負(fù)荷這種量級(jí)會(huì)隨時(shí)段變化的數(shù)據(jù)。4.4 結(jié)果如何回到可視化端模型訓(xùn)練完成后預(yù)測結(jié)果不能停在 Spark 里要把有用的結(jié)果導(dǎo)出到前端能讀取的地方。兩個(gè)常用方案一是把預(yù)測結(jié)果寫成 Parquet 或 CSV 放在 HDFS后端接口通過 Spark SQL 查詢二是寫入 MySQLFlask 后端直接查數(shù)據(jù)庫返回 JSON。在實(shí)際畢設(shè)系統(tǒng)中建議混合使用離線分析結(jié)果和預(yù)測結(jié)果落 MySQL方便前端低延遲查詢原始大表和中間明細(xì)保存在 HDFS體現(xiàn)分布式存儲(chǔ)的價(jià)值。這樣既保證了前端體驗(yàn)流暢又保住了大數(shù)據(jù)技術(shù)棧的完整敘事。5. 可視化看板讓評(píng)委一眼看懂你的系統(tǒng)5.1 選型為什么是 ECharts Flask可視化方案我用過不少給畢設(shè)項(xiàng)目的建議很明確優(yōu)先選 ECharts Flask/FastAPI而不是花大力氣去搭 Superset 或 Grafana。原因有三點(diǎn)第一ECharts 交互圖表非常豐富折線圖、柱狀圖、餅圖、熱力圖、儀表盤全部支持做數(shù)據(jù)看板完全夠用第二前后端分離簡單Flask 只需要提供 JSON 接口前端用原生 HTML 加 ECharts 就能搞定不用引入重型前端框架第三代碼是自己寫的論文里可以詳細(xì)描述每個(gè)圖表的實(shí)現(xiàn)邏輯評(píng)委能直接看到你的工作量。這里放一張選型對(duì)照供你參考方案優(yōu)點(diǎn)缺點(diǎn)適合場景ECharts Flask靈活、可控、代碼量適中、定制能力強(qiáng)需要自己寫前后端代碼畢設(shè)、中小型分析系統(tǒng)Apache Superset開箱即用、圖表類型多部署重、定制難度高、答辯難以展示細(xì)節(jié)團(tuán)隊(duì)BI、快速展示Grafana監(jiān)控圖表專業(yè)、實(shí)時(shí)性好偏運(yùn)維監(jiān)控、業(yè)務(wù)分析維度弱服務(wù)器監(jiān)控類項(xiàng)目DataV / 商業(yè)大屏視覺效果炫酷商業(yè)化限制、二次開發(fā)成本高演示大于分析5.2 看板的信息架構(gòu)三句話講完一張大屏做可視化看板最忌諱的是堆砌圖表每個(gè)圖表之間沒有邏輯關(guān)系。我的經(jīng)驗(yàn)是一張好的看板能讓講解者用三句話講完核心結(jié)論第一句講當(dāng)前狀態(tài)第二句講變化趨勢第三句講預(yù)測展望。按這個(gè)邏輯首頁可以分成五個(gè)模塊。頂部是 KPI 卡片區(qū)展示今日總耗電量、當(dāng)前實(shí)時(shí)功率、本時(shí)段同比變化率數(shù)字配一個(gè)簡單的趨勢箭頭讓觀者一眼抓到重點(diǎn)。左側(cè)是 24 小時(shí)負(fù)荷曲線圖展示當(dāng)日逐時(shí)用電走勢標(biāo)注出峰值出現(xiàn)的時(shí)間點(diǎn)。右側(cè)是近 30 日日均負(fù)荷柱狀圖加一條均值參考線展示近期大趨勢。下方左側(cè)放用電結(jié)構(gòu)餅圖用 UCI 數(shù)據(jù)集里的三個(gè)子回路字段展示廚房、洗衣、暖通空調(diào)在整體能耗中的占比下方右側(cè)放未來 24 小時(shí)負(fù)荷預(yù)測曲線用不同顏色區(qū)分實(shí)際值和預(yù)測值這也是整個(gè)看板里最有技術(shù)含金量的一張圖。圖表的配色建議用深色底加高對(duì)比度的亮色系ECharts 的深色主題能直接引入效果好還省事。標(biāo)題要寫人話比如未來24小時(shí)負(fù)荷預(yù)測RandomForest模型別用模型輸出圖這種含糊名字。5.3 后端接口與前端聯(lián)調(diào)的幾個(gè)注意點(diǎn)Flask 后端接口設(shè)計(jì)要遵循按需聚合原則。前端要展示 24 小時(shí)曲線后端就只返回 24 個(gè)點(diǎn)要展示 30 天趨勢就只返回 30 個(gè)聚合值。不要一次性把全量數(shù)據(jù)丟給前端否則頁面加載會(huì)非??ù疝q演示時(shí)出現(xiàn)白屏?xí)軐擂?。接口返回統(tǒng)一使用 JSON 格式結(jié)構(gòu)類似from flask import Flask, jsonify import pymysql app Flask(__name__) app.route(/api/power/trend) def power_trend(): conn pymysql.connect(hostlocalhost, userroot, password123456, databasepower_analysis) cursor conn.cursor() cursor.execute(SELECT date, avg_power FROM daily_power ORDER BY date DESC LIMIT 30) rows cursor.fetchall() return jsonify({ dates: [str(r[0]) for r in rows], values: [float(r[1]) for r in rows] })前后端聯(lián)調(diào)時(shí)最容易出問題的就是跨域和數(shù)據(jù)格式。如果你讓 Flask 直接渲染 HTML 模板就不存在跨域問題如果前后端分離在兩個(gè)端口需要額外處理 CORS。另外MySQL 里的時(shí)間字段返回給前端之前要轉(zhuǎn)成字符串否則 JSON 序列化會(huì)報(bào)錯(cuò)。這些細(xì)節(jié)別看小演示當(dāng)天一旦出問題現(xiàn)場修起來壓力非常大。6. 答辯前的自查清單與系統(tǒng)的下一步6.1 評(píng)委可能追問的問題現(xiàn)在就想好答案答辯能不能過一半在系統(tǒng)實(shí)現(xiàn)一半在回答問題。我整理了評(píng)委最可能追問的幾個(gè)問題你最好在答辯前逐條想清楚答案甚至能寫在 PPT 的備注頁里。第一個(gè)高頻問題你的數(shù)據(jù)量就幾百萬行為什么非要用 Hadoop回答思路是技術(shù)上我們的處理鏈路完全構(gòu)建在分布式框架之上包括 HDFS 存儲(chǔ)、YARN 資源調(diào)度、Spark 分布式計(jì)算業(yè)務(wù)上電力數(shù)據(jù)是持續(xù)產(chǎn)生的高頻數(shù)據(jù)一年幾百 GB、幾年幾個(gè) TB 是常態(tài)單機(jī) MySQL 在數(shù)據(jù)量和吞吐上都無法承載這樣的規(guī)模。這個(gè)回答既承認(rèn)了當(dāng)前數(shù)據(jù)規(guī)模不大又說明了架構(gòu)的合理性。第二個(gè)問題Spark 和 MapReduce 有什么區(qū)別注意別只背快和內(nèi)存計(jì)算要說清楚本質(zhì)Spark 通過 DAG 調(diào)度把多個(gè)計(jì)算階段串聯(lián)中間結(jié)果優(yōu)先落內(nèi)存減少磁盤 IOMapReduce 每個(gè)任務(wù)都要落盤Shuffle 開銷大??梢皂樖痔嵋痪湮覀冊陧?xiàng)目中用 Spark 處理分鐘級(jí)數(shù)據(jù)同樣的邏輯如果換成 MapReduce 任務(wù)耗時(shí)會(huì)有明顯差距。第三個(gè)問題為什么預(yù)測模型用隨機(jī)森林而不是 LSTM這是送分題。你可以說隨機(jī)森林訓(xùn)練效率高、對(duì)特征交互的擬合能力強(qiáng)、可解釋性好LSTM 適合長序列依賴但訓(xùn)練成本高、超參數(shù)敏感在小時(shí)級(jí)聚合數(shù)據(jù)上隨機(jī)森林完全夠用如果數(shù)據(jù)粒度升到分鐘級(jí)、序列長度增加可以再引入 LSTM 做對(duì)比實(shí)驗(yàn)。這個(gè)回答展示了你有建模的全局觀而不是只會(huì)套模型。6.2 系統(tǒng)可以往哪個(gè)方向擴(kuò)展畢設(shè)做完之后無論從論文的展望章節(jié)還是答辯的后期規(guī)劃角度你都應(yīng)該想清楚系統(tǒng)還能怎么升級(jí)。我給三個(gè)方向。第一個(gè)方向是實(shí)時(shí)化。現(xiàn)在的處理方式是離線批處理升級(jí)思路是在數(shù)據(jù)接入層加消息隊(duì)列模擬電表實(shí)時(shí)上報(bào)數(shù)據(jù)然后 Spark Structured Streaming 做流式聚合和實(shí)時(shí)異常檢測??窗迳系漠?dāng)前功率就不再是歷史數(shù)據(jù)而是實(shí)時(shí)值效果非常震撼。第二個(gè)方向是數(shù)據(jù)源的擴(kuò)展。加入天氣溫度數(shù)據(jù)用電負(fù)荷預(yù)測的準(zhǔn)確率通常會(huì)明顯提升可以擴(kuò)展多戶用電數(shù)據(jù)做群體用電行為畫像。第三個(gè)方向是模型層面的深度化。嘗試 Prophet 或 LightGBM和 Spark MLlib 的結(jié)果做對(duì)比形成模型選型實(shí)驗(yàn)論文的深度會(huì)再上一個(gè)臺(tái)階。6.3 最后幾個(gè)實(shí)用提醒臨近答辯前有幾樣事情值得專門花時(shí)間準(zhǔn)備。第一把 HDFS 里的數(shù)據(jù)目錄、Spark 任務(wù)提交日志、MySQL 表結(jié)構(gòu)截圖整理成一個(gè)附錄文檔演示的時(shí)候如果現(xiàn)場網(wǎng)絡(luò)或環(huán)境出問題直接用截圖也能講完整個(gè)系統(tǒng)架構(gòu)。第二準(zhǔn)備一個(gè)兩分鐘的電梯演示腳本開頭一句話說明背景中間三句話講數(shù)據(jù)鏈路和模型結(jié)果最后一句講未來擴(kuò)展方向。這能保證你在任何突發(fā)情況下都能穩(wěn)住節(jié)奏。我個(gè)人在實(shí)際帶畢設(shè)的過程中體會(huì)最深的一點(diǎn)是很多同學(xué)把精力花在調(diào)參和美化圖表上卻忘了反復(fù)演練從數(shù)據(jù)到結(jié)果的完整鏈路。其實(shí)評(píng)委更在意的是你能不能把每一步的理由說清楚。這個(gè)電力消耗可視化與分析系統(tǒng)數(shù)據(jù)鏈條長、涉及技術(shù)多只要把鏈路講順了就是一堂非常漂亮的大數(shù)據(jù)綜合實(shí)踐課。你照著這個(gè)框架做完剩下的就是把每一步的細(xì)節(jié)往論文里填畢業(yè)設(shè)計(jì)這關(guān)基本就穩(wěn)了。