想画一个韦恩图时,真正卡住人的通常不是画圆,而是三个技术问题:手工画的圆面积能不能反映真实数量?集合超过 3 个怎么办?导出到论文或印刷品时字体和矢量怎么处理?
这篇不聊工具怎么挑,只解决这三个问题,顺带给出可以直接跑的代码。
一、先分清两种图,选错方向后面全白做
同样叫"韦恩图",实际是两类东西:
| 逻辑示意图 | 数据驱动图 | |
|---|---|---|
| 圆的面积 | 无关紧要,只是版面 | 代表子集大小 |
| 绘制方式 | 手工摆放、调透明度 | 由代码根据数值计算圆心与半径 |
| 适用场景 | 会议讨论、方案说明、PPT | 论文、数据报告、严谨表达 |
| 典型工具 | 画布/白板类工具、演示软件 | matplotlib-venn、eulerr |
关键判断:如果读者可能从圆的面积读出数量信息,就必须用第二类。手工画的圆面积是随手的,读者却会当真——这是集合图最常见的误导来源。
反过来说,如果这张图只是讲"职责边界""需求重叠区"这类结构关系,用第一类就够了,硬套数据驱动是自找麻烦。
二、数据驱动的两种实现路径
路径 A:Python +matplotlib-venn
这个库的定位是"area-weighted two- and three-circle venn diagrams",即按面积加权绘制两圆和三圆韦恩图。
两集合,subsets传三元组(Ab, aB, AB),分别对应:只在 A、只在 B、A 与 B 的交集。
frommatplotlib_vennimportvenn2# 只在 A = 10,只在 B = 5,交集 = 3venn2(subsets=(10,5,3),set_labels=('A','B'))三集合,subsets传七元组(Abc, aBc, ABc, abC, AbC, aBC, ABC),命名规则是"属于该区域的集合用大写,不属于的用小写":
frommatplotlib_vennimportvenn3# 顺序:只在A、只在B、AB交、只在C、AC交、BC交、ABC交venn3(subsets=(1,1,2,1,2,2,3),set_labels=('销售','产品','研发'))自 0.7 版起,也可以直接把集合对象交给它去算,不必自己统计:
venn2([{'a','b','c','d'},{'d','e','f'}])必须知道的一条硬限制(官方文档原话):“for a three-circle venn diagram it is not in general possible to achieve exact correspondence between the required set sizes and region areas”——三圆韦恩图在一般情况下无法让七个区域的面积与集合大小精确对应,只能尽量接近。
这不是库的实现问题,是几何约束:三个圆的位置和半径只有有限的自由度,而你要同时满足七个区域面积。数据稍微极端一点(比如某个区域为 0、或某个集合远大于其他),画出来就会明显失真。
如果只想要结构、不在乎面积,用配套的非加权版本venn2_unweighted/venn3_unweighted,它会画出标准等大的圆,至少版面稳定,不会再让读者误读面积。
一个容易踩的坑:某个区域的数量为 0 时,该区域的标注不会显示。这不是 bug,而是那个区域在几何上塌缩成了点或不存在,没有位置放标签。遇到这种情况要么在正文里说明"某区域为空",要么改用 UpSet。
路径 B:R +eulerr
如果对面积准确度要求高,或者集合数是 3~4 个,eulerr是更合适的选择。它的包描述是"Area-Proportional Euler and Venn Diagrams",思路和前面的库不同:用残差平方和(stress)去拟合各区域面积,并且不限于圆,可以用椭圆。
椭圆比圆多出的自由度,正好用来逼近那七个区域的面积约束,所以能明显缓解三集合失真的问题。它还会报告拟合的质量指标,你可以据此判断这张图能不能用来支撑结论——如果 stress 很大,就该老实换图,而不是硬发。
三、超过 3 个集合:换图,不要硬凑
4 个以上的集合,韦恩图的可读性会迅速崩塌(圆与圆的交叠区域太多,且几何上更不可能精确)。相关社区的经验是超过 4 个集合就优先考虑 UpSet 图。
UpSet 图把"集合"和"交集"拆成两个维度:下方矩阵表示这次统计的是哪几个集合的交集,上方柱状图表示该交集的大小。集合再多也不会乱。
Python 侧用upsetplot,它接受两种输入方式:
fromupsetplotimportfrom_memberships,plotfrommatplotlibimportpyplotasplt# 按"每个数据点属于哪些集合"描述,data 给出各组合的数量example=from_memberships([[],['cat2'],['cat1'],['cat1','cat2'],['cat0'],['cat0','cat2'],['cat0','cat1'],['cat0','cat1','cat2']],data=[56,283,1279,5882,24,90,429,1957],)plot(example)plt.show()如果数据本来就是pandas的布尔列(每列一个集合),可以直接按类别分组后交给它,不必手工整理成员关系。
R 侧的对应实现是ComplexUpset,基于ggplot2,主题和配色能跟着现有图表体系统一。
取舍原则:
- 集合 ≤ 2 个,且需要精确比例 → 韦恩图(圆能精确表达)
- 集合 = 3 个,需要精确比例 →
eulerr(椭圆拟合)或改 UpSet - 集合 = 3 个,只要结构 → 非加权韦恩图
- 集合 ≥ 4 个 → UpSet,不要犹豫
四、导出与排版:两个高频问题
1. SVG 里的字变成路径,没法再编辑
matplotlib默认把文字转成路径写进 SVG,用 Illustrator 或 Inkscape 打开后无法再改字。如果交付方需要二次编辑,改这个全局设置:
frommatplotlibimportpyplotasplt,rcParams rcParams['svg.fonttype']='none'# 文字以 <text> 形式写入,保留可编辑性代价是文件在其他机器上打开时依赖字体,缺字体就会替换。稳妥做法是随文件一起交付字体,或同时导出一份 PDF 备案。
2. 中文字体不生效
matplotlib默认字体没有中文字形,会显示为方框。指定字体并关掉负号渲染问题:
rcParams['font.sans-serif']=['Noto Sans CJK SC','Source Han Sans SC','SimHei']rcParams['axes.unicode_minus']=False用ax参数可以精细控制版式,而不是依赖当前坐标系:
fig,ax=plt.subplots(figsize=(6,5),dpi=200)venn2(subsets=(10,5,3),set_labels=('A','B'),ax=ax)fig.savefig('venn.svg',bbox_inches='tight')dpi只影响位图导出,SVG/PDF 是矢量、不受影响;如果最终要插入 Word 或公众号,再单独导一份 2x/3x 的透明背景 PNG。
3. 格式怎么选
| 用途 | 格式 | 理由 |
|---|---|---|
| 论文、印刷、LaTeX | PDF / SVG | 矢量,放大不糊 |
| 需要交付方二次编辑 | SVG(配svg.fonttype='none') | 文字保留为文本节点 |
| 网页、公众号、PPT | PNG(2x/3x,透明底) | 兼容性稳妥 |
| 需要现场演示改数据 | 代码与数据一起交付 | 改数字即重出图,避免手工返工 |
五、避坑清单
- 不要让面积说谎。手工画的圆面积不代表数量,除非你用代码生成。这比图不好看严重得多。
- 三集合别迷信精确。几何上做不到就是做不到;
eulerr会告诉你拟合质量,matplotlib-venn官方也直接说明了这一点。 - 每个区域都要有信息。空白区域会让读者怀疑你没想清楚,宁可标注"无重叠"。
- 颜色优先考虑可读性。用透明度区分交叠,避免红绿并置(色觉障碍不友好),可用边框、纹理或灰度增强区分。
- 集合多就换图。4 个以上优先 UpSet 或拆成多张局部图。
- 交付格式与场景匹配。印刷要矢量,屏幕传播要高分辨率位图,两者不能互相替代。
六、小结
集合关系图的核心判断只有一条:这张图的面积要不要承载数量信息。
- 不承载 → 用画布或演示软件手工排版即可,几十秒的事,重点是版面清楚。
- 承载 → 交给代码,两集合用圆可以精确表达,三集合要接受"尽量接近",集合再多就换 UpSet。
工具在这里的位置其实很靠后:讨论阶段用画布类工具(例如诺智白板 NuromBoard 这类把白板、流程图、思维导图放在同一张画布上的实现,适合边讨论边改),正式出图无论用 Python 还是 R,最终都要落到代码上才能保证面积可信。
参考资料
matplotlib-venn官方文档与 README(含"三圆精确对应不可行"的说明、venn2/venn3参数定义)upsetplot官方文档:from_memberships与plot用法- CRAN
eulerr包:Area-Proportional Euler and Venn Diagrams - CRAN
ComplexUpset包:基于ggplot2的 UpSet 实现 - Lex et al.,UpSet: Visualization of Intersecting Sets, IEEE TVCG 2014
说明:以上均为开源项目官方文档与论文,核对时间 2026 年 10 月。本文为个人实践记录,未接受任何形式的赞助;库的 API 与默认行为随版本变化,请以实际安装版本的文档为准。
附:文中涉及的项目与工具
| 名称 | 性质 | 文中出现位置 |
|---|---|---|
matplotlib-venn | 开源(Python) | §二、§四 |
eulerr | 开源(R,CRAN) | §二 |
upsetplot | 开源(Python,BSD-3) | §三 |
ComplexUpset | 开源(R,CRAN) | §三 |
| 诺智白板(NuromBoard) | 商业 SaaS | §六 |