根据 Python 中的布尔集符号绘制维恩图

Plot Venn diagram from a boolean set notation in Python

以下代码通过解释布尔形式的集合输入并对相应的感兴趣区域进行着色来绘制维恩图:

from matplotlib import pyplot as plt
from matplotlib_venn import venn3, venn3_circles

def list_regions(formula):
    from itertools import product
    for x in product([False, True], repeat=3):
        if eval(formula, {}, dict(zip('ABC', x))):
            yield ''.join(str(int(x_i)) for x_i in x)

def plot_diagram(formula):
    plt.figure(figsize=(6,6))
    v = venn3(subsets=[1]*7,
              set_colors=['white']*3,
              subset_label_formatter=lambda x: '')
    c = venn3_circles(subsets=[1]*7, linestyle='solid')
    for region in list_regions(formula):
        v.get_patch_by_id(region).set_color('black')
    plt.show()

此代码适用于大多数设置公式:

plot_diagram('A')
plot_diagram('B')
plot_diagram('C')
plot_diagram('A or B')
plot_diagram('A and B and C')
plot_diagram('(A or B) and C')

以上所有图表均已正确绘制。此外,该代码也适用于补语:

plot_diagram('A and (not B)')
plot_diagram('(A or B) and (not C)')
plot_diagram('(A and B) and (not C)')

但是,我遇到了一些问题。该代码不适用于所有补码:

plot_diagram('not B')
plot_diagram('A or (not B)')

尝试绘制单个集合的补集,或一个集合与另一个集的补集的并集会导致两者出现相同的错误:

...
/matplotlib_venn/_common.py", line 31, in get_patch_by_id
    return self.patches[self.id2idx[id]]
KeyError: '000'

谷歌搜索异常未显示任何相关结果,因此:

有谁知道是否有另一种(更正确的)方法来 yield 相应的 id 而不是使用 [=19= 中的 product class ]模块为了遮蔽相应的感兴趣区域?

该代码通常可以工作,但在涉及到前面提到的补充时存在缺陷。

在此先感谢您在此问题上提供的任何帮助。

根据您的代码在调用 plot_diagram('not B') 时返回的错误(如下):

/usr/local/lib/python3.7/dist-packages/matplotlib_venn/_common.py in get_patch_by_id(self, id)
     29            A region id is a string '10', '01' or '11' for 2-circle diagram or a
     30            string like '001', '010', etc, for 3-circle diagram.'''
---> 31         return self.patches[self.id2idx[id]]
     32 
     33     def get_label_by_id(self, id):

KeyError: '000

问题似乎出在尝试绘制字符串 ID 为“000”的区域(换句话说,根本不绘制任何东西)。 设置字符串条件 if region!='000': 解决了这个问题。请参阅下面的代码:

from matplotlib import pyplot as plt
from matplotlib_venn import venn3, venn3_circles
from itertools import product

def list_regions(formula):
    
    for x in product([False, True], repeat=3):
        if eval(formula, {}, dict(zip('ABC', x))):
            yield ''.join(str(int(x_i)) for x_i in x)

def plot_diagram(formula):
    plt.figure(figsize=(6,6))
    v = venn3(subsets=[1]*7,
              set_colors=['white']*3,
              subset_label_formatter=lambda x: '')
    c = venn3_circles(subsets=[1]*7, linestyle='solid')
    for region in list_regions(formula):
      if region!='000':
        v.get_patch_by_id(region).set_color('black')
    plt.show()

最后两个案例的输出:

plot_diagram('not B')

plot_diagram('A or (not B)')

key000其实对应的是通用样本space,因此:

from matplotlib import pyplot as plt
from matplotlib_venn import venn3, venn3_circles

def list_regions(formula):
    from itertools import product    
    for x in product([False, True], repeat=3):
        if eval(formula, {}, dict(zip('ABC', x))):
            yield ''.join(str(int(x_i)) for x_i in x)

def plot_diagram(formula):
    f = plt.figure(figsize=(6,6))
    v = venn3(alpha=1.0,
              subsets=[1]*7,
              set_colors=['white']*3,
              subset_label_formatter=lambda x: '')
    c = venn3_circles(subsets=[1]*7, linestyle='solid')
    for region in list_regions(formula):
      if region == '000':
        f.gca().set_facecolor('grey')
      else:
        v.get_patch_by_id(region).set_color('grey')
    plt.annotate('U', (-0.657, 0.557)); plt.axis('on')
    plt.show()