如何平滑Python绘制的韦恩图(Venn diagram)所有边缘?
问题描述
使用Python结合matplotlib与matplotlib_venn库绘制韦恩图时,图中所有边缘(包括内部边缘)均呈现棱角,需解决:
- 棱角产生的原因
- 边缘平滑的实现方法
附原代码及效果图:
import matplotlib.pyplot as plt from matplotlib_venn import venn3 from matplotlib.patches import PathPatch from matplotlib.path import Path set1 = {i for i in range(150)} set2 = {i for i in range(150 - 75, 350 - 75)} set3 = {i for i in range(150 - 75, 150 - 75 + 20)} fig, ax = plt.subplots() venn = venn3([set1, set2, set3], set_labels=('', '', '')) circle1 = venn.get_patch_by_id('100') circle2 = venn.get_patch_by_id('010') circle3 = venn.get_patch_by_id('110') circle4 = venn.get_patch_by_id('111') circle1.set_edgecolor('none') circle2.set_edgecolor('none') circle3.set_edgecolor('none') circle4.set_edgecolor('none') path_data1 = circle1.get_path().vertices.tolist() patch1 = PathPatch(Path(path_data1), facecolor='yellow', edgecolor='#d6d3d6', hatch='/', lw=0) ax.add_patch(patch1) path_data2 = circle2.get_path().vertices.tolist() patch2 = PathPatch(Path(path_data2), facecolor='purple', edgecolor='#d6d3d6', hatch='\\', lw=0) ax.add_patch(patch2) path_data3 = circle3.get_path().vertices.tolist() patch3 = PathPatch(Path(path_data3), facecolor='pink', edgecolor='#d6d3d6', hatch="\/", lw=0) ax.add_patch(patch3) path_data4 = circle4.get_path().vertices.tolist() patch4 = PathPatch(Path(path_data4), facecolor='orange', edgecolor='#d6d3d6', hatch=".", lw=0) ax.add_patch(patch4) venn.get_label_by_id('100').set_text('') venn.get_label_by_id('010').set_text('') venn.get_label_by_id('110').set_text('') venn.get_label_by_id('111').set_text('') # Show the plot plt.axis('off') plt.show()
效果图:
棱角产生的原因
matplotlib_venn生成的韦恩图区块,其路径(Path)是通过贝塞尔曲线实现平滑边缘的。每个Path对象包含两部分核心数据:
vertices:路径的顶点坐标codes:每个顶点对应的路径指令(比如移动到起点、贝塞尔曲线控制点、绘制曲线等)
你在代码中仅提取了vertices来创建新Path,忽略了codes。此时matplotlib会默认用直线连接所有顶点,自然出现棱角。
边缘平滑的解决方案
有两种简单的修正方式:
方式一:直接复用原Patch的Path
无需重新创建Path,直接基于原区块的路径生成带样式的新PathPatch:
import matplotlib.pyplot as plt from matplotlib_venn import venn3 from matplotlib.patches import PathPatch set1 = {i for i in range(150)} set2 = {i for i in range(150 - 75, 350 - 75)} set3 = {i for i in range(150 - 75, 150 - 75 + 20)} fig, ax = plt.subplots() venn = venn3([set1, set2, set3], set_labels=('', '', '')) # 获取原区块并隐藏 circle1 = venn.get_patch_by_id('100') circle2 = venn.get_patch_by_id('010') circle3 = venn.get_patch_by_id('110') circle4 = venn.get_patch_by_id('111') for patch in [circle1, circle2, circle3, circle4]: patch.set_visible(False) # 基于原路径创建带填充和hatch的新Patch patch1 = PathPatch(circle1.get_path(), facecolor='yellow', edgecolor='#d6d3d6', hatch='/', lw=0) ax.add_patch(patch1) patch2 = PathPatch(circle2.get_path(), facecolor='purple', edgecolor='#d6d3d6', hatch='\\', lw=0) ax.add_patch(patch2) patch3 = PathPatch(circle3.get_path(), facecolor='pink', edgecolor='#d6d3d6', hatch="\/", lw=0) ax.add_patch(patch3) patch4 = PathPatch(circle4.get_path(), facecolor='orange', edgecolor='#d6d3d6', hatch=".", lw=0) ax.add_patch(patch4) # 隐藏所有标签 for label_id in ['100', '010', '110', '111']: venn.get_label_by_id(label_id).set_text('') plt.axis('off') plt.show()
方式二:同时提取vertices和codes创建Path
如果需要手动构建Path,需同时传入顶点坐标和路径指令:
import matplotlib.pyplot as plt from matplotlib_venn import venn3 from matplotlib.patches import PathPatch from matplotlib.path import Path set1 = {i for i in range(150)} set2 = {i for i in range(150 - 75, 350 - 75)} set3 = {i for i in range(150 - 75, 150 - 75 + 20)} fig, ax = plt.subplots() venn = venn3([set1, set2, set3], set_labels=('', '', '')) circle1 = venn.get_patch_by_id('100') circle2 = venn.get_patch_by_id('010') circle3 = venn.get_patch_by_id('110') circle4 = venn.get_patch_by_id('111') for patch in [circle1, circle2, circle3, circle4]: patch.set_edgecolor('none') # 同时提取vertices和codes path1 = circle1.get_path() patch1 = PathPatch(Path(path1.vertices, path1.codes), facecolor='yellow', edgecolor='#d6d3d6', hatch='/', lw=0) ax.add_patch(patch1) path2 = circle2.get_path() patch2 = PathPatch(Path(path2.vertices, path2.codes), facecolor='purple', edgecolor='#d6d3d6', hatch='\\', lw=0) ax.add_patch(patch2) path3 = circle3.get_path() patch3 = PathPatch(Path(path3.vertices, path3.codes), facecolor='pink', edgecolor='#d6d3d6', hatch="\/", lw=0) ax.add_patch(patch3) path4 = circle4.get_path() patch4 = PathPatch(Path(path4.vertices, path4.codes), facecolor='orange', edgecolor='#d6d3d6', hatch=".", lw=0) ax.add_patch(patch4) for label_id in ['100', '010', '110', '111']: venn.get_label_by_id(label_id).set_text('') plt.axis('off') plt.show()
内容的提问来源于stack exchange,提问作者maciejwww
相关产品推荐
相关产品推荐

