konstantint / matplotlib-venn

Area-weighted venn-diagrams for Python/matplotlib
MIT License
495 stars 67 forks source link

Inputting a set formula in Python to generate the corresponding Venn diagram #44

Closed felixlee0530 closed 4 years ago

felixlee0530 commented 4 years ago

Hello konstantint,

I am a student who's trying to make a simple Python-based program which can convert inputs of set formulas into a graphically generated Venn diagram using matplotlib-venn.

First, I understand that I can use the following form of function to color a specific sector in a 3-circle Venn diagram. v.get_patch_by_id('001/010/011/100/101/110/111').set_color('any color')

So I can use the function multiple times to color different sectors with different colors. Is there a possible method if I can use matplotlib-venn to point out or indicate a group of certain sectors of a specific inputted set formula? For example, in a 3-circle Venn Diagram which has seven sectors, a sample formula such like (A union B) intersection C inputted in Python would output a Venn diagram with three distinct sectors shaded (and other sectors white-colored).

Currently I am thinking to create a database with all the possible outcomes of inputted equations to lists of shaded areas, but it will take too much time and I cannot think of all possible inputs in the database. So, I am asking you if there is a Python or matplotlib algorithm which can help me with this.

Also, when I launch this python command, the venn diagram is kinda inversed. How can I make it as a upright triangular shape? (one circle at top and two circles at bottom)

from matplotlib import pyplot as plt
from matplotlib_venn import venn3, venn3_circles
plt.figure(figsize=(5,5))
v = venn3(subsets=(1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C'))
v.get_patch_by_id('001').set_color('white')
c = venn3_circles(subsets=(1, 1, 1, 1, 1, 1, 1), linestyle='solid')
plt.title("VENN DIAGRAM: THREE CIRCLES")
plt.show()

Thanks a lot.

konstantint commented 4 years ago

If you need to generate lists of regions matching a given formula, consider something like:

from itertools import product

def list_regions(formula):
  """NB: Uses eval(formula), unsafe to use with external input"""

  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)

You could then use it as follows:

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('(A or B) and C'):
  v.get_patch_by_id(region).set_color('black')

Note that the list_regions code above uses eval and is thus not safe for external inputs. If you want something that must work with externally-provided, possibly abusive user input, consider the asteval package or a custom expression interpreter made with pyparsing (way easier than you might think, really).

The simplest way to flip the image, I guess, would be to simply invert the y-axis. As a result the set labels may get a bit mispositioned and you can hack-fix this by changing their horizontal alignment or relocating them a bit higher or lower as necessary. Example:

def flip_vertically(venn_diag, axes):
  ylims = axes.get_ylim()
  axes.set_ylim(-ylims[0]+0.1, -ylims[1]-0.1)
  for lbl in 'ABC':
    venn_diag.get_label_by_id(lbl).set_verticalalignment('center')

(apply by calling flip_vertically(v, plt.gca()) after the main plot)