├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── example.ipynb ├── pandas_highcharts ├── __init__.py ├── core.py ├── data │ ├── ANGEL_SECTORS.csv │ └── DEU_PCPIPCH.csv ├── display.py └── tests.py ├── requirements.txt ├── setup.py └── tox.ini /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: objTGYRteVGW6nzYeLK5HIZaxRWiU97Vk -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | pandas_highcharts/models.py 56 | pandas_highcharts/views.py 57 | pandas_highcharts/urls.py 58 | pandas_highcharts/templates/ 59 | 60 | .ipynb_checkpoints/ 61 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - 2.7 5 | - 3.4 6 | 7 | env: 8 | 9 | install: 10 | - pip install -r requirements.txt 11 | - pip install coveralls 12 | 13 | script: 14 | - nosetests --with-cover --cover-package pandas_highcharts pandas_highcharts 15 | 16 | after_success: coveralls 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Running tests 2 | ============= 3 | 4 | To run the tests, install the dependencies in requirements.txt and also nose:: 5 | 6 | pip install nose 7 | 8 | Then run:: 9 | 10 | nosetests pandas_highcharts 11 | 12 | 13 | Alternatively, to run the tests on all supported versions, install 'tox':: 14 | 15 | pip install tox 16 | 17 | Then just run tox:: 18 | 19 | tox 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Guillaume Thomas 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 | 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include *.ipynb 3 | include *.yml 4 | include *.rst 5 | include LICENSE 6 | include tox.ini 7 | recursive-include pandas_highcharts *.csv 8 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pandas-highcharts 2 | ================= 3 | 4 | .. image:: https://travis-ci.org/gtnx/pandas-highcharts.svg?branch=master 5 | :target: https://travis-ci.org/gtnx/pandas-highcharts 6 | .. image:: https://coveralls.io/repos/gtnx/pandas-highcharts/badge.svg 7 | :target: https://coveralls.io/r/gtnx/pandas-highcharts 8 | 9 | What is it 10 | ---------- 11 | 12 | **pandas-highcharts** is a Python package which allows you to easily 13 | build `Highcharts plots`_ with `pandas`_.\ `DataFrame`_ objects. 14 | 15 | Motivation 16 | ---------- 17 | 18 | - pandas is the best tool to handle data in Python 19 | - pandas is able to produce matplotlib plots. They work pretty well but 20 | have two major drawbacks 21 | 22 | - Not very web friendly 23 | - Pretty ugly 24 | 25 | - Highcharts produce nice, interactive plot in your browser and is very 26 | complete 27 | 28 | Features 29 | -------- 30 | 31 | - Same interface as DataFrame.plot 32 | - Following parameters are handled 33 | 34 | - data 35 | - x 36 | - y 37 | - kind 38 | - figsize 39 | - use\_index 40 | - title 41 | - grid 42 | - legend 43 | - style 44 | - logx 45 | - logy 46 | - loglog 47 | - xticks 48 | - yticks 49 | - xlim 50 | - ylim 51 | - rot 52 | - fontsize 53 | - position 54 | - stacked 55 | - sort\_columns 56 | - secondary\_y 57 | - mark\_right 58 | 59 | - Following parameters are not handled (yet) : 60 | 61 | - ax 62 | - ay 63 | - subplots 64 | - sharex 65 | - sharey 66 | - layout 67 | - colormap 68 | - colorbar 69 | - layout 70 | - table 71 | - yerr 72 | - xerr 73 | - kwds 74 | 75 | - You can specify those specific highcharts parameters: 76 | 77 | - tooltip 78 | 79 | - Static files (highcharts.js) are not embedded 80 | 81 | Installation 82 | ------------ 83 | 84 | Install the package using pip 85 | 86 | .. code:: shell 87 | 88 | pip install pandas-highcharts 89 | 90 | Usage 91 | ----- 92 | 93 | Import it in your views 94 | 95 | .. code:: python 96 | 97 | import pandas_highcharts 98 | df = ... # create your dataframe here 99 | chart = pandas_highcharts.serialize(df, render_to='my-chart', output_type='json') 100 | 101 | In your templates 102 | 103 | .. code:: html 104 | 105 |
106 | 109 | 110 | Contributing 111 | ------------ 112 | 113 | See CONTRIBUTING.rst for information on how to contribute to pandas-highcharts. 114 | 115 | More examples 116 | ------------- 117 | 118 | Some examples are available on `nbviewer`_. 119 | 120 | Please read the doc for `DataFrame.plot`_. 121 | 122 | For example, with the following dataset: 123 | 124 | 125 | .. code:: python 126 | 127 | import pandas as pd 128 | from pandas_highcharts.core import serialize 129 | from pandas.compat import StringIO 130 | dat = """ts;A;B;C 131 | 2015-01-01 00:00:00;27451873;29956800;113 132 | 2015-01-01 01:00:00;20259882;17906600;76 133 | 2015-01-01 02:00:00;11592256;12311600;48 134 | 2015-01-01 03:00:00;11795562;11750100;50 135 | 2015-01-01 04:00:00;9396718;10203900;43 136 | 2015-01-01 05:00:00;14902826;14341100;53""" 137 | df = pd.read_csv(StringIO(dat), sep=';', index_col='ts', parse_dates='ts') 138 | 139 | # Basic line plot 140 | chart = serialize(df, render_to="my-chart", title="My Chart") 141 | # Basic column plot 142 | chart = serialize(df, render_to="my-chart", title="Test", kind="bar") 143 | # Basic column plot 144 | chart = serialize(df, render_to="my-chart", title="Test", kind="barh") 145 | # Plot C on secondary axis 146 | chart = serialize(df, render_to="my-chart", title="Test", secondary_y = ["C"]) 147 | # Plot on a 1000x700 div 148 | chart = serialize(df, render_to="my-chart", title="Test", figsize = (1000, 700)) 149 | 150 | .. _Highcharts plots: http://www.highcharts.com/ 151 | .. _pandas: https://github.com/pydata/pandas 152 | .. _DataFrame: http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.html 153 | .. _DataFrame.plot: http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.plot.html 154 | .. _nbviewer: http://nbviewer.ipython.org/github/gtnx/pandas-highcharts/blob/master/example.ipynb -------------------------------------------------------------------------------- /example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Pandas Highcharts Example" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "* Use [Highcharts](http://highcharts.com) to plot [pandas](http://pandas.pydata.org) DataFrame\n", 15 | "* Code on Github at [pandas-highcharts](https://github.com/gtnx/pandas-highcharts)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "## Import" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 1, 28 | "metadata": { 29 | "collapsed": false 30 | }, 31 | "outputs": [ 32 | { 33 | "data": { 34 | "text/html": [ 35 | "\n", 36 | "\n", 37 | "\n", 38 | "\n" 39 | ], 40 | "text/plain": [ 41 | "" 42 | ] 43 | }, 44 | "metadata": {}, 45 | "output_type": "display_data" 46 | } 47 | ], 48 | "source": [ 49 | "%load_ext autoreload\n", 50 | "%autoreload 2\n", 51 | "\n", 52 | "import pandas as pd\n", 53 | "import datetime\n", 54 | "import os\n", 55 | "import numpy as np\n", 56 | "from pandas.compat import StringIO\n", 57 | "from pandas.io.common import urlopen\n", 58 | "from IPython.display import display, display_pretty, Javascript, HTML\n", 59 | "from pandas_highcharts.core import serialize\n", 60 | "from pandas_highcharts.display import display_charts\n", 61 | "import matplotlib.pyplot as plt\n", 62 | "\n", 63 | "# Data retrieved from http://www.quandl.com/api/v1/datasets/ODA/DEU_PCPIPCH.csv?column=1\n", 64 | "data = \"\"\"Date,Value\\n2019-12-31,1.7\\n2018-12-31,1.7\\n2017-12-31,1.7\\n2016-12-31,1.5\\n2015-12-31,1.247\\n2014-12-31,0.896\\n2013-12-31,1.601\\n2012-12-31,2.13\\n2011-12-31,2.498\\n2010-12-31,1.158\\n2009-12-31,0.226\\n2008-12-31,2.738\\n2007-12-31,2.285\\n2006-12-31,1.784\\n2005-12-31,1.92\\n2004-12-31,1.799\\n2003-12-31,1.022\\n2002-12-31,1.346\\n2001-12-31,1.904\\n2000-12-31,1.418\\n1999-12-31,0.626\\n1998-12-31,0.593\\n1997-12-31,1.542\\n1996-12-31,1.19\\n1995-12-31,1.733\\n1994-12-31,2.717\\n1993-12-31,4.476\\n1992-12-31,5.046\\n1991-12-31,3.474\\n1990-12-31,2.687\\n1989-12-31,2.778\\n1988-12-31,1.274\\n1987-12-31,0.242\\n1986-12-31,-0.125\\n1985-12-31,2.084\\n1984-12-31,2.396\\n1983-12-31,3.284\\n1982-12-31,5.256\\n1981-12-31,6.324\\n1980-12-31,5.447\\n\"\"\"\n", 65 | "df = pd.read_csv(StringIO(data), index_col=0, parse_dates=True)\n", 66 | "df = df.sort_index()" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "## Basic examples" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 2, 79 | "metadata": { 80 | "collapsed": false 81 | }, 82 | "outputs": [ 83 | { 84 | "data": { 85 | "text/html": [ 86 | "
\n", 87 | " " 88 | ], 89 | "text/plain": [ 90 | "" 91 | ] 92 | }, 93 | "metadata": {}, 94 | "output_type": "display_data" 95 | } 96 | ], 97 | "source": [ 98 | "display_charts(df, title=\"Germany inflation rate\")" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 3, 104 | "metadata": { 105 | "collapsed": false 106 | }, 107 | "outputs": [ 108 | { 109 | "data": { 110 | "text/html": [ 111 | "
\n", 112 | " " 113 | ], 114 | "text/plain": [ 115 | "" 116 | ] 117 | }, 118 | "metadata": {}, 119 | "output_type": "display_data" 120 | } 121 | ], 122 | "source": [ 123 | "display_charts(df, chart_type=\"stock\", title=\"Germany inflation rate\")" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 4, 129 | "metadata": { 130 | "collapsed": false 131 | }, 132 | "outputs": [ 133 | { 134 | "data": { 135 | "text/html": [ 136 | "
\n", 137 | " " 138 | ], 139 | "text/plain": [ 140 | "" 141 | ] 142 | }, 143 | "metadata": {}, 144 | "output_type": "display_data" 145 | } 146 | ], 147 | "source": [ 148 | "display_charts(df, kind=\"bar\", title=\"Germany inflation rate\")" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 5, 154 | "metadata": { 155 | "collapsed": false 156 | }, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/html": [ 161 | "
\n", 162 | " " 163 | ], 164 | "text/plain": [ 165 | "" 166 | ] 167 | }, 168 | "metadata": {}, 169 | "output_type": "display_data" 170 | } 171 | ], 172 | "source": [ 173 | "display_charts(df, kind=\"barh\", title=\"Germany inflation rate\")" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 6, 179 | "metadata": { 180 | "collapsed": false 181 | }, 182 | "outputs": [ 183 | { 184 | "data": { 185 | "text/html": [ 186 | "
\n", 187 | " " 188 | ], 189 | "text/plain": [ 190 | "" 191 | ] 192 | }, 193 | "metadata": {}, 194 | "output_type": "display_data" 195 | } 196 | ], 197 | "source": [ 198 | "display_charts(df, title=\"Germany inflation rate\", legend=None, kind=\"bar\", figsize = (400, 200))" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 7, 204 | "metadata": { 205 | "collapsed": false 206 | }, 207 | "outputs": [ 208 | { 209 | "data": { 210 | "text/html": [ 211 | "
\n", 212 | " " 213 | ], 214 | "text/plain": [ 215 | "" 216 | ] 217 | }, 218 | "metadata": {}, 219 | "output_type": "display_data" 220 | } 221 | ], 222 | "source": [ 223 | "display_charts(df, title=\"Germany inflation rate\", kind=\"bar\", render_to=\"chart5\", zoom=\"xy\")" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 8, 229 | "metadata": { 230 | "collapsed": false 231 | }, 232 | "outputs": [], 233 | "source": [ 234 | "# Data retrieved from https://www.quandl.com/api/v1/datasets/CVR/ANGEL_SECTORS.csv\n", 235 | "data = \"\"\"Year,Software,Healthcare,Hardware,Biotech,Telecom,Manufacturing,Financial Products and Services,IT Services,Industrial/Energy,Retail,Media\\n2013-12-31,23.0,14.0,,11.0,,,7.0,,,7.0,16.0\\n2012-12-31,23.0,14.0,,11.0,,,,,7.0,12.0,7.0\\n2011-12-31,23.0,19.0,,13.0,,,,7.0,13.0,,5.0\\n2010-12-31,16.0,30.0,,15.0,,,,5.0,8.0,5.0,\\n2009-12-31,19.0,17.0,,8.0,,,5.0,,17.0,9.0,\\n2008-12-31,13.0,16.0,,11.0,,,,,8.0,12.0,7.0\\n2007-12-31,27.0,19.0,,12.0,,,,,8.0,6.0,5.0\\n2006-12-31,18.0,21.0,,18.0,,,6.0,,6.0,8.0,\\n2005-12-31,18.0,20.0,8.0,12.0,,,,6.0,6.0,,6.0\\n2004-12-31,22.0,16.0,10.0,10.0,6.0,,8.0,8.0,,7.0,\\n2003-12-31,26.0,13.0,12.0,11.0,5.0,12.0,,,,,\\n2002-12-31,40.0,14.0,5.0,5.0,5.0,,,,,,\\n\"\"\"\n", 236 | "df3 = pd.read_csv(StringIO(data), index_col=0, parse_dates=True)\n", 237 | "df3 = df3.fillna(0) / 100\n", 238 | "df4 = pd.DataFrame(df3.mean(), columns=['ratio'])\n", 239 | "df4['total'] = 1" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 9, 245 | "metadata": { 246 | "collapsed": false 247 | }, 248 | "outputs": [ 249 | { 250 | "data": { 251 | "text/html": [ 252 | "
\n", 253 | " " 254 | ], 255 | "text/plain": [ 256 | "" 257 | ] 258 | }, 259 | "metadata": {}, 260 | "output_type": "display_data" 261 | } 262 | ], 263 | "source": [ 264 | "display_charts(df4, kind='pie', y=['ratio'], title='Angel Deals By Sector', tooltip={'pointFormat': '{series.name}: {point.percentage:.1f}%'})" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "## Highcharts specific" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 10, 277 | "metadata": { 278 | "collapsed": false 279 | }, 280 | "outputs": [ 281 | { 282 | "data": { 283 | "text/html": [ 284 | "
\n", 285 | " " 286 | ], 287 | "text/plain": [ 288 | "" 289 | ] 290 | }, 291 | "metadata": {}, 292 | "output_type": "display_data" 293 | } 294 | ], 295 | "source": [ 296 | "df4 = pd.DataFrame(df3.sum(), columns=['sum'])\n", 297 | "#df4.to_dict('series').items()[0][1].tolist()\n", 298 | "display_charts(df4, polar=True, kind='bar', ylim=(0, 2.3), title='Angel Deals By Sector')" 299 | ] 300 | } 301 | ], 302 | "metadata": { 303 | "kernelspec": { 304 | "display_name": "Python 2", 305 | "language": "python", 306 | "name": "python2" 307 | }, 308 | "language_info": { 309 | "codemirror_mode": { 310 | "name": "ipython", 311 | "version": 2 312 | }, 313 | "file_extension": ".py", 314 | "mimetype": "text/x-python", 315 | "name": "python", 316 | "nbconvert_exporter": "python", 317 | "pygments_lexer": "ipython2", 318 | "version": "2.7.10" 319 | } 320 | }, 321 | "nbformat": 4, 322 | "nbformat_minor": 0 323 | } 324 | -------------------------------------------------------------------------------- /pandas_highcharts/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.5.1' -------------------------------------------------------------------------------- /pandas_highcharts/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pandas 4 | import copy 5 | 6 | 7 | _pd2hc_kind = { 8 | "bar": "column", 9 | "barh": "bar", 10 | "area": "area", 11 | "line": "line", 12 | "pie": "pie" 13 | } 14 | 15 | 16 | def pd2hc_kind(kind): 17 | if kind not in _pd2hc_kind: 18 | raise ValueError("%(kind)s plots are not yet supported" % locals()) 19 | return _pd2hc_kind[kind] 20 | 21 | _pd2hc_linestyle = { 22 | "-": "Solid", 23 | "--": "Dash", 24 | "-.": "DashDot", 25 | ":": "Dot" 26 | } 27 | 28 | 29 | def pd2hc_linestyle(linestyle): 30 | if linestyle not in _pd2hc_linestyle: 31 | raise ValueError("%(linestyle)s linestyles are not yet supported" % locals()) 32 | return _pd2hc_linestyle[linestyle] 33 | 34 | 35 | def json_encode(obj): 36 | return pandas.io.json.dumps(obj) 37 | 38 | 39 | def serialize(df, output_type="javascript", chart_type="default", *args, **kwargs): 40 | def serialize_chart(df, output, *args, **kwargs): 41 | output["chart"] = {} 42 | if 'render_to' in kwargs: 43 | output['chart']['renderTo'] = kwargs['render_to'] 44 | if "figsize" in kwargs: 45 | output["chart"]["width"] = kwargs["figsize"][0] 46 | output["chart"]["height"] = kwargs["figsize"][1] 47 | if "kind" in kwargs: 48 | output["chart"]["type"] = pd2hc_kind(kwargs["kind"]) 49 | if kwargs.get('polar'): 50 | output['chart']['polar'] = True 51 | 52 | def serialize_colors(df, output, *args, **kwargs): 53 | pass 54 | 55 | def serialize_credits(df, output, *args, **kwargs): 56 | pass 57 | 58 | def serialize_data(df, output, *args, **kwargs): 59 | pass 60 | 61 | def serialize_drilldown(df, output, *args, **kwargs): 62 | pass 63 | 64 | def serialize_exporting(df, output, *args, **kwargs): 65 | pass 66 | 67 | def serialize_labels(df, output, *args, **kwargs): 68 | pass 69 | 70 | def serialize_legend(df, output, *args, **kwargs): 71 | output["legend"] = { 72 | "enabled": kwargs.get("legend", True) 73 | } 74 | 75 | def serialize_loading(df, output, *args, **kwargs): 76 | pass 77 | 78 | def serialize_navigation(df, output, *args, **kwargs): 79 | pass 80 | 81 | def serialize_noData(df, output, *args, **kwargs): 82 | pass 83 | 84 | def serialize_pane(df, output, *args, **kwargs): 85 | pass 86 | 87 | def serialize_plotOptions(df, output, *args, **kwargs): 88 | pass 89 | 90 | def serialize_series(df, output, *args, **kwargs): 91 | def is_secondary(c, **kwargs): 92 | return c in kwargs.get("secondary_y", []) 93 | if kwargs.get('sort_columns'): 94 | df = df.sort_index() 95 | series = df.to_dict('series') 96 | output["series"] = [] 97 | for name, data in series.items(): 98 | if df[name].dtype.kind in "biufc": 99 | sec = is_secondary(name, **kwargs) 100 | d = { 101 | "name": name if not sec or not kwargs.get("mark_right", True) else name + " (right)", 102 | "yAxis": int(sec), 103 | "data": list(zip(df.index, data.values.tolist())) 104 | } 105 | if kwargs.get('polar'): 106 | d['data'] = [v for k, v in d['data']] 107 | if kwargs.get("kind") == "area" and kwargs.get("stacked", True): 108 | d["stacking"] = 'normal' 109 | if kwargs.get("style"): 110 | d["dashStyle"] = pd2hc_linestyle(kwargs["style"].get(name, "-")) 111 | output["series"].append(d) 112 | output['series'].sort(key=lambda s: s['name']) 113 | 114 | def serialize_subtitle(df, output, *args, **kwargs): 115 | pass 116 | 117 | def serialize_title(df, output, *args, **kwargs): 118 | if "title" in kwargs: 119 | output["title"] = {"text": kwargs["title"]} 120 | 121 | def serialize_tooltip(df, output, *args, **kwargs): 122 | if 'tooltip' in kwargs: 123 | output['tooltip'] = kwargs['tooltip'] 124 | 125 | def serialize_xAxis(df, output, *args, **kwargs): 126 | output["xAxis"] = {} 127 | if df.index.name: 128 | output["xAxis"]["title"] = {"text": df.index.name} 129 | if df.index.dtype.kind in "M": 130 | output["xAxis"]["type"] = "datetime" 131 | if df.index.dtype.kind == 'O': 132 | output['xAxis']['categories'] = sorted(list(df.index)) if kwargs.get('sort_columns') else list(df.index) 133 | if kwargs.get("grid"): 134 | output["xAxis"]["gridLineWidth"] = 1 135 | output["xAxis"]["gridLineDashStyle"] = "Dot" 136 | if kwargs.get("loglog") or kwargs.get("logx"): 137 | output["xAxis"]["type"] = 'logarithmic' 138 | if "xlim" in kwargs: 139 | output["xAxis"]["min"] = kwargs["xlim"][0] 140 | output["xAxis"]["max"] = kwargs["xlim"][1] 141 | if "rot" in kwargs: 142 | output["xAxis"]["labels"] = {"rotation": kwargs["rot"]} 143 | if "fontsize" in kwargs: 144 | output["xAxis"].setdefault("labels", {})["style"] = {"fontSize": kwargs["fontsize"]} 145 | if "xticks" in kwargs: 146 | output["xAxis"]["tickPositions"] = kwargs["xticks"] 147 | 148 | def serialize_yAxis(df, output, *args, **kwargs): 149 | yAxis = {} 150 | if kwargs.get("grid"): 151 | yAxis["gridLineWidth"] = 1 152 | yAxis["gridLineDashStyle"] = "Dot" 153 | if kwargs.get("loglog") or kwargs.get("logy"): 154 | yAxis["type"] = 'logarithmic' 155 | if "ylim" in kwargs: 156 | yAxis["min"] = kwargs["ylim"][0] 157 | yAxis["max"] = kwargs["ylim"][1] 158 | if "rot" in kwargs: 159 | yAxis["labels"] = {"rotation": kwargs["rot"]} 160 | if "fontsize" in kwargs: 161 | yAxis.setdefault("labels", {})["style"] = {"fontSize": kwargs["fontsize"]} 162 | if "yticks" in kwargs: 163 | yAxis["tickPositions"] = kwargs["yticks"] 164 | output["yAxis"] = [yAxis] 165 | if kwargs.get("secondary_y"): 166 | yAxis2 = copy.deepcopy(yAxis) 167 | yAxis2["opposite"] = True 168 | output["yAxis"].append(yAxis2) 169 | 170 | def serialize_zoom(df, output, *args, **kwargs): 171 | if "zoom" in kwargs: 172 | if kwargs["zoom"] not in ("x", "y", "xy"): 173 | raise ValueError("zoom must be in ('x', 'y', 'xy')") 174 | output["chart"]["zoomType"] = kwargs["zoom"] 175 | 176 | output = {} 177 | df_copy = copy.deepcopy(df) 178 | if "x" in kwargs: 179 | df_copy.index = df_copy.pop(kwargs["x"]) 180 | if kwargs.get("use_index", True) is False: 181 | df_copy = df_copy.reset_index() 182 | if "y" in kwargs: 183 | df_copy = pandas.DataFrame(df_copy, columns=kwargs["y"]) 184 | serialize_chart(df_copy, output, *args, **kwargs) 185 | serialize_colors(df_copy, output, *args, **kwargs) 186 | serialize_credits(df_copy, output, *args, **kwargs) 187 | serialize_data(df_copy, output, *args, **kwargs) 188 | serialize_drilldown(df_copy, output, *args, **kwargs) 189 | serialize_exporting(df_copy, output, *args, **kwargs) 190 | serialize_labels(df_copy, output, *args, **kwargs) 191 | serialize_legend(df_copy, output, *args, **kwargs) 192 | serialize_loading(df_copy, output, *args, **kwargs) 193 | serialize_navigation(df_copy, output, *args, **kwargs) 194 | serialize_noData(df_copy, output, *args, **kwargs) 195 | serialize_pane(df_copy, output, *args, **kwargs) 196 | serialize_plotOptions(df_copy, output, *args, **kwargs) 197 | serialize_series(df_copy, output, *args, **kwargs) 198 | serialize_subtitle(df_copy, output, *args, **kwargs) 199 | serialize_title(df_copy, output, *args, **kwargs) 200 | serialize_tooltip(df_copy, output, *args, **kwargs) 201 | serialize_xAxis(df_copy, output, *args, **kwargs) 202 | serialize_yAxis(df_copy, output, *args, **kwargs) 203 | serialize_zoom(df_copy, output, *args, **kwargs) 204 | if output_type == "dict": 205 | return output 206 | if output_type == "json": 207 | return json_encode(output) 208 | if chart_type == "stock": 209 | return "new Highcharts.StockChart(%s);" % json_encode(output) 210 | return "new Highcharts.Chart(%s);" % json_encode(output) 211 | -------------------------------------------------------------------------------- /pandas_highcharts/data/ANGEL_SECTORS.csv: -------------------------------------------------------------------------------- 1 | Year,Software,Healthcare,Hardware,Biotech,Telecom,Manufacturing,Financial Products and Services,IT Services,Industrial/Energy,Retail,Media 2 | 2013-12-31,23.0,14.0,,11.0,,,7.0,,,7.0,16.0 3 | 2012-12-31,23.0,14.0,,11.0,,,,,7.0,12.0,7.0 4 | 2011-12-31,23.0,19.0,,13.0,,,,7.0,13.0,,5.0 5 | 2010-12-31,16.0,30.0,,15.0,,,,5.0,8.0,5.0, 6 | 2009-12-31,19.0,17.0,,8.0,,,5.0,,17.0,9.0, 7 | 2008-12-31,13.0,16.0,,11.0,,,,,8.0,12.0,7.0 8 | 2007-12-31,27.0,19.0,,12.0,,,,,8.0,6.0,5.0 9 | 2006-12-31,18.0,21.0,,18.0,,,6.0,,6.0,8.0, 10 | 2005-12-31,18.0,20.0,8.0,12.0,,,,6.0,6.0,,6.0 11 | 2004-12-31,22.0,16.0,10.0,10.0,6.0,,8.0,8.0,,7.0, 12 | 2003-12-31,26.0,13.0,12.0,11.0,5.0,12.0,,,,, 13 | 2002-12-31,40.0,14.0,5.0,5.0,5.0,,,,,, 14 | -------------------------------------------------------------------------------- /pandas_highcharts/data/DEU_PCPIPCH.csv: -------------------------------------------------------------------------------- 1 | Date,Value 2 | 2019-12-31,1.7 3 | 2018-12-31,1.7 4 | 2017-12-31,1.7 5 | 2016-12-31,1.5 6 | 2015-12-31,1.247 7 | 2014-12-31,0.896 8 | 2013-12-31,1.601 9 | 2012-12-31,2.13 10 | 2011-12-31,2.498 11 | 2010-12-31,1.158 12 | 2009-12-31,0.226 13 | 2008-12-31,2.738 14 | 2007-12-31,2.285 15 | 2006-12-31,1.784 16 | 2005-12-31,1.92 17 | 2004-12-31,1.799 18 | 2003-12-31,1.022 19 | 2002-12-31,1.346 20 | 2001-12-31,1.904 21 | 2000-12-31,1.418 22 | 1999-12-31,0.626 23 | 1998-12-31,0.593 24 | 1997-12-31,1.542 25 | 1996-12-31,1.19 26 | 1995-12-31,1.733 27 | 1994-12-31,2.717 28 | 1993-12-31,4.476 29 | 1992-12-31,5.046 30 | 1991-12-31,3.474 31 | 1990-12-31,2.687 32 | 1989-12-31,2.778 33 | 1988-12-31,1.274 34 | 1987-12-31,0.242 35 | 1986-12-31,-0.125 36 | 1985-12-31,2.084 37 | 1984-12-31,2.396 38 | 1983-12-31,3.284 39 | 1982-12-31,5.256 40 | 1981-12-31,6.324 41 | 1980-12-31,5.447 42 | -------------------------------------------------------------------------------- /pandas_highcharts/display.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import absolute_import 4 | 5 | """Functions to quickly display charts in a Notebook. 6 | """ 7 | 8 | import string 9 | import random 10 | import json 11 | import copy 12 | 13 | from IPython.core import getipython 14 | from IPython.core.display import display, HTML 15 | 16 | from .core import serialize 17 | 18 | 19 | # Note that Highstock includes all Highcharts features. 20 | HIGHCHARTS_SCRIPTS = """ 21 | 22 | 23 | 24 | """ 25 | 26 | 27 | def load_highcharts(): 28 | return display(HTML(HIGHCHARTS_SCRIPTS)) 29 | 30 | # Automatically insert the script tag into your Notebook. 31 | # Call when you import this module. 32 | if 'IPKernelApp' in getipython.get_ipython().config: 33 | load_highcharts() 34 | 35 | 36 | def _generate_div_id_chart(prefix="chart_id", digits=8): 37 | """Generate a random id for div chart. 38 | """ 39 | choices = (random.randrange(0, 52) for _ in range(digits)) 40 | return prefix + "".join((string.ascii_letters[x] for x in choices)) 41 | 42 | 43 | def display_charts(df, chart_type="default", render_to=None, **kwargs): 44 | """Display you DataFrame with Highcharts. 45 | 46 | df: DataFrame 47 | chart_type: str 48 | 'default' or 'stock' 49 | render_to: str 50 | div id for plotting your data 51 | """ 52 | if chart_type not in ("default", "stock"): 53 | raise ValueError("Wrong chart_type: accept 'default' or 'stock'.") 54 | chart_id = render_to if render_to is not None else _generate_div_id_chart() 55 | json_data = serialize(df, render_to=chart_id, chart_type=chart_type, 56 | **kwargs) 57 | content = """
58 | """ 59 | return display(HTML(content.format(chart_id=chart_id, 60 | data=json_data))) 61 | 62 | 63 | def _series_data_filter(data): 64 | """Replace each 'data' key in the list stored under 'series' by "[...]". 65 | 66 | Use to not store and display the series data when you just want display and 67 | modify the Highcharts parameters. 68 | 69 | data: dict 70 | Serialized DataFrame in a dict for Highcharts 71 | 72 | Returns: a dict with filtered values 73 | 74 | See also `core.serialize` 75 | """ 76 | data = copy.deepcopy(data) 77 | if "series" in data: 78 | for series in data["series"]: 79 | series["data"] = "[...]" 80 | return data 81 | 82 | 83 | def pretty_params(data, indent=2): 84 | """Pretty print your Highcharts params (into a JSON). 85 | 86 | data: dict 87 | Serialized DataFrame in a dict for Highcharts 88 | """ 89 | data_to_print = _series_data_filter(data) 90 | print(json.dumps(data_to_print, indent=indent)) 91 | -------------------------------------------------------------------------------- /pandas_highcharts/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import absolute_import 4 | 5 | import datetime 6 | import json 7 | import pandas 8 | from unittest import TestCase 9 | 10 | from .core import serialize, json_encode 11 | 12 | df = pandas.DataFrame([ 13 | {'a': 1, 'b': 2, 'c': 3, 't': datetime.datetime(2015, 1, 1), 's': 's1'}, 14 | {'a': 2, 'b': 4, 'c': 6, 't': datetime.datetime(2015, 1, 2), 's': 's2'} 15 | ]) 16 | 17 | 18 | class CoreTest(TestCase): 19 | def test_type(self): 20 | self.assertEqual(type(serialize(df, render_to="chart")), str) 21 | obj = serialize(df, render_to="chart", output_type="dict") 22 | self.assertEqual(type(obj), dict) 23 | self.assertTrue('series' in obj) 24 | series = obj['series'][0] 25 | self.assertEqual(series['name'], 'a') 26 | self.assertTrue('data' in series) 27 | self.assertEqual(series['data'], [(0, 1), (1, 2)]) 28 | 29 | obj = serialize(df, render_to="chart", output_type="dict", zoom="xy") 30 | self.assertTrue("chart" in obj) 31 | self.assertEqual(type(obj["chart"]), dict) 32 | self.assertTrue("zoomType" in obj["chart"]) 33 | self.assertRaises(ValueError, serialize, df, **{"render_to": "chart", "zoom": "z"}) 34 | obj = serialize(df, render_to="chart", output_type="dict", kind="bar") 35 | self.assertTrue("chart" in obj) 36 | self.assertEqual(type(obj["chart"]), dict) 37 | self.assertEqual(obj["chart"].get("type"), "column") 38 | self.assertRaises(ValueError, serialize, df, **{"render_to": "chart", "kind": "z"}) 39 | obj = serialize(df, render_to="chart", output_type="dict", secondary_y="a") 40 | self.assertTrue(obj.get("yAxis", [])[1].get('opposite')) 41 | obj = serialize(df, render_to="chart", output_type="dict", rot=45, loglog=True) 42 | self.assertEqual(obj.get('xAxis', {}).get('labels'), {'rotation': 45}) 43 | self.assertEqual(obj.get('yAxis', [])[0].get('labels'), {'rotation': 45}) 44 | self.assertEqual(obj.get('xAxis', {}).get('type'), 'logarithmic') 45 | obj = serialize(df, render_to="chart", output_type="dict", x="t") 46 | self.assertEqual(obj.get('xAxis', {}).get('type'), 'datetime') 47 | obj = serialize(df, render_to="chart", output_type="dict", x="t", style={"a": ":"}) 48 | for series in obj.get("series"): 49 | if series["name"] == "a": 50 | self.assertEqual(series.get("dashStyle"), "Dot") 51 | self.assertRaises(ValueError, serialize, df, **{"render_to": "chart", "style": {"a": "u"}}) 52 | obj = serialize(df, render_to="chart", output_type="dict", kind="area", stacked=True) 53 | self.assertEqual(obj.get("series")[0].get("stacking"), "normal") 54 | 55 | obj = serialize(df, render_to="chart", output_type="dict", grid=True) 56 | self.assertEqual(obj.get('xAxis', {}).get('gridLineDashStyle'), 'Dot') 57 | self.assertEqual(obj.get('xAxis', {}).get('gridLineWidth'), 1) 58 | self.assertEqual(obj.get('yAxis', [])[0].get('gridLineDashStyle'), 'Dot') 59 | self.assertEqual(obj.get('yAxis', [])[0].get('gridLineWidth'), 1) 60 | 61 | obj = serialize(df, render_to="chart", output_type="dict", xlim=(0, 1), ylim=(0, 1)) 62 | self.assertEqual(obj.get('xAxis', {}).get('min'), 0) 63 | self.assertEqual(obj.get('xAxis', {}).get('max'), 1) 64 | self.assertEqual(obj.get('yAxis', [])[0].get('min'), 0) 65 | self.assertEqual(obj.get('yAxis', [])[0].get('max'), 1) 66 | 67 | obj = serialize(df, render_to="chart", output_type="dict", fontsize=12, figsize=(4, 5)) 68 | self.assertEqual(obj.get('xAxis', {}).get('labels', {}).get('style', {}).get('fontSize'), 12) 69 | self.assertEqual(obj.get('yAxis', [])[0].get('labels', {}).get('style', {}).get('fontSize'), 12) 70 | 71 | obj = serialize(df, render_to="chart", output_type="dict", title='Chart', xticks=[1], yticks=[2]) 72 | self.assertTrue(obj.get('title', {}).get('text')) 73 | self.assertTrue(obj.get('xAxis', {}).get('tickPositions')) 74 | for yaxis in obj.get('yAxis', []): 75 | self.assertTrue(yaxis.get('tickPositions')) 76 | 77 | obj = serialize(df, render_to="chart", output_type="dict", fontsize=12, kind='pie', x='s', y=['a'], tooltip={'pointFormat': '{series.name}: {point.percentage:.1f}%'}) 78 | self.assertTrue(obj.get('tooltip')) 79 | 80 | obj = serialize(df, render_to="chart", output_type="dict", polar=True, x='s', y=['a']) 81 | self.assertTrue(obj.get('chart', {}).get('polar')) 82 | 83 | df2 = pandas.DataFrame({'s': [2, 1]}, index=['b', 'a']) 84 | obj = serialize(df2, render_to='chart', output_type='dict', sort_columns=True) 85 | self.assertEqual(obj['series'], [{'data': [('a', 1), ('b', 2)], 'name': 's', 'yAxis': 0}]) 86 | obj = serialize(df2, render_to='chart', output_type='dict') 87 | self.assertEqual(obj['series'], [{'data': [('b', 2), ('a', 1)], 'name': 's', 'yAxis': 0}]) 88 | 89 | def test_json_output(self): 90 | json_output = serialize(df, output_type="json") 91 | self.assertEqual(type(json_output), str) 92 | decoded = json.loads(json_output) 93 | self.assertEqual(type(decoded), dict) 94 | 95 | def test_jsonencoder(self): 96 | self.assertEqual(json_encode(datetime.date(1970, 1, 1)), "0") 97 | self.assertEqual(json_encode(datetime.date(2015, 1, 1)), "1420070400000") 98 | self.assertEqual(json_encode(datetime.datetime(2015, 1, 1)), "1420070400000") 99 | self.assertEqual(json_encode(pandas.tslib.Timestamp(1420070400000000000)), "1420070400000") 100 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | ipython 3 | coverage -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from distutils.core import setup 4 | from setuptools import find_packages 5 | 6 | setup( 7 | name='pandas-highcharts', 8 | version='0.5.2', 9 | author='Guillaume Thomas', 10 | author_email='guillaume.thomas642@gmail.com', 11 | license='MIT', 12 | description='pandas-highcharts is a Python package which allows you to easily build Highcharts plots with pandas.DataFrame objects.', 13 | url='https://github.com/gtnx/pandas-highcharts', 14 | install_requires=[line.strip("\n") for line in open("requirements.txt", "r").readlines()], 15 | include_package_data=True, 16 | packages=find_packages(), 17 | ) 18 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py34 3 | 4 | [testenv] 5 | deps = nose 6 | commands = nosetests --with-cover --cover-package pandas_highcharts pandas_highcharts 7 | 8 | [flake8] 9 | ignore = E501 10 | --------------------------------------------------------------------------------