├── circos ├── __init__.py └── circos.py ├── MANIFEST.in ├── tests ├── test_circos.py └── test_graphs │ ├── interval_network.pkl │ ├── 07 inter-subtype interactions.pkl │ └── Test Graph Generator.ipynb ├── requirements.txt ├── environment.yml ├── README.md ├── .travis.yml ├── setup.py └── .gitignore /circos/__init__.py: -------------------------------------------------------------------------------- 1 | from .circos import CircosPlot 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include README.md -------------------------------------------------------------------------------- /tests/test_circos.py: -------------------------------------------------------------------------------- 1 | def test_import(): 2 | from circos import CircosPlot 3 | 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.11.1 2 | matplotlib==1.5.1 3 | setuptools==25.1.1 4 | pycodestyle==2.0.0 5 | -------------------------------------------------------------------------------- /tests/test_graphs/interval_network.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericmjl/Circos/HEAD/tests/test_graphs/interval_network.pkl -------------------------------------------------------------------------------- /tests/test_graphs/07 inter-subtype interactions.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericmjl/Circos/HEAD/tests/test_graphs/07 inter-subtype interactions.pkl -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | dependencies: 3 | - matplotlib 4 | - mkl 5 | - numpy 6 | - openssl 7 | - pip 8 | - python=3.5 9 | - readline 10 | - setuptools 11 | - tk 12 | - wheel 13 | - xz 14 | - zlib 15 | - pip: 16 | - pytest 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: This is a deprecated project! We will be moving Circos to [nxviz](http://github.com/ericmjl/nxviz) for future development. 2 | 3 | # Circos 4 | 5 | A utility for making circos plots in matplotlib. 6 | 7 | URL: circos.ca 8 | 9 | ## Changelog 10 | 11 | Version 1.2: 12 | 13 | 1. Fixed `setup.py` script. 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | # We don't actually use the Travis Python, but this keeps it organized. 4 | - "3.5" 5 | install: 6 | # We do this conditionally because it saves us some downloading if the 7 | # version is the same. 8 | - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; 9 | - bash miniconda.sh -b -p $HOME/miniconda 10 | - export PATH="$HOME/miniconda/bin:$PATH" 11 | - hash -r 12 | - conda config --set always_yes yes --set changeps1 no 13 | - conda update -q conda 14 | 15 | # Useful for debugging any issues with conda 16 | - conda info -a 17 | 18 | # Install Python, py.text, and required packages. 19 | - conda env create -f environment.yml 20 | - source activate build 21 | - python setup.py install 22 | 23 | script: 24 | # Your test script goes here 25 | - pycodestyle circos/. 26 | - py.test tests/. 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | from pip.req import parse_requirements 4 | 5 | 6 | def read(fname): 7 | """ 8 | Utility function to read the README file. Used for the 9 | long_description. It's nice, because now: 10 | 1. we have a top level README file and 11 | 2. it's easier to type in the README file than to put a raw 12 | string in below ... 13 | """ 14 | return open( 15 | os.path.join(os.path.dirname(__file__), fname) 16 | ).read() 17 | 18 | install_reqs = parse_requirements('requirements.txt', session=False) 19 | reqs = [str(ir.req) for ir in install_reqs] 20 | 21 | packages = ['circos'] 22 | 23 | setup(name="Circos", 24 | version="1.3.5", 25 | author="Eric J. Ma, Justin Zabilansky, Jon Charest", 26 | author_email="ericmajinglong@gmail.com", 27 | description=("Circos plots in Python!"), 28 | license="MIT", 29 | keywords="network visualization, matplotlib, circos", 30 | url="http://github.com/ericmjl/Circos", 31 | packages=packages, 32 | install_requires=reqs, 33 | long_description=read('README.md'), 34 | classifiers=["Topic :: Scientific/Engineering :: Visualization"], 35 | ) 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | *.py[cod] 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # IPython Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # dotenv 82 | .env 83 | 84 | # virtualenv 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject -------------------------------------------------------------------------------- /tests/test_graphs/Test Graph Generator.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "", 4 | "signature": "sha256:6b72ea3cc61f3a7c9b432f91b4ca5ca367de06435d755a2f72dda7619be4792d" 5 | }, 6 | "nbformat": 3, 7 | "nbformat_minor": 0, 8 | "worksheets": [ 9 | { 10 | "cells": [ 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "# Introduction\n", 16 | "\n", 17 | "This notebook will generate test graphs for testing with Circos plots." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "collapsed": false, 23 | "input": [ 24 | "import networkx as nx" 25 | ], 26 | "language": "python", 27 | "metadata": {}, 28 | "outputs": [], 29 | "prompt_number": 1 30 | }, 31 | { 32 | "cell_type": "code", 33 | "collapsed": false, 34 | "input": [ 35 | "G = nx.Graph()\n", 36 | "\n", 37 | "# Add nodes from 0 to 29\n", 38 | "for i in range(0, 30):\n", 39 | " G.add_node(i, time=i)\n", 40 | " \n", 41 | "# Add in edges from i to i + 2.\n", 42 | "for i in G.nodes():\n", 43 | " if i + 2 in G.nodes():\n", 44 | " G.add_edge(i, i+2)" 45 | ], 46 | "language": "python", 47 | "metadata": {}, 48 | "outputs": [], 49 | "prompt_number": 2 50 | }, 51 | { 52 | "cell_type": "code", 53 | "collapsed": false, 54 | "input": [ 55 | "G.edges()" 56 | ], 57 | "language": "python", 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "metadata": {}, 62 | "output_type": "pyout", 63 | "prompt_number": 4, 64 | "text": [ 65 | "[(0, 2),\n", 66 | " (1, 3),\n", 67 | " (2, 4),\n", 68 | " (3, 5),\n", 69 | " (4, 6),\n", 70 | " (5, 7),\n", 71 | " (6, 8),\n", 72 | " (7, 9),\n", 73 | " (8, 10),\n", 74 | " (9, 11),\n", 75 | " (10, 12),\n", 76 | " (11, 13),\n", 77 | " (12, 14),\n", 78 | " (13, 15),\n", 79 | " (14, 16),\n", 80 | " (15, 17),\n", 81 | " (16, 18),\n", 82 | " (17, 19),\n", 83 | " (18, 20),\n", 84 | " (19, 21),\n", 85 | " (20, 22),\n", 86 | " (21, 23),\n", 87 | " (22, 24),\n", 88 | " (23, 25),\n", 89 | " (24, 26),\n", 90 | " (25, 27),\n", 91 | " (26, 28),\n", 92 | " (27, 29)]" 93 | ] 94 | } 95 | ], 96 | "prompt_number": 4 97 | }, 98 | { 99 | "cell_type": "code", 100 | "collapsed": false, 101 | "input": [ 102 | "nx.write_gpickle(G, 'interval_network.pkl')" 103 | ], 104 | "language": "python", 105 | "metadata": {}, 106 | "outputs": [], 107 | "prompt_number": 5 108 | }, 109 | { 110 | "cell_type": "code", 111 | "collapsed": false, 112 | "input": [], 113 | "language": "python", 114 | "metadata": {}, 115 | "outputs": [] 116 | } 117 | ], 118 | "metadata": {} 119 | } 120 | ] 121 | } -------------------------------------------------------------------------------- /circos/circos.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import matplotlib.patches as patches 4 | 5 | from matplotlib.path import Path 6 | 7 | 8 | class CircosPlot(object): 9 | def __init__(self, nodes, edges, radius, 10 | nodecolor=None, edgecolor=None, 11 | nodeprops=None, edgeprops=None, 12 | figsize=(8, 8), ax=None, fig=None): 13 | self.nodes = nodes # list of nodes 14 | self.edges = edges # list of edge tuples 15 | 16 | # Make sure props are dictionaries if passed in 17 | # Node props 18 | if nodeprops is not None: 19 | if isinstance(nodeprops, dict): 20 | self.nodeprops = nodeprops 21 | else: 22 | raise TypeError("nodeprops must be a dictionary") 23 | else: 24 | self.nodeprops = {} 25 | # Edge props 26 | if edgeprops is not None: 27 | if isinstance(edgeprops, dict): 28 | self.edgeprops = edgeprops 29 | else: 30 | raise TypeError("edgeprops must be a dictionary") 31 | else: 32 | self.edgeprops = {} 33 | 34 | # Set colors. Priority: nodecolor > nodeprops > default 35 | # Node color 36 | if nodecolor is not None: 37 | self.nodecolor = nodecolor 38 | elif nodeprops: 39 | try: 40 | self.nodecolor = nodeprops.pop('facecolor') 41 | except KeyError: 42 | self.nodecolor = 'blue' 43 | else: 44 | self.nodecolor = 'blue' 45 | # Edge color 46 | if edgecolor is not None: 47 | self.edgecolor = edgecolor 48 | elif edgeprops: 49 | try: 50 | self.edgecolor = edgeprops.pop('edgecolor') 51 | except KeyError: 52 | self.edgecolor = 'black' 53 | else: 54 | self.edgecolor = 'black' 55 | 56 | self.radius = radius 57 | if fig is None: 58 | self.fig = plt.figure(figsize=figsize) 59 | else: 60 | self.fig = fig 61 | if ax is None: 62 | self.ax = self.fig.add_subplot(111) 63 | else: 64 | self.ax = ax 65 | self.node_radius = self.radius*0.05 66 | self.ax.set_xlim(-radius*1.05, radius*1.05) 67 | self.ax.set_ylim(-radius*1.05, radius*1.05) 68 | self.ax.xaxis.set_visible(False) 69 | self.ax.yaxis.set_visible(False) 70 | for k in self.ax.spines.keys(): 71 | self.ax.spines[k].set_visible(False) 72 | 73 | def draw(self): 74 | self.add_nodes() 75 | self.add_edges() 76 | 77 | def add_nodes(self): 78 | """ 79 | Draws nodes onto the canvas with colours. 80 | """ 81 | r = self.radius 82 | node_r = self.node_radius 83 | # if 'color' in self.nodeprops: 84 | # self.nodeprops.pop('color') 85 | if 'facecolor' in self.nodeprops: 86 | self.nodeprops.pop('facecolor') 87 | # Check if self.nodecolor is a string. If so, this color gets applied 88 | # to all nodes. 89 | if isinstance(self.nodecolor, str): 90 | nodes_and_colors = zip(self.nodes, 91 | [self.nodecolor] * len(self.nodes)) 92 | # Check if nodecolor is an iterable. If so and same length as nodes. 93 | # This applies each matched color to that node. 94 | elif hasattr(self.nodecolor, '__iter__') and \ 95 | (len(self.nodes) == len(self.nodecolor)): 96 | nodes_and_colors = zip(self.nodes, self.nodecolor) 97 | # Throw error if above two conditions are not met. 98 | else: 99 | raise TypeError("""nodecolor must be a string or iterable of the 100 | same length as nodes.""") 101 | # Draw the nodes to screen. 102 | for node, color in nodes_and_colors: 103 | theta = self.node_theta(node) 104 | x, y = get_cartesian(r, theta) 105 | self.nodeprops['facecolor'] = color 106 | node_patch = patches.Ellipse((x, y), node_r, node_r, 107 | lw=0, **self.nodeprops) 108 | self.ax.add_patch(node_patch) 109 | 110 | # def draw_edge(self, node1, node2): 111 | # start_theta = self.node_theta(node1) 112 | # end_theta = self.node_theta(node2) 113 | # # middle_theta = (start_theta + end_theta)/2.0 114 | # # delta_theta = abs(end_theta - start_theta) 115 | # # middle_r = self.radius * (1 - delta_theta / np.pi) 116 | 117 | # # verts = [get_cartesian(self.radius, start_theta), 118 | # # get_cartesian(middle_theta, middle_r), 119 | # # get_cartesian(self.radius,end_theta)] 120 | # verts = [get_cartesian(self.radius, start_theta), 121 | # (0, 0), 122 | # get_cartesian(self.radius, end_theta)] 123 | # codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] 124 | 125 | # path = Path(verts, codes) 126 | # self.edgeprops['facecolor'] = 'none' 127 | # self.edgeprops['edgecolor'] = self.edgecolor 128 | # patch = patches.PathPatch(path, lw=1, **self.edgeprops) 129 | # self.ax.add_patch(patch) 130 | 131 | def node_theta(self, node): 132 | """ 133 | Maps node to Angle. 134 | """ 135 | i = self.nodes.index(node) 136 | theta = i*2*np.pi/len(self.nodes) 137 | 138 | return theta 139 | 140 | def add_edges(self): 141 | """ 142 | Draws edges to screen. 143 | """ 144 | for start, end in self.edges: 145 | start_theta = self.node_theta(start) 146 | end_theta = self.node_theta(end) 147 | verts = [get_cartesian(self.radius, start_theta), 148 | (0, 0), 149 | get_cartesian(self.radius, end_theta)] 150 | codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] 151 | 152 | path = Path(verts, codes) 153 | self.edgeprops['facecolor'] = 'none' 154 | self.edgeprops['edgecolor'] = self.edgecolor 155 | patch = patches.PathPatch(path, lw=1, **self.edgeprops) 156 | self.ax.add_patch(patch) 157 | 158 | 159 | def get_cartesian(r, theta): 160 | x = r*np.sin(theta) 161 | y = r*np.cos(theta) 162 | 163 | return x, y 164 | --------------------------------------------------------------------------------