
Day 45Matplotlib 综合实战——制作数据分析报告Matplotlib 最后一课。从原始数据到一份拿得出手的多页数据报告——4张风格统一的专业图表覆盖趋势、对比、分布、关联四个分析维度。一、项目背景你是一家连锁咖啡店的区域经理手上有 12 个月的销售数据。现在需要做一份月度汇报。分析目标 趋势——月度销售额变化 对比——各门店/产品表现 分布——顾客消费金额分布 关联——气温与销量的关系importmatplotlib.pyplotaspltimportnumpyasnpimportpandasaspd# 全局设置 plt.rcParams.update({font.sans-serif:[SimHei,Microsoft YaHei],axes.unicode_minus:False,figure.dpi:120,axes.grid:True,grid.alpha:0.3,font.size:11,})# 统一配色 COLORS{primary:#2C3E50,accent1:#E74C3C,accent2:#3498DB,accent3:#2ECC71,accent4:#F39C12,accent5:#9B59B6,palette:[#E74C3C,#3498DB,#2ECC71,#F39C12,#9B59B6,#1ABC9C],}二、数据准备np.random.seed(42)# 生成模拟数据 monthsnp.arange(1,13)month_labels[f{m}月forminmonths]# 月销售数据有季节性夏秋高峰base500seasonal200*np.sin((months-3)*np.pi/6)trend_growthnp.linspace(0,150,12)noisenp.random.normal(0,30,12)monthly_revenuebaseseasonaltrend_growthnoise# 各门店数据stores[朝阳店,海淀店,西城店,东城店,丰台店]store_revenue[285,320,210,265,180]# 各产品数据products[拿铁,美式,摩卡,冷萃,茶饮,糕点]product_revenue[420,310,250,220,180,140]# 顾客单次消费金额模拟偏态分布np.random.seed(42)customer_spendingnp.concatenate([np.random.normal(25,8,600),# 主流客群np.random.normal(50,15,300),# 中高消费np.random.normal(80,20,100),# 高消费])# 气温与销量每日数据tempsnp.linspace(-5,38,365)# 全年气温变化ice_drink_salesnp.clip(508*(temps-10)np.random.normal(0,15,365),0,400)hot_drink_salesnp.clip(300-6*(temps-5)np.random.normal(0,20,365),0,400)三、图表1月度趋势双Y轴组合图# 计算环比增长率mom_growth[None][(monthly_revenue[i]-monthly_revenue[i-1])/monthly_revenue[i-1]*100foriinrange(1,12)]fig,ax1plt.subplots(figsize(12,5))# 柱状图——销售额barsax1.bar(months,monthly_revenue,width0.6,colorCOLORS[accent2],alpha0.75,label销售额,edgecolorwhite,zorder3)# 在柱子上标数值forbar,valinzip(bars,monthly_revenue):ax1.text(bar.get_x()bar.get_width()/2,bar.get_height()10,f{val:.0f},hacenter,fontsize9,fontweightbold)# 目标线target_linenp.full(12,600)ax1.plot(months,target_line,--,colorgray,linewidth1.5,alpha0.7,label目标线(600))ax1.fill_between(months,monthly_revenue,target_line,where(monthly_revenuetarget_line),colorgreen,alpha0.1)ax1.fill_between(months,monthly_revenue,target_line,where(monthly_revenuetarget_line),colorred,alpha0.1)ax1.set_xlabel(月份)ax1.set_ylabel(销售额万元,colorCOLORS[accent2])ax1.tick_params(axisy,labelcolorCOLORS[accent2])ax1.set_xticks(months)ax1.set_xticklabels(month_labels)ax1.set_ylim(0,900)# 右Y轴——环比增长率ax2ax1.twinx()valid_monthsmonths[1:]valid_growth[gforginmom_growthifgisnotNone]ax2.plot(valid_months,valid_growth,o-,colorCOLORS[accent1],linewidth2,markersize8,label环比增长率,zorder4)ax2.set_ylabel(环比增长率%,colorCOLORS[accent1])ax2.tick_params(axisy,labelcolorCOLORS[accent1])ax2.axhline(y0,colorCOLORS[accent1],linestyle--,alpha0.4)# 合并图例lines1,labels1ax1.get_legend_handles_labels()lines2,labels2ax2.get_legend_handles_labels()ax1.legend(lines1lines2,labels1labels2,locupper left,fontsize9)# 去掉多余边框ax1.spines[top].set_visible(False)ax1.set_title(连锁咖啡店月度销售额与环比增长,fontsize15,fontweightbold,pad15)plt.tight_layout()plt.savefig(report_01_monthly_trend.png,dpi150,bbox_inchestight)plt.show()四、图表2门店 × 产品对比堆叠柱状图# 模拟各门店各产品销售额np.random.seed(42)store_product_data{}forstoreinstores:store_product_data[store][np.random.randint(30,120)for_inproducts]df_sppd.DataFrame(store_product_data,indexproducts)fig,axesplt.subplots(1,2,figsize(14,5))# --- 左门店总销售额排名 ---sorted_storessorted(store_revenue,reverseTrue)sorted_names[sfor_,sinsorted(zip(store_revenue,stores),reverseTrue)]bars_haxes[0].barh(range(len(sorted_names)),sorted_stores,colorCOLORS[palette][:len(stores)],edgecolorwhite,height0.6)axes[0].set_yticks(range(len(sorted_names)))axes[0].set_yticklabels(sorted_names,fontsize11)axes[0].set_xlabel(销售额万元)axes[0].set_title(各门店总销售额排名,fontsize13,fontweightbold)# 标数值和排名fori,(bar,val)inenumerate(zip(bars_h,sorted_stores)):axes[0].text(val3,i,f#{i1}{val}万,vacenter,fontsize10,fontweightbold)# --- 右产品堆叠柱状图 ---xnp.arange(len(stores))bottomnp.zeros(len(stores))fori,(product,color)inenumerate(zip(products,COLORS[palette])):valuesdf_sp.loc[product].values axes[1].bar(x,values,bottombottom,labelproduct,colorcolor,edgecolorwhite,linewidth0.5)bottomvalues axes[1].set_xticks(x)axes[1].set_xticklabels(stores)axes[1].set_ylabel(销售额万元)axes[1].set_title(各门店产品销售构成,fontsize13,fontweightbold)axes[1].legend(locupper right,fontsize8,ncol2)plt.suptitle(门店与产品分析,fontsize15,fontweightbold)plt.tight_layout()plt.savefig(report_02_store_product.png,dpi150,bbox_inchestight)plt.show()五、图表3消费金额分布直方图累积曲线fig,ax1plt.subplots(figsize(10,5))# 直方图——消费金额分布counts,bins,patchesax1.hist(customer_spending,bins50,colorCOLORS[accent2],alpha0.6,edgecolorwhite,linewidth0.3,label频数)# 标记关键统计量mean_valnp.mean(customer_spending)median_valnp.median(customer_spending)ax1.axvline(mean_val,colorCOLORS[accent1],linestyle--,linewidth2,labelf均值:{mean_val:.1f}元)ax1.axvline(median_val,colorCOLORS[accent4],linestyle--,linewidth2,labelf中位数:{median_val:.1f}元)ax1.set_xlabel(单次消费金额元)ax1.set_ylabel(顾客数量,colorCOLORS[accent2])ax1.tick_params(axisy,labelcolorCOLORS[accent2])ax1.set_title(顾客单次消费金额分布,fontsize14,fontweightbold)# 右Y轴——累积百分比曲线ax2ax1.twinx()sorted_datanp.sort(customer_spending)cumulative_pctnp.arange(1,len(sorted_data)1)/len(sorted_data)*100ax2.plot(sorted_data,cumulative_pct,colorCOLORS[accent3],linewidth2,label累积百分比)ax2.set_ylabel(累积百分比%,colorCOLORS[accent3])ax2.tick_params(axisy,labelcolorCOLORS[accent3])# 合并图例并添加洞察文字lines1,labels1ax1.get_legend_handles_labels()lines2,labels2ax2.get_legend_handles_labels()pct_below_50(customer_spending50).sum()/len(customer_spending)*100ax1.text(0.95,0.85,f{pct_below_50:.0f}% 的顾客\n消费不超过50元,transformax1.transAxes,haright,fontsize10,bboxdict(boxstyleround,facecolorlightyellow,alpha0.8))ax1.legend(lines1lines2,labels1labels2,locupper right)plt.tight_layout()plt.savefig(report_03_spending_dist.png,dpi150,bbox_inchestight)plt.show()六、图表4气温 vs 冷热饮销量散点趋势fig,axplt.subplots(figsize(10,5))# 散点图sc1ax.scatter(temps,ice_drink_sales,c#3498DB,s15,alpha0.5,label冷饮,edgecolornone)sc2ax.scatter(temps,hot_drink_sales,c#E74C3C,s15,alpha0.5,label热饮,edgecolornone)# 趋势线多项式拟合fordata,color,labelin[(ice_drink_sales,#1A5276,冷饮趋势),(hot_drink_sales,#7B241C,热饮趋势),]:znp.polyfit(temps,data,2)pnp.poly1d(z)sorted_tempsnp.sort(temps)ax.plot(sorted_temps,p(sorted_temps),colorcolor,linewidth2.5,labellabel)# 标注交叉点冷热饮销量相等的大致温度cross_temp20ax.axvline(cross_temp,colorgray,linestyle:,linewidth1.5)ax.annotate(f约{cross_temp}°C\n冷热饮销量持平,xy(cross_temp,200),xytext(cross_temp8,300),arrowpropsdict(arrowstyle-,colorgray,lw1.5),fontsize10,bboxdict(boxstyleround,facecolorwhite,alpha0.8))ax.set_xlabel(气温°C,fontsize12)ax.set_ylabel(日销量杯,fontsize12)ax.set_title(气温对冷热饮品销量的影响,fontsize14,fontweightbold)ax.legend(locbest,fontsize9)plt.tight_layout()plt.savefig(report_04_temp_sales.png,dpi150,bbox_inchestight)plt.show()七、Matplotlib 五天回顾天数内容核心技能Day 41Matplotlib 入门基本图表类型、中文显示、Figure/Axes 概念Day 42坐标轴与图例刻度定制、双Y轴、legend、annotate、spinesDay 43颜色与子图colormap、imshow、subplot_mosaic、fill_betweenDay 44高级图表箱线图、小提琴图、hexbin、3D 绘图Day 45综合实战完整分析报告、四张专业图表、保存输出八、第二阶段回顾 明日预告第二阶段Day 31-50进度NumPy5天 ████████████████████████ ✅ 完成 Pandas5天 ████████████████████████ ✅ 完成 Matplotlib5天████████████████████████ ✅ 完成 数据分析实战5天 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 明天开始明天进入第二阶段最后一环——实战数据分析项目Day 46-50用 5 天完整走一个真实数据分析流程爬取数据 → 清洗 → 探索 → 可视化 → 写报告。5 天 Matplotlib从画第一根线到做出四张专业图表。你现在已经能把任何数据变成有说服力的视觉故事。第45天打卡完成本系列是个人学习笔记如有错误欢迎在评论区指正交流。