Skip to content

Commit

Permalink
Merge pull request pyecharts#70 from pyexcel/master
Browse files Browse the repository at this point in the history
Initial support for Flask Integration And burn down the npcast and pdcast
  • Loading branch information
chenjiandongx authored Aug 8, 2017
2 parents e014e3e + ca856c6 commit 2c505a1
Show file tree
Hide file tree
Showing 20 changed files with 451 additions and 330 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ $ python setup.py install
```python
from pyecharts import Bar

attr = ["{}month".format(i) for i in range(1, 13)]
attr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
v1 = [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
v2 = [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
Expand All @@ -49,17 +48,15 @@ from pyecharts import Bar

index = pd.date_range('3/8/2017', periods=6, freq='M')
df1 = pd.DataFrame(np.random.randn(6), index=index)
dtvalue1, pdattr1 = Bar.pdcast(df1)

df2 = pd.DataFrame(np.random.randn(6), index=index)
dtvalue2, pdattr2 = Bar.pdcast(df2)

dtvalue1 = [i[0] for i in dtvalue1]
dtvalue2 = [i[0] for i in dtvalue2]

bar = Bar('Bar chart', 'Profit and loss situation')
bar.add('profit', pdattr1, dtvalue1)
bar.add('loss', pdattr2, dtvalue2)
bar.add('profit', df1.index, dtvalue1)
bar.add('loss', df2.index, dtvalue2)
bar.render()
```
![usage-1](https://github.com/chenjiandongx/pyecharts/blob/master/images/usage-1.png)
Expand Down
11 changes: 2 additions & 9 deletions document/doc_en_US.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,9 @@ cast(seq)
3. Dictionaries
{A1: B1, A2: B2, A3: B3, A4: B4} -- > k_lst[ A[i1, i2...] ], v_lst[ B[i1, i2...] ]

如果使用的是 Numpy 或者 Pandas,直接将数据放入 ```add()``` 方法也可能会出现问题,因为 ```add()``` 方法接受的是两个 list 列表。最后所有的配置项都是要经过 JSON 序列化的,像 int64 这种类型的数据在这个过程是会报错的。
it provides ```pdcast(pddata)``` and ``` npcast(npdata)``` methods to handle numpy & pandas data。
In the context of Numpy and/or Pandas, ```pdcast(pddata)``` and ``` npcast(npdata)``` methods, provided in 0.19.2 are no log required. Please see the advanced example in README.

pdcast(),It accepts Series or DataFrame types.
```python
@staticmethod
pdcast(pddata)
``` handle Series and DataFrame type in pandas, return value_lst and index_list ```
```
传入的类型为 Series、DataFrame 的话,pdcast() 会返回两个确保类型正确的列表(整个列表的数据类型为 float 或者 str,会先尝试转换为数值类型的 float,出现异常再尝试转换为 str 类型),value_lst 和 index_lst,分别为 Series.values/DataFrame.values 和 Series.index/DataFrame.index 列表。再将得到的数据传入 ```add()``` 方法即可(DataFrame 多个维度时返回一个嵌套列表。比较适合像 Radar, Parallel, HeatMap 这些需要传入嵌套列表([[ ], [ ]])数据的图表。)
If your DataFrame returns a transposed list(such as, [ [1], [2], [3] ]), you have to tranpose it by yourself (make it like [ 1, 2, 3 ] ). This transpose operation applies to Radar, Parallel, HeatMap.

Series type
```python
Expand Down
115 changes: 115 additions & 0 deletions document/doc_flask.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# pyecharts integration with Flask

## Step 0: let's create a mini flask project

Please create new directory for the new project

```shell
$ make flask-echarts
$ cd flask-echarts
$ mkdir templates
```

## Step 1: provide your own template

Please save the following template as pyecharts.html in `templates` folder

```html
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>ECharts</title>
<script src="http://oog4yfyu0.bkt.clouddn.com/echarts.min.js"></script>
<script src="http://oog4yfyu0.bkt.clouddn.com/echarts-gl.js"></script>
<script type="text/javascript " src="http://echarts.baidu.com/gallery/vendors/echarts/map/js/china.js"></script>
<script type="text/javascript " src="http://echarts.baidu.com/gallery/vendors/echarts/map/js/world.js"></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/wordcloud.js"></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/anhui.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/aomen.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/beijing.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/chongqing.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/fujian.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/gansu.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/guangdong.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/guangxi.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/guizhou.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/hainan.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/hebei.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/heilongjiang.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/henan.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/hubei.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/hunan.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/jiangsu.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/jiangxi.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/jilin.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/liaoning.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/neimenggu.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/ningxia.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/qinghai.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/shangdong.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/shanghai.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/shanxi.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/sichuan.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/taiwan.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/tianjin.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/xianggang.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/xinjiang.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/xizang.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/yunnan.js "></script>
<script type="text/javascript " src="http://oog4yfyu0.bkt.clouddn.com/zhejiang.js "></script>
</head>

<body>
{{myechart|safe}}
</body>

</html>
```

## Step 2: render your chart using chart_instance.render_embed()

Please save the following python file as server.py under your project root directory.

```python
from flask import Flask, render_template
app = Flask(__name__)


@app.route("/")
def hello():
return render_template('pyecharts.html', myechart=scatter3d())


def scatter3d():
from pyecharts import Scatter3D

import random
data = [[random.randint(0, 100), random.randint(0, 100), random.randint(0, 100)] for _ in range(80)]
range_color = ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf',
'#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
scatter3D = Scatter3D("3D scattering plot demo", width=1200, height=600)
scatter3D.add("", data, is_visualmap=True, visual_range_color=range_color)
return scatter3D.render_embed()
```

## Step 3: run it

Now you shall have these:

```shell
$ ls
server.py templates
$ ls templates
pyecharts.html


```shell
$ pip install flask
$ export FLASK_APP=server.py
$ flask run
* Serving Flask app "server"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
12 changes: 2 additions & 10 deletions document/doc_zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,8 @@ cast(seq)
3. 字典
{A1: B1, A2: B2, A3: B3, A4: B4} -- > k_lst[ A[i1, i2...] ], v_lst[ B[i1, i2...] ]

如果使用的是 Numpy 或者 Pandas,直接将数据放入 ```add()``` 方法也可能会出现问题,因为 ```add()``` 方法接受的是两个 list 列表。最后所有的配置项都是要经过 JSON 序列化的,像 int64 这种类型的数据在这个过程是会报错的。
在这里提供了 ```pdcast(pddata)`````` npcast(npdata)``` 两个方法,用于这两个库数据类型的处理。

pdcast(),接受的参数可以为 Series 或者 DataFrame 类型。
```python
@staticmethod
pdcast(pddata)
``` 用于处理 Pandas 中的 Series 和 DataFrame 类型,返回 value_lst, index_list 两个列表 ```
```
传入的类型为 Series、DataFrame 的话,pdcast() 会返回两个确保类型正确的列表(整个列表的数据类型为 float 或者 str,会先尝试转换为数值类型的 float,出现异常再尝试转换为 str 类型),value_lst 和 index_lst,分别为 Series.values/DataFrame.values 和 Series.index/DataFrame.index 列表。再将得到的数据传入 ```add()``` 方法即可(DataFrame 多个维度时返回一个嵌套列表。比较适合像 Radar, Parallel, HeatMap 这些需要传入嵌套列表([[ ], [ ]])数据的图表。)
如果使用的是 Numpy 或者 Pandas,0.19.2以前提供的 ```pdcast(pddata)`````` npcast(npdata)``` 两个方法在0.19.3之后不需再用了,用于这两个库数据类型的处理。
DataFrame 多个维度时返回一个嵌套列表。比较适合像 Radar, Parallel, HeatMap 这些需要传入嵌套列表([[ ], [ ]])数据的图表。

Series 类型
```python
Expand Down
72 changes: 36 additions & 36 deletions pyecharts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import sys
import json
import uuid
import random
import datetime
import warnings
Expand Down Expand Up @@ -357,39 +358,6 @@ def cast(seq):
v_lst.append(v)
return k_lst, v_lst

@staticmethod
def npcast(npdata):
""" Convert the Numpy data into list type
:param npdata:
Numpy data -> ndarray
:return:
"""
try:
_npvalue = npdata.astype(float).tolist()
except:
_npvalue = npdata.astype(str).tolist()
return _npvalue

@staticmethod
def pdcast(pddata):
""" Convert the Pandas data into list type
Series.index -> attr(str/int)
Series.value -> value(str/int)
DataFrame -> [[], []], can be used on Radar chart or Parallel chart.
:param pddata:
Pandas data -> Series or DataFrame
:return:
"""
try:
_dtvalue = pddata.values.astype(float).tolist()
except:
_dtvalue = pddata.values.astype(str).tolist()
try:
_pdattr = pddata.index.astype(float).tolist()
except:
_pdattr = pddata.index.astype(str).tolist()
return _dtvalue, _pdattr

def _legend_visualmap_colorlst(self, is_visualmap=False, **kwargs):
""" config legend,visualmap and colorlst component.
Expand All @@ -412,6 +380,21 @@ def _legend_visualmap_colorlst(self, is_visualmap=False, **kwargs):
if kwargs.get('is_datazoom_show', None) is True:
self._option.update(dataZoom=chart['datazoom'])

def render_embed(self):
"""
Render the chart component and its options
You can include it into your web pages. And you will
provide all dependent echarts javascript libraries.
"""
embed = 'chart_component.html'
template = self._jinja2_env.get_template(embed)
my_option = json_dumps(self._option, indent=4)
html = template.render(myOption=my_option,
chart_id=uuid.uuid4().hex,
myWidth=self._width, myHeight=self._height)
return html

def render(self, path="render.html"):
""" Render the options string, generate the html file
Expand All @@ -427,9 +410,11 @@ def render(self, path="render.html"):
if s.get('type') == "liquidFill":
temple = "lq.html"
break
my_option = json.dumps(self._option, indent=4)
my_option = json_dumps(self._option, indent=4)
tmp = self._jinja2_env.get_template(temple)
html = tmp.render(myOption=my_option, myWidth=self._width, myHeight=self._height)
html = tmp.render(myOption=my_option,
chart_id=uuid.uuid4().hex,
myWidth=self._width, myHeight=self._height)
html = template.freeze_js(html)
if PY2:
html = html.encode('utf-8')
Expand All @@ -445,7 +430,7 @@ def _repr_html_(self):
:return:
"""
divid = datetime.datetime.now()
my_option = json.dumps(self._option, indent=4)
my_option = json_dumps(self._option, indent=4)
temple = 'notebook.html'
series = self._option.get("series")
map_keywords = {}
Expand Down Expand Up @@ -1135,3 +1120,18 @@ def _geo_cities(self):
'荆州': [112.239741, 30.335165],
'廊坊': [116.7, 39.53],
}


class PandasNumpyTypeEncoder(json.JSONEncoder):
def default(self, obj):
try:
return obj.astype(float).tolist()
except:
try:
return obj.astype(str).tolist()
except:
return json.JSONEncoder.default(self, obj)


def json_dumps(data, indent=0):
return json.dumps(data, indent=indent, cls=PandasNumpyTypeEncoder)
40 changes: 19 additions & 21 deletions pyecharts/charts/bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pyecharts.base import Base
from pyecharts.option import get_all_options


class Bar(Base):
"""
<<< Bar chart >>>
Expand Down Expand Up @@ -33,24 +34,21 @@ def __add(self, name, x_axis, y_axis,
It specifies whether to stack category axis.
:param kwargs:
"""
if isinstance(x_axis, list) and isinstance(y_axis, list):
assert len(x_axis) == len(y_axis)
kwargs.update(x_axis=x_axis)
chart = get_all_options(**kwargs)
is_stack = "stack" if is_stack else ""
xaxis, yaxis = chart['xy_axis']
self._option.update(xAxis=xaxis, yAxis=yaxis)
self._option.get('legend')[0].get('data').append(name)
self._option.get('series').append({
"type": "bar",
"name": name,
"data": y_axis,
"stack": is_stack,
"label": chart['label'],
"markPoint": chart['mark_point'],
"markLine": chart['mark_line'],
"indexflag": self._option.get('_index_flag')
})
self._legend_visualmap_colorlst(**kwargs)
else:
raise TypeError("x_axis and y_axis must be list")
assert len(x_axis) == len(y_axis)
kwargs.update(x_axis=x_axis)
chart = get_all_options(**kwargs)
is_stack = "stack" if is_stack else ""
xaxis, yaxis = chart['xy_axis']
self._option.update(xAxis=xaxis, yAxis=yaxis)
self._option.get('legend')[0].get('data').append(name)
self._option.get('series').append({
"type": "bar",
"name": name,
"data": y_axis,
"stack": is_stack,
"label": chart['label'],
"markPoint": chart['mark_point'],
"markLine": chart['mark_line'],
"indexflag": self._option.get('_index_flag')
})
self._legend_visualmap_colorlst(**kwargs)
39 changes: 18 additions & 21 deletions pyecharts/charts/effectscatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,21 @@ def __add(self, name, x_value, y_value,
symbol size
:param kwargs:
"""
if isinstance(x_value, list) and isinstance(y_value, list):
assert len(x_value) == len(y_value)
kwargs.update(type="scatter")
chart = get_all_options(**kwargs)
xaxis, yaxis = chart['xy_axis']
self._option.update(xAxis=xaxis, yAxis=yaxis)
self._option.get('legend')[0].get('data').append(name)
self._option.get('series').append({
"type": "effectScatter",
"name": name,
"showEffectOn":"render",
"rippleEffect": chart['effect'],
"symbol": chart['symbol'],
"symbolSize": symbol_size,
"data": [list(z) for z in zip(x_value, y_value)],
"label": chart['label'],
"indexflag": self._option.get('_index_flag')
})
self._legend_visualmap_colorlst(**kwargs)
else:
raise TypeError("x_axis and y_axis must be list")
assert len(x_value) == len(y_value)
kwargs.update(type="scatter")
chart = get_all_options(**kwargs)
xaxis, yaxis = chart['xy_axis']
self._option.update(xAxis=xaxis, yAxis=yaxis)
self._option.get('legend')[0].get('data').append(name)
self._option.get('series').append({
"type": "effectScatter",
"name": name,
"showEffectOn":"render",
"rippleEffect": chart['effect'],
"symbol": chart['symbol'],
"symbolSize": symbol_size,
"data": [list(z) for z in zip(x_value, y_value)],
"label": chart['label'],
"indexflag": self._option.get('_index_flag')
})
self._legend_visualmap_colorlst(**kwargs)
Loading

0 comments on commit 2c505a1

Please sign in to comment.