Search code examples
pythonmatplotlibmatplotlib-venn

Change line style circle segment Venn diagram


I have this Venn diagram:

from matplotlib_venn import venn2, venn2_circles

venn2(subsets = (30, 10, 5), set_labels = ('Group A', 'Group B'), set_colors=('r', 'g'), alpha = 0.5);
venn2_circles(subsets = (30, 10, 5));

enter image description here

Is it possible to change the line style to dashed of just the circle segments where Group A and B overlap (i.e. the lines around the brown area)?


Solution

  • Option 1

    By setting the edgecolor on each of the patches in the venn diagram (A, B, and C) we can see that the paths overlap around the overlapping section.

    import matplotlib.pyplot as plt
    from matplotlib_venn import venn2, venn2_circles
    
    for char in "ABC":
        v = venn2(
            subsets=(30, 10, 5),
            set_labels=("Group A", "Group B"),
            set_colors=("r", "g"),
            alpha=0.5,
        )
        overlap = v.get_patch_by_id(char)
        overlap.set_edgecolor("black")
        overlap.set_alpha(1)
        overlap.set_ls("dashed")
        overlap.set_lw(1.5)
        plt.show()
    
    A B C
    A B C

    But, by using a contrasting colour we can outline the overlap. The downside to this method is that we loose the alpha on the overlap.

    # plot venn
    v = venn2(
        subsets=(30, 10, 5),
        set_labels=("Group A", "Group B"),
        set_colors=("r", "g"),
        alpha=0.5,
    )
    # add circles
    c = venn2_circles(subsets=(30, 10, 5), linewidth=2, color="black")
    
    overlap = v.get_patch_by_id("C")  # get the overlap
    overlap.set_edgecolor("white")    # set the edgecolor
    overlap.set_alpha(1)              # set the alpha to 1 otherwise 
                                      #   we can't see the line
    overlap.set_linestyle("dashed")   # set a dashed line
    overlap.set_linewidth(2)          # set same linewidth as the circles
    overlap.set_zorder(2)             # bump overlap up a level so we can 
                                      #   see the line
    

    overlapped section with dashed edge


    Option 2

    This method is completely untested outside of this problem As far as I can tell it should work for most 2-circle venn diagrams.

    This one is a bit more involved, but it allows us to seamlessly add dashed lines around the overlap without affecting the alpha. It requires that we import a function from a private module in matplotlib_venn, circle_circle_intersection which we will use to create matplotlib.patches.Arcs.

    These arcs will replace the circles drawn by matplotlib_venn.

    import math
    import matplotlib.pyplot as plt
    from matplotlib.patches import Arc
    from matplotlib_venn._math import circle_circle_intersection
    
    # Create `v` and `c` as in the previous example
    
    # Get the current ax
    ax = plt.gca()
    # Get the intersections of the circles
    (P2x, P2y), (P3x, P3y) = circle_circle_intersection(
        c[0].center, c[0].radius, c[1].center, c[1].radius
    )
    
    # Iterate through the circles matplotlib_venn added
    for _c in c:
        # remove the circle
        _c.set_visible(False)
        # get the center or the circle and the angle at the center (theta)
        P1x, P1y = _c.center
        theta = math.degrees(
            math.atan2(P3y - P1y, P3x - P1x) - math.atan2(P2y - P1y, P2x - P1x)
        )
        # Add the Arcs
        ax.add_patch(
            Arc(
                xy=_c.center,
                width=_c.radius * 2,
                height=_c.radius * 2,
                linewidth=2,
                color="black",
                angle=theta / 2 if theta < 180 else theta / -2,
                theta2=360 - theta if theta < 180 else theta,
            )
        )
        ax.add_patch(
            Arc(
                xy=_c.center,
                width=_c.radius * 2,
                height=_c.radius * 2,
                linewidth=2,
                color="black",
                linestyle="dashed",
                angle=theta / 2 if theta > 180 else theta / -2,
                theta2=360 - theta if theta > 180 else theta,
            )
        )
    plt.show()
    

    Which produces:

    Overlapping venn diagram with different edges and preserved alpha