ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python数据可视化入门:Matplotlib从安装到进阶绘图实操指南

Python数据可视化入门:Matplotlib从安装到进阶绘图实操指南 先说个真实场景。有次我在处理一批销售数据数字算得倒是快但领导开口就要“一眼看懂趋势”的图。我打开终端敲了两行Python用pandas把数据读进来再交给Matplotlib画了个折线图前后不到30秒一张能直接放进汇报里的图就出来了。你可能会说Excel也能画啊为什么非要折腾python中的Matplotlib模块因为当你面临几十个文件需要批量出图、想复现论文里的高质量图表、或者要做一个自动化数据分析流程时只有“用代码画图”这条路靠得住。Matplotlib是python生态里最老牌、用得最广的可视化库几乎所有科学计算、数据分析相关项目都绕不开它。这篇文章不打算把官方文档抄一遍我就按自己平时用下来的经验从Matplotlib到底是什么、怎么装、怎么画出第一张图、怎么让它变好看、再到那些让人抓狂的坑一条线讲清楚适合python刚入门、准备做数据分析或科研绘图的朋友直接参考。1. Matplotlib到底是什么1.1 为什么数据分析离不开它先给个最简单定义Matplotlib是一个纯Python实现的2D绘图库它把你手里的数据变成折线图、散点图、柱状图、等高线图、3D图等等。它诞生的年头很早2003年就有人开始写了灵感来自MATLAB的绘图风格所以用过MATLAB的人上手Matplotlib会特别快。但真正让我离不开它的原因是自动化。同样是画图用Excel得先打开表格、选中数据、插入图表再手动调样式如果用Matplotlib上面这一步就是一行代码的事。数据一旦更新图跟着变根本不需要手动重新弄。批量处理几十个城市、几百个产品线的曲线图写个循环十几分钟全搞定这不是效率问题是能不能按时下班的问题。还有个特点很关键它跟numpy、pandas配合得近乎无缝。pandas做数据清洗和聚合numpy做数值计算Matplotlib负责把最后的结果可视化。这三样再加上scikit-learn基本就是python数据分析的日常全家桶。所以在各路招聘要求里“python数据分析与可视化”几乎必然包含Matplotlib。1.2 Figure、Axes与Artist先搞懂三个核心概念很多新手画图容易卡壳就是因为没搞明白Matplotlib里面的对象关系。官方文档里有一个非常经典的“层级结构”我说人话解释一下。Figure画布整个图形的容器可以理解成一张白纸。Axes坐标系白纸上画的坐标区域一个Figure里可以有多个Axes比如一张纸上左右各画一个图就是两个Axes。Artist元素坐标系里的标题、坐标轴、线条、图例、刻度这些乱七八糟的东西通通是Artist。所以你在代码里写的plt.plot(x, y)本质上是往“当前坐标系”里加了一个线条类型的Artist对象。到后期你会发现一旦图表复杂起来用fig, ax plt.subplots()这种面向对象的方式更靠谱因为可以明确地控制把这根线条画到哪个Axes上。我用个生活类比Figure是画板Axes是贴在画板上的画纸Artist是画笔在纸上画出的每条线、每个字。你要往某一格上画总得知道在哪个画纸吧。1.3 pyplot状态机与面向对象接口怎么选Matplotlib有两种常用写法。一种是pyplot它内部维护了一个“当前Figure、当前Axes”的概念你写plt.plot()它就知道画到当前坐标系上适合快速画图和交互式环境比如Jupyter Notebook里用。另一种是显式创建Figure和Axes比如fig, ax plt.subplots()然后通过ax.plot()往指定坐标系里画东西。我个人的建议很直接如果只是临时做个探索性可视化怎么快怎么来用pyplot完全没问题但只要是写正式脚本或者项目代码统一用面向对象的方式。原因很现实——当一张图上铺了多个子图、需要反复调整样式时状态机容易“画错地方”因为plt.plot()画的是那个看不见的“当前坐标轴”而ax.plot()明确指定了画到哪。很多二次修改的bug就是从这里冒出来的。# pyplot方式适合快速画 import matplotlib.pyplot as plt plt.plot([1, 2, 3], [4, 5, 6]) plt.show() # 面向对象方式适合正式项目 fig, ax plt.subplots() ax.plot([1, 2, 3], [4, 5, 6]) plt.show()2. 环境准备与安装新手最花时间的其实是这一步2.1 还没装Python先花10分钟把地基打好如果你电脑上还完全没有Python环境建议不要一上来就折腾IDE先把Python装好。去Python官网下载安装包Windows环境安装时一定记得勾选“Add Python to PATH”这个勾经常被忽略后面python命令找不到就是因为它。macOS和Linux用户一般自带Python 3但版本可能偏老最好也装个新版本。装好之后打开终端命令提示符验证一下python --version pip --version能看到版本号说明基础环境没问题。这一步别跳过很多人在后面pip install matplotlib失败就是连pip这个包管理工具都没跑通。如果你主要做数据分析也可以直接装Anaconda它内置了Python和大量常用库包括Matplotlib、numpy、pandas、jupyter等相当于“开箱即用”。缺点是包很大启动较慢但省心。我自己在写数据分析项目时会用Anaconda环境日常写小脚本就用系统Python加虚拟环境。2.2 pip安装与conda安装各自怎么选环境就绪之后装Matplotlib非常简单pip install matplotlib等它把依赖拉完就算成了。如果你网络条件不太理想可以指定国内镜像源速度会快很多pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple用Anaconda的话也可以conda install matplotlibconda会自己处理依赖冲突尤其在做科学计算的项目里会更稳。装完以后怎么确认成功了两条命令任选pip list | grep matplotlib或者在python交互环境里执行python -c import matplotlib; print(matplotlib.__version__)如果打印出版本号说明安装成功。另外建议顺手把numpy和pandas也装上后面画图几乎都会用到pip install numpy pandas2.3 别急着画图先处理中文显示乱码这一步是我最想让你提前做的。Matplotlib默认不配置中文字体你只要在标题或坐标轴标签里写中文出来的图全是方框特别打击积极性。解决办法也简单在绘图脚本开始处设置一下字体参数import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 指定默认中文字体 plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题SimHei黑体在Windows下基本通用macOS可以用Arial Unicode MS或者PingFang SCLinux常见的是WenQuanYi Zen Hei或者Noto Sans CJK SC。如果你不确定系统有什么字体可以打印一下可用列表import matplotlib.font_manager as fm fonts [f.name for f in fm.fontManager.ttflist] for f in sorted(set(fonts)): print(f)然后从里面挑一个中文字体名填进去就行。这个问题看似小不处理的话后面每张图都在劝退你。3. 从第一张图开始折线图、散点图与定制化3.1 画一张折线图核心就5行代码Matplotlib画折线图的入门代码长这样import matplotlib.pyplot as plt # 模拟从周一到周五的访问量 days [Mon, Tue, Wed, Thu, Fri] visits [320, 410, 285, 530, 470] plt.plot(days, visits) plt.title(Daily Website Visits) plt.xlabel(Day) plt.ylabel(Visits) plt.show()这段代码会弹出一个小窗口显示折线图。几个基础参数说明一下plt.plot(x, y)里的第一个列表是x轴数据第二个是y轴数据。如果不传x只写plt.plot(y)它会自动用索引0,1,2,...作为x轴。实际项目里数据经常是numpy数组或者pandas的Series用法完全相同。比如生成一个正弦曲线import numpy as np x np.linspace(0, 2 * np.pi, 100) y np.sin(x) plt.plot(x, y) plt.show()np.linspace的作用是在0到2π之间均匀生成100个点这一步在数学函数可视化里特别常用。多条折线放在一张图上也简单再写一个plt.plot()就行y2 np.cos(x) plt.plot(x, y, labelsin(x)) plt.plot(x, y2, labelcos(x)) plt.legend() plt.show()注意我用了label参数给每条线起了名字然后再调用plt.legend()把图例显示出来。漏了legend()是新手最常见的操作写了label却不显示图例代码检查半天才发现少了一行。3.2 散点图、柱状图怎么选场景决定类型折线图适合展示连续数据的变化趋势比如时间序列。但如果你想看两个变量之间的关系比如身高和体重、广告费用和销售额散点图更合适。height [158, 162, 165, 170, 172, 175, 180, 183] weight [50, 55, 58, 62, 65, 70, 75, 80] plt.scatter(height, weight) plt.xlabel(Height (cm)) plt.ylabel(Weight (kg)) plt.show()看到一堆点从左下往右上角聚集你就能直观判断出“高度和体重可能存在正相关”。散点图的优势是保留每个样本的位置信息不会因为聚合而丢失细节。柱状图则适合展示分类数据的对比比如不同产品的销量、不同部门的预算。基础用法products [A, B, C, D] sales [1200, 860, 1500, 990] plt.bar(products, sales) plt.xlabel(Product) plt.ylabel(Sales) plt.show()柱状图的变化形态还有横向柱状图barh当分类名称特别长时横向排列更容易看清。选图的原则其实很简单对比趋势用折线看分布和相关用散点做分类对比用柱状。不用把几十种图表都记下来先把这三种用熟大部分场景都覆盖了。3.3 把图表做得像样标题、坐标轴、图例、网格一个能直接放到工作汇报里的图不能只有一条光秃秃的线。我整理出几个高频定制项都是真实项目里天天用的。首先是标题和坐标轴标签这个前面已经用到了。坐标轴范围可以用xlim和ylim控制比如plt.ylim(0, 100)类似“摄像机取景范围”调整它可以让图表聚焦在关键区域避免离群点把整体曲线压扁。网格线plt.grid(True)能极大提升图的阅读性尤其当多条线挤在一起时有网格比没网格清晰太多了。我习惯设置成主网格线加虚线样式plt.grid(True, linestyle--, alpha0.6)线条本身也能定制样式。颜色color、线型linestyle、线宽linewidth、标记marker几个参数组合一下图就专业了plt.plot(x, y, colorred, linestyle--, linewidth2, markero, markersize4)最后是图例。除了前面说的label加legend()图例位置也可以调默认Matplotlib往往会找一个不那么碍事的地方但你自己看久了会知道locupper left、locbest这些选项各有适用场景。把定制项合起来展示一下x np.linspace(0, 10, 100) y1 np.sin(x) y2 np.cos(x) plt.figure(figsize(8, 5)) plt.plot(x, y1, labelsin, color#2E86AB, linewidth2) plt.plot(x, y2, labelcos, color#A23B72, linestyle--, linewidth2) plt.title(Sine and Cosine Curves, fontsize14) plt.xlabel(x, fontsize12) plt.ylabel(y, fontsize12) plt.legend(locupper right) plt.grid(True, linestyle--, alpha0.5) plt.show()figsize用来控制画布尺寸单位是英寸我画论文插图常用(6,4)或(8,5)画汇报大图用(10,6)。合理设置画布尺寸比你后期截图裁剪靠谱得多。4. 进阶操作多子图、对数坐标轴与数据框联动4.1 用subplots做多图对比一张画布放多个坐标轴分析数据时难免要“左图看趋势右图看分布”或者几个指标并排对比。此时用plt.subplots创建多个坐标轴。fig, axes plt.subplots(1, 2, figsize(12, 4)) # 左边折线图 axes[0].plot(x, y1, color#2E86AB) axes[0].set_title(Line Plot) # 右边散点图 axes[1].scatter(x, y2, color#A23B72, s10) axes[1].set_title(Scatter Plot) plt.tight_layout() plt.show()plt.subplots(1, 2)表示一行两列返回的axes是个数组分别代表两个子图。这里必须用axes[0]、axes[1]来定位别再想着plt.plot那会把线画到当前活动坐标轴上而当前活动坐标轴可能是最后一个结果全画到一个图里去了我在初学阶段经常犯这个错。2行2列时写法也类似axes是二维数组用axes[0, 0]、axes[0, 1]等来访问各个子图。tight_layout()很有用它自动调整子图间距避免标题和坐标轴标签互相挤压。我最开始不知道这个函数手动调间距调了半天。如果子图之间需要共享横轴可以在创建时加sharexTrue这样缩放一个图另一个也跟着动对比数据时特别方便。4.2 对数坐标轴怎么用数据跨度大时马上想到它你搜索“matplotlib对数坐标轴”说明已经遇到一个典型问题数据跨越范围特别大比如从0.01到10000普通线性坐标轴上小数值全挤在角落里根本看不出来。解决办法就是换对数坐标。x np.linspace(0.1, 1000, 100) y x ** 2 plt.plot(x, y) plt.xscale(log) plt.yscale(log) plt.grid(True, whichboth, linestyle--, alpha0.6) plt.show()xscale(log)把横轴变成以10为底的对数刻度纵轴同理。这里有个细节设置了对数坐标后想要网格线既出现在主刻度又出现在次刻度网格的参数要写成whichboth否则只在主刻度显示网格。为什么对数坐标能救命因为它把乘除关系映射成加减关系原本横跨好几个数量级的数据在坐标轴上能均匀铺开。真实场景里网络节点的度数分布、城市人口规模分布、生物实验中的浓度梯度这些数据动辄差几个数量级用对数坐标几乎是标配。4.3 与pandas和numpy配合让出图流程顺手起来实际工作中数据基本不会手工写在列表里而是从CSV、数据库读进来。pandas的DataFrame天然支持直接调用Matplotlib绘图省去很多转换步骤。import pandas as pd df pd.read_csv(sales_data.csv) df.plot(xdate, y[plan, actual], figsize(10, 5)) plt.title(Sales Plan vs Actual) plt.ylabel(Amount) plt.show()df.plot()底层就是调用Matplotlib列名直接作为线条标签。在pandas 2.x里大部分时候df.plot()返回的就是Axes对象如果你需要进一步定制可以把返回值接住再改ax df.plot(xdate, yactual, color#2E86AB) ax.set_title(Actual Sales) ax.axhline(df[actual].mean(), colorred, linestyle--, labelmean) plt.legend() plt.show()axhline用来画一条水平参考线拿来做“均值线”“目标线”非常直观。至于numpy的配合前面两个例子已经展示了用np.linspace生成连续数值用np.sin、np.cos生成函数值。真实的业务数据经过pandas处理之后变成NumPy数组或Series再丢给Matplotlib画图这套流程我每天都在走。5. 我踩过的坑常见问题与排查记录5.1 程序跑完不出图窗口一闪而过新手最容易遇到的坑就是写完plt.show()后弹窗闪一下就消失或者命令行模式下根本看不到图。闪退的原因往往是程序执行完窗口就关闭了。解决方式有三种一是在脚本末尾加input()挂住程序二是在Jupyter Notebook里用%matplotlib inline嵌入显示三是遇到服务器、无桌面环境时别想着show了直接用plt.savefig(output.png, dpi300)保存图片。我自己现在写数据处理脚本时几乎不用show()都是直接savefig落地成图片文件既方便归档也不会因为没图形界面而报错。5.2 中文乱码和负号方块到底怎么彻底解决中文方块的原因就是字体回退到了不支持中文的字体。前面已经给了配置命令我再补充一个更详细的处理思路先确认你系统里有哪个中文字体再把它设为默认简体中文字体。import matplotlib.pyplot as plt # Linux下常用方案 plt.rcParams[font.sans-serif] [WenQuanYi Zen Hei, Noto Sans CJK SC] # Windows下常用方案 plt.rcParams[font.sans-serif] [SimHei] # macOS下常用方案 plt.rcParams[font.sans-serif] [PingFang SC]这几行代码要放到所有绘图代码之前。另一个很隐蔽的问题是坐标轴上的负号会变成方块必须加plt.rcParams[axes.unicode_minus] False。如果设了中文字体但是仍然乱码大概率是字体名字写错了用我前面说的打印字体列表的方法确认一下。还有个偏门但有效的办法下载常用中文字体文件到matplotlib字体目录并重建缓存这个方法适合打包部署到服务器的场景但日常用rcParams就够了。5.3 横坐标太密集、刻度挤成一团怎么办画日期或长文本标签时经常出现刻度挤成一团的情况比如一年365天全部显示在x轴上那画面基本没法看。处理办法有三个级别。第一旋转刻度标签plt.xticks(rotation45)第二每隔N个刻度才显示一个。用pandas画图时可以手动设置刻度位置和标签import numpy as np # 只显示第0, 10, 20, ...个标签 ticks range(0, len(df), 10) plt.xticks(ticks, df[date].iloc[ticks], rotation30)第三设置MaxNLocator让matplotlib自动控制刻度数量from matplotlib.ticker import MaxNLocator ax plt.gca() ax.xaxis.set_major_locator(MaxNLocator(nbins10)) # 最多显示10个刻度到底选哪个取决于你希望刻度精确到日、月还是季度。如果是日期图更专业的做法是用matplotlib.dates的DateLocator能按天、周、月自动生成合适刻度不过日常处理上前面两种已经能解决80%的问题。5.4 图例重叠、标签被截断、图像有锯齿图例和标题重叠通常是把图例放在了数据不空的位置改成locbest让Matplotlib自己找空位或者直接用bbox_to_anchor精确指定位置。坐标轴标签被截断多半是画布尺寸太小或者tight_layout没调用先调大figsize再加plt.tight_layout()。保存出来的图片有锯齿是因为默认dpi偏低。保存时明确指定plt.savefig(figure.png, dpi300, bbox_inchestight)bbox_inchestight会自动裁掉多余白边让图片更紧凑。5.5 高频问题速查表问题现象常见原因推荐解决方案plt.show()无反应或闪退无GUI环境或窗口未保持改用plt.savefig()中文显示为方块默认字体不支持中文配置rcParams[font.sans-serif]负号显示为方块默认字体处理unicode负号异常设置axes.unicode_minus False横坐标标签挤成一团标签太多、方向水平plt.xticks(rotation45)或减少刻度图上没有图例忘了调plt.legend()给每条线加label并调用legend()保存的图片糊保存分辨率太低plt.savefig(dpi300)多图重叠、标签出界子图间距不够调plt.tight_layout()这几类问题我几乎每周都能在答疑群里看到一遍解决它们之后Matplotlib的基础使用就算打通了。我个人在实际使用中的一个体会是不要试图记住Matplotlib所有API用的时候查就行。真正要记住的是那套对象关系和常见画图套路——先建Figure和Axes再往Axes里面加各种Artist最后定制样式并保存或展示。把这个思路刻进脑子比背一百个函数名都管用。以后遇到再复杂的图表无非就是往这个框架里添砖加瓦。最后再分享一个小技巧画多系列数据时不妨把颜色统一成一整套色系比如用color_palette [#2E86AB, #A23B72, #F18F01]循环取色整套图会显得很协调肉眼看着舒服老板也更容易点头。
返回列表