據(jù)分析系統(tǒng)實戰(zhàn):從數(shù)據(jù)清洗到RFM用戶分層)
簡介面向Python課程設(shè)計與期末大作業(yè)的電商平臺數(shù)據(jù)分析系統(tǒng)完整資源適合需要可直接運行項目作為參考的高校學(xué)生。資源圍繞電商業(yè)務(wù)數(shù)據(jù)設(shè)計實現(xiàn)了用戶行為分析、RFM用戶分層、回購率統(tǒng)計、銷售趨勢分析、渠道來源拆解等常見模塊覆蓋數(shù)據(jù)分析課程中的典型實驗場景同時附帶臟數(shù)據(jù)處理方式說明和README文檔便于理解數(shù)據(jù)清洗、特征計算與可視化輸出的完整流程。壓縮包共30個文件以7個Python源碼腳本為主體實現(xiàn)數(shù)據(jù)清洗、特征分析與圖表繪制輔以19張結(jié)果展示圖與3個緩存文件整體僅1.68MB輕量易部署源碼經(jīng)本地編譯通過評審分98分助教審定難度適中可直接用于期末大作業(yè)、課程設(shè)計或畢業(yè)設(shè)計參考。目前已有197人學(xué)習(xí)瀏覽作為高分項目范例能幫助讀者快速搭建電商數(shù)據(jù)分析框架并借鑒實現(xiàn)思路。1. 為什么電商數(shù)據(jù)分析是大作業(yè)里的“安全牌”這個題到底在考你什么Python 大作業(yè)電商平臺數(shù)據(jù)分析系統(tǒng)實現(xiàn)源碼文檔說明高分項目這類題目幾乎每個開 Python 課的學(xué)校都會出現(xiàn)一版。它不是讓你做一個能下單的商城也不是讓你訓(xùn)練一個預(yù)測模型而是要把一整條數(shù)據(jù)處理鏈路走通從拿到或構(gòu)造訂單數(shù)據(jù)到清洗、聚合、算指標(biāo)再到畫圖、寫結(jié)論、打包成一份能答辯的文檔。評分老師看的就是“你是不是真的會分析而不是會抄代碼”。這個方向適合兩類人一類是課程要求必須做“信息系統(tǒng)類”大作業(yè)想找一個數(shù)據(jù)量可控、工作量肉眼可見的題目另一類是求職作品集里缺一個數(shù)據(jù)展示項目想用半天到兩天時間補上一個完整案例。它不依賴 GPU、不依賴真實業(yè)務(wù)接口一臺普通筆記本就能跑完。前面把技術(shù)棧和目錄結(jié)構(gòu)定好后面就是照方抓藥的事。2. 技術(shù)選型與工程結(jié)構(gòu)先擺平“用什么”和“怎么放”再談寫代碼2.1 技術(shù)棧決策純 Python 腳本還是帶 Web 殼先回答一個最常見的問題這個系統(tǒng)到底要不要做成網(wǎng)頁很多高分模板會給你一個 Flask 頁面打開能看到圖表和數(shù)據(jù)表格但這不代表你必須這么做。我見過兩種做法各有各的道理方案組成適合情況答辯風(fēng)險純腳本版pandas 清洗 matplotlib 出圖 控制臺輸出結(jié)論課程只要求“分析報告 圖表”功能偏少容易被問“系統(tǒng)在哪里”帶 Web 殼版腳本版基礎(chǔ) Flask 渲染頁面 ECharts 圖表課程要求“系統(tǒng)”或展示給非技術(shù) audience工作量多一天但效果顯著我的建議是如果老師明確說了“系統(tǒng)”兩個字就做帶 Web 殼的版本。這里的“系統(tǒng)”在答辯語境下就等于“有界面、能交互”。但如果時間只夠一天腳本版也能過只要文檔里寫清楚“數(shù)據(jù)入庫—清洗—分析—可視化”四層結(jié)構(gòu)把腳本輸出截圖放進(jìn)文檔觀感并不差。下文默認(rèn)按腳本版為主、最后補 Web 殼的方案講這樣兩頭都能落。技術(shù)棧選型上沒有懸念pandas 做數(shù)據(jù)清洗和聚合matplotlib 做靜態(tài)圖表Flask 只用來把結(jié)果展示成網(wǎng)頁。不用 Django因為只暴露幾個路由Django 的 admin、ORM、遷移機(jī)制在這里全是負(fù)資產(chǎn)。數(shù)據(jù)庫用 SQLite 還是直接 CSV如果你沒學(xué)過 SQL直接用 CSV pandas.read_csv 就行減少一個變量如果你會用 pandas 的 merge、groupby數(shù)據(jù)量在幾萬行以內(nèi)時 CSV 完全不輸數(shù)據(jù)庫。2.2 目錄設(shè)計與文檔聯(lián)動源碼和文檔說明怎么配套很多人的項目只有一個 main.py跑完輸出幾張 png這在小作業(yè)里可以但在“系統(tǒng) 文檔說明”的評分結(jié)構(gòu)下很吃虧。我一般會把工程拆成這樣你直接照著建ecommerce_analysis/ ├── data/ │ ├── raw_orders.csv # 原始訂單數(shù)據(jù)不手動改 │ └── clean_orders.csv # 清洗后數(shù)據(jù)分析只用這份 ├── scripts/ │ ├── generate_data.py # 造數(shù)腳本保證可復(fù)現(xiàn) │ ├── clean_data.py # 清洗腳本 │ ├── analysis.py # 核心指標(biāo)計算 │ └── visualize.py # 生成圖表 ├── output/ │ ├── daily_sales.png │ ├── top10_products.png │ ├── rfm_heatmap.png │ └── metrics_summary.json # 指標(biāo)匯總Web 版直接用 ├── web_app/ │ └── app.py # Flask 入口可選 ├── docs/ │ └── 說明文檔.md └── requirements.txt每個文件只干一件事是這套結(jié)構(gòu)能避免答辯翻車的核心。比如 generate_data.py 只負(fù)責(zé)產(chǎn)出 raw_orders.csvclean_data.py 只讀 raw絕不回頭改源文件analysis.py 輸出 JSON 而不是直接 print 一眼就過。這樣文檔里寫“各模塊職責(zé)”時你就有話可說而不是籠統(tǒng)的一句“主程序完成所有功能”。requirements.txt 也必須有這是文檔能復(fù)現(xiàn)的前提pandas2.1.4 matplotlib3.8.2 flask3.0.0 openpyxl3.1.22.3 環(huán)境準(zhǔn)備與本機(jī)跑通最小命令別用系統(tǒng)全局 Python大作業(yè)最大的隱性扣分項就是“換個環(huán)境跑不起來”。用虛擬環(huán)境隔離也能在文檔里寫上“virtualenv 創(chuàng)建獨立運行環(huán)境”這一句加分話。cd ecommerce_analysis python -m venv venv # Windows: venv\Scripts\activate # macOS / Linux: source venv/bin/activate pip install -r requirements.txt說明python -m venv venv 創(chuàng)建虛擬環(huán)境到 venv 目錄激活后 pip 裝的包只對當(dāng)前項目生效。裝依賴用 -r 從 requirements.txt 讀取避免手工逐個裝漏掉。如果你用的是 PyCharm創(chuàng)建項目時可以直接選 Virtualenv效果一樣。到這一步環(huán)境能跑通后面的流程就都是確定性的了。3. 數(shù)據(jù)準(zhǔn)備與清洗沒有干凈數(shù)據(jù)后面全是廢圖3.1 造一份可復(fù)現(xiàn)的模擬訂單數(shù)據(jù)隨機(jī)種子決定你報告里的“真實性”電商平臺的真實訂單數(shù)據(jù)拿不到也絕不能拿爬蟲去搞。最常見、最可靠的做法是自己造一份帶業(yè)務(wù)含義的模擬數(shù)據(jù)。造數(shù)不是隨便 random要讓分布大致合理晚上和周末訂單多、爆品銷量高但單價低、數(shù)碼類客單價明顯高于零食類。這樣你分析出來的結(jié)論才像話。import random import pandas as pd from datetime import datetime, timedelta random.seed(42) # 商品池不同品類給不同價格區(qū)間 products [ {name: 無線鼠標(biāo), category: 數(shù)碼配件, min_price: 49, max_price: 89}, {name: 機(jī)械鍵盤, category: 數(shù)碼配件, min_price: 199, max_price: 499}, {name: 手機(jī)支架, category: 數(shù)碼配件, min_price: 9, max_price: 29}, {name: 保溫杯, category: 家居日用, min_price: 39, max_price: 129}, {name: 收納箱, category: 家居日用, min_price: 25, max_price: 79}, {name: 零食禮包, category: 食品, min_price: 29, max_price: 99}, ] # 時間范圍近 90 天 start_date datetime(2024, 1, 1) end_date datetime(2024, 3, 31) rows [] for order_id in range(1, 3001): # 周末和晚上下單概率更高 day_offset random.randint(0, (end_date - start_date).days) order_time start_date timedelta(daysday_offset, hoursrandom.randint(10, 22), minutesrandom.randint(0, 59)) if order_time.weekday() 5: weight 2 # 周末訂單翻倍 else: weight 1 if 19 order_time.hour 22: weight 1 # 晚間再加權(quán) for _ in range(random.randint(1, 3)): # 一單可能含多件商品 product random.choice(products) qty random.randint(1, 3) if weight 1 else random.randint(1, 2) price round(random.uniform(product[min_price], product[max_price]), 2) rows.append({ order_id: order_id, user_id: random.randint(1000, 1500), product_name: product[name], category: product[category], quantity: qty, price: price, amount: round(price * qty, 2), order_time: order_time.strftime(%Y-%m-%d %H:%M:%S), }) df pd.DataFrame(rows) df.to_csv(data/raw_orders.csv, indexFalse, encodingutf-8-sig) print(f生成 {len(df)} 條訂單明細(xì))邏輯說明random.seed(42) 固定隨機(jī)種子保證每次運行生成完全相同的數(shù)據(jù)。你文檔里寫的所有數(shù)值都來自這份確定性的數(shù)據(jù)否則兩次運行結(jié)果不同答辯時老師讓你重跑一遍就對不上。weight 變量模擬業(yè)務(wù)權(quán)重周末權(quán)重為 2、晚間額外加 1但注意它只影響購買件數(shù)概率不是直接給金額乘系數(shù)——這只是為了讓數(shù)據(jù)分布更像真實訂單不必做成精確的概率模型。參數(shù)說明訂單數(shù) 3000用戶數(shù) 5011000-1500時間范圍 90 天。這三個參數(shù)決定你后續(xù)圖表的密度3000 條明細(xì)畫日趨勢足夠飽滿501 個用戶做分層也夠。如果你的報告需要月度對比就把日期范圍改成 6 個月并把訂單數(shù)加到 6000 以上。生成 CSV 時用 encodingutf-8-sig這是給 Excel 用的編碼Excel 直接打開不會亂碼——很多教程只寫 utf-8Windows 上 Excel 打開就是一堆亂碼這一步值得記住。3.2 清洗三步去重、補空、拒掉異常訂單造出來的數(shù)據(jù)也要走一遍真實清洗流程這是文檔里“數(shù)據(jù)預(yù)處理”章節(jié)的素材。清洗本身不在于數(shù)據(jù)有多臟而在于你展示了“我知道這些坑存在并做了處理”。import pandas as pd df pd.read_csv(data/raw_orders.csv) # 1. 去重同一訂單號加同一商品名才算重復(fù) df.drop_duplicates(subset[order_id, product_name], inplaceTrue) # 2. 缺失值金額為空或用戶為空的行直接剔除 df.dropna(subset[amount, user_id], inplaceTrue) # 3. 異常值數(shù)量0、金額0、單價9999 都視為臟數(shù)據(jù) df df[(df[quantity] 0) (df[amount] 0) (df[price] 9999)] # 4. 時間轉(zhuǎn) datetime方便后續(xù)按天/月聚合 df[order_time] pd.to_datetime(df[order_time]) df[order_date] df[order_time].dt.date df.to_csv(data/clean_orders.csv, indexFalse, encodingutf-8-sig) print(f清洗前 {len(df)} 行清洗后保留 {len(df)} 行)邏輯說明drop_duplicates 的 subset 參數(shù)限定判斷重復(fù)的列不要全列去重——同一個訂單含兩條不同商品是正常業(yè)務(wù)全列去重會誤刪。dropna 只針對關(guān)鍵字段因為商品名缺失可能不影響金額匯總而 user_id 缺失會導(dǎo)致后續(xù) RFM 分析無法做用戶分組。異常值過濾用布爾條件組合這里不用 replace直接剔除是更干凈的做法。參數(shù)說明price 9999 是防呆閾值你可以按業(yè)務(wù)調(diào)整為 100000關(guān)鍵是它必須比商品池里的最高單價大一個量級。清洗完的行數(shù)建議和原始行數(shù)對比在文檔里寫“共剔除 N 行無效數(shù)據(jù)”這就是數(shù)據(jù)質(zhì)量的量化證明。上面這段代碼只是標(biāo)準(zhǔn)流程如果你自己模擬數(shù)據(jù)時沒生成缺失值可以在 generate_data.py 里留 5% 的空金額字段——故意制造臟數(shù)據(jù)再清洗報告反而更有說服力。3.3 Excel 落盤與對賬拿 Excel 透視表當(dāng)裁判清洗后的數(shù)據(jù)除了存 CSV我會再導(dǎo)出一份 Excel目的是給文檔附件用也方便老師自己打開篩選。這不是冗余而是讓審核者能親手核對你算的每個數(shù)。import pandas as pd df pd.read_csv(data/clean_orders.csv) # 按天匯總銷售額寫入 Excel 的 sheet1 daily df.groupby(order_date)[amount].sum().reset_index() monthly df.groupby(df[order_time].dt.month)[amount].sum().reset_index() with pd.ExcelWriter(output/agg_tables.xlsx, engineopenpyxl) as writer: daily.to_excel(writer, sheet_name日銷售額, indexFalse) monthly.to_excel(writer, sheet_name月銷售額, indexFalse)代碼說明groupby 聚合后要 reset_index如果不重置order_date 會變成索引列to_excel 寫出來會帶一層奇怪的行名。pd.ExcelWriter 配合 with 語句能確保文件寫完后關(guān)閉句柄不然在 Windows 上文件可能被占用第二次運行報 PermissionError。參數(shù)說明engineopenpyxl 是寫 xlsx 必需的后端xls 老格式是 xlwt別混用。這個 Excel 文件生成后你可以在 Excel 里插入透視表拉一個“日銷售額”和程序算出來的數(shù)對一遍——對上了說明 groupby 邏輯沒問題對不上優(yōu)先檢查 order_date 是否真的轉(zhuǎn)了 datetime 類型。這一步對賬花五分鐘能避免“圖表數(shù)據(jù)和描述文字對不上”這種最尷尬的答辯事故。4. 分析指標(biāo)與可視化從平均值到 RFM 的完整鏈路4.1 時間趨勢分析先 resample 再畫線別直接 plot 原始數(shù)據(jù)做完清洗第一個必做的分析是時間趨勢。我見過很多人把 3000 行訂單直接按天 groupby 后畫線畫出來鋸齒狀極其難看。正確做法是按天聚合后再做時間序列重采樣把“天數(shù)”這個單位統(tǒng)一。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(data/clean_orders.csv) df[order_time] pd.to_datetime(df[order_time]) df.set_index(order_time, inplaceTrue) # 按天求和再取 7 日均線平滑 daily df[amount].resample(D).sum() weekly daily.rolling(window7, min_periods1).mean() plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(12, 5)) ax.plot(daily.index, daily.values, label日銷售額, color#888, linewidth0.8) ax.plot(weekly.index, weekly.values, label7日均線, color#E63946, linewidth2) ax.set_title(近90天銷售額趨勢) ax.legend() plt.tight_layout() plt.savefig(output/daily_sales.png, dpi150)邏輯說明resample(D) 是把索引為時間的數(shù)據(jù)重采樣到天和單純的 groupby 日期不同它會把沒有訂單的日期也補成 0畫出來是一條連續(xù)的時間軸不會有斷點。rolling(window7) 取 7 天滑動平均把周內(nèi)波動抹平趨勢線才看得出整體方向——這是報告里“銷售額整體呈上升趨勢”這句話的唯一依據(jù)。參數(shù)說明figsize(12, 5) 長寬比適合在文檔里插入不會過高占版dpi150 是打印清晰度的底線低于 100 放到答辯 PPT 里會明顯發(fā)虛。字體設(shè)置里 SimHei 是黑體Microsoft YaHei 是微軟雅黑前者在 Linux 上通常沒有所以列兩個備選系統(tǒng)按順序找。4.2 Top10 商品與二八法則用條形圖把差異拉出來趨勢圖證明“總量”接下來必須展示“結(jié)構(gòu)”。Top10 商品貢獻(xiàn)了多少銷售額在電商報告里幾乎必提。這一步能引出二八法則的分析結(jié)論讓你的報告從“羅列圖表”升級到“有觀點”。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(data/clean_orders.csv) top10 (df.groupby(product_name)[amount] .sum() .sort_values(ascendingFalse) .head(10)) total_sales df[amount].sum() top10_share top10.sum() / total_sales * 100 fig, ax plt.subplots(figsize(10, 6)) ax.barh(top10.index[::-1], top10.values[::-1], color#2A9D8F) ax.set_xlabel(銷售額元) ax.set_title(fTop10 商品銷售額貢獻(xiàn)占比 {top10_share:.1f}%) plt.tight_layout() plt.savefig(output/top10_products.png, dpi150) print(top10)邏輯說明sort_values(ascendingFalse) 按銷售額降序排head(10) 截前 10。barh 是橫向條形圖類別名稱長時橫向更易讀。這里用 top10.index[::-1] 反轉(zhuǎn)順序讓最大值在頂部如果不反轉(zhuǎn)matplotlib 默認(rèn)從底部往上排最大項反而不在最顯眼的位置。參數(shù)說明top10_share 是 Top10 銷售額占總銷售額的百分比這個數(shù)通常是報告里最有沖擊力的一個數(shù)字。例如算出來可能是 45% 左右你就可以在文檔里寫“Top10 商品貢獻(xiàn)了約 45% 的營收頭部商品集中度明顯”——有數(shù)據(jù)有判斷這就是高分項目和普通作業(yè)的差別。4.3 RFM 用戶分層找出“高價值沉默用戶”用戶分層是電商數(shù)據(jù)分析系統(tǒng)的標(biāo)配內(nèi)容也是拉開檔次的模塊。RFM 三個字母分別代表最近一次消費時間、消費頻率、消費金額。實現(xiàn)不復(fù)雜但必須講清理由你為什么要分層因為運營資源有限得優(yōu)先服務(wù)值得服務(wù)的用戶。import pandas as pd df pd.read_csv(data/clean_orders.csv) ref_date df[order_time].max() pd.Timedelta(days1) # 按用戶聚合 RFM 三指標(biāo) rfm df.groupby(user_id).agg( recency(order_time, lambda x: (ref_date - x.max()).days), frequency(order_id, count), monetary(amount, sum) ) # 四分位數(shù)分檔數(shù)值高于中位數(shù)的記為 1否則 0 def score(series): return (series series.median()).astype(int) rfm[R], rfm[F], rfm[M] score(rfm[recency]), score(rfm[frequency]), score(rfm[monetary]) # 分層規(guī)則8 類用戶合并成 4 類主型 def rfm_label(row): if row[R] 1 and row[F] 1 and row[M] 1: return 重要價值用戶 if row[M] 1: return 高消費潛力用戶 if row[R] 1: return 活躍普通用戶 return 沉默風(fēng)險用戶 rfm[label] rfm.apply(rfm_label, axis1) rfm.to_csv(output/rfm_result.csv, encodingutf-8-sig) # 看一眼分層結(jié)果 print(rfm[label].value_counts())邏輯說明recency 用 ref_date 減去每個用戶最近一次下單日期得到“距今多少天沒消費”frequency 用 order_id 出現(xiàn)次數(shù)統(tǒng)計注意 agg 里第一個元組 (order_time, lambda ...) 表示對 order_time 列應(yīng)用 lambda(order_id, count) 表示對 order_id 計數(shù)——字段映射別寫反。四分位數(shù)分檔是 RFM 最常見實現(xiàn)數(shù)值大于全體中位數(shù)為 1否則為 0。閾值用中位數(shù)而不是平均值因為消費金額分布通常右偏平均值會被大額用戶拉高中位數(shù)更穩(wěn)。參數(shù)說明ref_date 取數(shù)據(jù)最大時間 1 天確保“最近消費”是相對完整日期區(qū)間計算的。分層規(guī)則這里只寫了 4 類完整 RFM 是 238 類但我建議報告里合并成 4 類因為 8 類會分散用戶數(shù)答辯時解釋成本高。你可以把 rfm_result.csv 導(dǎo)入 Excel 篩選某個縣體用戶 ID并去明細(xì)表里驗證這個用戶的真實消費記錄——能驗證說明分層不是黑匣子。4.4 熱力圖與圖表保存可視化配置統(tǒng)一入口圖表風(fēng)格統(tǒng)一是“高分項目”的外觀底線。如果每張圖顏色、字體、尺寸都不一樣老師掃一眼就覺得是拼湊的。我會把可視化配置抽到一個公共函數(shù)然后熱力圖用 seaborn 風(fēng)格畫出來展示 RFM 分布。import pandas as pd import matplotlib.pyplot as plt # 統(tǒng)一圖表風(fēng)格 plt.rcParams[figure.figsize] (10, 6) plt.rcParams[axes.grid] True plt.rcParams[grid.linestyle] -- plt.rcParams[grid.alpha] 0.4 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False rfm pd.read_csv(output/rfm_result.csv) # 畫 R、F、M 三個分檔的平均值雷達(dá)式對比 summary rfm.groupby(label)[[recency, frequency, monetary]].mean() # 對數(shù)值歸一化到 0-1避免金額數(shù)值過大壓扁其他軸 summary_norm (summary - summary.min()) / (summary.max() - summary.min()) fig, ax plt.subplots(figsize(8, 6)) im ax.imshow(summary_norm.T, cmapYlOrRd, aspectauto) ax.set_xticks(range(len(summary_norm.index))) ax.set_xticklabels(summary_norm.index, rotation30) ax.set_yticks(range(3)) ax.set_yticklabels([最近消費, 消費頻率, 消費金額]) plt.colorbar(im, axax, label歸一化均值) plt.tight_layout() plt.savefig(output/rfm_heatmap.png, dpi150)邏輯說明imshow 把數(shù)值矩陣渲染為熱力圖cmapYlOrRd 從黃到紅漸變數(shù)值越大顏色越深。summary_norm.T 做轉(zhuǎn)置讓每一列是一個用戶類型、每一行是一個指標(biāo)這樣豎向看某個用戶類型的三項指標(biāo)強(qiáng)弱更直觀。歸一化到 0-1 是必要的否則 monetary 均值幾千recency 均值幾十兩者沒法在同一張圖上比較顏色深淺。參數(shù)說明aspectauto 讓單元格自動鋪滿畫布不加這個參數(shù)熱力圖會被拉成正方形導(dǎo)致變形。rotation30 讓用戶類型的標(biāo)簽斜著排避免文字相互遮擋。這一張熱力圖配合上一節(jié)的柱狀圖、趨勢圖整套圖表組合已經(jīng)覆蓋“時間、結(jié)構(gòu)、人群”三個維度文檔里每個分析結(jié)論都能落到圖上。5. 避坑會毀掉分?jǐn)?shù)的 5 個常見問題5.1 中文亂碼與方塊字編碼問題現(xiàn)場現(xiàn)象matplotlib 畫出的圖里所有中文標(biāo)題變成方框CSV 用 Excel 打開后中文全是亂碼或者讀 CSV 直接報 UnicodeDecodeError。原因三個環(huán)節(jié)各有差異。matplotlib 默認(rèn)字體不支持中文pandas to_csv 默認(rèn) utf-8而 Excel 默認(rèn) GBK 打開pandas read_csv 默認(rèn)按系統(tǒng)編碼讀取Windows 下可能是 GBK。解決matplotlib 在 pyplot 畫圖前設(shè)置 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] 和 plt.rcParams[axes.unicode_minus] FalseCSV 寫入統(tǒng)一 encodingutf-8-sig讀取時如果報錯顯式指定 pd.read_csv(path, encodingutf-8)還是報錯就試試 encodinggbk。這個坑最值得提前規(guī)避因為亂碼圖一旦生成只能重跑沒有后悔藥。5.2 日期軸變成密集黑線沒做重采樣現(xiàn)象畫日銷售額趨勢時橫軸密密麻麻擠成一團(tuán)線條雜亂看不出規(guī)律。原因直接把每行的訂單時間作為橫軸繪圖沒有先 resample 到天導(dǎo)致同一日期出現(xiàn)多條記錄或者日期格式不統(tǒng)一。另一個原因是日期索引沒有排序matplotlib 按出現(xiàn)順序畫線線就來回折。解決先 df[order_time] pd.to_datetime(...)然后 set_index(order_time)再 resample(D).sum()。resample 會自動按時間排序并補齊空日期。如果只想顯示 1 月到 3 月可以先用 df.loc[2024-01-01:2024-03-31] 切片再聚合。5.3 圖糊了或被截斷dpi 和 tight_layout 同時生效現(xiàn)象保存的 png 放到文檔里模糊或圖例、標(biāo)題被截掉一半。原因dpi 低于 100圖在文檔里被拉伸后像素不夠figsize 設(shè)置過大、內(nèi)容超出畫布邊界。解決保存時統(tǒng)一 dpi150畫布 figsize 控制在 (10, 6) 到 (12, 6) 之間。打印前調(diào) plt.tight_layout()它會自動收縮留白避免標(biāo)題被子圖遮擋。生成后先本地打開看一眼別直接插進(jìn)文檔——圖被截斷這一項幾乎每個班都有幾個人中招。5.4 造數(shù)“太假”分布不合理被老師當(dāng)場問住現(xiàn)象答辯時老師說“你這數(shù)據(jù)怎么每天銷售額都一樣高”或者“凌晨 3 點還在瘋狂下單”原因random.randint 均勻生成時間沒有模擬真實人群作息商品價格區(qū)間太窄導(dǎo)致銷量和金額高度線性相關(guān)。解決generate_data.py 里對時間加權(quán)——晚上和周末提高下單概率工作日上午 10 點到下午 4 點保持常規(guī)凌晨直接降低到接近零。價格區(qū)間要拉開差距數(shù)碼配件定價幾十到幾百食品就幾塊到幾十。還可以再往數(shù)據(jù)里加 3% 的退款記錄amount 為負(fù)清洗時剔除這樣報告里就能寫“剔除了 N 條退款異常數(shù)據(jù)”真實性明顯提升。5.5 文檔和代碼對不上換個電腦跑不出來現(xiàn)象代碼在你自己電腦上運行正??降嚼蠋煓C(jī)器上 ImportError 或者路徑報錯。原因依賴沒鎖版本用絕對路徑讀文件中文文件名在 macOS 和 Windows 編碼不一致。解決所有文件路徑用相對路徑比如 data/clean_orders.csv不要寫 C:/Users/xxx/Project/...。requirements.txt 鎖住 pandas、matplotlib、flask 的版本號。跑之前確認(rèn)當(dāng)前工作目錄在項目根目錄而不是在子目錄里執(zhí)行腳本。最后用 Python 的 ifname main: 包住所有執(zhí)行代碼防止被 import 時意外跑一遍。這三件事做齊換環(huán)境基本不會再翻車。6. 進(jìn)階技巧把靜態(tài)圖升級成數(shù)據(jù)看板再用一張表自證沒算錯如果還想往上夠一夠可以把 matplotlib 的 png 換成網(wǎng)頁看板工作量不大但觀感提升明顯。Flask 里寫一個路由讀取 analysis.py 輸出的 metrics_summary.json把日銷售額、Top10、RFM 分布渲染成 ECharts 圖表。關(guān)鍵在于Flask 只做數(shù)據(jù)中轉(zhuǎn)不做計算所有指標(biāo)都在 analysis.py 里算好網(wǎng)頁端只是換一種形式展示。我一般會在 app.py 里寫一個 /dashboard 路由返回一個模板頁頁面上的圖表數(shù)據(jù)全部來自 JSON 文件——這樣你可以在答辯現(xiàn)場刷新頁面給老師看比翻 png 圖片有說服力得多。計算正確性上最后務(wù)必跑一次對賬腳本把 analysis.py 算出的月度總銷售額和 agg_tables.xlsx 里 Excel 透視表的數(shù)核對。對不上優(yōu)先查 groupby 是否漏了過濾條件對得上文檔里可以加一句“輸出結(jié)果已與 Excel 透視表交叉驗證”。這套流程做完你會發(fā)現(xiàn)自己對數(shù)據(jù)鏈路的理解比照著教程敲二十遍都有用。我當(dāng)年做類似項目時就是在 RFM 分層上吃了虧——閾值用平均值而不是中位數(shù)導(dǎo)致所有用戶都被歸到“沉默風(fēng)險”答辯時被問得說不出話后來習(xí)慣所有分箱先畫分布圖再定閾值這個習(xí)慣一直留到現(xiàn)在。希望幫到你。本文還有配套的精品資源點擊獲取