forked from konstantint/matplotlib-venn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e1f2030
Showing
8 changed files
with
804 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
Venn diagram plotting routines for Python/Matplotlib | ||
==================================================== | ||
|
||
About | ||
----- | ||
This package contains a rountine for plotting area-weighted three-circle venn diagrams. | ||
|
||
Copyright 2012, Konstantin Tretyakov. | ||
http://kt.era.ee/ | ||
|
||
Licensed under BSD. | ||
|
||
|
||
Installation | ||
------------ | ||
Installable as any other Python package, either through :code:`easy_install`, or through :code:`python setup.py install`, or by simply including the package to :code:`sys.path`. | ||
|
||
Usage | ||
----- | ||
There are two main functions in the package: :code:`venn3_circles` and :code:`venn3` | ||
|
||
Both accept as their only required argument an 8-element list of set sizes, | ||
|
||
:code:`sets = (abc, Abc, aBc, ABc, abC, AbC, aBC, ABC)` | ||
|
||
That is, for example, :code:`sets[1]` contains the size of the set (A and not B and not C), | ||
:code:`sets[3]` contains the size of the set (A and B and not C), etc. Note that the value in :code:`sets[0]` is not used. | ||
|
||
The function :code:`venn3_circles` simply draws three circles such that their intersection areas would correspond | ||
to the desired set intersection sizes. Note that it is not impossible to achieve exact correspondence, but in | ||
most cases the picture will still provide a decent indication. | ||
|
||
The function :code:`venn3` draws the venn diagram as a collection of 8 separate colored patches with text labels. | ||
|
||
The function :code:`venn3_circles` returns the list of Circle patches that may be tuned further. | ||
The function :code:`venn3` returns an object of class :code:`Venn3`, which also gives access to diagram patches and text elements. | ||
|
||
Basic Example:: | ||
from matplotlib.venn import venn3 | ||
venn3(sets = (0, 1, 1, 1, 2, 1, 2, 2), set_labels = ('Set1', 'Set2', 'Set3')) | ||
More elaborate example:: | ||
|
||
from matplotlib import pyplot as plt | ||
import numpy as np | ||
from matplotlib.venn import venn3, venn3_circles | ||
plt.figure(figsize=(4,4)) | ||
v = venn3(sets=(0, 1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C')) | ||
v.get_patch_by_id('100').set_alpha(1.0) | ||
v.get_patch_by_id('100').set_color('white') | ||
v.get_text_by_id('100').set_text('Unknown') | ||
v.labels[0].set_text('Set "A"') | ||
c = venn3_circles(sets=(0, 1, 1, 1, 1, 1, 1, 1), linestyle='dashed') | ||
c[0].set_lw(1.0) | ||
c[0].set_ls('dotted') | ||
plt.title("Sample Venn diagram") | ||
plt.annotate('Unknown set', xy=v.get_text_by_id('100').get_position() - np.array([0, 0.05]), xytext=(-70,-70), | ||
ha='center', textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='gray', alpha=0.1), | ||
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5',color='gray')) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Namespace package | ||
try: | ||
__import__('pkg_resources').declare_namespace(__name__) | ||
except ImportError: | ||
__path__ = __import__('pkgutil').extend_path(__path__, __name__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
''' | ||
Venn diagram plotting routines. | ||
Copyright 2012, Konstantin Tretyakov. | ||
http://kt.era.ee/ | ||
Licensed under BSD. | ||
This package contains a rountine for plotting area-weighted three-circle venn diagrams. | ||
There are two main functions here: venn3_circles and venn3 | ||
Both accept as their only required argument an 8-element list of set sizes, | ||
sets = (abc, Abc, aBc, ABc, abC, AbC, aBC, ABC) | ||
That is, for example, sets[1] contains the size of the set (A and not B and not C), | ||
sets[3] contains the size of the set (A and B and not C), etc. Note that the value in sets[0] is not used. | ||
The function venn3_circles simply draws three circles such that their intersection areas would correspond | ||
to the desired set intersection sizes. Note that it is not impossible to achieve exact correspondence, but in | ||
most cases the picture will still provide a decent indication. | ||
The function venn3 draws the venn diagram as a collection of 8 separate colored patches with text labels. | ||
The function venn3_circles returns the list of Circle patches that may be tuned further. | ||
The function venn3 returns an object of class Venn3, which also gives access to diagram patches and text elements. | ||
Example: | ||
from matplotlib import pyplot as plt | ||
import numpy as np | ||
from matplotlib.venn import venn3, venn3_circles | ||
plt.figure(figsize=(4,4)) | ||
v = venn3(sets=(0, 1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C')) | ||
v.get_patch_by_id('100').set_alpha(1.0) | ||
v.get_patch_by_id('100').set_color('white') | ||
v.get_text_by_id('100').set_text('Unknown') | ||
v.labels[0].set_text('Set "A"') | ||
c = venn3_circles(sets=(0, 1, 1, 1, 1, 1, 1, 1), linestyle='dashed') | ||
c[0].set_lw(1.0) | ||
c[0].set_ls('dotted') | ||
plt.title("Sample Venn diagram") | ||
plt.annotate('Unknown set', xy=v.get_text_by_id('100').get_position() - np.array([0, 0.05]), xytext=(-70,-70), | ||
ha='center', textcoords='offset points', bbox=dict(boxstyle='round,pad=0.5', fc='gray', alpha=0.1), | ||
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0.5',color='gray')) | ||
''' | ||
from matplotlib.venn import * | ||
v = venn3(sets=(0, 1, 1, 1, 1, 1, 1, 1), set_labels = ('A', 'B', 'C')) | ||
venn3_circles(sets=(0, 1, 1, 1, 1, 1, 1, 1), linestyle='dashed') | ||
v.get_patch_by_id('100').set_alpha(1.0) | ||
v.get_patch_by_id('100').set_color('white') | ||
v.get_text_by_id('100').set_text('Unknown') | ||
|
||
|
||
from _venn3 import venn3, venn3_circles | ||
___all___ = ['venn3', 'venn3_circles'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
''' | ||
Venn diagram plotting routines. | ||
Math helper functions. | ||
Copyright 2012, Konstantin Tretyakov. | ||
http://kt.era.ee/ | ||
Licensed under BSD. | ||
''' | ||
|
||
from scipy.optimize import brentq | ||
import numpy as np | ||
|
||
tol = 1e-10 | ||
|
||
def circle_intersection_area(r, R, d): | ||
''' | ||
Formula from: http://mathworld.wolfram.com/Circle-CircleIntersection.html | ||
Does not make sense for negative r, R or d | ||
>>> circle_intersection_area(0.0, 0.0, 0.0) | ||
0.0 | ||
>>> circle_intersection_area(1.0, 1.0, 0.0) | ||
3.1415... | ||
>>> circle_intersection_area(1.0, 1.0, 1.0) | ||
1.2283... | ||
''' | ||
if np.abs(d) < tol: | ||
minR = np.min([r, R]) | ||
return np.pi * minR**2 | ||
if np.abs(r - 0) < tol or np.abs(R - 0) < tol: | ||
return 0.0 | ||
d2, r2, R2 = float(d**2), float(r**2), float(R**2) | ||
arg = (d2 + r2 - R2)/2/d/r | ||
arg = np.max([np.min([arg, 1.0]), -1.0]) # Even with valid arguments, the above computation may result in things like -1.001 | ||
A = r2 * np.arccos(arg) | ||
arg = (d2 + R2 - r2)/2/d/R | ||
arg = np.max([np.min([arg, 1.0]), -1.0]) | ||
B = R2 * np.arccos(arg) | ||
arg = (-d+r+R)*(d+r-R)*(d-r+R)*(d+r+R) | ||
arg = np.max([arg, 0]) | ||
C = -0.5*np.sqrt(arg) | ||
return A + B + C | ||
|
||
def circle_line_intersection(center, r, a, b): | ||
''' | ||
Computes two intersection points between the circle centered at <center> and radius <r> and a line given by two points a and b. | ||
If no intersection exists, or if a==b, None is returned. If one intersection exists, it is repeated in the answer. | ||
>>> circle_line_intersection(np.array([0.0, 0.0]), 1, np.array([-1.0, 0.0]), np.array([1.0, 0.0])) | ||
array([[ 1., 0.], | ||
[-1., 0.]]) | ||
>>> np.round(circle_line_intersection(np.array([1.0, 1.0]), np.sqrt(2), np.array([-1.0, 1.0]), np.array([1.0, -1.0])), 6) | ||
array([[ 0., 0.], | ||
[ 0., 0.]]) | ||
''' | ||
s = b - a | ||
# Quadratic eqn coefs | ||
A = np.linalg.norm(s)**2 | ||
if abs(A) < tol: | ||
return None | ||
B = 2*np.dot(a-center, s) | ||
C = np.linalg.norm(a-center)**2 - r**2 | ||
disc = B**2 - 4*A*C | ||
if disc < 0.0: | ||
return None | ||
t1 = (-B + np.sqrt(disc))/2.0/A | ||
t2 = (-B - np.sqrt(disc))/2.0/A | ||
return np.array([a + t1*s, a+t2*s]) | ||
|
||
def find_distance_by_area(r, R, a): | ||
''' | ||
Solves circle_intersection_area(r, R, d) == a for d numerically (analytical solution seems to be too ugly to pursue). | ||
Assumes that a < pi * min(r, R)**2, will fail otherwise. | ||
>>> find_distance_by_area(1, 1, 0) | ||
2.0 | ||
>>> round(find_distance_by_area(1, 1, 3.1415), 4) | ||
0.0 | ||
>>> d = find_distance_by_area(2, 3, 4) | ||
>>> d | ||
3.37... | ||
>>> round(circle_intersection_area(2, 3, d), 10) | ||
4.0 | ||
''' | ||
if r > R: | ||
r, R = R, r | ||
if np.abs(a) < tol: | ||
return float(r + R) | ||
if np.abs(min([r, R])**2*np.pi - a) < tol: | ||
return np.abs(R - r) | ||
return brentq(lambda x: circle_intersection_area(r, R, x) - a, R-r, R+r) | ||
|
||
def circle_circle_intersection(C_a, r_a, C_b, r_b): | ||
''' | ||
Finds the coordinates of the intersection points of two circles A and B. | ||
Circle center coordinates C_a and C_b, should be given as tuples (or 1x2 arrays). | ||
Returns a 2x2 array result with result[0] being the first intersection point (to the right of the vector C_a -> C_b) | ||
and result[1] being the second intersection point. | ||
If there is a single intersection point, it is repeated in output. | ||
If there are no intersection points or an infinite number of those, None is returned. | ||
>>> circle_circle_intersection([0, 0], 1, [1, 0], 1) # Two intersection points | ||
array([[ 0.5 , -0.866...], | ||
[ 0.5 , 0.866...]]) | ||
>>> circle_circle_intersection([0, 0], 1, [2, 0], 1) # Single intersection point (circles touch from outside) | ||
array([[ 1., 0.], | ||
[ 1., 0.]]) | ||
>>> circle_circle_intersection([0, 0], 1, [0.5, 0], 0.5) # Single intersection point (circles touch from inside) | ||
array([[ 1., 0.], | ||
[ 1., 0.]]) | ||
>>> circle_circle_intersection([0, 0], 1, [0, 0], 1) is None # Infinite number of intersections (circles coincide) | ||
True | ||
>>> circle_circle_intersection([0, 0], 1, [0, 0.1], 0.8) is None # No intersections (one circle inside another) | ||
True | ||
>>> circle_circle_intersection([0, 0], 1, [2.1, 0], 1) is None # No intersections (one circle outside another) | ||
True | ||
''' | ||
C_a, C_b = np.array(C_a, float), np.array(C_b, float) | ||
v_ab = C_b - C_a | ||
d_ab = np.linalg.norm(v_ab) | ||
if np.abs(d_ab) < tol: # No intersection points | ||
return None | ||
cos_gamma = (d_ab**2 + r_a**2 - r_b**2)/2.0/d_ab/r_a | ||
if abs(cos_gamma) > 1.0: | ||
return None | ||
sin_gamma = np.sqrt(1 - cos_gamma**2) | ||
u = v_ab / d_ab | ||
v = np.array([-u[1], u[0]]) | ||
pt1 = C_a + r_a * cos_gamma*u - r_a * sin_gamma*v | ||
pt2 = C_a + r_a * cos_gamma*u + r_a * sin_gamma*v | ||
return np.array([pt1, pt2]) | ||
|
||
def vector_angle_in_degrees(v): | ||
''' | ||
Given a vector, returns its elevation angle in degrees (-180..180). | ||
>>> vector_angle_in_degrees([1, 0]) | ||
0.0 | ||
>>> vector_angle_in_degrees([1, 1]) | ||
45.0 | ||
>>> vector_angle_in_degrees([0, 1]) | ||
90.0 | ||
>>> vector_angle_in_degrees([-1, 1]) | ||
135.0 | ||
>>> vector_angle_in_degrees([-1, 0]) | ||
180.0 | ||
>>> vector_angle_in_degrees([-1, -1]) | ||
-135.0 | ||
>>> vector_angle_in_degrees([0, -1]) | ||
-90.0 | ||
>>> vector_angle_in_degrees([1, -1]) | ||
-45.0 | ||
''' | ||
return np.arctan2(v[1], v[0])*180/np.pi |
Oops, something went wrong.