In this tutorial we will use Brython, an implementation of Python written in javascript and Python, to access the Highcharts javascript library and to manage the data to be used in the maps. To integrate Brython in the IPython notebook we are using an extension for the notebook called brythonmagic that provides a new magic cell, **%%brython**
, that allow us to write and execute Brython code in the notebook.
As stated before, we will use Brython, and brythonmagic so first of all we need to load the extension and the Brython library.
So, let's load the extension:
In [ ]:
%load_ext brythonmagic
And the brython js lib:
In [ ]:
from brythonmagic import load_brython_dev
load_brython_dev()
[It is highly recommended that, at least, you read the brythonmagic docs to understand what it does. It is also recommended to have a quick look at the Brython docs].
In order to load javascript libraries in a safety way you should try to use https instead of http when possible (read more here). If you don't trust the source and/or the source cannot be loaded using https then you could download the javascript library and load it from a local location.
In the following tutorial I will try to follow several conventions to try to make it more readable.
Code in cells that are not code cells:
# This is a block of code
print("Hello world!")
**this is a piece of Python/Brython code inline with the text**
**this is a piece of javascript code inline with the text**
Highcharts is an open source, client side JavaScript library for making interactive charts, viewable in nearly any modern web browser. Since it is a client side library, it requires no special server side software or settings — you can use it without even downloading anything!
This tutorial just try to explain a little bit what can be found in the official documentation and try to introduce this library to Python users.
The website for Highcharts is located at http://www.highcharts.com/. To begin, we need to download a copy of Highcharts (or, we can directly link to the library — this is what we will do in the present tutorial). You can download the compressed library as a .zip file.
So, before continuing let's load the Highcharts library.
In [ ]:
from brythonmagic import load_js_lib
load_js_lib("https://cdnjs.cloudflare.com/ajax/libs/highcharts/5.0.7/highcharts.js")
First we create some simple HTML code. This HTML code will contain our chart. We will not use complicated HTML code during the tutorial to keep it simple and to be focused in 'How to create interactive charts in the browser with Python'. There is a lot of amazing resources to learn about HTML and CSS.
In [ ]:
html = """<div id="hc_ex1" style="width: 700px; height: 300px;"></div>"""
Now the interesting part. To make Highcharts available to Brython we need to 'load' the Highchart object/namespace to Brython using **Highcharts = window.Highcharts**
. We will use the **new**
method injected by Brython to the Javascript object that would behave similarly as if we were using Javascript constructors, (ie functions used with the Javascript keyword **new**
).
The code is as follows:
In [ ]:
%%brython -h html -p
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart':{
'renderTo': 'hc_ex1'
},
'title': {
'text': 'Monthly Average Temperature',
'x': -20 #center
},
'subtitle': {
'text': 'Source: WorldClimate.com',
'x': -20
},
'xAxis': {
'categories': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
'yAxis': {
'title': {
'text': 'Temperature (°C)'
},
'plotLines': [{
'value': 0,
'width': 1,
'color': '#808080'
}]
},
'tooltip': {
'valueSuffix': '°C'
},
'legend': {
'layout': 'vertical',
'align': 'right',
'verticalAlign': 'middle',
'borderWidth': 0
},
'series': [{
'name': 'Tokyo',
'data': [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
}]
}
hc(config)
Pretty simple!!
Ok, let's dissect the code in the Brython cell above.
**%%brython -h html -p**
:
-h
to use the HTML code defined in the html
variable located in the cell above and -p
to print the final HTML code generated below the generated chart. In this example, the generated code should be something like the following:<script id="66406" type="text/python">
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart':{
'renderTo': 'hc_ex1'
},
'title': {
'text': 'Monthly Average Temperature',
'x': -20 #center
},
'subtitle': {
'text': 'Source: WorldClimate.com',
'x': -20
},
'xAxis': {
'categories': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
'yAxis': {
'title': {
'text': 'Temperature (°C)'
},
'plotLines': [{
'value': 0,
'width': 1,
'color': '#808080'
}]
},
'tooltip': {
'valueSuffix': '°C'
},
'legend': {
'layout': 'vertical',
'align': 'right',
'verticalAlign': 'middle',
'borderWidth': 0
},
'series': [{
'name': 'Tokyo',
'data': [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
}]
}
hc(config)
</script>
<div id="brython_container_66406"><div id="hc_ex1" style="width: 700px; height: 300px;"></div></div>
<script type="text/javascript">brython({debug:1, static_stdlib_import: false, ipy_id: ["66406"]});</script>
the -p
option only provides information and it isn't required to run the Brython code cell.
**hc = Highcharts.Chart.new**
**config = { ... }**
**hc(config))**
config
dict.We can configure how the chart will be shown, layout, dimensions, axis, titles, legends,... All this information can be managed on each plot or using a configuration object (in javascript or dictionary in Python).
In the previous example we have seen the config
dict. Let's dedicate some time to understand it. The config
dict contains a series of dicts and each of these dicts manage several pieces of the chart:
chart
dict (complete api): It contains information related with how the chart is presented (background colors, area plot colors, type of chart to be used, where the chart should be rendered (html element), margins and spacings,...). A more complete example would be something like the following:'chart': {
'renderTo': 'The_id_of_the_html_element',
'backgroundColor': 'a_valid_html_object',
'type': 'spline',
'plotBorderWidth': 1,
'plotBorderColor': '#3F4044',
...
}
colors
key: The value is a list of strings containing valid html colors. The default colors in the latest highcharts version are:'colors': ['#7cb5ec', '#434348', '#90ed7d', '#f7a35c', '#8085e9',
'#f15c80', '#e4d354', '#8085e8', '#8d4653', '#91e8e1']
credits
dict (complete api): This dict allows you to control the credits label in the chart. By default will be shown the credits in the bottom left area of the chart. To control the credits you can use the following:'credits': {
'enabled': Boolean,
'href': String,
'text': String,
...
}
legend
dict (complete api): This dict allows you to control how the legend is shown:'legend': {
'enabled': Boolean,
'align': String,
'backgroundColor': String,
...
}
plotOptions
dict (complete api): The plotOptions
dict is a wrapper object for config objects for each series type. The config objects for each series can also be overridden for each series item as given in the series array. Configuration options for the series are given in three levels. Options for all series in a chart are given in the plotOptions['series']
dict. Then options for all series of a specific type are given in the plotOptions
of that type, for example plotOptions[
line]
. Next, options for one single series are given in the specific series array:'plotOptions': {
'enabled': Boolean,
'align': String,
'backgroundColor': String,
...
}
title
and subtitle
dicts (complete api for title and for subtitle): this options controls the appeareance of the title and subtitle of the chart. The keys are almost similar for both options:'title': {
'align': String,
'text': String,
...
}
tooltip
dict (complete api): Options for the tooltip that appears when the user hovers over a series or point:'tooltip': {
'enabled': Boolean,
'backgroundColor': 'a_valid_html_color',
'borderColor': 'a_valid_html_color',
...
}
xAxis
and yAxis
dicts (complete api for xAxis and for yAxis): The x axis or category axis and the y axis or the value axis. Normally, xAxis
is the horizontal axis and yAxis
the vertical axis except if the chart is inverted. The keys are pretty similar for both options:'xAxis': {
'min': float or int,
'max': float or int,
'title': {a dict with options},
'lineColor': 'A_valid_html_color',
...
}
series
dict (complete api): The actual series to append to the chart. In addition to the possible options, any member of the plotOptions
for that specific type of plot can be added to a series individually. For example, even though a general lineWidth
is specified in plotOptions['series']
, an individual lineWidth
can be specified for each series:
data
list: It is a list that can be one of the following options (we will come back to this later):
An array of numerical values. In this case, the numerical values will be interpreted as y values, and x values will be automatically calculated, either starting at 0 and incrementing by 1, or from pointStart
and pointInterval
given in the plotOptions
dict. If the axis has categories, these will be used. This option is not available for range series.
An array of arrays with two values. In this case, the first value is the x
value and the second is the y
value. If the first value is a string, it is applied as the name of the point, and the x
value is incremented following the above rules. For range series, the arrays will be interpreted as [x, low, high]
. In this cases, the x
value can be skipped altogether to make use of pointStart
and pointRange
.
An array of objects with named values. In this case the objects are point configuration objects. Range series values are given by low
and high
.
'series': {
'data': [* see above],
'name': String,
'type': String,
...
}
Let's see a more complete (and ugly) example using several configuration options:
In [ ]:
html = """<div id="hc_ex2" style="width: 700px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart': {
'renderTo': 'hc_ex2',
'backgroundColor': {
'linearGradient': [0, 0, 500, 500],
'stops': [[0, 'rgb(255, 255, 255)'],
[1, 'rgb(200, 200, 255)']]
},
'borderRadius': 10
},
'title': {
'align': 'left',
'text': 'My dummy title',
'style': { "color": "green", "fontSize": "20px" }
},
'subtitle': {
'align': 'right',
'text': 'Ugly subtitle',
'style': { "color": "orange", "fontSize": "12px" }
},
'legend': {
'backgroundColor': 'green',
'borderColor': 'yellow',
'borderRadius': 10,
'borderWidth': 3,
},
'series': [{
'data': [1,2,3,4],
'type': 'line',
'name': 'Name of the series',
'color': 'orange',
}],
'tooltip': {
'backgroundColor': 'gray',
'borderColor': 'yellow',
'borderRadius': 10,
'borderWidth': 3,
},
'xAxis': {
'categories': ['data'] * 4,
'lineWidth': 5,
'lineColor': 'violet',
'gridLineColor': 'violet',
'gridLineWidth': 3,
'title': {'text': 'X axis title'}
},
'yAxis': {
'lineWidth': 5,
'lineColor': 'blue',
'gridLineColor': 'blue',
'gridLineWidth': 3,
'title': {'text': 'Y axis title'}
},
'credits': {
'text': "Pybonacci rules!",
'href': 'https://twitter.com/pybonacci'
}
}
hc(config)
I got it!!! It's the ugliest chart in the world!!!
If we want we can define some of the options to be used during a complete session so we don't have to define each of the options when defining a new chart.
We will use **Highcharts.setOptions**
to create a set of options that could be overwritten on each individual chart if necessary. You can learn more about how to create themes for highcharts here.
In general, I don't like some default values for Highcharts and as Randall Olson has said, sometimes less is more. Also, as I have some degree of color blindness I will use the 'color blind 10' palette of colors from Tableau.
In [ ]:
%%brython -s globaloptions
from browser import window
Highcharts = window.Highcharts
global_options = {
'colors': ['rgb(0, 107, 164)', 'rgb(255, 128, 114)',
'rgb(171, 171, 171)', 'rgb(89, 89, 89)',
'rgb(95, 158, 209)', 'rgb(200, 82, 0)',
'rgb(137, 137, 137)', 'rgb(162, 200, 236)',
'rgb(256, 188, 121)', 'rgb(207, 207, 207)'],
'chart':{
'plotBackgroundColor': 'rgb(229, 229, 229)'
},
'credits':{
'enabled': False
},
'legend':{
'align': 'right',
'verticalAlign': 'middle',
'layout': 'vertical',
'borderWidth': 0,
'enabled': True
},
'plotOptions':{
'area': {
'fillOpacity': 0.5,
'marker': {'enabled': False},
},
'arearange': {
'fillOpacity': 0.5,
'marker': {'enabled': False},
},
'areaspline': {
'fillOpacity': 0.5,
'marker': {'enabled': False},
},
'areasplinerange': {
'fillOpacity': 0.5,
'marker': {'enabled': False},
},
'bar': {
'borderWidth': 0
},
'boxplot': {
'fillColor': '#FAFAFA',
'lineWidth': 2,
'medianWidth': 4,
'stemDashStyle': 'line',
'stemWidth': 1,
'whiskerLength': '30%',
'whiskerWidth': 2
},
'column': {
'borderWidth': 0
},
'columnrange': {
'borderWidth': 0
},
'errorbar': {
'color': '#fefefe',
'lineWidth': 2
},
'line': {
'marker': {'enabled': False},
'lineWidth': 2
},
'scatter': {
'marker': {
'enabled': True,
'lineWidth': 0,
'symbol': 'circle',
'radius': 5
},
},
'spline': {
'marker': {'enabled': False},
'lineWidth': 2
},
'waterfall': {
'borderWidth': 0
}
},
'subtitle': {
'align': 'center',
'style': {
'color': '#555555',
'fontWeight': 'bold'
}
},
'title': {
'align': 'center',
'text': None,
'style': {
'color': '#000000',
'fontWeight': 'bold'
}
},
'tooltip': {
'backgroundColor': 'rgba(255,255,224,0.5)',
'borderRadius': 5,
'crosshairs': [{
'width': 3,
'color': '#ffffff',
'dashStyle': 'shortdot'
}, {
'width': 3,
'color': '#ffffff',
'dashStyle': 'shortdot'
}],
'hideDelay': 200,
'enabled': True,
'shadow': False,
},
'xAxis': {
'gridLineColor': '#FFFFFF',
'gridLineWidth': 1,
'lineColor': 'rgb(229, 229, 229)',
'tickColor': 'rgb(229, 229, 229)',
'shadow': False,
},
'yAxis': {
'gridLineColor': '#FFFFFF',
'gridLineWidth': 1,
'lineColor': 'rgb(229, 229, 229)',
'tickColor': 'rgb(229, 229, 229)',
'shadow': False,
}
}
Highcharts.setOptions.new(global_options)
And now, let's repeat our first example again after the global configuration:
In [ ]:
html = """<div id="hc_ex3" style="width: 700px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart':{'renderTo': 'hc_ex3'},
'title': {'text': 'Monthly Average Temperature'},
'subtitle': {'text': 'Source: WorldClimate.com'},
'xAxis': {'categories': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'tooltip': {'valueSuffix': '°C'},
'series': [{'name': 'Tokyo',
'data': [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]}]
}
hc(config)
Much better than the ugly chart.
And other simple charts will look like the following:
In [ ]:
html = """
<div style="float: left;">
<div id="hc_ex4a" style="width: 400px; height: 300px;"></div>
<div id="hc_ex4b" style="width: 400px; height: 300px;"></div>
</div>
<div style="float: left;">
<div id="hc_ex4c" style="width: 400px; height: 300px;"></div>
<div id="hc_ex4d" style="width: 400px; height: 300px;"></div>
</div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# Bar
config = {
'chart': {'renderTo': 'hc_ex4a', 'type': 'bar'},
'xAxis': {'categories': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'tooltip': {'valueSuffix': '°C'},
'series': [{'name': 'Tokyo',
'data': [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]}]
}
hc(config)
# Area
config = {
'chart': {'renderTo': 'hc_ex4b', 'type': 'area'},
'xAxis': {'categories': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'tooltip': {'valueSuffix': '°C'},
'series': [{'name': 'Tokyo',
'data': [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]}]
}
hc(config)
# Bubble
config = {
'chart': {'renderTo': 'hc_ex4c', 'type': 'scatter', 'zoomType': 'xy'},
'title': {'text': 'You can pan and zoom'},
'series': [{'name': 'Tokyo',
'data': [[7.0, 6.9], [9.5, 14.5], [18.2, 21.5], [25.2, 26.5], [23.3, 18.3], [13.9, 9.6]]}]
}
hc(config)
# Pie
config = {
'chart': {'renderTo': 'hc_ex4d', 'type': 'pie', 'plotBackgroundColor': 'white'},
'series': [{'name': 'Python scientific libs',
'data': [['scipy', 6.9], ['IPython', 14.5],
['Matplotlib', 21.5], ['Numpy', 26.5], ['Pandas', 18.3]]}]
}
hc(config)
**[NEW CODE]** In the previous figures we have included some new options in the chart
dict:
**'type': 'type_of_chart'**
. It indicates which chart will be used to render the data.
**'zoomType': 'xy'**
. It allows us to zoom after the selection of an area with the mouse.
Also, in the Pie chart, we have used the option **'plotBackgroundColor': 'white'**
so the global option defined before is not used.
Before, we have seen three different ways to insert data into a chart. Let's see it again to understand better how it is used:
pointStart
and pointInterval
given in the plotOptions
dict. If the axis has categories, these will be used. This option is not available for range series.We have seen examples before but let's create a more complete example using this way:
In [ ]:
html = """<div id="hc_ex5" style="width: 700px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart': {'renderTo': 'hc_ex5'},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'tooltip': {'valueSuffix': '°C'},
'series': [{'name': 'Tokyo',
'data': [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]},
{'name': 'Madrid',
'data': [3.0, 5.4, 6.5, 12.7, 16.8, 21.4, 26.5, 26.2, 24.3, 17.3, 11.8, 6.7]}]
}
hc(config)
[x, low, high]
. In this cases, the x value can be skipped altogether to make use of pointStart
and pointRange
.This way has been used in the scatter chart previously. In the example above we have seen that the x values start at 0. If we want a different behaviour we could define the x values as follows:
In [ ]:
html = """<div id="hc_ex6" style="width: 700px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart': {'renderTo': 'hc_ex6', 'type': 'line'},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'tooltip': {'valueSuffix': '°C'},
'series': [{'name': 'Neverland',
'data': [[1, 6.9], [3, 14.5], [7, 21.5], [8, 26.5], [9, 18.3], [10, 9.6]]}]
}
hc(config)
If the x values are not a valid number it will be used as a label in the tooltip and the x values will start by 0 and incrementing by 1. For example:
In [ ]:
html = """<div id="hc_ex7" style="width: 700px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart': {'renderTo': 'hc_ex7', 'type': 'line'},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'tooltip': {'valueSuffix': '°C'},
'series': [{'name': 'Neverland',
'data': [['Jan', 6.9], ['Mar', 14.5], ['Jul', 21.5], ['Aug', 26.5], ['Sep', 18.3], ['Oct', 9.6]]}]
}
hc(config)
low
and high
.This is the most complete case as you can define more precisely how the data is displayed.
[HINT] In the next chart we will use the 'bubble' chart type. We must load a new javascript file as the highcharts.js doesn't provide the complete functionality of Highcharts and some type of charts (like 'arearange', 'bubble',...) are included as extras. The new javascript file is highcharts-more.js.
In [ ]:
from brythonmagic import load_js_lib
load_js_lib("https://cdnjs.cloudflare.com/ajax/libs/highcharts/5.0.7/highcharts-more.js")
In [ ]:
html = """<div id="hc_ex8" style="width: 700px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
import random
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = [{'name': 'Data {}'.format(i+1),
'color': 'rgb(100,50,{0})'.format(random.randrange(0,255)),
'y': random.randrange(0,25),
'x': i+1} for i in range(10)]
config = {
'chart': {'renderTo': 'hc_ex8'},
'yAxis': {'title': {'text': 'Temperature (°C)'}},
'series': [{'data': data, 'type': 'line', 'color': 'black'},
{'data': data, 'type': 'bubble'}]
}
hc(config)
On each data value we have used a name
(shown in the tooltip), a color
(used in scatter, bar, column, bubble,..., charts but not in line or area charts, for example) and the x
and y
values.
We already have seen several line charts. Let's see something more advanced. In the following example we will include the data value for each record and we will be able to choose an area of the chart to zoom on the chosen subset.
In [ ]:
html = """<div id="hc_ex9" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
import random
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = [[i+1, random.randrange(0,35)] for i in range(100)]
config = {
'chart': {'renderTo': 'hc_ex9', 'type': 'line', 'zoomType': 'x'},
'yAxis': {'title': {'text': 'Wind speed (m/s)'}},
'series': [{'data': data}],
'plotOptions': {
'line': {'dataLabels': {'enabled': True}, 'enableMouseTracking': False}
},
'title': {'text': 'Click and drag to zoom'}
}
hc(config)
**[NEW CODE]** In the previous figure we have included some new options in the chart dict:
**'zoomType': 'x'**
. It allows us to zoom after the selection of an area with the mouse. In this case, the zoom is over the x axisAlso, in the **plotOptions**
dict, we have included some new options.
**'line': {'dataLabels': {'enabled': True}, 'enableMouseTracking': False}**
. The **'dataLabels'**
option allow us to show the value of each record while the **'enableMouseTracking'**
option allows us to disable the default mouse interaction with the plot (tooltip,...).In the following plot we are defining some areas in the background to help highlight some aspects of the dataset:
In [ ]:
html = """<div id="hc_ex10" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
import random
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = [[i+1, random.randrange(10,105)] for i in range(100)]
config = {
'chart': {'renderTo': 'hc_ex10'},
'xAxis': {'min': -20},
'yAxis': {
'title': {'text': 'Mean Daily NO2 (ug/m3)'},
'max': 110,
'minorGridLineWidth': 0,
'gridLineWidth': 0,
'alternateGridColor': None,
'plotBands': [{
'from': 0,
'to': 15,
'color': 'rgba(100,100,255,0.5)',
'label': {
'text': 'Clean air',
'style': {
'color': 'black'
}
}
}, {
'from': 15,
'to': 40,
'color': 'rgba(0,255,0,0.5)',
'label': {
'text': 'Below EU limit',
'style': {
'color': 'black'
}
}
}, {
'from': 40,
'to': 120,
'color': 'rgba(255,0,0,0.5)',
'label': {
'text': 'Above EU limit',
'style': {
'color': 'black'
}
}
}]
},
'series': [{'data': data, 'lineWidth': 2, 'color': 'black'}]
}
hc(config)
**[NEW CODE]** In the previous figure we have included some new options in the options
dict:
**'xAxis': {'min': -20}**
. This indicates the minimum value for the x axis.
With the following code we have suppresed the grid lines for the y axis and added some band colors. I think the code is self-explanatory.
In the next plot we are going to play a little bit with the axis, where is the y
axis located?, and the legend?:
In [ ]:
html = """<div id="hc_ex11" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = [[3**i, i] for i in range(1, 10)]
config = {
'chart': {'renderTo': 'hc_ex11', 'type': 'spline'},
'yAxis': {'type': 'logarithmic', 'opposite': True, 'offset': 30},
'legend': {'align': 'left'},
'series': [{'data': data, 'lineWidth': 4, 'color': 'black'}]
}
hc(config)
**[NEW CODE]** In the previous figure we have included some new options to modify the y axis:
**'yAxis': {'type': 'logarithmic', 'opposite': True, 'offset': 30}**
. The axis scale is logarithmic, it is located on the opposite site and is located with an offset of 30px from the plot area.We can include some interactivity to the plot using some basic controls. With the buttons you can update the plot (every second a new point is created), stop the updates or reset to the original state:
In [ ]:
html = """<div id="hc_ex12container" style="height: 350px;">
<div id="hc_ex12" style="width: 900px; height: 300px;"></div>
</div>"""
In [ ]:
%%brython -h html
from browser.timer import set_interval, clear_interval
from browser import window, document, html
from random import randrange
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = [[i, randrange(0,10)] for i in range(0, 20)]
data_tmp = data[:]
config = {
'chart': {
'renderTo': 'hc_ex12',
'type': 'spline'
},
'series': [{'data': data, 'lineWidth': 2, 'color': 'black', 'animation': False,
'marker': {'enabled': True}}]
}
hc(config)
### NEW CODE ###
idtimer = None
# A button to animate the plot with new data
document['hc_ex12container'] <= html.BUTTON('Animate', Id = 'anim')
def add_point():
global data_tmp
x = data_tmp[-1][0] + 1
y = randrange(0,10)
data_tmp.append([x, y])
config['series'][0]['data'] = data_tmp[-20:]
hc(config)
def animate(ev):
global idtimer, config, data_tmp
idtimer = set_interval(add_point, 1000)
document['anim'].bind('click', animate)
# A button to stop the plot with new data
document['hc_ex12container'] <= html.BUTTON('Stop', Id = 'stop')
def stop(ev):
global idtimer
clear_interval(idtimer)
document['stop'].bind('click', stop)
# A button to reset the plot with the original values
document['hc_ex12container'] <= html.BUTTON('Reset', Id = 'reset')
def reset(ev):
global idtimer, config, data, data_tmp
if idtimer:
clear_interval(idtimer)
data_tmp = data[:]
config['series'][0]['data'] = data
hc(config)
document['reset'].bind('click', reset)
**[NEW CODE]** In the previous figure we have included some Brython specific code to animate the figure:
**document['hc_ex12container'].append(html.BUTTON('xxx', Id = 'xxx'))**
. With this code we are appending some buttons to the html div with id
'hc_ex12container', i.e., the html div element that contains the chart and the buttons. See the Brython docs for more info.
**document['xxx'].bind('click', xxx)**
. With this code we are attaching some functionality (events) to some DOM elements. See the Brython docs for more info.
Finally, we have defined some functions to manage the events using the browser.timer
module. See the Brython docs for more info.
As some area charts are quite similar to line plots I will write some examples and I will only explain the code that is relevant:
A simple area chart:
In [ ]:
html = """<div id="hc_ex13" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data1 = [[i, randrange(0,10)] for i in range(0, 20)]
data2 = [[i, randrange(0,10)] for i in range(0, 20)]
config = {
'chart': {
'renderTo': 'hc_ex13',
'type': 'area'
},
'series': [{'data': data1, 'dashStyle': 'ShortDot', 'lineWidth': 3},
{'data': data2}]
}
hc(config)
**[NEW CODE]**
**'dashStyle': 'ShortDot'**
. This option in the first data series modifies the line defining the area. In this case we have used 'ShortDot' but there are ather options.A simple stacked area chart:
In [ ]:
html = """<div id="hc_ex14" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data1 = [[i, randrange(0,10)] for i in range(0, 20)]
data2 = [[i, randrange(0,10)] for i in range(0, 20)]
config = {
'chart': {
'renderTo': 'hc_ex14',
'type': 'area'
},
'series': [{'data': data1, 'lineWidth': 3},
{'data': data2}],
'plotOptions': {'area': {'stacking': 'normal'}},
'tooltip': {'shared': True},
}
hc(config)
**[NEW CODE]**
**'plotOptions': {'area': {'stacking': 'normal'}}**
. This indicates that the areas will be stacked. We can choose a 'normal' or a 'percent' stacking. Other chart types like line, bar, columns,.., can be stacked.
**'tooltip': {'shared': True}**
. This option indicates that the tooltip is shared between the datasets so the tooltip will show the information from all the available datasets.
A simple arearange chart combined with a line plot:
In [ ]:
html = """<div id="hc_ex15" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data1 = [[i, randrange(5,10), randrange(10,15)] for i in range(0, 20)]
data2 = [[i, (lst[1] + lst[2]) / 2.] for i, lst in enumerate(data1)]
config = {
'chart': {
'renderTo': 'hc_ex15'
},
'series': [{'data': data2, 'type': 'line', 'name': 'mean', 'lineWidth': 3, 'color': 'black'},
{'data': data1, 'lineWidth': 1, 'type': 'arearange', 'name': 'extremes'}],
'tooltip': {'shared': True}
}
hc(config)
A bar plot with positive and negative values:
In [ ]:
html = """<div id="hc_ex16" style="width: 900px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data1 = [[i, -randrange(1,15)] for i in range(0, 20)]
data2 = [[i, randrange(1,15)] for i in range(0, 20)]
config = {
'chart': {
'renderTo': 'hc_ex16',
'type': 'bar'
},
'series': [{'data': data1, 'name': 'negative'},
{'data': data2, 'name': 'positive'}],
'plotOptions': {'bar': {'stacking': 'normal'}},
'tooltip': {'shared': True},
'xAxis': [{'opposite': False}, {'opposite': True, 'linkedTo': 0}]
}
hc(config)
**[NEW CODE]**
**'xAxis': [{'opposite': False}, {'opposite': True, 'linkedTo': 0}]**
. We are defining two x axis, the second one in the opposite side of the plot area and the values are linked to the first axis, i.e., both axis are the same but each one on one side of the chart.A bar plot with interactive values:
In [ ]:
html = """<div id="hc_ex17container">
<div id="hc_ex17" style="position: relative; float: left; width: 700px; height: 300px; margin: 20px;"></div>
<div id="tablediv"></div>
</div>"""
In [ ]:
%%brython -h html
from browser import window, document, html
from random import randrange
Highcharts = window.Highcharts
# first we create a table with two series of data, X and Y:
tab = html.TABLE()
tab.style = {'textAlign': 'center', 'width': '50px'}
tab <= html.TR(html.TD('X') + html.TD('Y'))
for i in range(5):
tab <= html.TR(
html.TD(
html.INPUT(
Id = 'x' + str(i), value = randrange(1,5), style = {'width': '50px'}
)
) +
html.TD(
html.INPUT(
Id = 'y' + str(i), value = randrange(1,5), style = {'width': '50px'}
)
)
)
document['tablediv'] <= tab
# Function to retrieve the data from the table
def get_data():
data1 = []
data2 = []
for i in range(5):
print('x' + str(i))
data1.append(float(document['x' + str(i)].value))
data2.append(float(document['y' + str(i)].value))
return [data1, data2]
# Function to update the chart
def update(ev):
global config, hc
datasets = get_data()
config['series'][0]['data'] = datasets[0]
config['series'][1]['data'] = datasets[1]
hc(config)
print(datasets)
# Button and event
document['hc_ex17container'] <= html.BUTTON('Update', Id = 'btn')
document['btn'].bind('click', update)
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
datasets = get_data()
data1 = datasets[0]
data2 = datasets[1]
config = {
'chart': {
'renderTo': 'hc_ex17',
'type': 'column'
},
'series': [{'data': data1, 'name': 'X'},
{'data': data2, 'name': 'Y'}],
'title': {'text': 'Modify the values in the table and update'}
}
hc(config)
**[NEW CODE]**
In the examples above we have seen a simple pie plot. A donut plot would be quite similar:
In [ ]:
html = """<div id="hc_ex18" style="width: 600px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# Pie
config = {
'chart': {'renderTo': 'hc_ex18', 'type': 'pie'},
'series': [{'name': 'Python scientific libs', 'innerSize': '60%',
'data': [['scipy', 6.9], ['IPython', 14.5],
['Matplotlib', 21.5], ['Numpy', 26.5], ['Pandas', 18.3]]}]
}
hc(config)
**[NEW CODE]**
**'innerSize': '50%'**
. This is the only difference wth the previous pie plot example. It indicates radius where the pie plot should start from the centre.In the examples above we have seen a simple scatter plot. A scatter plot with two datasets is similar:
In [ ]:
html = """<div id="hc_ex19" style="width: 600px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data1 = [[i + randrange(-2, 2), randrange(i-3,i+3)] for i in range(0, 20)]
data2 = [[i + randrange(-2, 2) + 2, randrange(i-3,i+3)] for i in range(0, 20)]
# Scatter
config = {
'chart': {'renderTo': 'hc_ex19', 'type': 'scatter'},
'series': [{'name': 'Station 1 vs model', 'data': data1},
{'name': 'Station 2 vs model', 'data': data2}],
'xAxis': {'title': {'text': 'Station'}},
'yAxis': {'title': {'text': 'Model'}}
}
hc(config)
A bubble plot with different colors depending on the size for each record would be as follows:
In [ ]:
html = """<div id="hc_ex20" style="width: 600px; height: 300px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = []
for i in range(20):
x = randrange(-10, 10)
y = randrange(-10, 10)
z = randrange(1, 20)
data.append({'x': x, 'y': y, 'z': z,
'color': 'rgb(40,40,{0})'.format(int(z * 255 / 20)),
'marker': {'lineWidth': 1, 'lineColor': 'black'}})
# Scatter
config = {
'chart': {'renderTo': 'hc_ex20', 'type': 'bubble'},
'series': [{'name': 'bubbles', 'data': data}]
}
hc(config)
For this examples we should first load the 3d highcharts library:
In [ ]:
from brythonmagic import load_js_lib
load_js_lib("https://cdnjs.cloudflare.com/ajax/libs/highcharts/5.0.7/highcharts-3d.js")
With the 3d 'module' loaded we can see a new example where some interactivity is added:
In [ ]:
html = """
<div id="hc_ex21" style="width: 900px; height: 400px;"></div>
<div id="sliders">
<table>
<tr>
<td>Alpha Angle</td>
<td>
<input id="R0" type="range" min="0" max="45" value="15"/> <span id="R0-value" class="value"></span>
</td>
</tr>
<tr>
<td>Beta Angle</td>
<td>
<input id="R1" type="range" min="0" max="45" value="15"/> <span id="R1-value" class="value"></span>
</td>
</tr>
</table>
</div>"""
In [ ]:
%%brython -h html
from browser import window, document
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
config = {
'chart': {
'renderTo': 'hc_ex21',
'plotBackgroundColor': 'white',
'type': 'column',
'margin': 100,
'options3d': {
'enabled': True,
'alpha': 15,
'beta': 15,
'depth': 50,
'viewDistance': 50
}
},
'plotOptions': {'column': {'depth': 25}},
'series': [{'data': [29.9, 71.5, 106.4, 129.2, 144.0, 176.0,
135.6, 148.5, 216.4, 194.1, 95.6, 54.4]}],
'xAxis': {'gridLineColor': '#C0C0C0'},
'yAxis': {'gridLineColor': '#C0C0C0'}
}
columns = hc(config)
def show_values():
document['R0-value'].html = columns.options.chart.options3d.alpha
document['R1-value'].html = columns.options.chart.options3d.beta
show_values()
# activate the sliders
def change_alpha(ev):
columns.options.chart.options3d.alpha = ev.target.value
show_values()
columns.redraw(False)
def change_beta(ev):
columns.options.chart.options3d.beta = ev.target.value
show_values()
columns.redraw(False)
document['R0'].bind('change', change_alpha)
document['R1'].bind('change', change_beta)
**[NEW CODE]**
**'options3d': {'enabled': True,'alpha': 15,'beta': 15,'depth': 50,'viewDistance': 50}'**
. In this case, we are switching on the 3d options and the initial view of the plot.You can create 3D pie, donut, scatter,..., plots.
In this case, as we did it before, we have to add a new module called heatmap:
In [ ]:
from brythonmagic import load_js_lib
load_js_lib("https://cdnjs.cloudflare.com/ajax/libs/highcharts/5.0.7/modules/heatmap.js")
A simple heatmap would be as follows:
In [ ]:
html = """<div id="hc_ex22" style="width: 900px; height: 400px;"></div>"""
In [ ]:
%%brython -h html
from random import randrange
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
# First we create the data to be passed to the plot
data = []
for y in range(7):
for x in range(7):
data.append([y, x, randrange(0,150)])
config = {
'chart': {
'renderTo': 'hc_ex22',
'type': 'heatmap',
'marginTop': 40,
'marginBottom': 40
},
'title': {'text': 'Commits made last week :-P'},
'xAxis': {'categories': ['Numpy', 'Scipy', 'Matplotlib', 'IPython',
'Pandas', 'Brython', 'Brythonmagic']},
'yAxis': {'categories': ['Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday', 'Sunday'],
'title': {'text': None}},
'colorAxis': {'min': 0,
'minColor': '#FFFFFF',
'maxColor': JSConstructor(Highcharts.getOptions)().colors[0]},
'legend': {
'align': 'right',
'layout': 'vertical',
'margin': 0,
'verticalAlign': 'top',
'y': 25,
'symbolHeight': 300
},
'series': [{
'borderWidth': 1,
'data': data,
'dataLabels': {
'enabled': True,
'color': 'black',
'style': {
'textShadow': 'none',
'HcTextStroke': None
}
}
}]
}
hc(config)
**[NEW CODE]**
**'colorAxis': {'min': 0, 'minColor': '#FFFFFF', 'maxColor': JSConstructor(Highcharts.getOptions)().colors[0]},**
. The colorAxis adds a colorbar to the plot.All we have seen until now is better that writting plain javascript but the use of Highcharts is not very Pythonic.
Let's start with something very simple
In [ ]:
%%brython -s wrapper01
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
class HC:
def __init__(self, container):
self.options = {}
self.options['chart'] = {}
self.options['chart']['renderTo'] = container
self.options['title'] = {}
self.options['title']['text'] = ""
self.options['legend'] = {}
self.options['legend']['enabled'] = False
self.options['subtitle'] = {}
self.options['subtitle']['text'] = ""
self.options['series'] = []
def show(self):
hc(self.options)
In [ ]:
html = """<div id="hc_ex23" style="width: 900px; height: 400px;"></div>"""
In [ ]:
%%brython -h html -S wrapper01
plt = HC('hc_ex23')
plt.show()
print(plt.options)
Ok, not very exciting. We initialise with an empty dictionary of options and with the show
method we pass the options dictionary to Highcharts and we can plot an empty chart. As stated before, not very fascinating. Let's add options to include a title and a subtitle.
[HINT] The -s
option in brythonmagic is used to name a brython cell ('wrapper01' is the name for the portion of code defined in the first cell of this section). With the name of that cell the code of the cell can be used in a new Brython cell using the -S
option. The use of the -S
option would be like an import of the code of the cell with the defined name ('wrapper01' in this case).
In [ ]:
%%brython -s wrapper02
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
class HC:
def __init__(self, container):
self.options = {}
self.options['chart'] = {}
self.options['chart']['renderTo'] = container
self.options['title'] = {}
self.options['title']['text'] = ""
self.options['legend'] = {}
self.options['legend']['enabled'] = False
self.options['subtitle'] = {}
self.options['subtitle']['text'] = ""
self.options['series'] = []
def show(self):
hc(self.options)
def title(self, text):
self.options['title']['text'] = text
#self.show()
def subtitle(self, text):
self.options['subtitle']['text'] = text
#self.show()
In [ ]:
html = """<div id="hc_ex24" style="width: 900px; height: 400px;"></div>"""
In [ ]:
%%brython -h html -S wrapper02
plt = HC('hc_ex24')
plt.title('Dummy title')
plt.subtitle('Dummy title')
plt.show()
Still not very funny. If you see the two new methods added, title
and subtitle
, there is one line commented. We can uncomment these lines if we don't want to call the show
method but everytime we use a method the chart will be plotted. I didn't see any overhead doing this but let's avoid it for the moment.
Ok, let's do something more interesting plotting some data in a line chart.
In [ ]:
%%brython -s wrapper03
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
class HC:
def __init__(self, container):
self.options = {}
self.options['chart'] = {}
self.options['chart']['renderTo'] = container
self.options['title'] = {}
self.options['title']['text'] = ""
self.options['legend'] = {}
self.options['legend']['enabled'] = False
self.options['subtitle'] = {}
self.options['subtitle']['text'] = ""
self.options['series'] = []
def show(self):
hc(self.options)
def title(self, text):
self.options['title']['text'] = text
#self.draw()
def subtitle(self, text):
self.options['subtitle']['text'] = text
#self.draw()
def plot(self, x, y = None, label = None, color = None, linewidth = None):
if y:
data = [[i, j] for i, j in zip(x, y)]
else:
data = x
serie = {'data': data, 'type': 'line'}
if linewidth:
serie['lineWidth'] = linewidth
if label:
serie['name'] = label
if color:
serie['color'] = color
self.options['series'].append(serie)
#self.draw()
In [ ]:
html = """<div id="hc_ex25" style="width: 900px; height: 400px;"></div>"""
In [ ]:
%%brython -h html -S wrapper03
plt = HC('hc_ex25')
plt.plot([1,2,4,5], [3,6,4,7], label = 'lineplot1', linewidth = 5, color = 'red')
plt.plot([1,2,4,5], [8,5,9,2], label = 'lineplot2', linewidth = 2, color = 'blue')
plt.title('Some line plots')
plt.show()
Wow!!, with less than 40 lines of Brython code we have a wrapper to do very simple interactive charts using Highcharts in a pythonic way. We added the label
keyword in the plot
method. In Matplotlib, the label
is used by the legend in the case we want to add a legend. Let's add a legend
method to provide the label
keyword some utility.
In [ ]:
%%brython -s wrapper04
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
class HC:
def __init__(self, container):
self.options = {}
self.options['chart'] = {}
self.options['chart']['renderTo'] = container
self.options['title'] = {}
self.options['title']['text'] = ""
self.options['legend'] = {}
self.options['legend']['enabled'] = False
self.options['subtitle'] = {}
self.options['subtitle']['text'] = ""
self.options['series'] = []
def show(self):
hc(self.options)
def title(self, text):
self.options['title']['text'] = text
#self.draw()
def subtitle(self, text):
self.options['subtitle']['text'] = text
#self.draw()
def plot(self, x, y = None, label = None, color = None, linewidth = None):
if y:
data = [[i, j] for i, j in zip(x, y)]
else:
data = x
serie = {'data': data, 'type': 'line'}
if linewidth:
serie['lineWidth'] = linewidth
if label:
serie['name'] = label
if color:
serie['color'] = color
self.options['series'].append(serie)
#self.draw()
def legend(self, loc = 'right'):
self.options['legend']['enabled'] = True
if loc:
self.options['legend']['align'] = loc
#self.draw()
In [ ]:
html = """<div id="hc_ex26" style="width: 900px; height: 400px;"></div>"""
In [ ]:
%%brython -h html -S wrapper04
plt = HC('hc_ex26')
plt.title('Line plots')
plt.plot([1,2,4,5], [3,6,4,7], label = 'lineplot1', linewidth = 5, color = 'red')
plt.plot([1,2,4,5], [8,5,9,2], label = 'lineplot2', linewidth = 2, color = 'blue')
plt.legend(loc = 'left')
plt.show()
The behaviour is not similar to that in the pyplot module of the matplotlib library but we can get some basic functionality. We can add a scatter
method to combine line plots with scatter plots.
In [ ]:
%%brython -s wrapper05
from browser import window
Highcharts = window.Highcharts
hc = Highcharts.Chart.new
class HC:
def __init__(self, container):
self.options = {}
self.options['chart'] = {}
self.options['chart']['renderTo'] = container
self.options['title'] = {}
self.options['title']['text'] = ""
self.options['legend'] = {}
self.options['legend']['enabled'] = False
self.options['subtitle'] = {}
self.options['subtitle']['text'] = ""
self.options['series'] = []
def show(self):
hc(self.options)
def title(self, text):
self.options['title']['text'] = text
#self.draw()
def subtitle(self, text):
self.options['subtitle']['text'] = text
#self.draw()
def plot(self, x, y = None, label = None, color = None, linewidth = None):
if y:
data = [[i, j] for i, j in zip(x, y)]
else:
data = x
serie = {'data': data, 'type': 'line'}
if linewidth:
serie['lineWidth'] = linewidth
if label:
serie['name'] = label
if color:
serie['color'] = color
self.options['series'].append(serie)
#self.draw()
def legend(self, loc = 'right'):
self.options['legend']['enabled'] = True
if loc:
self.options['legend']['align'] = loc
#self.draw()
def scatter(self, x, y, label = None, color = None):
data = [[i, j] for i, j in zip(x, y)]
serie = {'data': data, 'type': 'scatter'}
if label:
serie['name'] = label
if color:
serie['color'] = color
self.options['series'].append(serie)
#self.draw()
In [ ]:
html = """<div id="hc_ex27" style="width: 900px; height: 400px;"></div>"""
In [ ]:
%%brython -h html -S wrapper05
plt = HC('hc_ex27')
plt.title('Line plots')
plt.plot([1,2,4,5], [3,6,4,7], label = 'lineplot1', linewidth = 5, color = 'red')
plt.plot([1,2,4,5], [8,5,9,2], label = 'lineplot2', linewidth = 2, color = 'blue')
plt.scatter([1,2,4,5], [2,4,6,8], label = 'scatter1', color = 'green')
plt.legend(loc = 'left')
plt.show()
Let's stop here the wrapper.
You can use Highstock to create beautiful and interactive time series visualisation. You can create very simple maps using Highmaps or check out the notebook about OpenLayers 2.x we made some time ago.
If you do something with this information and/or need some help just let me know about it opening an issue in the repo where this notebook is stored.