You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

matplotlib_venn绘制韦恩图时补集着色异常问题求助

韦恩图补集着色问题解决方案

问题描述

使用matplotlib_venn库绘制韦恩图时,计算集合补集或相关表达式出现着色异常,无法让未着色部分保持纯白色。例如输入表达式x-complement(表示集合A的补集)时,韦恩图的着色不符合预期。

原代码

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

#Initialize
universal_set = set()
empty_set = set()
complement = set()
expression = ""
setA = set()   
setB = set() 
setC = set()

def evaluate_expression(formula, empty_set):
    for x in product([False, True], repeat=3):
        x_dict = dict(zip('xyz', x))
        x_dict['universal_set'] = universal_set
        x_dict['empty_set'] = empty_set
        x_dict['complement'] = complement

        if 'empty_set' in formula:
            formula = formula.replace('empty_set', 'False')
        if 'universal_set' in formula:
            formula = formula.replace('universal_set', 'True')
        if 'complement' in formula:
            formula = formula.replace('complement', 'True')

        if eval(formula, {}, x_dict):
            yield ''.join(str(int(x_i)) for x_i in x)

def plot_diagram(formula, empty_set):
    plt.figure(figsize=(6, 6))
    v = venn3(subsets=[1] * 7,
              set_colors=['white'] * 3,
              subset_label_formatter=lambda x: '')

    for region in evaluate_expression(formula, empty_set):
        if region != '000':
            v.get_patch_by_id(region).set_color('#FFC0CB')
        else:
            ax= plt.axes()
            ax.figure.set_facecolor('#FFC0CB')

    # Add labels for setA, setB, and setC
    v.get_label_by_id('100').set_text(', '.join(map(str, setA - setB - setC)))
    v.get_label_by_id('010').set_text(', '.join(map(str, setB - setA - setC)))
    v.get_label_by_id('001').set_text(', '.join(map(str, setC - setB - setA)))

    # Add labels for intersections
    v.get_label_by_id('101').set_text(', '.join(map(str, setA & setC - setB)))
    v.get_label_by_id('110').set_text(', '.join(map(str, setA & setB - setC)))
    v.get_label_by_id('011').set_text(', '.join(map(str, setB & setC - setA)))
    v.get_label_by_id('111').set_text(', '.join(map(str, setB & setC & setA)))

    venn3_circles(subsets=[1] * 7, linestyle='solid')


#Create menu
def menu():
    print("#1 Enter a number to Set A")
    print("#2 Enter a number to Set B")
    print("#3 Enter a number to Set C")
    print("#4 Evaluate Expression")
    print("#5 Display the Venn Diagram")
    print("#6 Exit") 

#Execute depending on user's choice
while True:
    menu ()
    choice= input("Enter your number of choice: ")

    if choice == '1':
        try:
            setA.update(map(int, input("Enter a number to set A (separate using space): ").split()))
        except ValueError: 
            print("Error. Input must be an integer")
    elif choice == '2':
        try:
            setB.update(map(int, input("Enter a number to set B (separate using space): ").split()))
        except ValueError: 
            print("Error. Input must be an integer ")
    elif choice == '3':
        try:
            setC.update(map(int, input("Enter a number to set C (separate using space): ").split()))
        except ValueError: 
            print("Error. Input must be an integer")
    elif choice == '4': 
        print("x for set A, y for set B, z for set C") 
        print("| for union, & for intersection, -complement for complement, universal_set for universal set, empty_set for empty set")
        expression = input("Evaluate Expression: ")
        

    elif choice == '5':
        if expression:
            try:
                plot_diagram(expression, empty_set)
                plt.show()
            except Exception as e:
                print("Invalid formula. Error:", str(e))
        else:
            print("Please evaluate an expression first.")
    elif choice =='6':
        print("Exiting the program.")
        break
    else:
        print("Invalid input")

问题根源

  1. 补集表达式解析错误:原代码将complement直接替换为True,无法正确解析x-complement这类表示“x的补集”的逻辑,导致生成的区域ID错误。
  2. 未显式重置所有区域颜色:仅设置set_colors=['white']*3不足以确保所有子集区域为纯白,部分区域可能保留默认透明度或底色。
  3. 背景色处理逻辑冲突:处理000区域时直接修改整个figure背景,但未确保其他非目标区域保持白色。

解决方案

关键修改点

  1. 修正补集表达式解析:将x-complement这类语法转换为not x,正确计算补集逻辑。
  2. 初始化为纯白底色:在着色前,将所有韦恩图区域显式设置为白色,并关闭透明度。
  3. 正确处理背景色:仅当000区域属于目标集合时,才修改背景色,否则保持背景纯白。

修改后的完整代码

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

# Initialize
universal_set = set()
empty_set = set()
expression = ""
setA = set()   
setB = set() 
setC = set()

def evaluate_expression(formula):
    # 处理补集语法:将 x-complement 转换为 not x
    formula = formula.replace('x-complement', 'not x')
    formula = formula.replace('y-complement', 'not y')
    formula = formula.replace('z-complement', 'not z')
    # 替换全集和空集
    formula = formula.replace('universal_set', 'True')
    formula = formula.replace('empty_set', 'False')
    
    for x in product([False, True], repeat=3):
        x_dict = dict(zip('xyz', x))
        if eval(formula, {}, x_dict):
            yield ''.join(str(int(x_i)) for x_i in x)

def plot_diagram(formula):
    plt.figure(figsize=(6, 6), facecolor='white')  # 确保figure背景默认纯白
    v = venn3(subsets=[1] * 7,
              subset_label_formatter=lambda x: '')

    # 先将所有区域设置为纯白色,关闭透明度
    for region_id in ['100', '010', '001', '110', '101', '011', '111']:
        patch = v.get_patch_by_id(region_id)
        if patch:
            patch.set_color('white')
            patch.set_alpha(1.0)  # 关闭透明度,避免透显底层颜色

    target_regions = list(evaluate_expression(formula))
    # 对目标区域着色
    for region in target_regions:
        if region != '000':
            patch = v.get_patch_by_id(region)
            if patch:
                patch.set_color('#FFC0CB')
                patch.set_alpha(1.0)
        else:
            # 000区域是全集外部分,修改背景色
            plt.gcf().set_facecolor('#FFC0CB')

    # 添加集合元素标签(空集合时显示空字符串)
    v.get_label_by_id('100').set_text(', '.join(map(str, setA - setB - setC)) if (setA - setB - setC) else '')
    v.get_label_by_id('010').set_text(', '.join(map(str, setB - setA - setC)) if (setB - setA - setC) else '')
    v.get_label_by_id('001').set_text(', '.join(map(str, setC - setB - setA)) if (setC - setB - setA) else '')

    v.get_label_by_id('101').set_text(', '.join(map(str, setA & setC - setB)) if (setA & setC - setB) else '')
    v.get_label_by_id('110').set_text(', '.join(map(str, setA & setB - setC)) if (setA & setB - setC) else '')
    v.get_label_by_id('011').set_text(', '.join(map(str, setB & setC - setA)) if (setB & setC - setA) else '')
    v.get_label_by_id('111').set_text(', '.join(map(str, setA & setB & setC)) if (setA & setB & setC) else '')

    venn3_circles(subsets=[1] * 7, linestyle='solid')


# Create menu
def menu():
    print("#1 Enter a number to Set A")
    print("#2 Enter a number to Set B")
    print("#3 Enter a number to Set C")
    print("#4 Evaluate Expression")
    print("#5 Display the Venn Diagram")
    print("#6 Exit") 

# Execute depending on user's choice
while True:
    menu ()
    choice= input("Enter your number of choice: ")

    if choice == '1':
        try:
            setA.update(map(int, input("Enter a number to set A (separate using space): ").split()))
        except ValueError: 
            print("Error. Input must be an integer")
    elif choice == '2':
        try:
            setB.update(map(int, input("Enter a number to set B (separate using space): ").split()))
        except ValueError: 
            print("Error. Input must be an integer ")
    elif choice == '3':
        try:
            setC.update(map(int, input("Enter a number to set C (separate using space): ").split()))
        except ValueError: 
            print("Error. Input must be an integer")
    elif choice == '4': 
        print("x for set A, y for set B, z for set C") 
        print("| for union, & for intersection, x-complement for complement of A, universal_set for universal set, empty_set for empty set")
        expression = input("Evaluate Expression: ")
        

    elif choice == '5':
        if expression:
            try:
                plot_diagram(expression)
                plt.show()
            except Exception as e:
                print("Invalid formula. Error:", str(e))
        else:
            print("Please evaluate an expression first.")
    elif choice =='6':
        print("Exiting the program.")
        break
    else:
        print("Invalid input")

说明

  • 补集语法改为x-complement对应“集合A的补集”,解析时转换为not x,符合逻辑运算规则。
  • 所有韦恩图区域初始强制设为白色并关闭透明度,确保未着色区域不会出现底色透显。
  • 仅当000区域(全集外)属于目标表达式时,才修改figure背景色,否则保持纯白。

内容的提问来源于stack exchange,提问作者mbmocls

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 14:20:59