├── .gitignore ├── LICENSE ├── README.rst ├── d3py ├── __init__.py ├── core.py ├── plot.py └── version.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Jaakko Luttinen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. warning:: 2 | 3 | Currently, the package is just a quick experimental skeleton for personal use, not really usable. 4 | 5 | 6 | D3Py 7 | ==== 8 | 9 | D3Py is a thin Python wrapper for D3.js. The main goal is to enable users to 10 | easily copy-paste beautiful D3.js visualizations from http://bl.ocks.org and use 11 | them in their Jupyter Notebooks for their own data. 12 | 13 | Other features: 14 | 15 | - two-way synchronization: 16 | - update graphs based on updates on Python data 17 | - update Python data based on (user) interactions on the graph 18 | 19 | - support other JS libraries 20 | 21 | Change of plans: 22 | 23 | Support general JavaScript plotting libraries. D3.js is often too low level, so 24 | make it possible to use other JS libraries easily. Some of them are built on top 25 | of D3.js and some are not, but that shouldn't matter. 26 | 27 | Here's one list: http://thenextweb.com/dd/2015/06/12/20-best-javascript-chart-libraries/ 28 | 29 | Some highlights: 30 | 31 | - Really nice timeseries plotting: 32 | http://dygraphs.com/gallery/ 33 | - Nice innovation for timeseries plotting to save vertical space: 34 | https://square.github.io/cubism/ 35 | - Basics of several plot types: http://code.shutterstock.com/rickshaw/examples/ 36 | - Basics of several plot types: http://c3js.org/examples.html 37 | - Basics of several plot types: http://nvd3.org/examples/index.html 38 | - Graphs: http://js.cytoscape.org/ 39 | - Graphs: http://sigmajs.org/ 40 | - Miscellaneous plots: http://plottablejs.org/ 41 | 42 | 43 | Installation 44 | ------------ 45 | 46 | Install latest release from PyPI: 47 | 48 | .. code:: console 49 | 50 | pip install d3py 51 | 52 | 53 | Install latest development version from GitHub: 54 | 55 | .. code:: console 56 | 57 | pip install git+https://github.com/jluttine/d3py.git@develop 58 | 59 | 60 | Usage 61 | ----- 62 | 63 | 64 | Contributing 65 | ------------ 66 | 67 | Contributions are most welcome. You can, for instance, improve the core methods 68 | in order to make using D3.js easier, or provide example graphs as a function in 69 | `plot` module along the Javascript and CSS files under `lib` folder. 70 | 71 | 72 | License 73 | ------- 74 | 75 | The GNU GPL v2 applies for the package as a whole. Most of the individual files 76 | are released under the MIT license. However, some Javascript and CSS files of 77 | the example graphs from other people have been released under other FLOSS 78 | licenses. Thus, the package as a whole is released under the GPLv2 license. 79 | Contributions are assumed to be MIT licensed unless otherwise stated. 80 | -------------------------------------------------------------------------------- /d3py/__init__.py: -------------------------------------------------------------------------------- 1 | from d3py.core import * 2 | import d3py.plot as plot 3 | -------------------------------------------------------------------------------- /d3py/core.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Jaakko Luttinen 2 | # This file is released under the MIT license 3 | 4 | """ 5 | Functions for handling HTML, Javascript and CSS snippets. 6 | """ 7 | 8 | import string 9 | import random 10 | from IPython.display import HTML, display 11 | import os 12 | import json 13 | import numpy as np 14 | 15 | 16 | def _get_random_id(): 17 | """ Get a random (i.e., unique) string identifier""" 18 | symbols = string.ascii_uppercase + string.ascii_lowercase + string.digits 19 | return ''.join(random.choice(symbols) for _ in range(15)) 20 | 21 | 22 | def read_file(filename): 23 | """ Return the contents of a file as a string. """ 24 | with open(filename) as f: 25 | str = f.read() 26 | return str 27 | 28 | 29 | def get_lib_filename(category, name): 30 | """ Get a filename of a built-in library file. """ 31 | base_dir = os.path.dirname(os.path.abspath(__file__)) 32 | if category == 'js': 33 | filename = os.path.join('js', '{0}.js'.format(name)) 34 | elif category == 'css': 35 | filename = os.path.join('css', '{0}.css'.format(name)) 36 | elif category == 'html': 37 | filename = os.path.join('html', '{0}.html'.format(name)) 38 | else: 39 | raise ValueError("Unknown category") 40 | return os.path.join(base_dir, 'lib', filename) 41 | 42 | 43 | def read_lib(category, name): 44 | """ Get contents of a built-in library file. """ 45 | return read_file(get_lib_filename(category, name)) 46 | 47 | 48 | def populate_template(template, **kwargs): 49 | """ Return a template string with filled argument values. """ 50 | return string.Template(template).safe_substitute(kwargs) 51 | 52 | 53 | def display_html(html): 54 | """ Display given HTML code string. """ 55 | display(HTML(html)) 56 | return 57 | 58 | 59 | def output_notebook( 60 | d3js_url="//d3js.org/d3.v3.min", 61 | requirejs_url="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.10/require.min.js", 62 | html_template=None 63 | ): 64 | """ Import required Javascript libraries to Jupyter Notebook. """ 65 | 66 | if html_template is None: 67 | html_template = read_lib('html', 'setup') 68 | 69 | setup_html = populate_template( 70 | html_template, 71 | d3js=d3js_url, 72 | requirejs=requirejs_url 73 | ) 74 | 75 | display_html(setup_html) 76 | return 77 | 78 | 79 | def create_graph_html(js_template, css_template, html_template=None): 80 | """ Create HTML code block given the graph Javascript and CSS. """ 81 | if html_template is None: 82 | html_template = read_lib('html', 'graph') 83 | 84 | # Create div ID for the graph and give it to the JS and CSS templates so 85 | # they can reference the graph. 86 | graph_id = 'graph-{0}'.format(_get_random_id()) 87 | js = populate_template(js_template, graph_id=graph_id) 88 | css = populate_template(css_template, graph_id=graph_id) 89 | 90 | return populate_template( 91 | html_template, 92 | graph_id=graph_id, 93 | css=css, 94 | js=js 95 | ) 96 | 97 | 98 | def graph(js_template, css_template): 99 | """ Create and display a graph HTML given Javascript and CSS. """ 100 | html = create_graph_html(js_template, css_template) 101 | return display_html(html) 102 | 103 | 104 | def array_to_json(data): 105 | """ Convert an array or a list to JSON. """ 106 | return json.dumps(np.asanyarray(data).tolist()) 107 | 108 | def dict_to_json(d): 109 | return json.dumps(d) 110 | -------------------------------------------------------------------------------- /d3py/plot.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Jaakko Luttinen 2 | # This file is released under the MIT license 3 | 4 | """ 5 | Some examples of D3.js graphs 6 | """ 7 | 8 | from d3py import core 9 | 10 | 11 | def line_chart(x, y): 12 | 13 | # Build Javascript 14 | js = core.populate_template( 15 | core.read_lib('js', 'line_chart'), 16 | data=core.dict_to_json([dict(x=xi, y=yi) for (xi, yi) in zip(x, y)]), 17 | # x=core.array_to_json(x), 18 | # y=core.array_to_json(y), 19 | #width=width, 20 | #height=height, 21 | #colors=core.array_to_json(colors) 22 | ) 23 | 24 | # Build CSS 25 | css = core.populate_template(core.read_lib('css', 'line_chart')) 26 | 27 | # Build graph HTML 28 | return core.graph(js, css) 29 | 30 | 31 | def chord_diagram(data, width=950, height=500, colors=None): 32 | 33 | # Define default color cycle 34 | if colors is None: 35 | colors = [ 36 | "#000000", "#FFDD89", "#957244", "#F26223" 37 | ] 38 | 39 | # Build Javascript 40 | js = core.populate_template( 41 | core.read_lib('js', 'chord_diagram'), 42 | data=core.array_to_json(data), 43 | width=width, 44 | height=height, 45 | colors=core.array_to_json(colors) 46 | ) 47 | 48 | # Build CSS 49 | css = core.populate_template(core.read_lib('css', 'chord_diagram')) 50 | 51 | # Build graph HTML 52 | return core.graph(js, css) 53 | -------------------------------------------------------------------------------- /d3py/version.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.0' 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = dev 3 | tag_date = true 4 | 5 | [aliases] 6 | release = egg_info -RDb '' 7 | release_pypi = release register sdist upload -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ################################################################################ 4 | # Copyright (C) 2016 Jaakko Luttinen 5 | # 6 | # This file is licensed under the MIT License. 7 | ################################################################################ 8 | 9 | 10 | # Read version number __version__ from the file 11 | # See: https://packaging.python.org/en/latest/single_source_version/#single-sourcing-the-version 12 | import os 13 | version = {} 14 | base_dir = os.path.dirname(os.path.abspath(__file__)) 15 | with open(os.path.join(base_dir, 'd3py', 'version.py')) as fp: 16 | exec(fp.read(), version) 17 | __version__ = version['__version__'] 18 | 19 | NAME = 'd3py' 20 | DESCRIPTION = 'Thin Python wrapper for D3.js' 21 | AUTHOR = 'Jaakko Luttinen' 22 | AUTHOR_EMAIL = 'jaakko.luttinen@iki.fi' 23 | URL = 'https://github.com/jluttine/d3py' 24 | LICENSE = 'MIT' 25 | VERSION = __version__ 26 | 27 | 28 | if __name__ == "__main__": 29 | 30 | # Utility function to read the README file. 31 | # Used for the long_description. It's nice, because now 1) we have a top level 32 | # README file and 2) it's easier to type in the README file than to put a raw 33 | # string in below ... 34 | def read(fname): 35 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 36 | 37 | from setuptools import setup, find_packages 38 | 39 | setup( 40 | install_requires = [ 41 | ], 42 | packages = find_packages(), 43 | name = NAME, 44 | version = VERSION, 45 | author = AUTHOR, 46 | author_email = AUTHOR_EMAIL, 47 | description = DESCRIPTION, 48 | license = LICENSE, 49 | url = URL, 50 | long_description = read('README.rst'), 51 | keywords = [ 52 | 'visualization', 53 | ], 54 | classifiers = [ 55 | 'Programming Language :: Python', 56 | 'Development Status :: 1 - Planning', 57 | 'Framework :: IPython', 58 | 'Intended Audience :: Developers', 59 | 'Intended Audience :: Science/Research', 60 | 'License :: OSI Approved :: MIT License', 61 | 'Operating System :: OS Independent', 62 | 'Topic :: Multimedia :: Graphics :: Presentation', 63 | 'Topic :: Scientific/Engineering :: Visualization', 64 | ], 65 | ) 66 | 67 | --------------------------------------------------------------------------------