echarts: A python Package for Visualization




In [6]:
# option

"""
    echarts.option
    ~~~~~~~~~~~~~~
    Options for chart
"""

import json


class Base(object):
    def __str__(self):
        """JSON stringify format data."""
        return json.dumps(self.json)

    def __getitem__(self, key):
        return self.json.get(key)

    def keys(self):
        return self.json.keys()

    @property
    def json(self):
        raise NotImplementedError


class Axis(Base):
    """Axis data structure."""

    def __init__(self, type, position, name='', data=None, **kwargs):
        assert type in ('category', 'value', 'time')
        self.type = type
        assert position in ('bottom', 'top', 'left', 'right')
        self.position = position
        self.name = name
        self.data = data or []
        self._kwargs = kwargs

    def __repr__(self):
        return 'Axis<%s/%s>' % (self.type, self.position)

    @property
    def json(self):
        """JSON format data."""
        json = dict(
            type=self.type,
            position=self.position,
            data=self.data
        )
        if self.name:
            json['name'] = self.name

        if self._kwargs:
            json.update(self._kwargs)
        return json


class Legend(Base):
    """Legend section for Echart."""

    def __init__(self, data, orient='horizontal', position=None, **kwargs):
        self.data = data

        assert orient in ('horizontal', 'vertical')
        self.orient = orient
        if not position:
            position = ('center', 'top')
        self.position = position
        self._kwargs = kwargs

    @property
    def json(self):
        """JSON format data."""
        json = {
            'data': self.data,
            'orient': self.orient,
            'x': self.position[0],
            'y': self.position[1]
        }

        if self._kwargs:
            json.update(self._kwargs)
        return json


class Tooltip(Base):
    """A tooltip when hovering."""

    def __init__(self, trigger='axis', **kwargs):
        assert trigger in ('axis', 'item')
        self.trigger = trigger

        self._kwargs = kwargs

    @property
    def json(self):
        """JSON format data."""
        json = {
            'trigger': self.trigger,
        }
        if self._kwargs:
            json.update(self._kwargs)
        return json


class Series(Base):
    """ Data series holding. """
    def __init__(self, type, name=None, data=None, **kwargs):
        types = (
            'bar', 'boxplot', 'candlestick', 'chord', 'effectScatter',
            'eventRiver', 'force', 'funnel', 'gauge', 'graph', 'heatmap',
            'k', 'line', 'lines', 'map', 'parallel', 'pie', 'radar',
            'sankey', 'scatter', 'tree', 'treemap', 'venn', 'wordCloud'
        )
        assert type in types
        self.type = type
        self.name = name
        self.data = data or []
        self._kwargs = kwargs

    @property
    def json(self):
        """JSON format data."""
        json = {
            'type': self.type,
            'data': self.data
        }
        if self.name:
            json['name'] = self.name
        if self._kwargs:
            json.update(self._kwargs)
        return json


class Toolbox(Base):
    """ A toolbox for visitor. """

    def __init__(self, orient='horizontal', position=None, **kwargs):
        assert orient in ('horizontal', 'vertical')
        self.orient = orient
        if not position:
            position = ('right', 'top')
        self.position = position
        self._kwargs = kwargs

    @property
    def json(self):
        """JSON format data."""
        json = {
            'orient': self.orient,
            'x': self.position[0],
            'y': self.position[1]
        }
        if self._kwargs:
            json.update(self._kwargs)
        return json

    
class VisualMap(Base):
    """maps data to visual channels"""

    def __init__(self, type, min, max,  **kwargs):
        assert type in ("continuous", "piecewise")
        self.type = type
        self.min = min
        self.max = max
        self._kwargs = kwargs

    @property
    def json(self):
        """JSON format data"""
        json = {
            "type": self.type,
            'min': self.min,
            'max': self.max
        }
        if self._kwargs:
            json.update(self._kwargs)
        return json

In [7]:
# data structure
"""
    echarts.datastructure
    ~~~~~~~~~~~~~~~~~~~~~
    Datastructure for describing the chart types.
"""

# from .option import Series


class Line(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Line, self).__init__('line', name=name, data=data, **kwargs)


class Bar(Series):
    " Docs "
    def __init__(self, name=None, data=None, **kwargs):
        super(Bar, self).__init__('bar', name=name, data=data, **kwargs)


class Pie(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Pie, self).__init__('pie', name=name, data=data, **kwargs)


class Scatter(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Scatter, self).__init__(
            'scatter', name=name, data=data, **kwargs
        )


class EffectScatter(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(EffectScatter, self).__init__(
            'effectScatter', name=name, data=data, **kwargs
        )


class Radar(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Radar, self).__init__('radar', name=name, data=data, **kwargs)


class Treemap(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Treemap, self).__init__(
            'treemap', name=name, data=data, **kwargs
        )


class Boxplot(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Boxplot, self).__init__(
            'boxplot', name=name, data=data, **kwargs
        )


class Candlestick(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Candlestick, self).__init__(
            'candlestick', name=name, data=data, **kwargs
        )


class Heatmap(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Heatmap, self).__init__(
            'heatmap', name=name, data=data, **kwargs
        )


class Map(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Map, self).__init__('map', name=name, data=data, **kwargs)


class Parallel(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Parallel, self).__init__(
            'parallel', name=name, data=data, **kwargs
        )


class Lines(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Lines, self).__init__('lines', name=name, data=data, **kwargs)


class Graph(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Graph, self).__init__('graph', name=name, data=data, **kwargs)


class Sankey(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Sankey, self).__init__('sankey', name=name, data=data, **kwargs)


class Funnel(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Funnel, self).__init__('funnel', name=name, data=data, **kwargs)


class Gauge(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Gauge, self).__init__('gauge', name=name, data=data, **kwargs)


# The following chart types are only available in echarts 2 version.

class K(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(K, self).__init__('k', name=name, data=data, **kwargs)


class Force(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Force, self).__init__('force', name=name, data=data, **kwargs)


class Chord(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Chord, self).__init__('chord', name=name, data=data, **kwargs)


class Venn(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Venn, self).__init__('venn', name=name, data=data, **kwargs)


class Tree(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(Tree, self).__init__('tree', name=name, data=data, **kwargs)


class EventRiver(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(EventRiver, self).__init__(
            'eventRiver', name=name, data=data, **kwargs
        )


class WordCloud(Series):
    def __init__(self, name=None, data=None, **kwargs):
        super(WordCloud, self).__init__(
            'wordCloud', name=name, data=data, **kwargs
        )

VERSION_2 = (
    Line, Bar, Scatter, K, Pie, Radar, Chord, Force, Map,
    Gauge, Funnel, EventRiver, Venn, Treemap, Tree, WordCloud, Heatmap
)
VERSION_3 = (
    Line, Bar, Pie, Scatter, EffectScatter, Radar, Treemap, Boxplot,
    Candlestick, Heatmap, Map, Parallel, Lines, Graph, Sankey, Funnel, Gauge
)
VERSION_ALL = tuple(set(VERSION_2).union(set(VERSION_3)))

In [8]:
# -*- coding: utf-8 -*-

"""
    echarts
    ~~~~~~~
    An unofficial Echarts options generator with Python.
    :copyright: (c) 2014 by Hsiaoming Yang <me@lepture.com>.
    :license: MIT, see `MIT <https://opensource.org/licenses/MIT>`_ for more details.
"""

import os
import json
import logging
import tempfile
import webbrowser


__version__ = '0.1'
__release__ = '0.1.3'
__author__ = 'Hsiaoming Yang <me@lepture.com>'



class Echart(Base):
    def __init__(self, title, description=None, axis=True, **kwargs):
        self.title = {
            'text': title,
            'subtext': description,
        }

        self.axis = axis
        if self.axis:
            self.x_axis = []
            self.y_axis = []

        self.series = []
        self.kwargs = kwargs

        self.logger = logging.getLogger(__name__)

    def use(self, obj):
        if isinstance(obj, Axis):
            if obj.position in ('bottom', 'top'):
                self.x_axis.append(obj)
            else:
                self.y_axis.append(obj)
            return self

        if isinstance(obj, Legend):
            self.legend = obj
        elif isinstance(obj, Tooltip):
            self.tooltip = obj
        elif isinstance(obj, Series):
            self.series.append(obj)
        elif isinstance(obj, Toolbox):
            self.toolbox = obj
        elif isinstance(obj, VisualMap):
            self.visualMap = obj

        return self

    @property
    def data(self):
        return self.series

    @property
    def json(self):
        """JSON format data."""
        json = {
            'title': self.title,
            'series': list(map(dict, self.series)),
        }

        if self.axis:
            json['xAxis'] = list(map(dict, self.x_axis)) or [{}]
            json['yAxis'] = list(map(dict, self.y_axis)) or [{}]

        if hasattr(self, 'legend'):
            json['legend'] = self.legend.json
        if hasattr(self, 'tooltip'):
            json['tooltip'] = self.tooltip.json
        if hasattr(self, 'toolbox'):
            json['toolbox'] = self.toolbox.json
        if hasattr(self, 'visualMap'):
            json['visualMap'] = self.visualMap.json

        json.update(self.kwargs)
        return json

    def _html(self):
        with open(os.path.join(os.path.dirname(__file__), 'plot.j2')) as f:
            template = f.read()
            return template.replace('{{ opt }}', json.dumps(self.json, indent=4))

    def plot(self, persist=True):
        """
        Plot into html file
        :param persist: persist output html to disk
        """
        with tempfile.NamedTemporaryFile(suffix='.html', delete=not persist) as fobj:
            fobj.write(self._html())
            fobj.flush()
            webbrowser.open('file://' + os.path.realpath(fobj.name))
            persist or raw_input('Press enter for continue')
                
    def save(self, path, name):
        """
        Save html file into project dir
        :param path: project dir
        :param name: html file name
        """
        if not os.path.exists(path):
            os.makedirs(path)
        with open(path+str(name)+".html", "w") as html_file:
            html_file.write(self._html())

In [9]:
import os

__file__ = '/Users/chengjun/github/cjc/vis/echarts/'

os.path.dirname(__file__)


Out[9]:
'/Users/chengjun/github/cjc/vis/echarts'

In [10]:
help(Echart)


Help on class Echart in module __main__:

class Echart(Base)
 |  Method resolution order:
 |      Echart
 |      Base
 |      __builtin__.object
 |  
 |  Methods defined here:
 |  
 |  __init__(self, title, description=None, axis=True, **kwargs)
 |  
 |  plot(self, persist=True)
 |      Plot into html file
 |      :param persist: persist output html to disk
 |  
 |  save(self, path, name)
 |      Save html file into project dir
 |      :param path: project dir
 |      :param name: html file name
 |  
 |  use(self, obj)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  data
 |  
 |  json
 |      JSON format data.
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from Base:
 |  
 |  __getitem__(self, key)
 |  
 |  __str__(self)
 |      JSON stringify format data.
 |  
 |  keys(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Base:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)


In [ ]:


In [12]:
chart = Echart(title = 'GDP', description = 'subtitle')
chart.use(Bar('China', [2, 3, 4, 5]))
chart.use(Legend(['GDP']))
chart.use(Axis('category', 'bottom', data=['Nov', 'Dec', 'Jan', 'Feb']))


Out[12]:
<__main__.Echart at 0x1075afed0>

In [13]:
help(chart.use)


Help on method use in module __main__:

use(self, obj) method of __main__.Echart instance


In [14]:
chart.plot()

In [31]:
chart.title


Out[31]:
{'subtext': 'subtitle', 'text': 'GDP'}

In [ ]: