Skip to content

Commit

Permalink
Add arcs ordering and names rotations (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
Silmathoron authored Sep 14, 2020
1 parent 2844e77 commit fda286c
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 12 deletions.
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
from .chord_diagram import chord_diagram


__version__ = "0.1.0"
__version__ = "0.2.0"

__author__ = "Tanguy Fardet"
64 changes: 53 additions & 11 deletions chord_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
LW = 0.3


def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
ax=None, colors=None, cmap=None, alpha=0.7,
def chord_diagram(mat, names=None, order=None, width=0.1, pad=2., gap=0.03,
chordwidth=0.7, ax=None, colors=None, cmap=None, alpha=0.7,
use_gradient=False, show=False, **kwargs):
"""
Plot a chord diagram.
Expand All @@ -30,7 +30,11 @@ def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
mat : square matrix
Flux data, mat[i, j] is the flux from i to j
names : list of str, optional (default: no names)
Names of the nodes that will be displayed.
Names of the nodes that will be displayed (must be ordered as the
matrix entries).
order : list, optional (default: order of the matrix entries)
Order in which the arcs should be placed around the trigonometric
circle.
width : float, optional (default: 0.1)
Width/thickness of the ideogram arc.
pad : float, optional (default: 2)
Expand All @@ -52,7 +56,9 @@ def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
same color as the arc they belong to.
**kwargs : keyword arguments
Available kwargs are "fontsize" and "sort" (either "size" or
"distance"), "zero_entry_size" (in degrees, default: 0.5).
"distance"), "zero_entry_size" (in degrees, default: 0.5),
"rotate_names" (a bool or list of bools) to rotate (some of) the
names by 90°.
"""
import matplotlib.pyplot as plt

Expand Down Expand Up @@ -87,6 +93,24 @@ def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
if mat[j, i] != 0:
mat[i, j] = min_deg

# check name rotations
rotate_names = kwargs.get("rotate_names", False)

if isinstance(rotate_names, Sequence):
assert len(rotate_names) == num_nodes, \
"Wrong number of entries in 'rotate_names'."
else:
rotate_names = [rotate_names]*num_nodes

# check order
if order is not None:
mat = mat[order][:, order]

rotate_names = [rotate_names[i] for i in order]

if names is not None:
names = [names[i] for i in order]

# sum over rows
x = mat.sum(axis=1).A1 if is_sparse else mat.sum(axis=1)

Expand Down Expand Up @@ -130,7 +154,7 @@ def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
angle -= 270

nodePos.append(
tuple(polar2xy(1.1, 0.5*(start + end)*np.pi/180.)) + (angle,))
tuple(polar2xy(1.05, 0.5*(start + end)*np.pi/180.)) + (angle,))

z = _get_normed_line(mat, i, x, start, end, is_sparse)

Expand All @@ -143,7 +167,8 @@ def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
remainder = 0 if num_nodes % 2 else -1

ids = list(range(i - int(0.5*num_nodes), i))[::-1]
ids += list(range(i, i + int(0.5*num_nodes) + remainder + 1))
ids += [i]
ids += list(range(i + int(0.5*num_nodes) + remainder, i, -1))

# put them back into [0, num_nodes[
ids = np.array(ids)
Expand Down Expand Up @@ -191,11 +216,28 @@ def chord_diagram(mat, names=None, width=0.1, pad=2., gap=0.03, chordwidth=0.7,
prop = {
"fontsize": kwargs.get("fontsize", 16*0.8),
"ha": "center",
"va": "center"
"va": "center",
}

for pos, name in zip(nodePos, names):
ax.text(pos[0], pos[1], name, rotation=pos[2], **prop)
for i, (pos, name) in enumerate(zip(nodePos, names)):
rotate = rotate_names[i]
pp = prop.copy()

if rotate:
angle = np.average(arc[i])
rotate = 90

if 90 < angle < 180 or 270 < angle:
rotate = -90

if 90 < angle < 270:
pp["ha"] = "right"
else:
pp["ha"] = "left"

pp["rotation_mode"] = "anchor"

ax.text(pos[0], pos[1], name, rotation=pos[2] + rotate, **pp)

# configure axis
ax.set_xlim(-1.1, 1.1)
Expand Down Expand Up @@ -403,9 +445,9 @@ def chord_arc(start1, end1, start2, end2, radius=1.0, pad=2, chordwidth=0.7,

start2, end2, verts2, _ = initial_path(start2, end2, radius, chordwidth)

chordwidth2 *= np.clip(0.4 + (dtheta1 - 2*pad) / (15*pad), 0.4, 1)
chordwidth2 *= np.clip(0.4 + (dtheta1 - 2*pad) / (15*pad), 0.2, 1)

chordwidth *= np.clip(0.4 + (dtheta2 - 2*pad) / (15*pad), 0.4, 1)
chordwidth *= np.clip(0.4 + (dtheta2 - 2*pad) / (15*pad), 0.2, 1)

rchord = radius * (1-chordwidth)
rchord2 = radius * (1-chordwidth2)
Expand Down
6 changes: 6 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os, errno
from setuptools import setup, find_packages
from shutil import rmtree


# create directory
Expand Down Expand Up @@ -82,3 +83,8 @@
os.rename(directory + fname, fname)
except:
pass

try:
rmtree(directory)
except:
pass

0 comments on commit fda286c

Please sign in to comment.