站數(shù)據(jù)分析與可視化:從數(shù)據(jù)清洗到答辯高分指南)
簡介一份基于Python的旅游網(wǎng)站數(shù)據(jù)分析及可視化完整項目以2020年五一旅游數(shù)據(jù)為案例面向需要完成數(shù)據(jù)采集、清洗、分析與可視化大作業(yè)的高校學生。項目源碼評審分超過95分經(jīng)過嚴格調試可運行適合數(shù)據(jù)分析初學者參考。壓縮包共74個文件、大小13.62MB包含3個Python腳本、3個ipynb分析筆記、36個html可視化頁面、1個csv旅游數(shù)據(jù)集并附docx說明文檔及js/css靜態(tài)資源覆蓋數(shù)據(jù)處理、圖表生成到頁面展示的完整流程已有616人學習下載。通過該項目可掌握從數(shù)據(jù)清洗到可視化展示的完整分析流程項目目錄按數(shù)據(jù)、腳本、模板、靜態(tài)資源分層便于逐模塊學習與二次開發(fā)內置詞云圖、水球圖、漏斗圖、地圖等多種可視化圖表其中景點評價率水球圖、熱門景點詞云圖、門票銷售漏斗圖等案例均可遷移至其他城市或領域快速搭建同類旅游數(shù)據(jù)分析系統(tǒng)無論用于課程設計、期末答辯還是個人作品集都能獲得直接可用的源碼與參考。1. 期末大作業(yè)選題旅游網(wǎng)站數(shù)據(jù)分析與可視化到底在考什么“基于Python的旅游網(wǎng)站數(shù)據(jù)分析及可視化”這類期末大作業(yè)聽起來像是個標準模板——數(shù)據(jù)好找、工具成熟、可視化庫一抓一大把可實際交上去能拿95分的很少。原因在于這門課考核的不是你會不會調plot而是你有沒有把一個數(shù)據(jù)分析項目完整走通從數(shù)據(jù)獲取、清洗、分析到圖表呈現(xiàn)最后落成一份能說服人的業(yè)務結論。我見過太多人卡在數(shù)據(jù)階段就花了三天或者畫出一堆圖卻完全答不上“為什么用折線不用柱狀”。這門題適合剛學完 Python 基礎、想找一份數(shù)據(jù)分析項目實踐練手、并且期末想沖高分的人。接下來我把完整套路拆給你照著做至少能避開八成翻車點。另外拿到的.zip先別急著解壓跑代碼看一遍 README 和目錄結構能省不少時間。2. 數(shù)據(jù)準備階段從抓包到清洗三條路徑與一套 Pandas 模板2.1 先看包里有什么README、數(shù)據(jù)集與代碼的目錄結構拿到一個.zip第一件事不是雙擊解壓然后試圖直接運行而是先看壓縮包里的文件清單。期末大作業(yè)的壓縮包通常會有這幾類東西README 說明、數(shù)據(jù)文件CSV 或 JSON、腳本或 Notebook、報告目錄。README 里一般會寫清楚數(shù)據(jù)來源、字段含義、Python 版本要求如果作者沒寫你就得自己做“考古”——用 Excel 打開數(shù)據(jù)文件先看列名再找有沒有requirements.txt。這一步的意義在于確定后續(xù)所有分析都圍繞同一套字段展開避免代碼寫到一半發(fā)現(xiàn)列名對不上。我一般會在項目根目錄建一個數(shù)據(jù)說明.txt把字段名、類型、時間格式、哪些列有空值記下來后面寫清洗和分析代碼時直接對照能減少接近三分之一的無意義返工。如果你發(fā)現(xiàn)壓縮包里只有代碼沒有數(shù)據(jù)別慌看下一節(jié)數(shù)據(jù)來源不只一條路。2.2 數(shù)據(jù)獲取的三條路徑公開接口、開源數(shù)據(jù)集與“結構化模擬”如果 ZIP 里沒給現(xiàn)成數(shù)據(jù)你需要自己解決數(shù)據(jù)來源。期末大作業(yè)場景下三條路我都走過經(jīng)驗如下第一是抓包找公開接口。主流旅游網(wǎng)站的景點頁面、點評列表前端幾乎都是異步加載 JSON打開瀏覽器開發(fā)者工具切到 Network 面板刷新頁面就能看到一批 XHR 請求里面往往直接返回結構化的 JSON 數(shù)據(jù)。用requests拉下來再用pandas解析就行。但爬蟲要考慮請求頻率、登錄態(tài)、反爬策略時間成本高適合數(shù)據(jù)量小、結構清晰的場景。第二是去 GitHub 找公開旅游數(shù)據(jù)集。很多開源項目整理了景點評分、游客量、門票價格這類結構化數(shù)據(jù)字段質量和真實業(yè)務幾乎一致優(yōu)勢是省時間缺點是需要花一點時間篩選字段口徑。第三是老師只給了題目、沒給數(shù)據(jù)的情況。常見做法是按真實業(yè)務字段結構生成一份仿真樣本數(shù)據(jù)例如城市、景點、評分、評論數(shù)、門票價格、月份、游客量。答辯時明確說明這是按公開統(tǒng)計口徑構造的演示數(shù)據(jù)用于驗證分析流程合理性。這不算造假只要誠實標注老師通常能接受。# 路徑一請求公開 JSON 接口注意遵守目標站點協(xié)議僅限學習使用 import requests import pandas as pd # headers {User-Agent: Mozilla/5.0} # 基本瀏覽器標識 # resp requests.get(https://api.example.com/scenic/list, headersheaders, timeout10) # data resp.json() # df pd.json_normalize(data[data][list]) # 更穩(wěn)的路徑二直接讀本地 CSV df pd.read_csv(tourism_data.csv, encodingutf-8) print(df.head()) print(df.dtypes) # 檢查字段類型是清洗的第一步這段代碼里requests.get的timeout10很關鍵沒有超時控制遇到慢接口會一直掛住pd.json_normalize能把嵌套 JSON 展平成表格比手動循環(huán)json.loads省事得多。實際大作業(yè)里我建議優(yōu)先走 CSV 或 Excel 數(shù)據(jù)因為評分和游客量字段可以直接用 Pandas 處理不用處理嵌套結構。2.3 數(shù)據(jù)清洗去重、補缺失、時間格式化與異常值過濾無論數(shù)據(jù)從哪來清洗都是最耗時、也最能體現(xiàn)基本功的環(huán)節(jié)。四個動作是必須做的按業(yè)務唯一鍵去重、用中位數(shù)或眾數(shù)填充缺失值、把時間字段統(tǒng)一成datetime類型、過濾明顯不合理的異常值。很多人跳過去重直接畫圖結果趨勢線出現(xiàn)雙倍峰值答辯時被問住非常尷尬。clean df.copy() # 1. 去重三個字段同時相同才算重復行 clean.drop_duplicates(subset[city, attraction, month], inplaceTrue) # 2. 缺失值評分用中位數(shù)填充價格缺省按 0 處理 clean[rating] clean[rating].fillna(clean[rating].median()) clean[price] clean[price].fillna(0) # 3. 時間統(tǒng)一字符串 2024-01 轉成月度時間對象 clean[month] pd.to_datetime(clean[month], format%Y-%m) # 4. 異常值過濾游客量為負或單月超千萬的刪掉 clean clean[(clean[visitors] 0) (clean[visitors] 1000)] # 清洗結果單獨存一份后續(xù)所有腳本都從這份讀入 clean.to_csv(clean_tourism.csv, indexFalse, encodingutf-8-sig) print(clean.info())drop_duplicates里的subset參數(shù)指定了“哪幾列一致才算重復”不用它的話只要有一列相同就會被刪。fillna用中位數(shù)而不是均值是因為評分字段往往有偏態(tài)分布均值容易被極端值拉偏。to_datetime的format%Y-%m指定了解析格式不寫它也能猜但遇到 “2024/01” 這類變體會解析失敗。最后utf-8-sig是為了讓 Excel 打開 CSV 時不亂碼具體原因在避坑章節(jié)細說。3. 分析維度拆解游客量、評分與價格的四個核心口徑3.1 月度游客量趨勢先聚合再畫圖別拿明細直接上折線拿到clean_tourism.csv之后最容易犯的錯是直接拿明細數(shù)據(jù)去畫折線。每一行是一個景點的月度記錄幾百條線疊在一起畫出來的圖跟毛線團一樣答辯老師一眼就看出你沒思路。正確做法是按月度匯總游客量再做趨勢分析。trend clean.groupby(clean[month].dt.to_period(M))[visitors].sum().reset_index() trend[month] trend[month].astype(str) # Period 對象轉字符串方便畫圖 print(trend.head())dt.to_period(M)把datetime截斷到月份groupby后sum()得到每月總游客量。注意reset_index()不能省否則month會留在索引里后面畫圖時x軸取不到。這里先不急著畫聚合結果還可以繼續(xù)做環(huán)比計算trend[visitors].pct_change()能直接算月度環(huán)比增速寫報告時一句“暑期環(huán)比增長 42%”比放十張圖都有說服力。3.2 熱門目的地排名評論數(shù)、游客量與評分的交叉解讀排名是大作業(yè)里的必做項但只排一個指標太單薄。常見做法是同時算三份排名按游客量、按評論數(shù)、按平均評分然后把它們放在一起交叉解讀。你會發(fā)現(xiàn)有意思的業(yè)務結論游客量最高的城市評分不一定最高評論數(shù)很多但評分偏低的景點很可能是服務沒跟上人流。top_visitors clean.groupby(city)[visitors].sum().sort_values(ascendingFalse).head(10) top_reviews clean.groupby(city)[reviews].sum().sort_values(ascendingFalse).head(10) top_rating clean.groupby(city)[rating].mean().sort_values(ascendingFalse).head(10) print(top_visitors) print(top_reviews) print(top_rating)這三個groupby的差異點在于聚合函數(shù)游客量和評論數(shù)用sum評分用mean。評分聚合后要留意樣本量某個城市只有一條評論且打了 5 分它就會排到第一這不代表真實水平。穩(wěn)妥做法是加一個過濾條件只保留評論數(shù)超過一定閾值的城市再算均分代碼里加一句clean clean[clean[reviews] 50]就行。3.3 門票價格與滿意度箱線圖與分組均值價格和滿意度之間的關系是最容易出彩的分析點。常見做法是把價格切成幾個區(qū)間對比各區(qū)間內的平均評分。這里有一個 Pandas 2.x 用戶容易踩的坑pd.cut分箱后groupby默認會忽略空箱需要加observedTrue。bins [0, 50, 100, 200, 500] labels [0-50, 50-100, 100-200, 200以上] clean[price_band] pd.cut(clean[price], binsbins, labelslabels) band_stats clean.groupby(price_band, observedTrue)[rating].mean().reset_index() print(band_stats)pd.cut的bins定義了價格邊界labels是每個區(qū)間顯示的名字。observedTrue在 Pandas 2.0 之后是必須的否則會出現(xiàn)一排 NaN 的警告。分析結論通常是中低價位景區(qū)的滿意度反而高于高價景區(qū)這背后的業(yè)務解釋可以是“高價景區(qū)承載量過大體驗下降”。寫報告時把這個結論放在圖旁邊比單純放一張箱線圖更顯思考深度。3.4 評論數(shù)與游客量的相關性先做正態(tài)檢驗再選相關系數(shù)很多大作業(yè)喜歡算相關性但直接df.corr()就算完忽略了一個前提條件Pearson 相關系數(shù)要求變量近似正態(tài)分布。評論數(shù)和游客量這類業(yè)務數(shù)據(jù)幾乎都是右偏分布用 Pearson 會低估相關強度正確做法是先做正態(tài)性檢驗再決定用什么系數(shù)。from scipy import stats # 先檢驗評分字段是否正態(tài) shapiro stats.shapiro(clean[rating].dropna()) if shapiro.pvalue 0.05: corr clean[[rating, reviews]].corr(methodpearson) else: corr clean[[rating, reviews]].corr(methodspearman) print(corr)stats.shapiro返回(統(tǒng)計量, p值)p 值大于 0.05 認為數(shù)據(jù)是正態(tài)的。業(yè)務數(shù)據(jù)大多落在 else 分支所以methodspearman是更常見的組合。另外注意corr()只對數(shù)值列生效如果city列混了進來會報錯所以要先取[rating, reviews]兩列。報告中寫清楚“評分與評論數(shù)呈弱正相關Spearman r0.32”其中方法名、系數(shù)、樣本量三要素齊備評分自然不會低。4. 可視化落地Matplotlib 靜態(tài)圖與 Pyecharts 大屏的搭配方案4.1 Matplotlib 出論文級靜態(tài)圖字體、配色與分辨率期末大作業(yè)的推薦策略是靜態(tài)圖放在報告正文里交互圖放在附錄或演示 HTML 里。Matplotlib 負責前者三個參數(shù)是隱形扣分點中文字體、負號顯示、保存分辨率。沒有配置中文字體所有標簽都會變成方塊這在后面避坑章節(jié)會細講。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 中文字體 plt.rcParams[axes.unicode_minus] False # 負號正常顯示 fig, ax plt.subplots(figsize(10, 5)) ax.plot(trend[month], trend[visitors], markero, linewidth2) ax.set_title(月度游客量趨勢2024) ax.set_xlabel(月份) ax.set_ylabel(游客量萬人次) ax.tick_params(axisx, rotation45) # 月份過多時旋轉標簽防重疊 fig.tight_layout() fig.savefig(trend.png, dpi300, bbox_inchestight)dpi300是期刊配圖的標準期末報告用 150 也夠但 300 會讓老師覺得你認真。bbox_inchestight能自動裁掉空白邊避免保存出來的圖四周一大片白。tick_params(rotation45)解決標簽重疊問題別靠plt.xticks(rotation45)那種老寫法前者更清晰。如果SimHei在你的機器上不存在換成Microsoft YaHei或Noto Sans CJK SC都行關鍵是先確認系統(tǒng)里有這個字體文件。4.2 Pyecharts 拼接可視化大屏Page 布局與交互效果Pyecharts 是當前數(shù)據(jù)分析大作業(yè)里做“可視化大屏”的主流選擇因為它生成 HTML可以動態(tài)交互不用像 Flask 那樣自己搭前端。期末場景用Page組件把多張圖拼成一個縱向可滾動的頁面即可不需要炫技。from pyecharts.charts import Bar, Line, Page from pyecharts import options as opts bar (Bar() .add_xaxis(top_visitors.index.tolist()) .add_yaxis(游客量, top_visitors.values.tolist()) .set_global_opts(title_optsopts.TitleOpts(title熱門目的地 TOP10))) line (Line() .add_xaxis(trend[month].tolist()) .add_yaxis(游客量, trend[visitors].tolist(), is_smoothTrue) .set_global_opts(title_optsopts.TitleOpts(title月度趨勢))) page Page(layoutPage.SimplePageLayout) page.add(bar, line) page.render(analysis_report.html)Page.SimplePageLayout會讓圖表縱向排列適合報告展示。add_xaxis需要 Python 列表所以前面分析結果里的index和values都要.tolist()這是最常見的類型報錯來源。中間章節(jié)里的圖表關鍵還在于Pyecharts 版本不同 API 相差很大0.5.x 和 1.x 的寫法完全不同裝的時候直接指定pyecharts1.3.1能省下一堆報錯。4.3 圖表選型表折線、柱狀、地圖、玫瑰圖各用在哪大作業(yè)最怕圖表“為用而用”。下面這張選型表是我做項目時的標準參考你可以直接抄進報告附錄分析目標推薦圖表Pyecharts 類型注意點月度趨勢折線圖Linex 軸必須是排序后的時間序列城市排名柱狀圖Bar先排序再取前 N地理分布地圖Map需要額外地圖資源離線易空白價格區(qū)間滿意度箱線圖Boxplot先做異常值過濾評分分布玫瑰圖Pierosetyperadius適合展示占比而非精確值評論數(shù)與游客量關系散點圖Scatter日志換算后再畫避免長尾擠壓這里重點說MapPyecharts 自帶的 1.x 版本地圖數(shù)據(jù)需要在聯(lián)網(wǎng)環(huán)境下載離線跑大概率白屏。期末答辯現(xiàn)場如果網(wǎng)絡不穩(wěn)定地圖組件會直接變成空白塊。穩(wěn)妥方案是放棄地圖改用柱狀圖按城市排布或者提前把地圖 JS 文件下載到本地在init_opts{js_host: ./js/}中指定本地路徑。5. 旅游網(wǎng)站數(shù)據(jù)分析避坑指南環(huán)境、字體與數(shù)據(jù)不一致的 5 個現(xiàn)場5.1 現(xiàn)象cmd 里輸 python 沒反應雙擊腳本直接閃退這是最基礎也最影響心態(tài)的坑。原因九成是安裝時沒勾選 “Add Python to PATH”系統(tǒng)根本找不到python.exe。解決方法是重裝并勾選或者手動把C:\Python3x\和C:\Python3x\Scripts\加進系統(tǒng)環(huán)境變量。裝了 Anaconda 的用戶優(yōu)先用 “Anaconda Prompt” 運行腳本里面的環(huán)境變量是配好的。血淚經(jīng)驗不要在 Windows 自帶 cmd 里試python失敗后就放棄去查一下環(huán)境變量配置這屬于 python 安裝教程里反復強調但總有人忽略的一步。5.2 現(xiàn)象圖表標題和坐標軸中文全是方塊Matplotlib 默認字體不含中文字形。如果你只設了font.sans-serif [SimHei]但系統(tǒng)沒有這個字體還是方塊。先執(zhí)行fc-list :langzhLinux/macOS或檢查C:\Windows\Fonts確認字體是否存在沒有就去下載Noto Sans CJK SC安裝。裝完還沒用則是 Matplotlib 字體緩存的問題刪除~/.cache/matplotlib重啟 Python 即可。這個坑在 Linux 服務器上跑代碼時尤其頻繁本地 Windows 一般不缺 SimHei。5.3 現(xiàn)象Pyecharts 渲染出來是空白 HTML控制臺報 404兩種原因。第一種是 Pyecharts 版本錯位用了 0.5.x 的舊代碼跑在 1.x 的環(huán)境里圖表對象直接不是預期結構HTML 里什么都不會顯示。解決方法是統(tǒng)一釘版本pip install pyecharts1.3.1。第二種是資源加載問題1.x 默認從 CDN 拉取 echarts 依賴網(wǎng)絡受限或離線環(huán)境下圖表區(qū)域空白。解決方法是把echarts.min.js下載到項目js/目錄然后生成圖表時傳入init_opts{js_host: ./js/, theme: white}。5.4 現(xiàn)象CSV 用 Excel 打開中文全亂Notepad 打開正常原因很明確Pandas 默認用utf-8寫入 CSV而 Excel 對無 BOM 的 UTF-8 識別不穩(wěn)定。解決方法是寫文件時指定encodingutf-8-sig這是 UTF-8 帶 BOM 的別名。前面第 2 章的清洗代碼里我已經(jīng)用了這個參數(shù)但如果你使用to_excel則不存在這個問題Excel 原生支持 xlsx 格式的中文。注意盡量不要在清洗階段用latin1或gbk編碼后續(xù)重新讀入時會遇到一堆編碼報錯。5.5 現(xiàn)象答辯 PPT 里的數(shù)字和數(shù)據(jù)腳本跑出來的對不上這個問題非常隱蔽。原因通常是數(shù)據(jù)源不止一個或者腳本每次運行都重新生成了模擬數(shù)據(jù)。比如你報告里寫“全年游客量 3200 萬”但答辯前重新運行腳本因為模擬數(shù)據(jù)的隨機種子沒固定數(shù)字變成了 3100 萬當場翻車。解決方法是清洗完成后立刻存一份clean_tourism.csv快照后續(xù)分析和畫圖全部從快照讀入如果數(shù)據(jù)是隨機生成的在頂部加一行np.random.seed(42)。另外統(tǒng)計口徑也要寫清楚比如“游客量”是包含所有月份還是只含旺季避免口徑不一致。6. 答辯加分技巧把“能跑”變成“有說服力”的三個習慣6.1 把項目拆成三個腳本答辯時能一鍵復現(xiàn)不要把所有代碼寫在一個大 Notebook 里。按階段拆成data_clean.py、analysis.py、visual.py三個文件每個文件末尾留一個if __name__ __main__:入口。答辯時老師如果問“這個圖怎么來的”你可以當場運行python visual.py幾秒鐘重現(xiàn)全部圖表。這個習慣在期末大作業(yè)和后續(xù)工作中都受用。6.2 用 Page 生成一個總 HTML所有圖表放一頁與其讓老師在多個 Notebook 輸出之間來回跳不如用 Pyecharts 的Page把所有圖表拼成一個analysis_report.html。打開就是完整可視化報告滾動即可查看全部圖表。如果你想更進一步也可以使用Tab組件把圖表按“趨勢 / 排名 / 價格”分組放在不同標簽頁交互感更強。6.3 每張圖標題直接寫業(yè)務結論不寫“游客量趨勢圖”圖表標題是老師的閱讀路徑。把“游客量趨勢圖”改成“暑期游客量為全年峰值、較春季增長 42%”把“熱門目的地排名”改成“TOP10 城市游客量占全網(wǎng) 73%頭部效應明顯”。這一改動不需要額外代碼只是把你的分析結論前置效果立竿見影。數(shù)據(jù)會自己說話是假的需要你替它說。我做這類項目養(yǎng)成的習慣是每次跑完分析先在記事本上寫三行“最核心的發(fā)現(xiàn)”再回頭決定畫哪些圖。圖表永遠為結論服務而不是為了湊頁數(shù)。希望這份思路能幫你把那份.zip變成一份真正能打的大作業(yè)也希望你答辯順利。本文還有配套的精品資源點擊獲取