量分析與可視化實(shí)戰(zhàn))
看到“基于Python的陜西省空氣質(zhì)量AQI指數(shù)分析與可視化”這個(gè)畢設(shè)題目不少同學(xué)的第一反應(yīng)是這不就是爬點(diǎn)數(shù)據(jù)畫幾張折線圖和地圖然后寫個(gè)論文嗎真把一個(gè)完整項(xiàng)目從數(shù)據(jù)采集、指標(biāo)計(jì)算到可視化系統(tǒng)搭完、再走一遍答辯你會(huì)發(fā)現(xiàn)任何一個(gè)環(huán)節(jié)都能讓你折騰兩三天。這篇文章我把做這套體系時(shí)踩過的坑、驗(yàn)證過的方法、以及答辯時(shí)被老師重點(diǎn)追問的問題完整梳理一遍給正在選這個(gè)方向的人一份直接能落地的路線圖。這個(gè)項(xiàng)目到底適合誰(shuí)如果你正在準(zhǔn)備計(jì)算機(jī)類畢業(yè)設(shè)計(jì)方向是數(shù)據(jù)分析、Python應(yīng)用、可視化前端又不想碰那種純算法推導(dǎo)到頭疼的題目這個(gè)選題性價(jià)比很高。它覆蓋了數(shù)據(jù)采集、數(shù)據(jù)清洗、指標(biāo)體系計(jì)算、統(tǒng)計(jì)分析、可視化呈現(xiàn)一整套流程工作量好展示技術(shù)難度適中而且可擴(kuò)展空間很大。如果你只是想用Python做環(huán)境數(shù)據(jù)分析的愛好者這篇文章里的清洗思路和可視化坑點(diǎn)也值得看看。1. 選題背景與需求拆解為什么選空氣質(zhì)量AQI分析1.1 一個(gè)“數(shù)據(jù)—算法—展示”三步齊全的題目畢設(shè)選題最怕什么怕題目太空、沒有抓手也怕題目太偏、找不到參考資料??諝赓|(zhì)量AQI分析屬于典型的“數(shù)據(jù)密集型應(yīng)用”題目它可以被拆成一條非常清晰的技術(shù)鏈路數(shù)據(jù)采集層獲取空氣質(zhì)量監(jiān)測(cè)站點(diǎn)數(shù)據(jù)、氣象輔助數(shù)據(jù)數(shù)據(jù)清洗層處理缺失值、重復(fù)值、異常值統(tǒng)一時(shí)間格式指標(biāo)計(jì)算層根據(jù)六項(xiàng)污染物濃度計(jì)算IAQI分指數(shù)再合成AQI分析評(píng)估層做時(shí)間趨勢(shì)、空間對(duì)比、污染特征與氣象相關(guān)性分析可視化展示層用圖表、地圖、交互面板呈現(xiàn)結(jié)果。這一條鏈正好對(duì)應(yīng)了計(jì)算機(jī)專業(yè)里“數(shù)據(jù)的獲取、處理、計(jì)算、呈現(xiàn)”四大基本功。老師在開題時(shí)最關(guān)心的問題——“你的畢設(shè)工作量體現(xiàn)在哪”——用這條鏈路能回答得非常充分因?yàn)槊恳粚佣加锌闪炕某晒?。這個(gè)題目還有一個(gè)隱形優(yōu)勢(shì)數(shù)據(jù)是客觀真實(shí)的。環(huán)境監(jiān)測(cè)數(shù)據(jù)不像用戶行為數(shù)據(jù)或網(wǎng)絡(luò)爬蟲數(shù)據(jù)那樣涉及大量隱私和反爬風(fēng)險(xiǎn)數(shù)據(jù)源公開、結(jié)構(gòu)清晰、字段語(yǔ)義明確。對(duì)一個(gè)畢設(shè)來說“數(shù)據(jù)能不能穩(wěn)定拿到”往往是最大的風(fēng)險(xiǎn)點(diǎn)而這恰恰是AQI項(xiàng)目最不擔(dān)心的部分。1.2 陜西省數(shù)據(jù)的“先天優(yōu)勢(shì)”一個(gè)省就是一個(gè)小型分析樣本庫(kù)選陜西省作為研究區(qū)域不是隨手一拍而是因?yàn)樗牡乩斫Y(jié)構(gòu)和污染特征非常適合做對(duì)比分析。陜西從北到南可以粗略分成三大塊陜北是黃土高原邊緣地帶靠近毛烏素沙地邊緣春季容易出現(xiàn)沙塵天氣顆粒物濃度波動(dòng)大關(guān)中地區(qū)是平原城市密集、工業(yè)集中冬季供暖期和靜穩(wěn)天氣疊加時(shí)容易出現(xiàn)污染累積陜南地處秦嶺以南山脈對(duì)污染物有天然阻隔作用整體空氣質(zhì)量明顯好于關(guān)中。這種“一個(gè)省內(nèi)有明顯區(qū)域差異”的特點(diǎn)讓分析內(nèi)容變得非常豐富。你可以做城市間的橫向?qū)Ρ茸鲫儽鄙硥m事件與關(guān)中冬季污染的成因?qū)Ρ茸鯬M2.5和PM10比值來區(qū)分“燃燒型污染”和“沙塵型污染”。如果選一個(gè)污染特征高度一致的小省份反而很難寫出有深度的分析章節(jié)。從工作量角度看陜西省下轄的城市數(shù)量適中數(shù)據(jù)量既不小到?jīng)]東西可分析也不大到需要分布式計(jì)算。做全省近幾年的日尺度數(shù)據(jù)單機(jī)Python處理起來毫無壓力這在畢設(shè)的時(shí)間預(yù)算里非常重要。1.3 技術(shù)棧選型與整體架構(gòu)設(shè)計(jì)我的選型思路是“能解決問題就夠用但不回避主流工具”最終使用的技術(shù)棧如下模塊工具選擇理由數(shù)據(jù)采集requests BeautifulSoup / 官方數(shù)據(jù)接口輕量、可控適合單機(jī)跑數(shù)據(jù)處理pandas numpy表格處理事實(shí)標(biāo)準(zhǔn)向量化計(jì)算效率高統(tǒng)計(jì)分析scipy statsmodels相關(guān)性檢驗(yàn)、描述性統(tǒng)計(jì)足夠基礎(chǔ)可視化matplotlib seaborn高度可控適合論文配圖地圖可視化pyecharts國(guó)內(nèi)地圖JSON適配好交互性強(qiáng)交互面板Streamlit 或 Flask ECharts答辯演示效果好開發(fā)成本低整體架構(gòu)上我建議把項(xiàng)目拆成四個(gè)獨(dú)立模塊data_crawler、data_processor、analysis_engine、visualizer。每個(gè)模塊之間只通過標(biāo)準(zhǔn)化后的CSV文件或DataFrame接口通信。這樣做的最大好處是哪怕某一步出了問題時(shí)不用從頭跑一遍哪個(gè)模塊報(bào)錯(cuò)就修哪個(gè)模塊后期想換成別的省份數(shù)據(jù)也只需要修改配置。架構(gòu)上還有一個(gè)容易被忽略的點(diǎn)數(shù)據(jù)文件管理和結(jié)果輸出要規(guī)范化。我建議所有原始數(shù)據(jù)放raw_data/目錄清洗后的數(shù)據(jù)放clean_data/目錄生成的圖表按“圖類型_區(qū)域_時(shí)間粒度”的命名規(guī)則存放。別小看這個(gè)習(xí)慣做分析時(shí)你會(huì)反復(fù)重跑圖表規(guī)范的命名能讓你少走很多彎路。2. 數(shù)據(jù)從哪來AQI原始數(shù)據(jù)的獲取與清洗方案2.1 三條可行的數(shù)據(jù)獲取路徑AQI數(shù)據(jù)獲取方式我前后試過三種各有優(yōu)劣這里直接說結(jié)論。第一種是直接調(diào)用全國(guó)城市空氣質(zhì)量實(shí)時(shí)發(fā)布平臺(tái)的歷史數(shù)據(jù)接口。這個(gè)路徑最正規(guī)數(shù)據(jù)質(zhì)量最高但接口需要申請(qǐng)、可能有調(diào)用頻率限制而且部分歷史數(shù)據(jù)接口現(xiàn)在改成了登錄授權(quán)模式。如果時(shí)間緊張不建議把寶全押在這上面。第二種是從公開的空氣質(zhì)量歷史數(shù)據(jù)網(wǎng)站上抓取。很多環(huán)境數(shù)據(jù)網(wǎng)站提供歷史逐日和逐小時(shí)數(shù)據(jù)頁(yè)面結(jié)構(gòu)相對(duì)規(guī)律。用requests加BeautifulSoup就能解析。缺陷是部分站點(diǎn)有反爬機(jī)制比如限制頻率、需要處理接口簽名參數(shù)。第三種是下載公開數(shù)據(jù)集。網(wǎng)上有網(wǎng)友整理好的歷史空氣質(zhì)量壓縮包通常覆蓋多個(gè)省份多年數(shù)據(jù)字段完整。這種數(shù)據(jù)拿來練手最省事但你要做一次“來源可信度說明”——在畢設(shè)論文里如實(shí)寫清楚數(shù)據(jù)是二手整理、原始來源是全球或國(guó)家層面公開數(shù)據(jù)并交叉驗(yàn)證一部分?jǐn)?shù)值。我的建議是組合方案以官方接口和公開網(wǎng)站為主抓取2019年至今的全省逐日數(shù)據(jù)再用公開數(shù)據(jù)集做交叉驗(yàn)證。一個(gè)合理的數(shù)據(jù)規(guī)模是陜西省全部地級(jí)市最近三到四年的日尺度數(shù)據(jù)外加關(guān)中某一個(gè)重點(diǎn)城市的逐小時(shí)數(shù)據(jù)用于展示短時(shí)污染過程。逐小時(shí)數(shù)據(jù)不是必須的但對(duì)論文的“突發(fā)污染事件分析”章節(jié)非常有幫助。另外別忘了氣象數(shù)據(jù)。做AQI與氣象因素相關(guān)性分析需要溫度、相對(duì)濕度、風(fēng)速、降水量等要素。這部分可以從氣象歷史數(shù)據(jù)接口獲取通常是逐日數(shù)據(jù)字段語(yǔ)義比空氣質(zhì)量數(shù)據(jù)還要規(guī)范。2.2 用pandas做清洗的完整流程拿到原始數(shù)據(jù)后直接進(jìn)分析是新手最常犯的錯(cuò)誤。實(shí)測(cè)中原始數(shù)據(jù)至少存在四類問題時(shí)間格式不統(tǒng)一、日期缺失、字段重復(fù)、存在異常極值。下面這段代碼是清洗流程的核心邏輯import pandas as pd df pd.read_csv(raw_data/shaanxi_aqi_2019_2023.csv, encodingutf-8) # 1. 統(tǒng)一列名保留核心字段 df df.rename(columns{ 城市: city, 日期: date, AQI: aqi, PM2.5: pm25, PM10: pm10, SO2: so2, NO2: no2, CO: co, O3: o3 }) df[date] pd.to_datetime(df[date], format%Y-%m-%d, errorscoerce) # 2. 去重同一城市同一天的記錄只保留一條 df df.drop_duplicates(subset[city, date], keepfirst) # 3. 剔除日期無效和濃度異常的記錄 df df[df[date].notna()] df df[(df[aqi] 0) (df[aqi] 500)] # 4. 按城市分組填充缺失值前向填充 線性插值互補(bǔ) df df.sort_values([city, date]) for col in [pm25, pm10, so2, no2, co, o3, aqi]: df[col] df.groupby(city)[col].transform(lambda s: s.ffill().rolling(7, min_periods1).mean()) df df.dropna() df.to_csv(clean_data/shaanxi_aqi_clean.csv, indexFalse, encodingutf-8-sig)這段代碼里有兩個(gè)設(shè)計(jì)思路值得說明。第一去重必須是“城市日期”組合維度而不是只看日期。因?yàn)樵紨?shù)據(jù)中可能出現(xiàn)同一城市在同一天有兩條記錄比如接口分頁(yè)重復(fù)返回如果只按日期去重會(huì)把一個(gè)城市的數(shù)據(jù)誤刪成一條而另一個(gè)城市的數(shù)據(jù)還在導(dǎo)致后續(xù)分析出現(xiàn)嚴(yán)重偏差。第二缺失值填充我用了“前向填充 7日滾動(dòng)均值”的組合。前向填充的思路很簡(jiǎn)單空氣質(zhì)量的短時(shí)連續(xù)性很強(qiáng)今天缺失就用昨天的數(shù)值頂上。但連續(xù)多天全部缺失時(shí)比如站點(diǎn)維護(hù)了一周滾動(dòng)均值能把長(zhǎng)期缺測(cè)段的噪聲壓平。這個(gè)組合在實(shí)測(cè)中對(duì)AQI序列的重建效果比單純用均值插值更貼近真實(shí)變化。關(guān)于異常值特別提醒一句不要把所有“看起來不合理”的高值都當(dāng)異常刪掉。AQI超過300的重度污染日是真實(shí)存在的刪除這類記錄等于人為抹掉了你論文里最有分析價(jià)值的事件。真正要?jiǎng)h的是物理上不可能的值比如PM2.5濃度為負(fù)數(shù)、AQI為800以上且連續(xù)出現(xiàn)多數(shù)天的機(jī)械重復(fù)值等。2.3 數(shù)據(jù)質(zhì)量驗(yàn)證拿到數(shù)據(jù)先別急著分析清洗完成后做一次系統(tǒng)的質(zhì)量驗(yàn)證能省掉后面無數(shù)排查的時(shí)間。我常用的驗(yàn)證手段是三類檢查。第一類是范圍檢查AQI理論范圍是0到500六項(xiàng)污染物濃度也都有物理上下限。寫一個(gè)describe()輸出各字段的最大最小和中位數(shù)肉眼掃一遍就能發(fā)現(xiàn)明顯異常。PM2.5超過1000、O3小時(shí)值超過400的都要單獨(dú)盯一眼。第二類是完整性檢查統(tǒng)計(jì)每個(gè)城市的日期數(shù)量。正常應(yīng)該等于“采集年份天數(shù)×年份數(shù)”。如果某個(gè)城市的條目數(shù)明顯偏少大概率是抓取時(shí)漏頁(yè)或者站點(diǎn)上線晚??梢杂孟旅孢@段代碼檢查date_range pd.date_range(2019-01-01, 2023-12-31, freqD) for city in df[city].unique(): actual_dates set(df[df[city] city][date]) missing len(set(date_range) - actual_dates) print(f{city}: 缺失 {missing} 天)第三類是交叉驗(yàn)證隨機(jī)抽取10到15天把清洗后的數(shù)據(jù)和國(guó)家公開發(fā)布的數(shù)據(jù)做比對(duì)。如果個(gè)別日期偏差超過2個(gè)AQI指數(shù)單位就得回去檢查是不是時(shí)間對(duì)齊出了問題。我在實(shí)測(cè)中就遇到過日期格式帶時(shí)區(qū)偏移導(dǎo)致所有時(shí)間往后挪了8小時(shí)的問題。數(shù)據(jù)質(zhì)量驗(yàn)證這件事在畢設(shè)答辯時(shí)幾乎一定會(huì)被問到“你怎么保證你分析的這些數(shù)據(jù)是可靠的”能把驗(yàn)證流程講清楚“范圍檢查、完整性檢查、交叉驗(yàn)證”三步一擺這個(gè)問題基本就過關(guān)了。3. AQI計(jì)算原理與Python實(shí)現(xiàn)從污染物濃度到發(fā)布指數(shù)3.1 為什么不能直接用官網(wǎng)AQI理解IAQI分指數(shù)的意義AQI數(shù)據(jù)可以直接從數(shù)據(jù)源拿到但一個(gè)合格的畢設(shè)必須要能自己從污染物濃度算出AQI。這既是論文的一個(gè)重要章節(jié)也是區(qū)分“會(huì)調(diào)用數(shù)據(jù)”和“懂指標(biāo)體系”的關(guān)鍵點(diǎn)。AQI全稱是空氣質(zhì)量指數(shù)它是一個(gè)無量綱數(shù)值。計(jì)算它要先對(duì)六項(xiàng)污染物分別計(jì)算分指數(shù)IAQI再取最大值。六項(xiàng)污染物是PM2.5、PM10、SO2、NO2、CO、O3。其中CO的單位是毫克每立方米其他五項(xiàng)都是微克每立方米計(jì)算時(shí)一定要先統(tǒng)一單位。單個(gè)污染物的分指數(shù)公式是IAQI (IAQI_hi - IAQI_lo) / (BP_hi - BP_lo) × (C - BP_lo) IAQI_lo其中C是污染物濃度實(shí)測(cè)值BP_lo和BP_hi是對(duì)應(yīng)濃度區(qū)間斷點(diǎn)IAQI_lo和IAQI_hi是對(duì)應(yīng)的指數(shù)區(qū)間。這個(gè)公式的本質(zhì)是線性插值把污染物濃度映射到0到500的指數(shù)空間中。AQI的最終值就是六項(xiàng)IAQI中的最大值而“首要污染物”則是那個(gè)讓IAQI達(dá)到最高的污染物。如果所有分指數(shù)都小于50首要污染物這一欄通常填“—”。為什么國(guó)家不直接給出AQI原始濃度而要用這樣一個(gè)指數(shù)直觀理解是不同污染物的濃度量綱差別很大SO2濃度10微克每立方米和PM10濃度10微克每立方米對(duì)健康的影響完全不同。AQI把這些不同量綱的濃度統(tǒng)一映射到同一把“尺子”上公眾只看一個(gè)數(shù)就能判斷空氣品質(zhì)。理解了這個(gè)背景你就明白為什么IAQI的斷點(diǎn)表設(shè)計(jì)得如此“千奇百怪”——它是依據(jù)健康風(fēng)險(xiǎn)研究和流行病學(xué)證據(jù)制定的不是單純按濃度均勻切分。3.2 六項(xiàng)污染物的濃度斷點(diǎn)表國(guó)標(biāo)《環(huán)境空氣質(zhì)量指數(shù)AQI技術(shù)規(guī)定》給出了各項(xiàng)污染物的分指數(shù)與濃度限值。這里列出核心斷點(diǎn)完整版以最新標(biāo)準(zhǔn)為準(zhǔn)IAQIPM2.5 24h均值PM10 24h均值SO2 24h均值NO2 24h均值CO 24h均值O3 8h滑動(dòng)值000000050355050402100100751501508041601501152504751801421520015035080028024265300250420160056536800400350500210075048—500500600262094060—這張表里藏著幾個(gè)常見掉分點(diǎn)。第一個(gè)是O3用的是“日最大8小時(shí)滑動(dòng)平均”不是24小時(shí)平均值也不是某整點(diǎn)的小時(shí)值。第二個(gè)是CO和SO2在IAQI超過150后斷點(diǎn)變化非常劇烈這意味著它們的分指數(shù)在高濃度下增長(zhǎng)很快往往成為污染日的首要污染物。第三個(gè)是PM10的500分指數(shù)濃度只有600微克每立方米而PM2.5要500這種差異直接反映了不同顆粒物在健康風(fēng)險(xiǎn)權(quán)重上的區(qū)別。3.3 向量化實(shí)現(xiàn)用pandas而不是逐行for循環(huán)很多人拿到這個(gè)公式后的第一反應(yīng)是寫一個(gè)for循環(huán)逐行計(jì)算。數(shù)據(jù)量小倒無所謂但更專業(yè)、也更能體現(xiàn)工程素養(yǎng)的做法是利用pandas的向量化操作。代碼邏輯是每種污染物單獨(dú)做一個(gè)分度區(qū)間查找函數(shù)然后在DataFrame上逐列計(jì)算IAQI最后取最大值。import numpy as np import pandas as pd def calc_iaqi(series, bp_lo, bp_hi, iaqi_lo, iaqi_hi): iaqi pd.Series(indexseries.index, dtypefloat) for i in range(len(bp_lo)): mask (series bp_lo[i]) (series bp_hi[i]) iaqi[mask] ( (iaqi_hi[i] - iaqi_lo[i]) / (bp_hi[i] - bp_lo[i]) * (series[mask] - bp_lo[i]) iaqi_lo[i] ) return iaqi # 以PM2.5為例 pm25_bp_lo [0, 35, 75, 115, 150, 250, 350, 500] pm25_bp_hi [35, 75, 115, 150, 250, 350, 500, 700] pm25_iaqi [0, 50, 100, 150, 200, 300, 400, 500] df[iaqi_pm25] calc_iaqi(df[pm25], pm25_bp_lo, pm25_bp_hi, pm25_iaqi, pm25_iaqi) # 其他五項(xiàng)同理最后合成 df[iaqi_pm10] calc_iaqi(df[pm10], pm10_bp_lo, pm10_bp_hi, pm10_iaqi, pm10_iaqi) # ... 省略其余四項(xiàng) iaqi_cols [iaqi_pm25, iaqi_pm10, iaqi_so2, iaqi_no2, iaqi_co, iaqi_o3] df[aqi_calc] df[iaqi_cols].max(axis1).round()這里有個(gè)細(xì)節(jié)我為什么用了round()國(guó)標(biāo)規(guī)定AQI保留整數(shù)而計(jì)算過程中的IAQI可以保留小數(shù)。不同數(shù)據(jù)源在最后一步的舍入策略有細(xì)微差異——有的是四舍五入有的直接用astype(int)向下取整。實(shí)測(cè)中這兩種策略的差最多就1個(gè)指數(shù)單位但如果拿自算值跟官方發(fā)布值比你會(huì)發(fā)現(xiàn)那一兩個(gè)單位的偏差往往就來自這里。所以校驗(yàn)時(shí)只要偏差在±2以內(nèi)基本可以認(rèn)定計(jì)算邏輯正確。另外一個(gè)常見錯(cuò)誤是漏掉對(duì)O3的特殊處理。前面說了O3要用8小時(shí)滑動(dòng)平均的最大值你在清洗階段不能直接拿日平均O3濃度去算必須單獨(dú)準(zhǔn)備一列“O3_8h_max”數(shù)據(jù)。如果數(shù)據(jù)源里只有小時(shí)值需要先按小時(shí)序列做8小時(shí)窗口滑動(dòng)平均再按天取最大。這一步是計(jì)算章節(jié)里最有“技術(shù)含量”的部分之一寫進(jìn)論文里非常加分。3.4 用官方發(fā)布值做雙重校驗(yàn)自己算完AQI后一定要和官方發(fā)布值做一次系統(tǒng)性對(duì)比。方法很簡(jiǎn)單把官方AQI字段和自己算出來的aqi_calc做差值統(tǒng)計(jì)df[diff] df[aqi] - df[aqi_calc] print(df[diff].describe()) print((df[diff].abs() 2).mean())如果兩列的一致性在95%以上你的計(jì)算邏輯基本沒問題。剩下的那5%差異可能來自O(shè)3的8小時(shí)滑動(dòng)窗口邊界對(duì)齊方式不同也可能來自原始數(shù)據(jù)中小數(shù)位數(shù)截?cái)?。把這部分差異樣本列出來逐個(gè)觀察每個(gè)案例都能寫進(jìn)論文的“數(shù)據(jù)驗(yàn)證”小節(jié)。我還做過一個(gè)更有說服力的校驗(yàn)按月份統(tǒng)計(jì)官方AQI和自算AQI的均值差。如果整體差異在±1以內(nèi)說明計(jì)算邏輯沒有系統(tǒng)性偏差。這一步做好了答辯時(shí)老師問“你算的AQI靠譜嗎”你就可以直接給出這個(gè)統(tǒng)計(jì)結(jié)果。4. 分析維度的設(shè)計(jì)時(shí)間趨勢(shì)、空間分布與氣象關(guān)聯(lián)4.1 時(shí)間維度月度、季節(jié)、年度三個(gè)尺度同時(shí)看AQI分析的核心輸出之一是“污染在時(shí)間上的規(guī)律”。我在項(xiàng)目里分了三個(gè)尺度來看。月度尺度是最常用的。按“城市-月份”分組求平均AQI再看多年同期的月度均值。陜西的整體規(guī)律通常是冬季12月、1月明顯偏高夏季6到8月偏低春季會(huì)有一個(gè)或大或小的波動(dòng)。冬季偏高的原因很直接——供暖季排放增加加上靜穩(wěn)天氣多、逆溫層容易出現(xiàn)污染物擴(kuò)散條件差。春季的波動(dòng)則多半來自沙塵過程這時(shí)候首要污染物往往從PM2.5切換成PM10。年度尺度用來判斷整體趨勢(shì)。把每年的年均AQI畫成柱狀圖通??梢钥吹浇鼛啄暾w在緩慢改善。但要注意的是單看年均值會(huì)掩蓋季節(jié)性差異所以年度部分最好配一張“月均AQI熱力圖”橫軸是月份、縱軸是年份、顏色代表AQI值。這張圖能一眼看出“改善主要發(fā)生在哪個(gè)季節(jié)”比單講一個(gè)年均值的信息量大得多。月度分析的代碼核心是分組聚合monthly df.groupby([df[date].dt.year, df[date].dt.month])[aqi].mean().unstack()如果你想做全省整體情況就把所有城市的AQI先按天求全省均值再按月聚合。如果做城市個(gè)體分析按“城市月份”分組即可。兩張圖結(jié)合展示“全省整體月變化”和“典型城市的月變化對(duì)比”。4.2 空間維度城市間的橫向?qū)Ρ瓤臻g分析的目的是回答“哪些地方空氣差、差在哪”。最直接的操作是把每個(gè)城市的年均AQI排序畫條形圖。陜西的典型格局是關(guān)中城市群整體排在前面陜北居中陜南城市排在最后。這個(gè)結(jié)果和地理直觀感受高度一致但論文里不能只擺結(jié)論得把原因拆出來。拆解原因的一個(gè)好方法是計(jì)算PM2.5/PM10比值。這個(gè)比值很能說明問題比值接近0.6到0.7說明污染以細(xì)顆粒物為主大概率來自燃燒排放工業(yè)、機(jī)動(dòng)車、供暖比值偏低比如0.3到0.4說明粗顆粒物占主導(dǎo)多半受到沙塵、揚(yáng)塵影響。把每個(gè)城市的年均比值列出來你會(huì)發(fā)現(xiàn)一個(gè)有意思的現(xiàn)象陜南部分城市的AQI雖然低但PM2.5/PM10比值反而偏高說明細(xì)顆粒物占比不小而陜北某些城市在沙塵季會(huì)有明顯的低比值波動(dòng)。再來一層就是“污染天頻次”的對(duì)比。定義AQI大于100的天數(shù)為超標(biāo)天統(tǒng)計(jì)每個(gè)城市每年的超標(biāo)天數(shù)。這個(gè)指標(biāo)比年均值更直觀也更有民生意義。實(shí)測(cè)中關(guān)中城市超標(biāo)天通常在60到90天區(qū)間浮動(dòng)陜南基本能控制在30天以下這個(gè)差異寫進(jìn)分析章節(jié)非常有說服力??臻g對(duì)比還可以和地圖結(jié)合做“年均AQI城市分布圖”。地圖部分我會(huì)在第5節(jié)專門講pyecharts的正確打開方式這里先提一個(gè)關(guān)鍵點(diǎn)城市名稱必須在數(shù)據(jù)里和地圖注冊(cè)名完全一致否則地圖上會(huì)顯示空白。常見的坑是數(shù)據(jù)里寫“西安”地圖注冊(cè)名是“西安市”或者縣級(jí)市名稱不在地圖JSON里。4.3 與氣象因素的相關(guān)分析別把相關(guān)當(dāng)因果AQI與氣象要素的相關(guān)分析是論文里很出彩、但也容易被挑毛病的一部分。我用的方法是先做Pearson相關(guān)矩陣再按季節(jié)拆開來看。實(shí)際操作是把兩部分?jǐn)?shù)據(jù)按城市和日期對(duì)齊合并AQI、PM2.5、PM10作為空氣質(zhì)量變量溫度、相對(duì)濕度、風(fēng)速、降水量作為氣象變量。合并后直接調(diào)用pandas的corr()方法merged df.merge(weather, on[city, date], howinner) corr merged[[aqi, pm25, pm10, temp, humidity, wind, precip]].corr()典型結(jié)論一般是AQI與風(fēng)速呈負(fù)相關(guān)風(fēng)速越大污染物擴(kuò)散越快AQI與降水量呈負(fù)相關(guān)雨雪天氣對(duì)顆粒物有清除作用AQI與相對(duì)濕度的關(guān)系比較復(fù)雜有時(shí)正相關(guān)高濕環(huán)境下顆粒物吸濕增長(zhǎng)、能見度下降有時(shí)因?yàn)榻邓殡S高濕而表現(xiàn)為負(fù)相關(guān)。這里必須提醒一句相關(guān)性不是因果關(guān)系。高濕度既可能促進(jìn)顆粒物二次生成也可能只是靜穩(wěn)天氣和逆溫共同作用下的伴生現(xiàn)象。如果你想在論文里做更深一層建議把數(shù)據(jù)按季節(jié)拆開再分別算相關(guān)——冬季的濕度效應(yīng)和夏季的濕度效應(yīng)很可能正負(fù)相反混在一起算會(huì)把真實(shí)關(guān)系掩蓋掉。氣象分析還有一個(gè)小技巧做“靜穩(wěn)天氣”的簡(jiǎn)單識(shí)別。定義風(fēng)速低于某個(gè)閾值比如日平均風(fēng)速小于1.5米每秒且連續(xù)持續(xù)兩天以上作為一次靜穩(wěn)過程然后對(duì)比靜穩(wěn)過程期間和非靜穩(wěn)期間的平均AQI差。這個(gè)操作不復(fù)雜但結(jié)論表述非常直觀答辯現(xiàn)場(chǎng)也很好講。4.4 污染事件識(shí)別從“描述”走向“結(jié)論”最后一個(gè)分析維度是識(shí)別重污染事件。這個(gè)維度很多二手教程沒有講但它最能體現(xiàn)工作量。我的做法是先篩選AQI大于200的重污染日按城市統(tǒng)計(jì)全年出現(xiàn)次數(shù)和月份分布然后把“重污染事件”定義成“連續(xù)至少兩天AQI大于150”的過程統(tǒng)計(jì)每次事件的起始日期、持續(xù)天數(shù)、平均AQI和過程中的首要污染物變化。以關(guān)中地區(qū)為例典型的冬季重污染事件往往持續(xù)3到5天污染物以PM2.5為主過程通常從AQI緩慢爬升開始、持續(xù)累積、在強(qiáng)冷空氣過境后迅速回落。把這個(gè)過程逐日畫出來配合氣象要素的逐日變化就是一個(gè)完整的“個(gè)案分析”章節(jié)。這種案例分析在論文里占一頁(yè)但含金量遠(yuǎn)比幾個(gè)統(tǒng)計(jì)圖表高。選擇哪些城市做案例也有講究。選一個(gè)污染最重的城市能突出問題的嚴(yán)重性選一個(gè)污染改善幅度最大的城市能展示治理成效兩者搭配能讓分析章節(jié)更有層次。5. 可視化呈現(xiàn)地圖、時(shí)序圖與交互面板的實(shí)現(xiàn)思路5.1 基礎(chǔ)圖表matplotlib環(huán)境配置與三張必備圖可視化的第一步其實(shí)是環(huán)境配置因?yàn)橹形膩y碼問題幾乎每個(gè)人都會(huì)遇到。matplotlib默認(rèn)字體不包含中文字符直接畫圖全是方塊。正確的處理方式有兩種一是全局設(shè)置字體二是臨時(shí)指定。實(shí)測(cè)最穩(wěn)的做法是先檢查系統(tǒng)里有沒有中文字體再注冊(cè)進(jìn)matplotlibimport matplotlib.pyplot as plt from matplotlib import font_manager # 以黑體為例字體路徑按實(shí)際系統(tǒng)調(diào)整 font_manager.fontManager.addfont(C:/Windows/Fonts/simhei.ttf) plt.rcParams[font.family] SimHei plt.rcParams[axes.unicode_minus] Falseaxes.unicode_minus這一行很容易漏掉。不設(shè)的話坐標(biāo)軸上的負(fù)號(hào)會(huì)顯示成方塊整個(gè)圖的檔次瞬間下降?;A(chǔ)圖里我推薦至少做三張全省月均AQI折線圖、各城市年均AQI排序條形圖、逐日AQI熱力圖。前兩張用matplotlib就能搞定第三張是整篇論文里最出彩的一張圖——橫軸是月份、縱軸是年份、方格顏色代表AQI值一眼就能看出冬季污染帶。實(shí)現(xiàn)方法是用pivot構(gòu)造矩陣再交給imshow或seaborn.heatmap。畫圖時(shí)有個(gè)容易被忽視的細(xì)節(jié)圖例、坐標(biāo)軸標(biāo)簽、數(shù)據(jù)標(biāo)注一個(gè)都不能少。論文里的圖評(píng)閱老師不會(huì)像讀者那樣仔細(xì)看他們掃一眼圖的標(biāo)題、坐標(biāo)軸、圖例就能判斷這張圖是否規(guī)范。坐標(biāo)軸標(biāo)簽建議寫全稱比如“月均AQI指數(shù)”而不是“AQI”圖例要標(biāo)明單位比如“空氣質(zhì)量指數(shù)AQI”。5.2 地圖可視化pyecharts的正確打開方式城市間空間分布用地圖展示最直觀。pyecharts是主流選擇但我必須把幾個(gè)坑先擺出來。第一個(gè)坑是版本。pyecharts在早期版本和當(dāng)前版本之間的API變化非常大網(wǎng)上搜到的大多是舊版寫法。新版本創(chuàng)建地圖的推薦方式是from pyecharts.charts import Map from pyecharts import options as opts city_data [(西安市, 98), (寶雞市, 82), (咸陽(yáng)市, 105)] map_chart ( Map(init_optsopts.InitOpts(width800px, height600px)) .add(年均AQI, city_data, maptype陜西) .set_global_opts( title_optsopts.TitleOpts(title陜西省各城市年均AQI分布), visualmap_optsopts.VisualMapOpts(min_0, max_200, range_color[#00E400, #FF0000]) ) ) map_chart.render(output/map_aqi.html)第二個(gè)坑是地圖JSON文件的加載。pyecharts的在線地圖依賴網(wǎng)絡(luò)資源畢設(shè)現(xiàn)場(chǎng)萬(wàn)一沒有外網(wǎng)地圖可能渲染不出來。穩(wěn)妥做法是把需要的JS文件下載到本地在InitOpts里通過js_host參數(shù)指向本地路徑。第三個(gè)坑是地市名稱必須匹配地圖注冊(cè)名。你用“西安”作為數(shù)據(jù)key時(shí)可能圖上一片空白改成“西安市”就好了。解決方法是先查看地圖對(duì)象的注冊(cè)名列表再寫一個(gè)映射函數(shù)統(tǒng)一數(shù)據(jù)里的城市名。這個(gè)坑我在第6節(jié)還會(huì)詳細(xì)展開。地圖配色也要符合常識(shí)。AQI的顏色體系是由國(guó)家技術(shù)規(guī)范規(guī)定的綠色代表優(yōu)0到50、黃色代表良51到100、橙色代表輕度污染101到150、紅色代表中度污染151到200、紫色代表重度污染201到300、褐紅色代表嚴(yán)重污染大于300。做地圖可視化時(shí)直接用這套標(biāo)準(zhǔn)色會(huì)在視覺上一眼看出區(qū)域差異也讓圖表的專業(yè)性更強(qiáng)。VisualMap的區(qū)間顏色按這個(gè)順序設(shè)置即可。5.3 進(jìn)階把靜態(tài)圖升級(jí)成在線交互面板如果做到這一步時(shí)間還夠強(qiáng)烈建議加一個(gè)交互面板。理由很簡(jiǎn)單答辯演示的時(shí)候一張會(huì)動(dòng)的、能切換城市的交互頁(yè)面效果遠(yuǎn)好于十張靜態(tài)圖。實(shí)現(xiàn)方案首選Streamlit。它比Flask輕量太多不需要自己寫HTML模板和路由邏輯幾行代碼就能把圖表組織到頁(yè)面上import streamlit as st import pandas as pd st.title(陜西省空氣質(zhì)量AQI分析) city st.selectbox(選擇城市, df[city].unique()) metric st.selectbox(選擇指標(biāo), [AQI, PM2.5, PM10, O3]) filtered df[df[city] city] st.line_chart(filtered.set_index(date)[metric])Streamlit的line_chart和bar_chart雖然簡(jiǎn)單但樣式偏基礎(chǔ)。如果追求更好的視覺效果可以用st.components.v1.html嵌入pyecharts生成的HTML文件這樣地圖和交互圖表都能整合進(jìn)來。交互面板的設(shè)計(jì)有個(gè)原則不要把所有支持的功能都堆在頁(yè)面上。畢設(shè)面板通常做三個(gè)區(qū)域就夠了——“總體概覽”放全省月均趨勢(shì)和城市排名“城市對(duì)比”放兩三個(gè)城市的并行折線“事件分析”放重污染個(gè)案的逐日序列。功能太多反而暴露出邏輯松散的問題。5.4 配色與排版規(guī)范讓論文配圖達(dá)到發(fā)表級(jí)最后再說說美觀度。很多同學(xué)畫完圖不調(diào)樣式就直接貼進(jìn)論文其實(shí)只要做三件事圖就能立刻提升一個(gè)檔次。第一件事是統(tǒng)一畫布尺寸。論文里常用的圖寬是14厘米左右折線圖長(zhǎng)寬比大概3比2熱力圖可以方一點(diǎn)。設(shè)置figsize(10, 6)或者論文模板要求的尺寸比默認(rèn)大小要好看很多。第二件事是配色不踩雷。除了AQI標(biāo)準(zhǔn)色之外普通統(tǒng)計(jì)圖建議選一套低飽和度的漸變色不要用matplotlib默認(rèn)的藍(lán)色和橙色混搭。seaborn自帶的配色方案deep或muted就很好用。第三件事是去除不必要的信息干擾。默認(rèn)的坐標(biāo)軸邊框、刻度線、多余的網(wǎng)格全部可以關(guān)掉。只保留必要的坐標(biāo)軸標(biāo)簽和水印圖會(huì)顯得干凈得多。細(xì)節(jié)決定論文的觀感這一部分花半小時(shí)調(diào)整很值。6. 踩坑記錄與答辯經(jīng)驗(yàn)讓畢設(shè)更具競(jìng)爭(zhēng)力的關(guān)鍵細(xì)節(jié)6.1 六個(gè)真實(shí)踩過的坑與排查過程這部分是我最想分享的內(nèi)容。我實(shí)際排查過的坑至少有六個(gè)每一個(gè)都浪費(fèi)過半天以上的時(shí)間。第一個(gè)坑是pyecharts版本導(dǎo)致的API寫法錯(cuò)誤。我先后試過在線文檔里的新寫法和老教程里的舊寫法報(bào)錯(cuò)信息完全不一樣。排查過程是先定位到報(bào)錯(cuò)的類名去查當(dāng)前安裝版本的源碼發(fā)現(xiàn)是新版把Geo的部分方法改成了add_coordinate和add_geo兩種模式。解決方法是固定版本或者嚴(yán)格按當(dāng)前版本的官方示例改寫。第二個(gè)坑是地圖上的城市名不顯示。排查過程很典型圖表成功渲染了但陜西省地圖上只有輪廓沒有數(shù)據(jù)塊。我先檢查了數(shù)據(jù)key是不是“西安市”而不是“西安”改成全稱后地圖正常。如果你也遇到地圖空白第一反應(yīng)就是去查地名詞典。第三個(gè)坑是matplotlib中文亂碼。前面講過解決方案這里補(bǔ)充一個(gè)排查技巧先執(zhí)行matplotlib.font_manager查看當(dāng)前可用字體如果沒有中文字體需要手動(dòng)下載一個(gè)SimHei.ttf或者用系統(tǒng)自帶的其他中文字體文件注冊(cè)。第四個(gè)坑是數(shù)據(jù)接口的編碼問題。部分第三方站點(diǎn)返回的頁(yè)面編碼是GBK直接用utf-8解析會(huì)導(dǎo)致亂碼甚至報(bào)錯(cuò)。排查方法是先用requests拿到原始字節(jié)流用chardet檢測(cè)編碼再?zèng)Q定解碼方式。這個(gè)坑對(duì)于爬蟲類的畢設(shè)非常普遍。第五個(gè)坑是官方AQI與自算AQI的差值。我一開始計(jì)算差異時(shí)發(fā)現(xiàn)很多樣本差了5到8個(gè)指數(shù)值排查了很久最后定位到O3用的是8小時(shí)滑動(dòng)平均而我用的數(shù)據(jù)源里只有O3的24小時(shí)均值。換成O3_8h字段后差值立刻收斂到±1以內(nèi)。這個(gè)細(xì)節(jié)特別適合寫進(jìn)論文的“數(shù)據(jù)說明”部分。第六個(gè)坑是合并數(shù)據(jù)集時(shí)的NaN膨脹。空氣質(zhì)量數(shù)據(jù)和氣象數(shù)據(jù)在日期覆蓋范圍上不一致用merge(howleft)會(huì)導(dǎo)致大量NaN進(jìn)而讓相關(guān)系數(shù)的計(jì)算樣本量驟減。解決辦法是把howinner作為主要合并方式只保留兩邊都有數(shù)據(jù)的日期。這個(gè)操作的邏輯是寧可樣本量稍微小一點(diǎn)也不要引入不完整的偽記錄。6.2 答辯追問清單提前準(zhǔn)備好這六類問題根據(jù)我參加答辯的經(jīng)驗(yàn)評(píng)委對(duì)AQI項(xiàng)目的提問高度集中在六個(gè)方向提前準(zhǔn)備起來并不難。第一類是數(shù)據(jù)來源問題“你的數(shù)據(jù)從哪里來的怎么保證可信”回答框架是如實(shí)說明數(shù)據(jù)源 交叉驗(yàn)證結(jié)果 清洗流程。如果你用的是公開數(shù)據(jù)集誠(chéng)實(shí)說明二手整理和原始出處評(píng)委不會(huì)為難你。第二類是計(jì)算邏輯問題“AQI為什么是取最大分指數(shù)而不是平均”這是最核心的原理問題。你要能解釋清楚AQI反映的是“最差一項(xiàng)”暴露的健康風(fēng)險(xiǎn)而不是整體污染平均水平。同時(shí)要能說出IAQI分指數(shù)的含義和斷點(diǎn)表的制定依據(jù)。第三類是結(jié)果爭(zhēng)議問題“你分析的結(jié)論靠譜嗎和官方發(fā)布信息一致嗎”用你自己的校驗(yàn)數(shù)據(jù)說話比如自算AQI和官方AQI的一致性在95%以上。承認(rèn)局限也不丟人——“本研究基于公開數(shù)據(jù)未納入臨時(shí)管控措施等未公開信息”這種表述反而顯嚴(yán)謹(jǐn)。第四類是選型問題“為什么用pyecharts而不用ECharts原生或Leaflet”回答方向是開發(fā)效率與項(xiàng)目復(fù)雜度匹配pyecharts封裝了地理JSON加載、地圖配色、Tooltip等細(xì)節(jié)能把精力留給數(shù)據(jù)處理和分析本身。第五類是擴(kuò)展問題“如果給你更多時(shí)間你怎么改進(jìn)”建議準(zhǔn)備兩到三個(gè)方向引入逐小時(shí)數(shù)據(jù)做污染過程分析、加入機(jī)器學(xué)習(xí)模型做AQI預(yù)測(cè)、接入更多省份數(shù)據(jù)做區(qū)域?qū)Ρ?。第六類是工作量問題“這個(gè)項(xiàng)目里哪些是你自己寫的哪些是調(diào)包”誠(chéng)實(shí)回答并強(qiáng)調(diào)“調(diào)用庫(kù)不等于沒有工作”——數(shù)據(jù)清洗邏輯、污染事件識(shí)別規(guī)則、指標(biāo)體系計(jì)算、業(yè)務(wù)分析都是自己設(shè)計(jì)的工程量。6.3 個(gè)人體會(huì)做完這個(gè)項(xiàng)目我學(xué)到的幾件事最后聊一點(diǎn)我個(gè)人體會(huì)。這個(gè)項(xiàng)目做完之后我最大的感受是一個(gè)看似“簡(jiǎn)單”的數(shù)據(jù)分析畢設(shè)做好和做完之間差得非常遠(yuǎn)?!白鐾辍笔前褦?shù)據(jù)爬下來、畫幾張圖、寫一篇論文“做好”是數(shù)據(jù)經(jīng)過嚴(yán)格的清洗和交叉驗(yàn)證、分析有清晰的邏輯鏈、每張圖都在回答一個(gè)明確的問題。后者需要你在每個(gè)環(huán)節(jié)都多問一句“為什么”——為什么這樣清洗、為什么這樣分組、為什么這樣配色而這些“為什么”恰恰是答辯老師最愛問的地方。還有一個(gè)很實(shí)際的經(jīng)驗(yàn)數(shù)據(jù)備份要趁早。我中途重裝過一次環(huán)境差點(diǎn)丟掉已經(jīng)清洗完的中間數(shù)據(jù)。從那以后我養(yǎng)成習(xí)慣每天的原始數(shù)據(jù)、清洗數(shù)據(jù)、圖表輸出都按日期歸檔重要內(nèi)容上傳到網(wǎng)盤。這件事看似和“技術(shù)含量”無關(guān)但關(guān)鍵時(shí)刻救過我一命。再分享一個(gè)小技巧把清洗好的數(shù)據(jù)、計(jì)算腳本、分析圖表按“章節(jié)名”整理成目錄論文寫到哪一節(jié)就對(duì)應(yīng)到哪個(gè)目錄。整個(gè)項(xiàng)目做完之后你會(huì)發(fā)現(xiàn)素材已經(jīng)自動(dòng)變成了一篇結(jié)構(gòu)完整的論文初稿。這種“代碼與論文同步推進(jìn)”的方式比最后幾周熬夜趕工要舒服得多也更不容易出錯(cuò)。如果你也想在這個(gè)題目上加點(diǎn)差異化的東西我建議考慮兩條路一條是往“預(yù)測(cè)”方向走用歷史AQI和氣象數(shù)據(jù)做一個(gè)時(shí)間序列預(yù)測(cè)模型哪怕只是簡(jiǎn)單的線性回歸或隨機(jī)森林也會(huì)讓項(xiàng)目的技術(shù)棧多出一層另一條是往“交互”方向走把可視化面板做成一個(gè)完整的應(yīng)用加了預(yù)測(cè)的話面板里的“明天空氣質(zhì)量會(huì)怎樣”就是一個(gè)很自然的展示功能。這個(gè)方向接得住、也出得了成果。