Matplotlib入门:用图表让数字说话
2026/9/15 16:33:08 网站建设 项目流程

阿文是我们厂薄膜沉积(CVD)工段的一名工艺工程师。每天早上八点整,他到岗后的第一件事不是喝咖啡,而是打开MES导出昨天的工艺参数Excel,然后手工把二十台CVD腔室的膜厚数据一条条复制进PPT,再手动画上控制上限、控制下限,标出超出规格线的异常点。一台设备一天大约两百个数据点,二十台就是四千个,他每天要花两个半小时做这件事,还经常因为复制错位把第3腔室的数据填到第7腔室去。

三个月前的一个凌晨,阿文被值班电话叫醒,说第12腔室膜厚连续三批偏离目标值。他爬起来翻白天的PPT,发现异常其实在前天的控制图里已经露头,只是因为他那天赶着下班,手动画图时漏标了一个点。那一刻他下定决心:这件事必须交给代码。两周后,他用不到一百行matplotlib代码,把原来两个半小时的活压到了十秒,而且再没填错过一行。这篇文章,就是把他踩过的坑和最终沉淀下来的方法,完整地讲给你听。

先说结论:在半导体制造现场,图表不是给领导看的装饰,而是工艺是否受控的“仪表盘”。当数据量从几百涨到几万,人眼根本看不出趋势,只有控制图、趋势图、帕累托图这些“会说话的数字”能替你发现异常。而matplotlib,就是把这些数字变成图的最趁手的工具。

一、matplotlib 在半导体现场到底能画什么

很多刚入门的同学以为matplotlib只是画个折线图、柱状图。其实在FAB里,它几乎能覆盖工程师日常需要的全部图表类型:SPC控制图(Xbar-R、Xbar-S、I-MR、EWMA)、良率趋势图、机台状态甘特图、缺陷分布热力图(WaferMap)、帕累托分析图、箱线图、雷达图,以及OEE三因子分解瀑布图。

它最大的三个优点是:完全免费、代码可复用、风格统一。一旦你写好了一种图的模板,明天换一批数据只要改数据源,图的外观、配色、标注位置纹丝不动。这对需要每天出日报、周报的工程师来说,价值是巨大的——你再也不用每次都从零开始拖鼠标。

下面这张图,就是阿文每天给主管看的CVD膜厚控制图。它同时画了Xbar(均值)和R(极差)两张子图,红色虚线是控制限,超出控制限的点用红圈标了出来。看到这张图,主管不用看任何一个数字,就能判断“今天工艺受控还是失控”。

1CVD膜厚Xbar-R控制图,第12子组均值突破控制上限(红圈)

二、从数据到深色主题PNG:完整可运行代码

下面这段代码,就是阿文最终落地的版本。它做了四件关键的事:第一,配置中文字体,避免中文变方块;第二,设置两层背景色(画布和坐标轴)保证深色主题统一;第三,用计算出来的控制限画线;第四,把越界点用红圈单独标出。你直接复制就能跑。

import matplotlib
matplotlib.use('Agg') #
服务器/定时任务无界面必须加
import matplotlib.pyplot as plt
import numpy as np

# 1)
中文字体:按优先级指定多个备选,matplotlib会依次尝试
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial']
plt.rcParams['axes.unicode_minus'] = False #
避免负号显示成方块

# 2)真实CVD膜厚数据:20个子组,每组5个样本(单位Å
sub = np.array([
[1001, 998, 1003, 997, 1002], [999, 1004, 998, 1001, 1000],
[1002, 999, 1000, 1003, 998], [997, 1001, 1000, 999, 1003],
[1000, 1002, 998, 1001, 999], [1003, 997, 1001, 1000, 998],
[999, 1000, 1002, 998, 1001], [1001, 1003, 999, 1000, 997],
[998, 999, 1001, 1003, 1000], [1000, 997, 1002, 999, 1001],
[1002, 1001, 998, 1000, 999], [999, 1000, 1001, 1012, 1003], #
12组偏移
[1001, 997, 1000, 1002, 998], [998, 1001, 999, 1000, 1002],
[1000, 1003, 997, 1001, 999], [999, 998, 1002, 1000, 1001],
[1001, 1000, 999, 1003, 997], [998, 1002, 1001, 998, 1000],
[1000, 999, 1001, 997, 1002], [1002, 998, 1000, 1001, 999],
], dtype=float)
n = sub.shape[1]
xbar = sub.mean(axis=1)
r = sub.max(axis=1) - sub.min(axis=1)
xbar_bar, r_bar = xbar.mean(), r.mean()
A2 = {2:0.880, 3:0.729, 4:0.577, 5:0.483, 6:0.419}[n]
D4 = {2:3.267, 3:2.574, 4:2.282, 5:2.114, 6:2.004}[n]
ucl_x = xbar_bar + A2 * r_bar
lcl_x = xbar_bar - A2 * r_bar
ucl_r = D4 * r_bar

# 3)
画深色主题控制图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(7.2, 5.0), facecolor='#1e1e1e')
ax1.set_facecolor('#2d2d2d'); ax2.set_facecolor('#2d2d2d')
ax1.plot(xbar, color='#36c5d0', marker='o', label='
均值Xbar')
ax1.axhline(xbar_bar, color='#ffd84d', ls='--', label='
中心线')
ax1.axhline(ucl_x, color='#ff6b6b', ls='--', label='
控制上限')
ax1.axhline(lcl_x, color='#ff6b6b', ls='--', label='
控制下限')
ax1.set_title('CVD
膜厚Xbar-R控制图', color='white')
ax1.tick_params(colors='#b9b9b9'); ax1.grid(True, color='#3a3a3a')
for s in ax1.spines.values(): s.set_color('#5a5a5a')
ax2.plot(r, color='#ff9d3b', marker='s', label='
极差R')
ax2.axhline(ucl_r, color='#ff6b6b', ls='--', label='UCL')
for s in ax2.spines.values(): s.set_color('#5a5a5a')
ax2.tick_params(colors='#b9b9b9'); ax2.grid(True, color='#3a3a3a')

# 4)
保存PNGBytesIO,方便直接塞进Word/邮件)
from io import BytesIO
buf = BytesIO()
fig.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor='#1e1e1e')
buf.seek(0)
print('
控制图已生成,UCL_X=%.2f, LCL_X=%.2f' % (ucl_x, lcl_x))

讲几个新手必踩的坑。第一,中文显示成方块,根因是系统没有你指定的字体——所以一定要在rcParams里按优先级写多个备选,matplotlib会从左到右找到第一个存在的。第二,负号变成方块,加一句axes.unicode_minus=False就能解决。第三,深色主题要设置两层背景色:figure的facecolor和axes的facecolor,只设一层,图例和坐标轴之间的区域还是白的,会很丑。第四,保存时一定带上facecolor参数,否则PIL默认用白底,你精心调的深色主题就白调了。

三、良率趋势图:让下跌提前被人看见

控制图管的是“单台设备某一参数是否受控”,而良率趋势图管的是“整条线、整条产品是不是在变坏”。阿文后来把两张图合并进同一个日报:上面是设备控制图,下面是良率趋势图。主管一眼就能把“设备异常”和“良率下跌”对应起来。

良率趋势图最常见的问题是:下跌已经发生很久,图上才看出来。解决办法是加一条“预警线”和一条“规格线”,一旦周良率跌破预警线,就把那段曲线标红。下面这张图对比了两款产品(A0和B0)近十二周的良率,可以清楚看到B0在第9周跌破预警线,团队据此提前介入,避免了更大损失。

2B0产品第9周起跌破预警线(红圈),需提前介入

画这张图的代码和上面一脉相承,核心只是多了一条axhline预警线和一段“标红”逻辑:

weeks = np.arange(1, 13)
yB = np.array([93.1, 93.4, 92.9, 93.2, 93.0, 92.8, 93.3, 92.7,
90.9, 89.6, 88.4, 87.1]) #
9周起明显下跌
warn_line, spec_line = 92.0, 95.0

ax.plot(weeks, yB, color='#36c5d0', marker='o', lw=2.0, label='B0
良率')
ax.axhline(spec_line, color='#ffd84d', ls='--', label='
规格线95%')
ax.axhline(warn_line, color='#ff6b6b', ls='--', label='
预警线92%')
#
跌破预警线的点用红圈单独标出
bad = weeks[yB < warn_line]
ax.scatter(bad, yB[yB < warn_line], s=140, facecolors='none',
edgecolors='#ff6b6b', linewidths=2.4, zorder=5, label='
跌破预警')

到这里,你已经掌握了半导体现场最常用、也最能体现价值的两种图:控制图和监督图。它们共同的逻辑是——用一条线把“正常”和“异常”划开,让异常自己跳出来找你,而不是你每天盯着几千个数字去找异常。

matplotlib真正厉害的地方在于“一次写好,天天复用”。阿文现在每天下班前让计划任务跑一遍脚本,第二天早上控制图和良率趋势图已经躺在主管的邮箱里。他再也不用为画图熬夜,把时间花在了真正该做的工艺分析上。

四、批量导出:二十台设备一次出图

阿文的故事还没讲完。单台CVD控制图画通之后,下一个需求马上来了:二十台设备、每台五个腔室,能不能一次性把所有控制图都导出来?答案当然可以,核心就一句话——把画图逻辑封装成函数,外面套一层循环。

具体做法:写一个draw_control_chart(subgroups, title)函数,内部用plt.subplots(2,1)在一个Figure里画Xbar和R两张子图,画完savefig到BytesIO,最后plt.close(fig)释放内存。主循环遍历二十台设备的子组数据,每次调用一次函数,十秒跑完全部。关键是循环里一定不要忘了close,否则几千个Figure对象累积,内存很快被吃光。

我见过一个真实翻车:同事的批量脚本忘写plt.close,跑到第两百张图时进程被系统OOM杀掉,前面导出的图也因为没flush全丢了。从那以后我们团队定了一条铁律——任何循环画图,函数结束前必须close。封装成函数后,这点最容易保证。

def draw_control_chart(subgroups, title):
sub = np.array(subgroups, dtype=float)
xbar = sub.mean(axis=1); r = sub.max(axis=1)-sub.min(axis=1)
fig, (a1, a2) = plt.subplots(2, 1, figsize=(7, 5), facecolor='#1e1e1e')
a1.set_facecolor('#2d2d2d'); a2.set_facecolor('#2d2d2d')
a1.plot(xbar, color='#36c5d0', marker='o'); a2.plot(r, color='#ff9d3b', marker='s')
a1.set_title(title, color='white')
buf = BytesIO(); fig.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor='#1e1e1e')
buf.seek(0); plt.close(fig); return buf

for fname in equip_list: #
二十台设备循环出图
sub = load_subgroups(fname)
img = draw_control_chart(sub, fname)
doc.add_picture(img, width=Inches(5.6))

这段代码的要点:函数入参是子组数据和控制图标题,出参是BytesIO,不直接落盘,方便后续统一塞进Word或邮件;tight_layout()自动调整子图间距,避免标题被切;dpi=150在清晰度和文件体积间取平衡,嵌进日报邮件不会模糊也不会太大。

当你把'画一张图'升级成'画一类图',matplotlib才真正从玩具变成生产力工具。

五、什么数据用什么图:FAB现场选型清单

图表不是越炫越好,选错图比不画还让人误解。这里给一份FAB现场最常用的选型清单,按数据类型对号入座,基本不会错。

第一类,时间序列趋势——良率、OEE、机台产出、缺陷率,统统用折线图plot,并配一条目标线或控制限,跌破就标红,看图的人一眼知道现在好不好。第二类,稳定性监控——单个工艺参数是否受控,用控制图Xbar-R或I-MR,必须有中心线和控制上下限,用来区分普通波动和异常波动。

第三类,问题归因——哪种缺陷代码贡献了最多良率损失,用帕累托图(柱状加累计百分比曲线),帮你看清关键的少数。第四类,分布对比——不同批次膜厚、不同腔室电阻是否来自同一总体,用箱线图boxplot或直方图,一眼看出偏移和离散。

第五类,空间分布——缺陷在晶圆上的具体位置,用WaferMap散点图叠加晶圆轮廓圆,定位是不是边缘环带或中心区。第六类,构成分解——OEE三因子、质量成本结构,用瀑布图或堆叠柱图,看清钱漏在哪个环节。第七类,相关性——温度与膜厚、压力与均匀性,用散点图加拟合线,判断是不是真的相关。

一条黄金原则:一张图只讲一件事,标题直接写结论。比如不要写'膜厚趋势',而写'CVD-12膜厚第12子组超控制上限,疑似工艺偏移'。阿文后来把是否受控直接写进图标题,主管说终于不用戴着老花镜找异常点了。

六、深色主题配色与可读性

深色背景不是赶时髦,是工程师在车间现场看投屏的真实需要——白底图在强光下刺眼、在暗处发灰,深灰底反而清楚。但深色主题要讲究,否则比白底还难看。

第一要务是对比度够:亮色线条(橙、青、黄)配深灰面板,文字用近白色,坐标轴脊线用中灰而不是纯白,纯白太抢戏反而干扰读数。第二,避开红绿配色,约8%的男性有红色盲,红绿对他们几乎不可分;区分正负好坏请用橙青或黄蓝这对色盲安全组合。

第三,线宽至少1.5pt、数据点标记至少4pt,投到会议室大屏才看得清;第四,字号底线——标题12到14pt、轴标签10pt、图例9pt;第五,savefig的dpi设150,嵌进Word或邮件不会被拉伸糊掉;第六,中文label一定在rcParams里配好字体,否则全变方块,前面所有努力白费。

七、新手最常踩的五个matplotlib坑

坑一,中文变方块:系统没装SimHei或没在rcParams指定,解决是写多个字体备选并确认系统存在。坑二,负号变方块:漏了axes.unicode_minus=False。坑三,保存出来是白底:savefig忘带facecolor参数,精心调的深色主题瞬间消失。

坑四,内存爆炸:循环批量画图忘写plt.close(fig)。坑五,两层背景只设了一层——figure.facecolor和axes.facecolor只设一个,结果是坐标轴区域是深的、画布边缘是白的,割裂又难看。把这五个坑记牢,你画的图至少能体面地进周报。

把这七节串起来,matplotlib在FAB的定位就很清楚:它不是炫技玩具,而是把海量工艺数据翻译成人一眼能决策的语言的翻译器。你越早把它用顺手,就越早从重复画图里解放出来。

八、把控制图嵌进自动化日报

阿文最终把控制图做成了日报的固定模块:每天凌晨脚本拉取前一日二十台CVD的子组膜厚,逐台算控制限,凡越界点在图上标红,再把整份图批量塞进Word邮件发出。主管每天到岗第一件事就是看这封带图的日报,不用再等阿文手工做。

这里有个工程细节值得记:控制限要用历史稳定期的数据算,而不是用含异常的那批数据算,否则异常会把控制限撑宽,反而掩盖问题。我们规定控制限每四周用最近稳定四周重算一次,期间固定,这样趋势可比、异常才显眼。

#'未判异'的子组作基准算控制限,避免异常污染限
import numpy as np
def stable_limits(subgroups, ooc_mask):
base = np.array(subgroups)[~ooc_mask] #
只取稳定子组
xbar = base.mean(axis=1)
r = base.max(axis=1) - base.min(axis=1)
cb, rb = xbar.mean(), r.mean()
ucl = cb + A2[base.shape[1]] * rb
return cb, ucl, cb - A2[base.shape[1]] * rb

代码要点:先筛出未判异的子组作为基准,再算控制限,避免用异常数据污染限。这一步是SPC正确性的命门,很多新手直接用全量算限,结果限被异常撑宽,控制图成了摆设。

当数据量上来,还要学会用numpy向量化代替循环:子组数据直接进二维ndarray,xbar=sub.mean(axis=1)一行算完所有组均值,比for循环快几十倍,也更容易写对。把简单交给numpy,把判断留给自己,是FAB数据工程的基本功。

九、从一张图到一套体系

单张控制图解决一台设备一个点;把图按设备、按层别、按时间排成体系,才真正形成监控网。阿文现在的日报里,控制图、良率趋势图、OEE图、帕累托图各占一屏,主管翻一遍就知道全厂健康度。

体系的威力在于关联:控制图异常能立刻跳到对应良率趋势,良率下跌能立刻定位到哪台设备的控制图越界。图与图之间用lot_id、eq_id串起来,异常不再是孤岛,而是一张可追溯的网。这一步,才是matplotlib在FAB的终极价值。

十、真实案例:一张控制图救回一批wafer

去年Q3我们厂CVD-07连续五批膜厚漂移,前四批已经流到后段才被发现,报废损失近四十万人民币。问题出在当时的监控还靠人工周报,等质量工程师看到趋势线时,那几批wafer早就流走了。上了阿文的控制图日报后,第五批在子组层面刚刚越界就被标红,当班工程师两小时内干预、切换腔室,后段四批被全部拦下,直接止损三十多万。这个案例让全厂意识到:控制图的价值不在画图,在早一拍发现。

异常每多流一段工序,损失就指数级放大——前段报废一片可能只值几百,流到封装端才发现就是几万。一张控制图的成本几乎为零,它拦下的报废却是实打实的成本。这也是为什么我反复强调,matplotlib在FAB不是炫技工具,而是印钞机的一部分:它把事后救火变成事前拦截,每一张标红的图背后,可能就是一笔被保住的良率。

十一、进阶:多变量控制图与报警降噪

单变量控制图一次管一个参数,但现代CVD一次采集几十个参数,全画单图会被报警淹没,工程师见红就麻。进阶做法是多变量控制图,比如Hotelling T²统计量,把相关的几个参数合成一个统计量来监控;或者先做PCA降维,再监控主成分得分。这样原本几十张图的信息,浓缩成一张,异常一红就值得看。

但浓缩也带来误报风险,所以必须报警降噪:我们叠加Nelson八规则做判异(连续9点落在中心线同侧、连续6点单调上升等),再要求连续3个点趋势才升级告警,把单点随机波动过滤掉。降噪之后,误报率从每天几十条降到个位数。控制图从吵闹的闹钟变成可靠的哨兵,工程师才愿意信它、用它、按它行动。

走到这一步,你手里的matplotlib已经不是画图工具,而是一套工艺监控方法论的载体。从一张Xbar-R图,到全厂多变量监控网,中间差的不是代码行数,而是你想清楚三件事:要监控什么参数、怎么定义异常、怎么在不吵的前提下及时告警。想清楚这三件,matplotlib只是顺手实现的手段。

十二、把图表能力沉淀为团队资产

阿文一个人的控制图,价值有限;当它变成团队共享的图表模板库,价值才放大。我们把常用图(控制图、良率趋势、帕累托、WaferMap、OEE瀑布)做成带统一深色主题、统一字体、统一标注规范的模板函数,任何人import就能出风格一致的图。新人不从零踩坑,出的图直接能进周报。

模板库还带来一个隐性好处:图表口径统一。以前每人手画的良率趋势,目标线画在哪、标红阈值多少各不同,跨人对比时容易误读。模板固定了这些口径,看图的人不必再猜这张图的标准是什么。可视化的最高境界,不是某张图多漂亮,而是全公司看图用同一套语言。

回头看,matplotlib入门这一课,真正要学的不是plt.plot那几行,而是用图表让数字说话这件事背后的工程习惯:数据要可追溯、口径要统一、异常要早显、图表要可复用。把这几条变成团队资产,你一个人省下的时间,会变成所有人省下的时间。

十三、控制图不是画完就完事

新手最容易把控制图当静态图,画完贴墙上就完了。控制图是活的:控制限要定期用稳定期重算,数据基准变了限要跟着变,否则图会慢慢失真。我们规定每四周用最近稳定四周重算限,期间固定,确保趋势可比。

另一个误区是见红就慌。控制图偶尔一个点越界可能是普通波动,先用Nelson规则确认是不是真的判异,再决定要不要停机。过度反应比不反应更伤产能,我们吃过因单点误报停线两小时反而拖累交付的亏。

第三个误区是只看Xbar不看R。R图往往比Xbar更早暴露问题:极差变大说明过程离散度上升,常预示设备开始不稳。老工程师看控制图先扫R再看Xbar,顺序错了容易漏掉早期信号。

第四个误区是图表不归档。控制图的价值在纵向对比,今天和上月同设备同参数放一起,趋势才看得清。我们把每日控制图按设备归档,月度复盘直接拉历史,异常何时起、何时消一目了然。

把这四个误区记牢,你的控制图才是真的在监控,而不是在制造焦虑。matplotlib只是画笔,画什么、怎么读,才是工程师的真本事,也是它能不能进周报的底气。

十四、给想深入matplotlib的你

如果你不满足于画控制图,matplotlib还能做动画(FuncAnimation看参数实时演化)、3D图(晶圆三维形貌)、交互(滑块调控制限)。这些进阶用法,本质还是Figure和Axes那一套,学会了基础,进阶只是加几个API。

最后一句:matplotlib这门课,考的不是你会画几种图,而是你愿不愿意把重复的手活交给代码。阿文的故事已经证明,早一拍自动化,省的是真金白银。官网独享资源里有完整图表模板库,拿来改改就能用。

💡数字不会主动说话,但一张好的控制图会。把重复的手活交给代码,把清醒的大脑留给判断——这才是工程师该有的工作方式。

——官网独享资源www.yezhihui.cn ——

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询