diff --git a/pyecharts/__init__.py b/pyecharts/__init__.py index ff5e4758..9036b8ec 100644 --- a/pyecharts/__init__.py +++ b/pyecharts/__init__.py @@ -4,6 +4,7 @@ __version__ = '0.1.9.4' __author__ = 'chenjiandongx' +# chart from pyecharts.charts.bar import Bar from pyecharts.charts.bar3D import Bar3D from pyecharts.charts.effectscatter import EffectScatter @@ -24,4 +25,9 @@ from pyecharts.charts.radar import Radar from pyecharts.charts.scatter import Scatter from pyecharts.charts.scatter3D import Scatter3D from pyecharts.charts.wordcloud import WordCloud + +# customize component from pyecharts.page import Page +from pyecharts.grid import Grid +from pyecharts.overlap import Overlap +from pyecharts.timeline import Timeline diff --git a/pyecharts/base.py b/pyecharts/base.py index 76c6763e..f28ebbf5 100644 --- a/pyecharts/base.py +++ b/pyecharts/base.py @@ -23,8 +23,7 @@ class Base(object): subtitle_color="#aaa", title_text_size=18, subtitle_text_size=12, - background_color="#fff", - is_grid=False): + background_color="#fff"): """ :param title: @@ -62,12 +61,8 @@ class Base(object): Color can be represented in RGB, for example 'rgb(128, 128, 128)'. RGBA can be used when you need alpha channel, for example 'rgba(128, 128, 128, 0.5)'. You may also use hexadecimal format, for example '#ccc'. - :param is_grid: - It specifies whether to use the grid component. """ self._option = {} - if is_grid: - self._option.update(grid=[]) self._width, self._height = width, height self._colorlst = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#749f83', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3', @@ -245,103 +240,6 @@ class Base(object): """ The base class's add() is just to provide a hint option """ pass - def custom(self, series): - """ Appends the data for the series of the chart type - - :param series: - series data - """ - _name, _series, _xaxis, _yaxis, _legend, _title = series - for n in _name: - self._option.get('legend')[0].get('data').append(n) - for s in _series: - self._option.get('series').append(s) - - def __custom_for_grid(self, series): - """ - - :param series: - series data - :return: - """ - _name, _series, _xaxis, _yaxis, _legend, _title = series - for s in _series: - self._option.get('series').append(s) - return len(self._option.get('series')), len(_series), _xaxis, _yaxis, _legend, _title - - def grid(self, series, - grid_width=None, - grid_height=None, - grid_top=None, - grid_bottom=None, - grid_left=None, - grid_right=None): - """ Concurrently show charts - - :param series: - append other chart series data - :param grid_width: - Width of grid component. Adaptive by default. - :param grid_height: - Height of grid component. Adaptive by default. - :param grid_top: - Distance between grid component and the top side of the container. - grid_top value can be instant pixel value like 20; - it can also be percentage value relative to container width like '20%'; - and it can also be 'top', 'middle', or 'bottom'. - If the grid_top value is set to be 'top', 'middle', or 'bottom', - then the component will be aligned automatically based on position. - :param grid_bottom: - Distance between grid component and the bottom side of the container. - grid_bottom value can be instant pixel value like 20; - it can also be percentage value relative to container width like '20%'. - :param grid_left: - Distance between grid component and the left side of the container. - grid_left value can be instant pixel value like 20; - it can also be percentage value relative to container width like '20%'; - and it can also be 'left', 'center', or 'right'. - If the grid_left value is set to be 'left', 'center', or 'right', - then the component will be aligned automatically based on position. - :param grid_right: - Distance between grid component and the right side of the container. - grid_right value can be instant pixel value like 20; - it can also be percentage value relative to container width like '20%'. - :return: - """ - from pyecharts.option import grid - _index, _index_once, _xaxis, _yaxis, _legned, _title = self.__custom_for_grid(series) - self._option.get('legend').append(_legned) - self._option.get('title').append(_title) - if _xaxis and _yaxis is not None: - try: - _xaxis[0].update(gridIndex=_index - 1) - _yaxis[0].update(gridIndex=_index - 1) - self._option.get('xAxis').append(_xaxis[0]) - self._option.get('yAxis').append(_yaxis[0]) - except: - pass - # indexflag is only identify for every series - _flag = self._option.get('series')[0].get('indexflag') - _series_index = 0 - for s in self._option.get('series'): - if _flag == s.get('indexflag'): - s.update(xAxisIndex=_series_index, yAxisIndex=_series_index) - else: - _series_index += 1 - s.update(xAxisIndex=_series_index, yAxisIndex=_series_index) - _flag = s.get('indexflag') - - _grid = grid(grid_width, grid_height, grid_top, - grid_bottom, grid_left, grid_right) - for _ in range(_index_once): - self._option.get('grid').append(_grid) - - def get_series(self): - """ Get chart series data """ - return self._option.get('legend')[0].get('data'), self._option.get('series'),\ - self._option.get('xAxis', None), self._option.get('yAxis', None),\ - self._option.get('legend')[0], self._option.get('title')[0] - def show_config(self): """ Print all options of charts""" pprint(self._option) @@ -391,10 +289,6 @@ class Base(object): self._option.get('legend')[0].update(chart['legend']) self._option.update(color=chart['color']) - # grid component - if chart['grid']: - self._option.get('grid').append(chart['grid']) - # datazoom component if kwargs.get('is_datazoom_show', None) is True: self._option.update(dataZoom=chart['datazoom']) diff --git a/pyecharts/grid.py b/pyecharts/grid.py new file mode 100644 index 00000000..ea59b451 --- /dev/null +++ b/pyecharts/grid.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# coding=utf-8 + +from pyecharts.option import grid + +class Grid(object): + + def __init__(self): + self._chart = None + + def add(self, chart, + grid_width=None, + grid_height=None, + grid_top=None, + grid_bottom=None, + grid_left=None, + grid_right=None): + """ + + :param chart: + chart instance + :param grid_width: + Width of grid component. Adaptive by default. + :param grid_height: + Height of grid component. Adaptive by default. + :param grid_top: + Distance between grid component and the top side of the container. + :param grid_bottom: + Distance between grid component and the bottom side of the container. + :param grid_left: + Distance between grid component and the left side of the container. + :param grid_right: + Distance between grid component and the right side of the container. + :return: + """ + if self._chart is None: + self._chart = chart + self._chart._option.update(grid=[]) + + _grid = grid(grid_width, grid_height, grid_top, grid_bottom, grid_left, grid_right) + if _grid: + for _ in range(len(self._chart._option.get('series'))): + self._chart._option.get('grid').append(_grid) + else: + _index, _index_once, _xaxis, _yaxis, _legned, _title = self.__custom(self.__get_series(chart)) + self._chart._option.get('legend').append(_legned) + self._chart._option.get('title').append(_title) + if _xaxis and _yaxis is not None: + try: + _xaxis[0].update(gridIndex=_index - 1) + _yaxis[0].update(gridIndex=_index - 1) + self._chart._option.get('xAxis').append(_xaxis[0]) + self._chart._option.get('yAxis').append(_yaxis[0]) + except: + pass + + # indexflag is only identify for every series + _flag = self._chart._option.get('series')[0].get('indexflag') + _series_index = 0 + for s in self._chart._option.get('series'): + if _flag == s.get('indexflag'): + s.update(xAxisIndex=_series_index, yAxisIndex=_series_index) + else: + _series_index += 1 + s.update(xAxisIndex=_series_index, yAxisIndex=_series_index) + _flag = s.get('indexflag') + _grid = grid(grid_width, grid_height, grid_top, grid_bottom, grid_left, grid_right) + for _ in range(_index_once): + self._chart._option.get('grid').append(_grid) + + def __get_series(self, chart): + """ Get chart series data """ + return ( + chart._option.get('legend')[0].get('data'), + chart._option.get('series'), + chart._option.get('xAxis', None), + chart._option.get('yAxis', None), + chart._option.get('legend')[0], + chart._option.get('title')[0] + ) + + def __custom(self, series): + """ + + :param series: + series data + :return: + """ + _name, _series, _xaxis, _yaxis, _legend, _title = series + for s in _series: + self._chart._option.get('series').append(s) + return len(self._chart._option.get('series')), len(_series), _xaxis, _yaxis, _legend, _title + + def render(self, path="render.html"): + """ + + :param path: + :return: + """ + self._chart.render(path) + + def render_embed(self): + """ + + :return: + """ + return self._chart.render_embed() + + def show_config(self): + """ + + :return: + """ + import pprint + return pprint.pprint(self._chart._option) + + def get_chart(self): + """ + + :return: + """ + return self._chart \ No newline at end of file diff --git a/pyecharts/overlap.py b/pyecharts/overlap.py new file mode 100644 index 00000000..a09edea8 --- /dev/null +++ b/pyecharts/overlap.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# coding=utf-8 + + +class Overlap(object): + + def __init__(self): + self._chart = None + + def add(self, chart): + """ + + :param chart: + chart instance + :return: + """ + if self._chart is None: + self._chart = chart + else: + self.__custom(self.__get_series(chart)) + + def __get_series(self, chart): + """ Get chart series data + + :param chart: + chart instance + :return: + """ + return ( + chart._option.get('legend')[0].get('data'), + chart._option.get('series'), + ) + + def __custom(self, series): + """ Appends the data for the series of the chart type + + :param series: + series data + """ + _name, _series = series + for n in _name: + self._chart._option.get('legend')[0].get('data').append(n) + for s in _series: + self._chart._option.get('series').append(s) + + def render(self, path="render.html"): + """ + + :param path: + :return: + """ + self._chart.render(path) + + def render_embed(self): + """ + + :return: + """ + return self._chart.render_embed() + + def show_config(self): + """ + + :return: + """ + import pprint + return pprint.pprint(self._chart._option) + + def get_chart(self): + """ + + :return: + """ + return self._chart diff --git a/pyecharts/page.py b/pyecharts/page.py index a5ecf525..54bddcb4 100644 --- a/pyecharts/page.py +++ b/pyecharts/page.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding=utf-8 + from pyecharts import template diff --git a/test/test_custom.py b/test/test_custom.py index dd7302ad..d0a90c84 100644 --- a/test/test_custom.py +++ b/test/test_custom.py @@ -3,10 +3,11 @@ from __future__ import unicode_literals from pyecharts import Bar, Line, Scatter, EffectScatter, Kline +from pyecharts import Overlap -def test_custom(): - # custom_0 +def test_custom_0(): + attr = ['A', 'B', 'C', 'D', 'E', 'F'] v1 = [10, 20, 30, 40, 50, 60] v2 = [15, 25, 35, 45, 55, 65] @@ -15,11 +16,15 @@ def test_custom(): bar.add("bar", attr, v1) line = Line() line.add("line", v2, v3) - bar.custom(line.get_series()) - bar.show_config() - bar.render() - # custom_1 + overlap = Overlap() + overlap.add(bar) + overlap.add(line) + overlap.render() + + +def test_custom_1(): + v1 = [10, 20, 30, 40, 50, 60] v2 = [30, 30, 30, 30, 30, 30] v3 = [50, 50, 50, 50, 50, 50] @@ -28,14 +33,18 @@ def test_custom(): es.add("es", v1, v2) scatter = Scatter() scatter.add("scatter", v1, v3) - es.custom(scatter.get_series()) es_1 = EffectScatter() es_1.add("es_1", v1, v4, symbol='pin', effect_scale=5) - es.custom(es_1.get_series()) - es.show_config() - es.render() - # custom_2 + overlap = Overlap() + overlap.add(es) + overlap.add(scatter) + overlap.add(es_1) + overlap.render() + + +def test_custom_2(): + import random v1 = [[2320.26, 2320.26, 2287.3, 2362.94], [2300, 2291.3, 2288.26, 2308.38], @@ -75,7 +84,9 @@ def test_custom(): line_1.add("line-1", attr, [random.randint(2400, 2500) for _ in range(31)]) line_2 = Line() line_2.add("line-2", attr, [random.randint(2400, 2500) for _ in range(31)]) - kline.custom(line_1.get_series()) - kline.custom(line_2.get_series()) - kline.show_config() - kline.render() + + overlap = Overlap() + overlap.add(kline) + overlap.add(line_1) + overlap.add(line_2) + overlap.render() diff --git a/test/test_grid.py b/test/test_grid.py index d5287ed6..223e7147 100644 --- a/test/test_grid.py +++ b/test/test_grid.py @@ -3,44 +3,53 @@ from __future__ import unicode_literals from pyecharts import Bar, Line, Scatter, EffectScatter, Pie, Kline, HeatMap +from pyecharts import Grid -def test_grid(): - # grid_0 +def test_grid_0(): + attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] v1 = [5, 20, 36, 10, 75, 90] v2 = [10, 25, 8, 60, 20, 80] - bar = Bar("柱状图示例", height=720, is_grid=True) - bar.add("商家A", attr, v1, is_stack=True, grid_bottom="60%") - bar.add("商家B", attr, v2, is_stack=True, grid_bottom="60%") + bar = Bar("柱状图示例", height=720) + bar.add("商家A", attr, v1, is_stack=True) + bar.add("商家B", attr, v2, is_stack=True) line = Line("折线图示例", title_top="50%") attr = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] line.add("最高气温", attr, [11, 11, 15, 13, 12, 13, 10], mark_point=["max", "min"], mark_line=["average"]) line.add("最低气温", attr, [1, -2, 2, 5, 3, 2, 0], mark_point=["max", "min"], mark_line=["average"], legend_top="50%") - bar.grid(line.get_series(), grid_top="60%") + grid = Grid() + grid.add(bar, grid_bottom="60%") + grid.add(line, grid_top="60%") bar.show_config() - bar.render() + grid.render() + + +def test_grid_1(): - # grid_1 v1 = [5, 20, 36, 10, 75, 90] v2 = [10, 25, 8, 60, 20, 80] - scatter = Scatter(width=1200, is_grid=True) - scatter.add("散点图示例", v1, v2, grid_left="60%", legend_pos="70%") + scatter = Scatter(width=1200) + scatter.add("散点图示例", v1, v2, legend_pos="70%") es = EffectScatter() es.add("动态散点图示例", [11, 11, 15, 13, 12, 13, 10], [1, -2, 2, 5, 3, 2, 0], effect_scale=6, legend_pos="20%") - scatter.grid(es.get_series(), grid_right="60%") - scatter.show_config() - scatter.render() - # grid_2 + grid = Grid() + grid.add(scatter, grid_left="60%") + grid.add(es, grid_right="60%") + grid.render() + + +def test_grid_2(): + attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] v1 = [5, 20, 36, 10, 75, 90] v2 = [10, 25, 8, 60, 20, 80] - bar = Bar("柱状图示例", height=720, width=1200, title_pos="65%", is_grid=True) - bar.add("商家A", attr, v1, is_stack=True, grid_bottom="60%", grid_left="60%") - bar.add("商家B", attr, v2, is_stack=True, grid_bottom="60%", grid_left="60%", legend_pos="80%") + bar = Bar("柱状图示例", height=720, width=1200, title_pos="65%") + bar.add("商家A", attr, v1, is_stack=True) + bar.add("商家B", attr, v2, is_stack=True, legend_pos="80%") line = Line("折线图示例") attr = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] line.add("最高气温", attr, [11, 11, 15, 13, 12, 13, 10], mark_point=["max", "min"], mark_line=["average"]) @@ -53,36 +62,40 @@ def test_grid(): es = EffectScatter("动态散点图示例", title_top="50%") es.add("es", [11, 11, 15, 13, 12, 13, 10], [1, -2, 2, 5, 3, 2, 0], effect_scale=6, legend_top="50%", legend_pos="20%") - bar.grid(line.get_series(), grid_bottom="60%", grid_right="60%") - bar.grid(scatter.get_series(), grid_top="60%", grid_left="60%") - bar.grid(es.get_series(), grid_top="60%", grid_right="60%") - bar.show_config() - bar.render() - # grid_3 - line = Line("折线图示例", width=1200, is_grid=True) + grid = Grid() + grid.add(bar, grid_bottom="60%", grid_left="60%") + grid.add(line, grid_bottom="60%", grid_right="60%") + grid.add(scatter, grid_top="60%", grid_left="60%") + grid.add(es, grid_top="60%", grid_right="60%") + grid.render() + + +def test_grid_3(): + + line = Line("折线图示例", width=1200) attr = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] - line.add("最高气温", attr, [11, 11, 15, 13, 12, 13, 10], mark_point=["max", "min"], - mark_line=["average"], grid_right="65%") + line.add("最高气温", attr, [11, 11, 15, 13, 12, 13, 10], mark_point=["max", "min"], mark_line=["average"]) line.add("最低气温", attr, [1, -2, 2, 5, 3, 2, 0], mark_point=["max", "min"], mark_line=["average"], legend_pos="20%") attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] v1 = [11, 12, 13, 10, 10, 10] pie = Pie("饼图示例", title_pos="45%") pie.add("", attr, v1, radius=[30, 55], legend_pos="65%", legend_orient='vertical') - line.grid(pie.get_series(), grid_left="60%") - line.show_config() - line.render() - # grid_4 - line = Line("折线图示例", width=1200, is_grid=True) + grid = Grid() + grid.add(line, grid_right="65%") + grid.add(pie, grid_left="60%") + grid.render() + + +def test_grid_4(): + + line = Line("折线图示例", width=1200) attr = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] - line.add("最高气温", attr, [11, 11, 15, 13, 12, 13, 10], mark_point=["max", "min"], - mark_line=["average"], grid_right="60%") + line.add("最高气温", attr, [11, 11, 15, 13, 12, 13, 10], mark_point=["max", "min"], mark_line=["average"]) line.add("最低气温", attr, [1, -2, 2, 5, 3, 2, 0], mark_point=["max", "min"], - mark_line=["average"], legend_pos="20%", grid_right="60%") - attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] - value = [20, 40, 60, 80, 100, 120] + mark_line=["average"], legend_pos="20%") v1 = [[2320.26, 2320.26, 2287.3, 2362.94], [2300, 2291.3, 2288.26, 2308.38], [2295.35, 2346.5, 2295.35, 2345.92], @@ -116,25 +129,31 @@ def test_grid(): [2255.77, 2270.28, 2253.31, 2276.22]] kline = Kline("K 线图示例", title_pos="60%") kline.add("日K", ["2017/7/{}".format(i + 1) for i in range(31)], v1, legend_pos="80%") - line.grid(kline.get_series(), grid_left="55%") - line.show_config() - line.render() - # grid_5 + grid = Grid() + grid.add(line, grid_right="60%") + grid.add(kline, grid_left="55%") + grid.render() + + +def test_grid_5(): + import random x_axis = ["12a", "1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12p", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p"] y_aixs = ["Saturday", "Friday", "Thursday", "Wednesday", "Tuesday", "Monday", "Sunday"] data = [[i, j, random.randint(0, 50)] for i in range(24) for j in range(7)] - heatmap = HeatMap("热力图示例", height=700, is_grid=True) + heatmap = HeatMap("热力图示例", height=700) heatmap.add("热力图直角坐标系", x_axis, y_aixs, data, is_visualmap=True, visual_top="45%", - visual_text_color="#000", visual_orient='horizontal', grid_bottom="60%") + visual_text_color="#000", visual_orient='horizontal') attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] v1 = [5, 20, 36, 10, 75, 90] v2 = [10, 25, 8, 60, 20, 80] bar = Bar("柱状图示例", title_top="52%") bar.add("商家A", attr, v1, is_stack=True) bar.add("商家B", attr, v2, is_stack=True, legend_top="50%") - heatmap.grid(bar.get_series(), grid_top="60%") - heatmap.show_config() - heatmap.render() + + grid = Grid() + grid.add(heatmap, grid_bottom="60%") + grid.add(bar, grid_top="60%") + grid.render() diff --git a/test/test_timeline.py b/test/test_timeline.py index 1c8fd7c3..17196eaf 100644 --- a/test/test_timeline.py +++ b/test/test_timeline.py @@ -1,10 +1,11 @@ +#!/usr/bin/env python #coding=utf-8 from __future__ import unicode_literals from random import randint from pyecharts.timeline import Timeline -from pyecharts import Bar, Pie +from pyecharts import Bar, Pie, Line def test_timeline(): @@ -80,3 +81,30 @@ def test_timeline_pie(): timeline.add(pie_5, '2016 年') timeline.show_config() timeline.render() + + +def test_timeline_bar_line(): + + from pyecharts import Overlap + attr = ["{}月".format(i) for i in range(1, 7)] + bar = Bar("Line - Bar 示例") + bar.add("bar", attr, [randint(10, 50) for _ in range(6)]) + line = Line() + line.add("line", attr, [randint(50, 80) for _ in range(6)]) + overlap = Overlap() + overlap.add(bar) + overlap.add(line) + + bar_1 = Bar("Line") + bar_1.add("bar", attr, [randint(10, 50) for _ in range(6)]) + line_1 = Line() + line_1.add("line", attr, [randint(50, 80) for _ in range(6)]) + overlap_1 = Overlap() + overlap_1.add(bar_1) + overlap_1.add(line_1) + overlap_1.get_chart() + + timeline = Timeline(timeline_bottom=0) + timeline.add(overlap.get_chart(), '2012') + timeline.add(overlap_1.get_chart(), '2013') + timeline.render()