├── notebookjs
├── __init__.py
├── resources
│ ├── template.html
│ └── CommAPI.js
├── _comm.py
└── _display.py
├── Images
├── notebookJS.ai
├── notebookJS.png
└── example_radial_bar.png
├── MANIFEST.in
├── Examples
├── 3_RadialBarChart
│ ├── energy.csv
│ ├── radial_bar.css
│ ├── RadialBarChart.ipynb
│ └── radial_bar_lib.js
├── 2_SimpleD3_Circle
│ ├── draw_circle_lib.js
│ └── SimpleD3_Circle.ipynb
├── 5_Webpack_BaseballAnnotator_Bidirectional
│ ├── BaseballVisualizer
│ │ ├── js
│ │ │ ├── index.js
│ │ │ ├── DraggableTimeline.js
│ │ │ ├── field.svg
│ │ │ ├── helpers.js
│ │ │ ├── PlayDiagram.js
│ │ │ └── TrajectoryAnnotator.js
│ │ ├── webpack.config.js
│ │ ├── play_annotated.csv
│ │ └── package.json
│ └── BaseballAnnotator.ipynb
├── 4_Bidirectional_Comm
│ ├── bar_chart_lib.js
│ └── Bar_Chart_Bidirectional_Comm.ipynb
├── 1_HelloWorld
│ └── HelloWorld.ipynb
├── 7_D3_scatterplot
│ ├── scatterplot_lib.js
│ ├── D3_Scatter.ipynb
│ └── Prices.csv
└── 6_HelloWorld_Bidirectional
│ └── HelloWorld_Bidirectional.ipynb
├── .gitignore
├── setup.py
├── LICENSE
└── README.md
/notebookjs/__init__.py:
--------------------------------------------------------------------------------
1 | from ._display import execute_js, save_html
--------------------------------------------------------------------------------
/Images/notebookJS.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgehpo/notebookJS/HEAD/Images/notebookJS.ai
--------------------------------------------------------------------------------
/Images/notebookJS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgehpo/notebookJS/HEAD/Images/notebookJS.png
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include notebookjs/resources/CommAPI.js
2 | include notebookjs/resources/template.html
--------------------------------------------------------------------------------
/Examples/3_RadialBarChart/energy.csv:
--------------------------------------------------------------------------------
1 | name,value
2 | Jan,432
3 | Feb,340
4 | Mar,382
5 | Apr,398
6 | May,410
--------------------------------------------------------------------------------
/Images/example_radial_bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorgehpo/notebookJS/HEAD/Images/example_radial_bar.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | .DS_Store
3 | __pycache__
4 | build
5 | dist
6 | node_modules
7 | .ipynb_checkpoints
8 |
--------------------------------------------------------------------------------
/Examples/2_SimpleD3_Circle/draw_circle_lib.js:
--------------------------------------------------------------------------------
1 | function draw_circle(id, data){
2 | d3.select(id)
3 | .append("div")
4 | .style("width", "50px")
5 | .style("height", "50px")
6 | .style("background-color", data.color)
7 | .style("border-radius", "50px")
8 | }
--------------------------------------------------------------------------------
/notebookjs/resources/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { select } from "d3-selection";
4 | import TrajectoryAnnotator from "./TrajectoryAnnotator";
5 |
6 | export function renderBaseballAnnotator(divName, data){
7 | ReactDOM.render(
8 |
9 | , select(divName).node());
10 | }
11 |
--------------------------------------------------------------------------------
/Examples/3_RadialBarChart/radial_bar.css:
--------------------------------------------------------------------------------
1 | body {
2 | font: 12px sans-serif;
3 | }
4 |
5 | svg {
6 | margin: 0px auto;
7 | display: block;
8 | }
9 |
10 | path.arc {
11 | opacity: 0.9;
12 | transition: opacity 0.5s;
13 | }
14 |
15 | path.arc:hover {
16 | opacity: 0.7;
17 | }
18 |
19 | .axis line, .axis circle {
20 | stroke: #cccccc;
21 | stroke-width: 1px
22 | }
23 |
24 | .axis circle {
25 | fill: none;
26 | }
27 |
28 | .r.axis text {
29 | text-anchor: end
30 | }
31 |
32 | .tooltip {
33 | position: absolute;
34 | display: none;
35 | background: rgba(0, 0, 0, 0.6);
36 | border-radius: 3px;
37 | box-shadow: -3px 3px 15px #888;
38 | color: white;
39 | padding: 6px;
40 | }
--------------------------------------------------------------------------------
/Examples/4_Bidirectional_Comm/bar_chart_lib.js:
--------------------------------------------------------------------------------
1 | function bar_chart(div_id, data_dict){
2 | const data = data_dict.array;
3 | const div = d3.select(div_id)
4 | const height = data.length * 20;
5 |
6 | const scaleY = d3.scale.ordinal()
7 | .domain(d3.range(data.length))
8 | .rangeRoundBands([0, height], .1);
9 | const scaleX = d3.scale.linear()
10 | .domain(d3.extent(data))
11 | .range([1, 120]);
12 |
13 | const svg = div.append("svg")
14 | .style("width", "120px")
15 | .style("height", height+"px");
16 |
17 | svg.selectAll("rect")
18 | .data(data)
19 | .enter()
20 | .append("rect")
21 | .attr("x", 0)
22 | .attr("y", (d, i) => scaleY(i))
23 | .attr("width", d => scaleX(d))
24 | .attr("height", scaleY.rangeBand());
25 | }
26 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | with open("README.md", "r") as fh:
4 | long_description = fh.read()
5 |
6 | setup(
7 | name="notebookjs",
8 | version="0.1.4",
9 | author="Jorge Piazentin Ono, Juliana Freire, Claudio Silva",
10 | author_email="jorgehpo@nyu.edu",
11 | description="notebookJS library - Seamless JavaScript integration in Python Notebooks",
12 | long_description=long_description,
13 | long_description_content_type="text/markdown",
14 | url="https://github.com/jorgehpo/notebookJS",
15 | packages=find_packages(exclude=['resources']),
16 | include_package_data=True,
17 | license="MIT",
18 | classifiers=[
19 | "Programming Language :: Python :: 3",
20 | "License :: OSI Approved :: MIT License",
21 | "Operating System :: OS Independent",
22 | ],
23 | python_requires='>=3.6',
24 | install_requires=[
25 | "notebook"
26 | ]
27 | )
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 notebookJS Developers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/webpack.config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | entry: ['./js/index.js'],
3 | output: {
4 | path: __dirname + '/build',
5 | filename: 'baseballvisualizer.js',
6 | library: 'baseballvisualizer'
7 | },
8 | module: {
9 | rules: [
10 | {
11 | test: /\.js$/,
12 | exclude: /node_modules/,
13 | loader: 'babel-loader',
14 | options: {
15 | presets: ['@babel/preset-env', '@babel/react']
16 | }
17 | },
18 | {
19 | test: /\.css$/,
20 | use: [
21 | 'style-loader',
22 | 'css-loader'
23 | ]
24 | },
25 | {
26 | test: /\.less$/,
27 | use: ['style-loader', 'css-loader', 'less-loader'],
28 | },
29 | {
30 | test: /\.(jpg|png|svg)$/,
31 | loader: "url-loader",
32 | options: {
33 | limit: Infinity // everything
34 | }
35 | }
36 | ]
37 | },
38 | resolve: {
39 | extensions: ['.js']
40 | },
41 | devServer:{
42 | writeToDisk:true,
43 | hot:false,
44 | inline: false,
45 | },
46 | mode: 'development'
47 | };
48 | module.exports = config;
49 |
--------------------------------------------------------------------------------
/notebookjs/resources/CommAPI.js:
--------------------------------------------------------------------------------
1 | const COMM_TYPES = {
2 | JUPYTER: 'JUPYTER',
3 | COLAB: 'COLAB'
4 | };
5 |
6 | class CommAPI{
7 | constructor(api_call_id, callback) {
8 | this.callback = callback;
9 | this.mode = null;
10 | if (window.Jupyter !== undefined) {
11 | this.mode = COMM_TYPES.JUPYTER;
12 | this.comm = window.Jupyter.notebook.kernel.comm_manager.new_comm(api_call_id, {});
13 | this.comm.on_msg(msg => {
14 | const data = msg.content.data;
15 | callback(data);
16 | });
17 | } else if (window.google !== undefined) {
18 | this.mode = COMM_TYPES.COLAB;
19 | this.comm = async function(msg){
20 | const result = await google.colab.kernel.invokeFunction(
21 | api_call_id,
22 | [msg], // The argument
23 | {}); // kwargs
24 | callback(result.data['application/json']);
25 | };
26 | } else {
27 | console.error(new Error("Cannot find Jupyter/Colab namespace from javascript"));
28 | }
29 | }
30 |
31 | call(msg) {
32 | if (this.comm){
33 | if (this.mode === COMM_TYPES.JUPYTER){
34 | this.comm.send(msg);
35 | } else if (this.mode === COMM_TYPES.COLAB){
36 | this.comm(msg);
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/notebookjs/_comm.py:
--------------------------------------------------------------------------------
1 | from IPython.core.display import display, HTML, Javascript
2 | from IPython import get_ipython
3 |
4 |
5 | def setup_comm_colab(api_call_id, callback):
6 | """Function that connects javascript call with a Colab Notebook"""
7 | from google.colab import output
8 | from IPython import display
9 | def _recv(msg):
10 | return display.JSON(callback(msg)) # Use display.JSON to transfer an object
11 | output.register_callback(api_call_id, _recv)
12 |
13 | def setup_comm_jupyter(api_call_id, callback):
14 | """Function that connects javascript call with a Jupyter Notebook"""
15 | def _comm_api(comm, open_msg):
16 | @comm.on_msg
17 | def _recv(msg):
18 | ret = callback(msg['content']['data'])
19 | comm.send(ret)
20 | get_ipython().kernel.comm_manager.register_target(api_call_id, _comm_api)
21 |
22 | def setup_comm_api(api_call_id, callback):
23 | """Function that abstracts notebook connection (Jupyter or Colab) to javascript"""
24 | try:
25 | jupyter_setup = True
26 | setup_comm_jupyter(api_call_id, callback)
27 | except Exception:
28 | jupyter_setup = False
29 | try:
30 | colab_setup = True
31 | setup_comm_colab(api_call_id, callback)
32 | except Exception:
33 | colab_setup = False
34 | if not jupyter_setup and not colab_setup:
35 | print("Error: Cannot find Jupyter/Colab namespace for Python")
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/play_annotated.csv:
--------------------------------------------------------------------------------
1 | time_elapsed_sec,P_x,P_y,B_x,B_y,R@1_x,R@1_y,R@2_x,R@2_y,R@3_x,R@3_y,1B_x,1B_y,2B_x,2B_y,3B_x,3B_y,SS_x,SS_y,LF_x,LF_y,CF_x,CF_y,RF_x,RF_y,C_x,C_y,BALL_x,BALL_y
2 | 0.0,-1.0465815337,62.2709968754,-2.2541642719,-2.9384709841,,,,,,,,,,,-72.29396308400649,91.01148815468284,-59.010552964461894,136.89963220401873,,,,,,,-2.2541642719,-6.5612191986,,
3 | 3.243,,,,,,,,,,,,,,,,,,,,,,,,,,,0.1207582738,62.7943023833
4 | 3.443,,,,,,,,,,,,,,,,,,,,,,,,,,,-1.0868244643,8.453079167
5 | 3.68,,,,,,,,,,,55.7098071589,96.0833135434,,,,,,,,,,,,,,,,
6 | 3.98,,,-1.0465815337,-2.9384709841,,,,,,,,,,,,,,,,,,,,,,,,
7 | 4.03,,,,,,,,,,,,,,,,,,,,,,,139.03301609056658,260.0730714943413,,,,
8 | 4.44,,,,,,,,,,,,,,,,,,,,,-16.74515712954728,316.82946018694093,,,,,,
9 | 4.47,,,,,,,,,,,,,,,,,,,-129.0503517766061,243.16691316037546,,,,,,,,
10 | 4.88,,,,,,,,,,,,,,,,,-42.10439463049605,136.89963220401873,,,,,,,,,,
11 | 5.493,,,,,,,,,,,,,,,,,,,,,,,,,,,31.5179094655,156.9857559583
12 | 5.81,,,,,,,,,,,64.1628863259,81.5923206857,,,,,,,,,,,,,,,,
13 | 6.34,,,,,,,,,,,,,,,,,,,,,,,128.16477144730283,217.8076756594267,,,,
14 | 6.57,,,,,,,,,,,,,,,,,,,-102.48353153751694,233.50625125525212,,,,,,,,
15 | 7.25,,,,,,,,,,,,,,,,,,,,,,,,,3.7837494188,-0.5233055079,,
16 | 7.383,,,,,,,,,,,,,,,,,,,,,,,,,,,61.707477919,65.2094678596
17 | 7.503,,,,,,,,,,,,,30.3103267273,156.9857559583,,,,,,,,,,,,,,
18 | 8.8,,,,,,,,,,,,,,,,,,,,,-33.65131546351312,249.20482685107754,,,,,,
19 | 8.81,,,,,,,,,,,,,,,,,-48.142308321198136,126.03138756075496,,,,,,,,,,
20 | 8.85,3.7837494188,52.6103349703,,,,,,,,,,,,,,,,,-102.48353153751694,214.18492744500543,,,,,,,,
21 | 8.89,,,62.9553035877,65.8937450899,,,,,,,64.1628863259,61.0634141373,,,,,,,,,,,131.78751966172408,180.37261077707376,,,,
22 | 8.893,,,,,,,,,,,,,38.7634058943,153.3630077438,,,,,,,,,,,,,,
23 |
--------------------------------------------------------------------------------
/Examples/1_HelloWorld/HelloWorld.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Hello World\n",
8 | "\n",
9 | "Adding a text element to a Notebook output cell using plain JavaScript"
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": null,
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "from notebookjs import execute_js"
19 | ]
20 | },
21 | {
22 | "cell_type": "markdown",
23 | "metadata": {},
24 | "source": [
25 | "### Writing the js function"
26 | ]
27 | },
28 | {
29 | "cell_type": "code",
30 | "execution_count": null,
31 | "metadata": {},
32 | "outputs": [],
33 | "source": [
34 | "helloworld_js = \"\"\"\n",
35 | "function helloworld(div_id, data){\n",
36 | " document.querySelector(div_id).textContent=data.text;\n",
37 | "}\n",
38 | "\"\"\""
39 | ]
40 | },
41 | {
42 | "cell_type": "markdown",
43 | "metadata": {},
44 | "source": [
45 | "### Running the code"
46 | ]
47 | },
48 | {
49 | "cell_type": "code",
50 | "execution_count": null,
51 | "metadata": {},
52 | "outputs": [],
53 | "source": [
54 | "execute_js(helloworld_js, \"helloworld\", {\"text\": \"Hello World\"})"
55 | ]
56 | }
57 | ],
58 | "metadata": {
59 | "kernelspec": {
60 | "display_name": "Python 3",
61 | "language": "python",
62 | "name": "python3"
63 | },
64 | "language_info": {
65 | "codemirror_mode": {
66 | "name": "ipython",
67 | "version": 3
68 | },
69 | "file_extension": ".py",
70 | "mimetype": "text/x-python",
71 | "name": "python",
72 | "nbconvert_exporter": "python",
73 | "pygments_lexer": "ipython3",
74 | "version": "3.8.5"
75 | }
76 | },
77 | "nbformat": 4,
78 | "nbformat_minor": 4
79 | }
80 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/DraggableTimeline.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from 'prop-types';
3 | import * as d3 from 'd3';
4 | import Slider from 'rc-slider';
5 | import 'rc-slider/assets/index.css';
6 |
7 | export default class DraggableTimeline extends React.Component {
8 | shouldComponentUpdate(nextProps, nextState) {
9 | if (this.props.tracking === nextProps.tracking) {
10 | return false;
11 | }
12 | return true;
13 | }
14 |
15 | render () {
16 | const tracking = this.props.tracking;
17 | const annotatedPoints = {};
18 |
19 | tracking.forEach(({x, y, t}, idx) =>
20 | {
21 | annotatedPoints[t] = {};
22 | });
23 |
24 | const createSliderWithTooltip = Slider.createSliderWithTooltip;
25 | const SliderTP = createSliderWithTooltip(Slider);
26 |
27 | return (
28 |
29 | {this.props.onDragTime(value)}}
35 | marks={annotatedPoints}
36 | activeDotStyle={{ borderColor: 'steelblue' }}
37 | dotStyle={{borderColor: 'steelblue' }}
38 | trackStyle={{ backgroundColor: 'steelblue' }}
39 | railStyle={{ backgroundColor: 'steelblue' }}
40 | width={this.props.width}
41 | />
42 |
43 | );
44 | }
45 | }
46 |
47 | DraggableTimeline.propTypes = {
48 | width: PropTypes.number.isRequired,
49 | height: PropTypes.number.isRequired,
50 | tracking: PropTypes.array,
51 | playerHead: PropTypes.number,
52 | margin: PropTypes.object,
53 | onDragTime: PropTypes.func.isRequired,
54 | trackingDuration: PropTypes.number.isRequired,
55 | };
56 |
57 | DraggableTimeline.defaultProps = {
58 | width: 400,
59 | height: 10,
60 | margin: {top:10, bottom: 10, left: 10, right:10}
61 | };
62 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "baseballvisualizer",
3 | "version": "1.0.0",
4 | "description": "library to plot and edit baseball trajectories",
5 | "main": "./js/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "webpack",
9 | "start": "webpack-dev-server --progress --colors --content-base dist/"
10 | },
11 | "author": "Jorge Piazentin Ono",
12 | "license": "UNLICENSED",
13 | "devDependencies": {
14 | "@babel/core": "^7.12.9",
15 | "@babel/preset-env": "^7.12.7",
16 | "@babel/preset-react": "^7.12.7",
17 | "babel-loader": "^8.2.2",
18 | "css-loader": "^2.1.1",
19 | "file-loader": "^3.0.1",
20 | "less": "^4.0.0",
21 | "less-loader": "^7.1.0",
22 | "prop-types": "^15.6.2",
23 | "react": "^16.14.0",
24 | "react-dom": "^16.14.0",
25 | "style-loader": "^0.23.1",
26 | "url-loader": "^4.1.1",
27 | "webpack": "^4.44.2",
28 | "webpack-cli": "^3.3.11",
29 | "webpack-dev-server": "^3.11.0",
30 | "worker-loader": "^2.0.0"
31 | },
32 | "dependencies": {
33 | "@babel/polyfill": "^7.12.1",
34 | "@material-ui/core": "^4.11.2",
35 | "@material-ui/icons": "^4.11.2",
36 | "crossfilter2": "^1.5.4",
37 | "d3": "^6.3.1",
38 | "d3-array": "^2.9.0",
39 | "d3-axis": "^1.0.12",
40 | "d3-fetch": "^1.1.2",
41 | "d3-scale": "^2.2.2",
42 | "d3-scale-chromatic": "^1.3.3",
43 | "d3-selection": "^1.4.0",
44 | "d3-shape": "^1.3.7",
45 | "d3-transition": "^1.3.2",
46 | "d3-zoom": "^1.8.3",
47 | "dagre": "^0.8.4",
48 | "everpolate": "0.0.3",
49 | "install": "^0.12.2",
50 | "jquery": "^3.5.1",
51 | "lodash": "^4.17.20",
52 | "npm": "^7.3.0",
53 | "rc-checkbox": "^2.3.1",
54 | "rc-select": "^11.5.3",
55 | "rc-slider": "^9.6.5",
56 | "react-bootstrap": "^1.4.0",
57 | "react-redux": "^6.0.0",
58 | "react-select": "^2.4.1",
59 | "redux": "^4.0.1",
60 | "redux-thunk": "^2.3.0",
61 | "styled-components": "^5.2.1"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/field.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Examples/7_D3_scatterplot/scatterplot_lib.js:
--------------------------------------------------------------------------------
1 | function scatter(div_id, data){
2 | // Code adapted from https://www.d3-graph-gallery.com/graph/scatter_animation_start.html
3 | // Dataset https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/2_TwoNum.csv
4 | // set the dimensions and margins of the graph
5 | var margin = {top: 10, right: 30, bottom: 30, left: 60},
6 | width = 460 - margin.left - margin.right,
7 | height = 400 - margin.top - margin.bottom;
8 | // append the svg object to the body of the page
9 | var svg = d3.select(div_id)
10 | .append("svg")
11 | .attr("width", width + margin.left + margin.right)
12 | .attr("height", height + margin.top + margin.bottom)
13 | .append("g")
14 | .attr("transform",
15 | "translate(" + margin.left + "," + margin.top + ")")
16 |
17 | // Add X axis
18 | var x = d3.scale.linear()
19 | .domain([0, 0])
20 | .range([ 0, width ]);
21 | svg.append("g")
22 | .attr("class", "myXaxis axis") // Note that here we give a class to the X axis, to be able to call it later and modify it
23 | .attr("transform", "translate(0," + height + ")")
24 | .call(d3.svg.axis().scale(x))
25 | .attr("opacity", "0")
26 |
27 | // Add Y axis
28 | var y = d3.scale.linear()
29 | .domain([0, 500000])
30 | .range([ height, 0]);
31 | svg.append("g")
32 | .attr("class", "axis")
33 | .call(d3.svg.axis()
34 | .orient('left')
35 | .scale(y));
36 |
37 | // Add dots
38 | svg.append('g')
39 | .selectAll("dot")
40 | .data(data)
41 | .enter()
42 | .append("circle")
43 | .attr("cx", function (d) { return x(d.GrLivArea); } )
44 | .attr("cy", function (d) { return y(d.SalePrice); } )
45 | .attr("r", 1.5)
46 | .style("fill", "#69b3a2")
47 |
48 | // new X axis
49 | x.domain([0, 4000])
50 | svg.select(".myXaxis")
51 | .transition()
52 | .duration(2000)
53 | .attr("opacity", "1")
54 | .call(d3.svg.axis().scale(x));
55 |
56 | svg.selectAll("circle")
57 | .transition()
58 | .delay(function(d,i){return(i*3)})
59 | .duration(2000)
60 | .attr("cx", function (d) { return x(d.GrLivArea); } )
61 | .attr("cy", function (d) { return y(d.SalePrice); } )
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/Examples/7_D3_scatterplot/D3_Scatter.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# D3 Scatter Plot"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "D3 V3 scatter plot. Code adapted from https://www.d3-graph-gallery.com/graph/scatter_animation_start.html"
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "### Loading libraries"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": null,
27 | "metadata": {},
28 | "outputs": [],
29 | "source": [
30 | "with open(\"scatterplot_lib.js\", \"r\") as f:\n",
31 | " scatterplot_lib = f.read()\n",
32 | " \n",
33 | "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"\n",
34 | "\n",
35 | "css = \"\"\"\n",
36 | ".axis path,\n",
37 | ".axis line {\n",
38 | " fill: none;\n",
39 | " stroke: slategray;\n",
40 | " shape-rendering: crispEdges;\n",
41 | "}\n",
42 | "\"\"\""
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {},
48 | "source": [
49 | "### Loading data"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "metadata": {},
56 | "outputs": [],
57 | "source": [
58 | "import pandas as pd\n",
59 | "data = pd.read_csv(\"Prices.csv\").to_dict(orient=\"records\")"
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {},
65 | "source": [
66 | "### Plotting Scatter Plot"
67 | ]
68 | },
69 | {
70 | "cell_type": "code",
71 | "execution_count": null,
72 | "metadata": {},
73 | "outputs": [],
74 | "source": [
75 | "from notebookjs import execute_js\n",
76 | "execute_js(library_list=[d3_lib_url, scatterplot_lib], main_function=\"scatter\", data_dict=data, css_list=css)"
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": null,
82 | "metadata": {},
83 | "outputs": [],
84 | "source": []
85 | }
86 | ],
87 | "metadata": {
88 | "kernelspec": {
89 | "display_name": "Python 3",
90 | "language": "python",
91 | "name": "python3"
92 | },
93 | "language_info": {
94 | "codemirror_mode": {
95 | "name": "ipython",
96 | "version": 3
97 | },
98 | "file_extension": ".py",
99 | "mimetype": "text/x-python",
100 | "name": "python",
101 | "nbconvert_exporter": "python",
102 | "pygments_lexer": "ipython3",
103 | "version": "3.8.5"
104 | }
105 | },
106 | "nbformat": 4,
107 | "nbformat_minor": 4
108 | }
109 |
--------------------------------------------------------------------------------
/Examples/3_RadialBarChart/RadialBarChart.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Radial Bar Chart\n",
8 | "\n",
9 | "We port the Radial Bar Chart implemented in D3 to a Jupyter Notebook.\n",
10 | "Code adapted from https://bl.ocks.org/AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c"
11 | ]
12 | },
13 | {
14 | "cell_type": "markdown",
15 | "metadata": {},
16 | "source": [
17 | "### Loading the radial bar chart code. \n",
18 | "\n",
19 | "We load both js and css scripts"
20 | ]
21 | },
22 | {
23 | "cell_type": "code",
24 | "execution_count": null,
25 | "metadata": {},
26 | "outputs": [],
27 | "source": [
28 | "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"\n",
29 | "\n",
30 | "with open(\"radial_bar.css\", \"r\") as f:\n",
31 | " radial_bar_css = f.read()\n",
32 | " \n",
33 | "with open (\"radial_bar_lib.js\", \"r\") as f:\n",
34 | " radial_bar_lib = f.read()"
35 | ]
36 | },
37 | {
38 | "cell_type": "markdown",
39 | "metadata": {},
40 | "source": [
41 | "### Loading data"
42 | ]
43 | },
44 | {
45 | "cell_type": "code",
46 | "execution_count": null,
47 | "metadata": {},
48 | "outputs": [],
49 | "source": [
50 | "import pandas as pd\n",
51 | "energy = pd.read_csv(\"energy.csv\")"
52 | ]
53 | },
54 | {
55 | "cell_type": "markdown",
56 | "metadata": {},
57 | "source": [
58 | "### Plotting the bar chart \n",
59 | "\n",
60 | "Radial Bar Chart of energy consumption over five months"
61 | ]
62 | },
63 | {
64 | "cell_type": "code",
65 | "execution_count": null,
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "from notebookjs import execute_js\n",
70 | "execute_js(library_list=[d3_lib_url, radial_bar_lib], main_function=\"radial_bar\", \n",
71 | " data_dict=energy.to_dict(orient=\"records\"), css_list=[radial_bar_css])"
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": null,
77 | "metadata": {},
78 | "outputs": [],
79 | "source": []
80 | }
81 | ],
82 | "metadata": {
83 | "kernelspec": {
84 | "display_name": "Python 3",
85 | "language": "python",
86 | "name": "python3"
87 | },
88 | "language_info": {
89 | "codemirror_mode": {
90 | "name": "ipython",
91 | "version": 3
92 | },
93 | "file_extension": ".py",
94 | "mimetype": "text/x-python",
95 | "name": "python",
96 | "nbconvert_exporter": "python",
97 | "pygments_lexer": "ipython3",
98 | "version": "3.8.5"
99 | }
100 | },
101 | "nbformat": 4,
102 | "nbformat_minor": 4
103 | }
104 |
--------------------------------------------------------------------------------
/Examples/2_SimpleD3_Circle/SimpleD3_Circle.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Simple D3 Circle\n",
8 | "\n",
9 | "This notebook shows how to load D3js from an URL and draw a simple circle in a notebook"
10 | ]
11 | },
12 | {
13 | "cell_type": "code",
14 | "execution_count": null,
15 | "metadata": {},
16 | "outputs": [],
17 | "source": [
18 | "from notebookjs import execute_js"
19 | ]
20 | },
21 | {
22 | "cell_type": "markdown",
23 | "metadata": {},
24 | "source": [
25 | "### Setting up the JavaScript libraries\n",
26 | "\n",
27 | "We are using two JavaScript libraries: D3 and a local \"draw_circle_lib\" library.\n",
28 | "\n",
29 | "- D3 is loaded from the web (notebookJS takes care of downloading files from URLs). \n",
30 | "\n",
31 | "- draw_circle_lib is loaded from a local file.\n",
32 | "\n",
33 | "**Note that we are using D3 V3.**\n",
34 | "More recent versions of D3 use ES6 and cannot be directly loaded in the notebook script tag. We recommend using a javascript build tool such as babel + webpack to use more modern libraries."
35 | ]
36 | },
37 | {
38 | "cell_type": "code",
39 | "execution_count": null,
40 | "metadata": {},
41 | "outputs": [],
42 | "source": [
43 | "with open(\"./draw_circle_lib.js\", \"r\") as f:\n",
44 | " draw_circle_lib = f.read()\n",
45 | " \n",
46 | "d3_lib_url = \"https://d3js.org/d3.v3.min.js\""
47 | ]
48 | },
49 | {
50 | "cell_type": "markdown",
51 | "metadata": {},
52 | "source": [
53 | "This is the D3 function to draw a circle: "
54 | ]
55 | },
56 | {
57 | "cell_type": "code",
58 | "execution_count": null,
59 | "metadata": {},
60 | "outputs": [],
61 | "source": [
62 | "print(draw_circle_lib)"
63 | ]
64 | },
65 | {
66 | "cell_type": "markdown",
67 | "metadata": {},
68 | "source": [
69 | "### Running the code"
70 | ]
71 | },
72 | {
73 | "cell_type": "code",
74 | "execution_count": null,
75 | "metadata": {},
76 | "outputs": [],
77 | "source": [
78 | "execute_js([d3_lib_url, draw_circle_lib], \"draw_circle\", {\"color\": \"#4682B4\"})"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {},
85 | "outputs": [],
86 | "source": []
87 | }
88 | ],
89 | "metadata": {
90 | "kernelspec": {
91 | "display_name": "Python 3",
92 | "language": "python",
93 | "name": "python3"
94 | },
95 | "language_info": {
96 | "codemirror_mode": {
97 | "name": "ipython",
98 | "version": 3
99 | },
100 | "file_extension": ".py",
101 | "mimetype": "text/x-python",
102 | "name": "python",
103 | "nbconvert_exporter": "python",
104 | "pygments_lexer": "ipython3",
105 | "version": "3.8.5"
106 | }
107 | },
108 | "nbformat": 4,
109 | "nbformat_minor": 4
110 | }
111 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/helpers.js:
--------------------------------------------------------------------------------
1 | import {linear} from 'everpolate';
2 |
3 | export const constants = {
4 | gameElements: ["P", "B", "R@1", "R@2", "R@3", "1B", "2B", "3B", "SS", "LF", "CF", "RF", "C",
5 | "BALL"],
6 | gameElementsDesc: {
7 | "P": "Pitcher",
8 | "B": "Batter",
9 | "R@1": "Runner at first",
10 | "R@2": "Runner at second",
11 | "R@3": "Runner at third",
12 | "1B": "First baseman",
13 | "2B": "Second baseman",
14 | "3B": "Third baseman",
15 | "SS": "Shortstop",
16 | "LF": "Left fielder",
17 | "CF": "Center fielder",
18 | "RF": "Right fielder",
19 | "C": "Catcher",
20 | "BALL": "Ball"
21 | },
22 | };
23 |
24 | const input2ndBase = [0.0, 127.2792206],
25 | outputHomePlate = [125.2, 203.5],
26 | output2ndBase = [125.2, 150.8],
27 | inputScale = 1.0 / input2ndBase[1],
28 | outputScale = 1.0 / (output2ndBase[1] - outputHomePlate[1]);
29 |
30 | export function mapFieldSVGX(x){
31 | const x2 = (((-x) * inputScale) / outputScale) + outputHomePlate[0];
32 | return x2;
33 | }
34 |
35 | export function mapFieldSVGY(y){
36 | const y2 = (y * inputScale) / outputScale + outputHomePlate[1];
37 | return y2;
38 | }
39 |
40 | export function mapFieldSVG([x, y]){
41 | const x2 = (((-x) * inputScale) / outputScale) + outputHomePlate[0];
42 | const y2 = (y * inputScale) / outputScale + outputHomePlate[1];
43 | return [x2, y2];
44 | }
45 |
46 | export function mapSVGField([x2, y2]){
47 | const x = -((x2 - outputHomePlate[0]) * outputScale) / inputScale;
48 | const y = ((y2 - outputHomePlate[1]) * outputScale) / inputScale;
49 | return [x, y];
50 | }
51 |
52 | export function interpolatePositions(tQuery, positions) {
53 | //positions: array of [{x, y, t}, ...]
54 | if (positions.length === 1){
55 | return {x: positions[0].x, y:positions[0].y};
56 | }
57 | positions.sort((a,b) => a.t - b.t);
58 | if (tQuery <= positions[0].t) {
59 | return {x: positions[0].x, y: positions[0].y}
60 | }
61 | if (tQuery >= positions[positions.length-1].t) {
62 | return {x: positions[positions.length-1].x, y: positions[positions.length-1].y}
63 | }
64 | const t = positions.map(p=>p.t);
65 | const x = positions.map(p=>p.x);
66 | const y = positions.map(p=>p.y);
67 | const mappedX = linear(tQuery, t, x);
68 | const mappedY = linear(tQuery, t, y);
69 | return {x: mappedX, y: mappedY};
70 | }
71 |
72 |
73 | export function convert_data_format(tracking){
74 | let csvObj = {};
75 | constants.gameElements.forEach(elem => {
76 | tracking[elem].forEach( ({x, y, t}) => {
77 | if (!csvObj[t]) {
78 | csvObj[t] = {"time_elapsed_sec": t};
79 | constants.gameElements.forEach(ename => {
80 | csvObj[t][ename + "_x"] = null;
81 | csvObj[t][ename + "_y"] = null;
82 | })
83 | }
84 | csvObj[t][elem + "_x"] = x;
85 | csvObj[t][elem + "_y"] = y;
86 | });
87 | });
88 | const timeKeys = Object.keys(csvObj);
89 | timeKeys.sort((a, b) => a - b);
90 | const csvArray = [];
91 | timeKeys.forEach(key => {
92 | csvArray.push(csvObj[key]);
93 | });
94 | return csvArray;
95 | }
--------------------------------------------------------------------------------
/Examples/4_Bidirectional_Comm/Bar_Chart_Bidirectional_Comm.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Bar chart example with bidirectional communication"
8 | ]
9 | },
10 | {
11 | "cell_type": "code",
12 | "execution_count": null,
13 | "metadata": {},
14 | "outputs": [],
15 | "source": [
16 | "from notebookjs import execute_js"
17 | ]
18 | },
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {},
22 | "source": [
23 | "### Loading libraries"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "d3_lib_url = \"https://d3js.org/d3.v3.min.js\"\n",
33 | "\n",
34 | "with open(\"bar_chart_lib.js\", \"r\") as f:\n",
35 | " bar_chart_lib = f.read()"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "metadata": {},
41 | "source": [
42 | "### The bar chart displays the array data in order"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "metadata": {},
49 | "outputs": [],
50 | "source": [
51 | "data_dict = {\n",
52 | " \"array\": [1,2,3]\n",
53 | "}\n",
54 | "execute_js(library_list=[d3_lib_url, bar_chart_lib], main_function=\"bar_chart\", data_dict=data_dict)"
55 | ]
56 | },
57 | {
58 | "cell_type": "markdown",
59 | "metadata": {},
60 | "source": [
61 | "### Set up data update callback"
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "metadata": {},
68 | "outputs": [],
69 | "source": [
70 | "import random\n",
71 | "\n",
72 | "def random_array(data):\n",
73 | " # Makes an array of random numbers\n",
74 | " n = data[\"n\"]\n",
75 | " return {'array': [random.random() for x in range(n)]}\n",
76 | "\n",
77 | "callbacks = {'random_array': random_array}"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "### Update the bar chart every 2 seconds using callback"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "metadata": {},
91 | "outputs": [],
92 | "source": [
93 | "update_func_js = \"\"\"\n",
94 | "function set_update(div_id, _){ \n",
95 | " comm = new CommAPI(\"random_array\", (data) => {\n",
96 | " d3.select(div_id).selectAll(\"*\").remove();\n",
97 | " bar_chart(div_id, data);\n",
98 | " });\n",
99 | " comm.call({n: 5})\n",
100 | " setInterval(function(){ comm.call({n: 5}) }, 2000);\n",
101 | "}\n",
102 | "\"\"\"\n",
103 | "\n",
104 | "execute_js(library_list=[d3_lib_url, bar_chart_lib, update_func_js], main_function=\"set_update\", callbacks=callbacks)"
105 | ]
106 | },
107 | {
108 | "cell_type": "code",
109 | "execution_count": null,
110 | "metadata": {},
111 | "outputs": [],
112 | "source": []
113 | }
114 | ],
115 | "metadata": {
116 | "kernelspec": {
117 | "display_name": "Python 3",
118 | "language": "python",
119 | "name": "python3"
120 | },
121 | "language_info": {
122 | "codemirror_mode": {
123 | "name": "ipython",
124 | "version": 3
125 | },
126 | "file_extension": ".py",
127 | "mimetype": "text/x-python",
128 | "name": "python",
129 | "nbconvert_exporter": "python",
130 | "pygments_lexer": "ipython3",
131 | "version": "3.8.5"
132 | }
133 | },
134 | "nbformat": 4,
135 | "nbformat_minor": 4
136 | }
137 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/PlayDiagram.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from "prop-types";
3 | import { mapFieldSVG, mapSVGField, constants, mapFieldSVGY, mapFieldSVGX, interpolatePositions } from './helpers';
4 | import $ from "jquery";
5 | import * as d3 from "d3";
6 | import fieldsvg from "./field.svg";
7 |
8 | export default class PlayDiagram extends Component {
9 | render() {
10 | let ball = null;
11 |
12 | let line = d3.line()
13 | .x((d) => {
14 | return mapFieldSVGX(d.x);
15 | })
16 | .y((d) => {
17 | return mapFieldSVGY(d.y);
18 | });
19 |
20 | const lineElems = [];
21 | const endElems = [];
22 |
23 | constants.gameElements.forEach( elem => {
24 | if (this.props.tracking && this.props.tracking[elem] && this.props.tracking[elem].length > 0){
25 | const tracking = this.props.tracking;
26 | const newTrackingElem = [];
27 | for (let i = 0; i < tracking[elem].length; ++i){
28 | if (tracking[elem][i].t > this.props.playUpTo){
29 | break;
30 | }
31 | newTrackingElem.push(tracking[elem][i]);
32 | }
33 | const lastPos = interpolatePositions(this.props.playUpTo, tracking[elem]);
34 | newTrackingElem.push( {...lastPos, t: this.props.playUpTo} );
35 | lineElems.push(
36 | );
47 |
48 | if (elem === this.props.annotationElem) {
49 | endElems.push();
60 |
61 | } else {
62 | endElems.push(
63 |
73 | );
74 | }
75 | }
76 | });
77 | return (
78 |
93 | );
94 | }
95 | }
96 |
97 | PlayDiagram.propTypes = {
98 | width: PropTypes.number.isRequired,
99 | height: PropTypes.number.isRequired,
100 | onClick: PropTypes.func,
101 | playUpTo: PropTypes.number,
102 | tracking: PropTypes.object,
103 | annotationElem: PropTypes.string
104 | };
105 |
106 | PlayDiagram.defaultProps = {
107 | width: 500,
108 | height: 500
109 | };
110 |
--------------------------------------------------------------------------------
/Examples/6_HelloWorld_Bidirectional/HelloWorld_Bidirectional.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "id": "IaxAGrTGttuV"
7 | },
8 | "source": [
9 | "# notebookJS Colab HelloWorld\n",
10 | "\n",
11 | "In this notebook, we show how to use notebookJS to run custom Javascript code. We also show how to send messages between Python and Javascript."
12 | ]
13 | },
14 | {
15 | "cell_type": "code",
16 | "execution_count": null,
17 | "metadata": {
18 | "id": "vYD-yjPtn_ik"
19 | },
20 | "outputs": [],
21 | "source": [
22 | "!pip install notebookjs"
23 | ]
24 | },
25 | {
26 | "cell_type": "markdown",
27 | "metadata": {
28 | "id": "BjePMHoyt9TF"
29 | },
30 | "source": [
31 | "## Defining the JavaScript drawing function.\n",
32 | "\n",
33 | "The function requests a message to python every 1 second. The callback identifier is called *get_hello*"
34 | ]
35 | },
36 | {
37 | "cell_type": "code",
38 | "execution_count": null,
39 | "metadata": {
40 | "id": "10am7Nm9oCc9"
41 | },
42 | "outputs": [],
43 | "source": [
44 | "helloworld_js = \"\"\"\n",
45 | "function helloworld(div_id, data){\n",
46 | " comm = new CommAPI(\"get_hello\", (ret) => {\n",
47 | " document.querySelector(div_id).textContent = ret.text;\n",
48 | " });\n",
49 | " setInterval(() => {comm.call({})}, 1000);\n",
50 | " comm.call({});\n",
51 | "}\n",
52 | "\"\"\""
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "metadata": {
58 | "id": "zsXaw6XxuJF2"
59 | },
60 | "source": [
61 | "## Defining the Python callback"
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "metadata": {
68 | "id": "L0ucikc4oIOW"
69 | },
70 | "outputs": [],
71 | "source": [
72 | "import random\n",
73 | "def hello_world_random(data):\n",
74 | " hello_world_languages = [\n",
75 | " \"Ola Mundo\", # Portuguese\n",
76 | " \"Hello World\", # English\n",
77 | " \"Hola Mundo\", # Spanish\n",
78 | " \"Geiá sou Kósme\", # Greek\n",
79 | " \"Kon'nichiwa sekai\", # Japanese\n",
80 | " \"Hallo Welt\", # German\n",
81 | " \"namaste duniya\" #Hindi\n",
82 | " ]\n",
83 | " i = random.randint(0, len(hello_world_languages)-1)\n",
84 | " return {'text': hello_world_languages[i]}"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "metadata": {
90 | "id": "g-rkBdq7uXSL"
91 | },
92 | "source": [
93 | "## Running the javascript function with Python callbacks"
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {
100 | "id": "HaxtaaB-pj0z"
101 | },
102 | "outputs": [],
103 | "source": [
104 | "from notebookjs import execute_js\n",
105 | "execute_js(helloworld_js, \"helloworld\", callbacks={\"get_hello\": hello_world_random})"
106 | ]
107 | },
108 | {
109 | "cell_type": "code",
110 | "execution_count": null,
111 | "metadata": {
112 | "id": "848VKX8jqGbW"
113 | },
114 | "outputs": [],
115 | "source": []
116 | }
117 | ],
118 | "metadata": {
119 | "colab": {
120 | "collapsed_sections": [],
121 | "name": "HelloWorld_notebookJS_Colab.ipynb",
122 | "provenance": []
123 | },
124 | "kernelspec": {
125 | "display_name": "Python 3",
126 | "language": "python",
127 | "name": "python3"
128 | },
129 | "language_info": {
130 | "codemirror_mode": {
131 | "name": "ipython",
132 | "version": 3
133 | },
134 | "file_extension": ".py",
135 | "mimetype": "text/x-python",
136 | "name": "python",
137 | "nbconvert_exporter": "python",
138 | "pygments_lexer": "ipython3",
139 | "version": "3.8.5"
140 | }
141 | },
142 | "nbformat": 4,
143 | "nbformat_minor": 1
144 | }
145 |
--------------------------------------------------------------------------------
/Examples/3_RadialBarChart/radial_bar_lib.js:
--------------------------------------------------------------------------------
1 | function radial_bar(div_id, data){
2 | // Radial Bar Chart
3 |
4 | // Code adapted from: https://bl.ocks.org/AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c
5 | // We have changed the data loading lines (to receive the parameter data) and ported the code to D3 V3.
6 |
7 | const width = 960,
8 | height = 500,
9 | chartRadius = height / 2 - 40;
10 |
11 | const color = d3.scale.category10();
12 |
13 | let svg = d3.select(div_id).append('svg')
14 | .attr('width', width)
15 | .attr('height', height)
16 | .append('g')
17 | .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
18 |
19 | let tooltip = d3.select('body').append('div')
20 | .attr('class', 'tooltip');
21 |
22 | const PI = Math.PI,
23 | arcMinRadius = 10,
24 | arcPadding = 10,
25 | labelPadding = -5,
26 | numTicks = 10;
27 |
28 |
29 | let scale = d3.scale.linear()
30 | .domain([0, d3.max(data, d => d.value) * 1.1])
31 | .range([0, 2 * PI]);
32 |
33 | let ticks = scale.ticks(numTicks).slice(0, -1);
34 | let keys = data.map((d, i) => d.name);
35 | //number of arcs
36 | const numArcs = keys.length;
37 | const arcWidth = (chartRadius - arcMinRadius - numArcs * arcPadding) / numArcs;
38 |
39 | let arc = d3.svg.arc()
40 | .innerRadius((d, i) => getInnerRadius(i))
41 | .outerRadius((d, i) => getOuterRadius(i))
42 | .startAngle(0)
43 | .endAngle((d, i) => scale(d))
44 |
45 | let radialAxis = svg.append('g')
46 | .attr('class', 'r axis')
47 | .selectAll('g')
48 | .data(data)
49 | .enter().append('g');
50 |
51 | radialAxis.append('circle')
52 | .attr('r', (d, i) => getOuterRadius(i) + arcPadding);
53 |
54 | radialAxis.append('text')
55 | .attr('x', labelPadding)
56 | .attr('y', (d, i) => -getOuterRadius(i) + arcPadding)
57 | .text(d => d.name);
58 |
59 | let axialAxis = svg.append('g')
60 | .attr('class', 'a axis')
61 | .selectAll('g')
62 | .data(ticks)
63 | .enter().append('g')
64 | .attr('transform', d => 'rotate(' + (rad2deg(scale(d)) - 90) + ')');
65 |
66 | axialAxis.append('line')
67 | .attr('x2', chartRadius);
68 |
69 | axialAxis.append('text')
70 | .attr('x', chartRadius + 10)
71 | .style('text-anchor', d => (scale(d) >= PI && scale(d) < 2 * PI ? 'end' : null))
72 | .attr('transform', d => 'rotate(' + (90 - rad2deg(scale(d))) + ',' + (chartRadius + 10) + ',0)')
73 | .text(d => d);
74 |
75 | //data arcs
76 | let arcs = svg.append('g')
77 | .attr('class', 'data')
78 | .selectAll('path')
79 | .data(data)
80 | .enter().append('path')
81 | .attr('class', 'arc')
82 | .style('fill', (d, i) => color(i))
83 |
84 | arcs.transition()
85 | .delay((d, i) => i * 200)
86 | .duration(1000)
87 | .attrTween('d', arcTween);
88 |
89 | arcs.on('mousemove', showTooltip)
90 | arcs.on('mouseout', hideTooltip)
91 |
92 |
93 | function arcTween(d, i) {
94 | let interpolate = d3.interpolate(0, d.value);
95 | return t => arc(interpolate(t), i);
96 | }
97 |
98 | function showTooltip(d) {
99 | tooltip.style('left', (d3.event.pageX + 10) + 'px')
100 | .style('top', (d3.event.pageY - 25) + 'px')
101 | .style('display', 'inline-block')
102 | .html(d.value);
103 | }
104 |
105 | function hideTooltip() {
106 | tooltip.style('display', 'none');
107 | }
108 |
109 | function rad2deg(angle) {
110 | return angle * 180 / PI;
111 | }
112 |
113 | function getInnerRadius(index) {
114 | return arcMinRadius + (numArcs - (index + 1)) * (arcWidth + arcPadding);
115 | }
116 |
117 | function getOuterRadius(index) {
118 | return getInnerRadius(index) + arcWidth;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballVisualizer/js/TrajectoryAnnotator.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PlayDiagram from './PlayDiagram';
3 | import {interpolatePositions, constants, convert_data_format} from './helpers';
4 | import {Container, Row, Col, Button} from 'react-bootstrap';
5 | import 'rc-select/assets/index.less';
6 |
7 | import DraggableTimeline from "./DraggableTimeline";
8 | import PropTypes from 'prop-types';
9 |
10 | function submitTrajectoryToServer(tracking){
11 | const alert_sent = ()=>{alert("Trajectory sent to Jupyter Notebook.")};
12 | let comm = new CommAPI("submit_trajectory", alert_sent)
13 |
14 | // Send data
15 | comm.call({'trajectory': tracking})
16 | }
17 |
18 | function convertRawTrackingToTrajectory(rawTracking) {
19 | if (rawTracking === null) return [];
20 | let tracking = Object.create(null);
21 | constants.gameElements.forEach(element => {
22 | tracking[element] = [];
23 | for (let i = 0; i < rawTracking.length; ++i) {
24 | if (rawTracking[i][element + "_x"] !== null) {
25 | tracking[element].push({
26 | x: rawTracking[i][element + "_x"],
27 | y: rawTracking[i][element + "_y"],
28 | t: rawTracking[i]["time_elapsed_sec"]
29 | });
30 | }
31 | }
32 | });
33 | return tracking;
34 | }
35 |
36 | export default class TrajectoryAnnotator extends Component {
37 | constructor(props){
38 | super(props);
39 | this.state = {
40 | tracking: convertRawTrackingToTrajectory(this.props.tracking),
41 | trackingDuration: this.props.tracking[this.props.tracking.length-1].time_elapsed_sec,
42 | playerHead: 0,
43 | curTracking: "B"
44 | };
45 | this.clickPlayDiagram = this.clickPlayDiagram.bind(this);
46 | }
47 |
48 | clickPlayDiagram(position) {
49 | const curTracking = this.state.curTracking;
50 | let newTracking = this.state.tracking[curTracking].filter(annotation => {
51 | return Math.abs(annotation.t - this.state.playerHead) > 1e-1;
52 | });
53 | newTracking.push({
54 | x: position[0],
55 | y: position[1],
56 | t: this.state.playerHead
57 | });
58 | const allTrackings = {
59 | ...this.state.tracking,
60 | };
61 | allTrackings[curTracking] = newTracking;
62 | this.setState({tracking: allTrackings});
63 | }
64 |
65 | render() {
66 | return (
67 |
68 | Selected Position
69 |
70 | {
71 | constants.gameElements.map((elem) => {
72 | return
81 | })
82 | }
83 |
84 |
92 |
{this.setState({playerHead: time})}}
100 | trackingDuration={this.state.trackingDuration}
101 | />
102 |
103 |
111 |
112 |
124 |
125 | );
126 | }
127 | }
128 |
129 |
130 | TrajectoryAnnotator.propTypes = {
131 | tracking: PropTypes.arrayOf(PropTypes.object).isRequired
132 | };
--------------------------------------------------------------------------------
/Examples/5_Webpack_BaseballAnnotator_Bidirectional/BaseballAnnotator.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Baseball Visualizer and Annotator - Bidirectional communication"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "This visualization shows an interactive chart that displays baseball game trajectories. The user can control the progress of the play using a slider. Furthermore, the user can select a player or the ball to edit its trajectory (either clicking on the field, or using the button \"Clear trajectory\"). Visualization based on the paper HistoryTracker (Ono et al, 2019)."
15 | ]
16 | },
17 | {
18 | "cell_type": "markdown",
19 | "metadata": {},
20 | "source": [
21 | "### Loading code bundle (built with Webpack)\n",
22 | "\n",
23 | "Source code for the bundle is in BaseballVisualizer/js.\n",
24 | "\n",
25 | "To build the library from scratch, run\n",
26 | "\n",
27 | "```\n",
28 | "cd BaseballVisualizer\n",
29 | "npm install\n",
30 | "npm run build\n",
31 | "```"
32 | ]
33 | },
34 | {
35 | "cell_type": "code",
36 | "execution_count": null,
37 | "metadata": {},
38 | "outputs": [],
39 | "source": [
40 | "with open(\"BaseballVisualizer/build/baseballvisualizer.js\", \"r\") as f:\n",
41 | " code_bundle = f.read()"
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "metadata": {},
47 | "source": [
48 | "### Loading the data (baseball play trajectory in CSV)"
49 | ]
50 | },
51 | {
52 | "cell_type": "code",
53 | "execution_count": null,
54 | "metadata": {},
55 | "outputs": [],
56 | "source": [
57 | "import pandas as pd\n",
58 | "play_csv = pd.read_csv(\"./BaseballVisualizer/play_annotated.csv\")\n",
59 | "data_dict = {'tracking': play_csv.to_json(orient=\"records\")}"
60 | ]
61 | },
62 | {
63 | "cell_type": "markdown",
64 | "metadata": {},
65 | "source": [
66 | "### Setting up callback\n",
67 | "\n",
68 | "The callback function will set the received_trajectory variable when the user clicks the button \"Submit\""
69 | ]
70 | },
71 | {
72 | "cell_type": "code",
73 | "execution_count": null,
74 | "metadata": {},
75 | "outputs": [],
76 | "source": [
77 | "received_trajectory = None\n",
78 | "\n",
79 | "def receive_trajectory(data):\n",
80 | " global received_trajectory \n",
81 | " received_trajectory = data['trajectory']\n",
82 | " return {\"received\": True}\n",
83 | " \n",
84 | "callbacks = {'submit_trajectory': receive_trajectory}"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "metadata": {},
90 | "source": [
91 | "In javascript, the callback call is done in BaseballVisualizer/js/TrajectoryAnnotator.js\n",
92 | "\n",
93 | "```Javascript\n",
94 | "function submitTrajectoryToServer(tracking){\n",
95 | " const alert_sent = ()=>{alert(\"Trajectory sent to Jupyter Notebook.\")};\n",
96 | " let comm = new CommAPI(\"submit_trajectory\", alert_sent)\n",
97 | "\n",
98 | " // Send data\n",
99 | " comm.call({'trajectory': tracking})\n",
100 | "}\n",
101 | "```"
102 | ]
103 | },
104 | {
105 | "cell_type": "markdown",
106 | "metadata": {},
107 | "source": [
108 | "### Rendering Visualization"
109 | ]
110 | },
111 | {
112 | "cell_type": "code",
113 | "execution_count": null,
114 | "metadata": {},
115 | "outputs": [],
116 | "source": [
117 | "from notebookjs import execute_js\n",
118 | "\n",
119 | "execute_js(library_list=code_bundle, \n",
120 | " main_function=\"baseballvisualizer.renderBaseballAnnotator\", \n",
121 | " data_dict=data_dict, \n",
122 | " callbacks=callbacks)"
123 | ]
124 | },
125 | {
126 | "cell_type": "code",
127 | "execution_count": null,
128 | "metadata": {},
129 | "outputs": [],
130 | "source": [
131 | "received_trajectory"
132 | ]
133 | },
134 | {
135 | "cell_type": "code",
136 | "execution_count": null,
137 | "metadata": {},
138 | "outputs": [],
139 | "source": []
140 | }
141 | ],
142 | "metadata": {
143 | "kernelspec": {
144 | "display_name": "Python 3",
145 | "language": "python",
146 | "name": "python3"
147 | },
148 | "language_info": {
149 | "codemirror_mode": {
150 | "name": "ipython",
151 | "version": 3
152 | },
153 | "file_extension": ".py",
154 | "mimetype": "text/x-python",
155 | "name": "python",
156 | "nbconvert_exporter": "python",
157 | "pygments_lexer": "ipython3",
158 | "version": "3.8.5"
159 | }
160 | },
161 | "nbformat": 4,
162 | "nbformat_minor": 4
163 | }
164 |
--------------------------------------------------------------------------------
/notebookjs/_display.py:
--------------------------------------------------------------------------------
1 | from IPython.core.display import display, HTML, Javascript
2 | from string import Template, ascii_uppercase
3 | import pkg_resources
4 | import random
5 | import re
6 | import json
7 | from ._comm import setup_comm_api
8 |
9 | def id_generator(size=15):
10 | """Helper function to generate random div ids."""
11 | chars = list(ascii_uppercase)
12 | return ''.join(random.choice(chars) for i in range(size))
13 |
14 | def make_html(library_list, main_function, parameter_dict, css_list):
15 | """Makes the HTML that will be added to the Notebook"""
16 | # Loading Python CommAPI
17 | comm_api_path = pkg_resources.resource_filename(__name__, "resources/CommAPI.js")
18 | with open(comm_api_path, "r") as f:
19 | comm_api_js = f.read()
20 |
21 | # Making sure library_list and css_list are lists.
22 | if type(library_list) is not list:
23 | library_list = [library_list]
24 | if type(css_list) is not list:
25 | css_list = [css_list]
26 |
27 | # Downloading web resources
28 | for idx in range(len(library_list)):
29 | if check_url(library_list[idx]):
30 | library_list[idx] = download_url(library_list[idx])
31 | for idx in range(len(css_list)):
32 | if check_url(css_list[idx]):
33 | css_list[idx] = download_url(css_list[idx])
34 |
35 | # Adding CommAPI to library_list
36 | library_list.insert(0, comm_api_js)
37 |
38 | # Generating HTML
39 | div_id = id_generator()
40 | library_bundle = '\n\n'.join(library_list)
41 | css_bundle = '\n'.join(css_list)
42 | template_path = pkg_resources.resource_filename(__name__, "resources/template.html")
43 | with open(template_path, "r") as f:
44 | html_all_template = f.read()
45 | html_all_template = Template(html_all_template)
46 |
47 | return html_all_template.substitute(div_id=div_id,
48 | library_bundle=library_bundle,
49 | main_function=main_function,
50 | parameter_dict=json.dumps(parameter_dict),
51 | css_bundle=css_bundle)
52 |
53 | # Regex expression to test if a string is a URL
54 | regex_url = re.compile(
55 | r'^(?:http|ftp)s?://' # http:// or https://
56 | r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain...
57 | r'localhost|' #localhost...
58 | r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
59 | r'(?::\d+)?' # optional port
60 | r'(?:/?|[/?]\S+)$', re.IGNORECASE)
61 |
62 | def check_url(string):
63 | """Checks if the string argument is a URL"""
64 | return re.match(regex_url, string) is not None
65 |
66 | def download_url(url):
67 | """Downloads a URL file as a browser."""
68 | import requests
69 | headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0'}
70 | r = requests.get(url, headers=headers, stream=False)
71 | return r.content.decode("utf-8")
72 |
73 |
74 |
75 | def save_html(html_dest, library_list, main_function, data_dict = {}, callbacks = None, css_list=[]):
76 | """Saves the bundled code (output of execute_js) to an HTML file
77 |
78 | Parameters
79 | ----------
80 | html_dest : str
81 | Path to the output HTML dest file. Example: "./output.html"
82 | library_list : list of str
83 | List of strings containing either 1) URL to a javascript library, 2) javascript code
84 | main_function : str
85 | Name of the main function to be called. The function will be called with two parameters:
86 | , for example "#my_div", and .
87 | data_dict : dict
88 | Dictionary containing the data to be passed to
89 | callbacks : dict
90 | Dictionary of the form { : }. The javascript library can
91 | use callbacks to talk to python.
92 | css_list : list of str
93 | List of strings containing either 1) URL to a CSS stylesheet or 2) CSS styles
94 | """
95 | if callbacks is not None:
96 | print ("Warning: Python callbacks do not work in standalone HTML file.")
97 | print ("Saving file...")
98 |
99 | html_all = make_html(library_list, main_function, data_dict, css_list)
100 | with open(html_dest, "w") as f:
101 | f.write(html_all)
102 |
103 | def execute_js(library_list, main_function, data_dict = {}, callbacks = {}, css_list=[]):
104 | """Executes a javascript function that can add content to an output div
105 |
106 | Parameters
107 | ----------
108 | library_list : list of str
109 | List of strings containing either 1) URL to a javascript library, 2) javascript code
110 | main_function : str
111 | Name of the main function to be called. The function will be called with two parameters:
112 | , for example "#my_div", and .
113 | data_dict : dict
114 | Dictionary containing the data to be passed to
115 | callbacks : dict
116 | Dictionary of the form { : }. The javascript library can
117 | use callbacks to talk to python.
118 | css_list : list of str
119 | List of strings containing either 1) URL to a CSS stylesheet or 2) CSS styles
120 | """
121 | html_all = make_html(library_list, main_function, data_dict, css_list)
122 | for callback_id in callbacks.keys():
123 | setup_comm_api(callback_id, callbacks[callback_id])
124 | display(HTML(html_all))
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # notebookJS: seamless JavaScript integration in Python Notebooks
2 |
3 | [](https://www.python.org/)
4 | [](https://colab.research.google.com/drive/1g8WOn9oZ5G_3-Y8DYmpV1MIj59dnd81u?usp=sharing)
5 | [](https://pypi.org/project/notebookjs)
6 |
7 |
8 |
14 |
15 | *notebookJS* enables the execution of custom JavaScript code in Python Notebooks (Jupyter Notebook and Google Colab). This Python library can be useful for implementing and reusing interactive Data Visualizations in the Notebook environment.
16 |
17 | *notebookJS* takes care of downloading and handling Javascript libraries and CSS stylesheets from the web. Furthermore, it supports bidirectional communication between Python and JavaScript. User interactions in HTML/JavaScript can trigger Python callbacks that process data on demand and send the results back to the front-end code.
18 |
19 | Implementation details in [our paper](https://ieeexplore.ieee.org/iel7/5992/9387473/09391750.pdf?casa_token=v05VFeWM3gwAAAAA:ra4uhd2Xpsd6lllS62Woz1IOjaSOZJGGhh4jpF_ZXOlm1Kq4HTBFHJU7Z-Ez6DDQOUE_djlI5Gk).
20 |
21 | See our [blog post](https://jorgehpo.medium.com/introducing-notebookjs-seamless-integration-between-python-and-javascript-in-computational-e654ec3fbd18).
22 |
23 | [](https://github.com/jorgehpo/notebookJS/tree/main/Examples/7_D3_scatterplot)
24 |
25 |
26 | ## Install
27 |
28 | To install, run:
29 | `pip install notebookjs`
30 |
31 | Or clone this repository and run:
32 | `python setup.py install`
33 |
34 | ## API
35 |
36 | ### execute_js
37 | This method executes a javascript function and sets up the infrastructure for bidirectional communication between Python and Javascript using callbacks.
38 |
39 | ```python
40 | execute_js(
41 | library_list,
42 | main_function,
43 | data_dict={},
44 | callbacks={},
45 | css_list=[],
46 | )
47 | ```
48 |
49 | **Parameters**
50 |
51 | - library_list : list of str.
52 | List of strings containing either 1) URL to a javascript library, 2) javascript code, 3) javascript [bundle](https://github.com/jorgehpo/notebookJS/tree/main/Examples/5_Webpack_BaseballAnnotator_Bidirectional) (Plain JS only - No support for ES6 Modules)
53 | - main_function : str.
54 | Name of the main function to be called. The function will be called with two parameters:
55 | , for example "#my_div", and .
56 | - data_dict : dict.
57 | Dictionary containing the data to be passed to
58 | - callbacks : dict.
59 | Dictionary of the form { : }. The javascript library can
60 | use callbacks to talk to python.
61 | - css_list : list of str.
62 | List of strings containing either 1) URL to a CSS stylesheet or 2) CSS styles
63 |
64 | **Main Function**
65 |
66 | *main_function* is the javascript function that will be run when execute_js is called. It has the following signature:
67 | ```Javascript
68 | function main_function(div_id, data_dict)
69 | ```
70 |
71 | **Example of Main Function**
72 |
73 | As a simple example, we can use D3 to add a circular div to the output cell:
74 |
75 | ```Javascript
76 | function draw_circle(div_id, data){
77 | // Function that draws a circle of color inside the div using D3
78 | d3.select(div_id)
79 | .append("div")
80 | .style("width", "50px")
81 | .style("height", "50px")
82 | .style("background-color", data.color)
83 | .style("border-radius", "50px")
84 | }
85 | ```
86 |
87 | **Callbacks**
88 |
89 | *callbacks* contains a dictionary that maps an identifier string to a Python function. Data is passed to/from callbacks using json/dicts.
90 |
91 | For example, the following callback computes the number to the power of 2.
92 |
93 | ``` Python
94 | def compute_power_2(data){
95 | n = data.n
96 | n2 = n**2
97 | return {"power2": n2}
98 | }
99 |
100 | callbacks = {
101 | "compute_power_2": compute_power_2
102 | }
103 |
104 | execute_js(..., callbacks=callbacks)
105 | ```
106 |
107 | In Javascript, we can call this callback with the class *CommAPI*. *CommAPI* is automatically injected in the Javascript by *notebookJS*.
108 |
109 | ``` Javascript
110 | let comm = new CommAPI("compute_power_2", (ret)=>{alert("The returned value is " + ret.power2)})
111 |
112 | comm.call({n: 3})
113 | // An alert will be shown with the message: "The returned value is 9"
114 | ```
115 |
116 | Jupyter Notebook and Google Colab have different APIs for sending data to/from Javascript/Python. *CommAPI* abstracts the different APIs in a single convenient class.
117 |
118 | **Warning**: Callbacks between Python and JS are only available in Jupyter and Colab notebooks. Jupyter Lab is not supported currently.
119 |
120 | ### save_html
121 | This method creates a standalone HTML bundle (containing all data, JS and CSS resources) and saves it to disk. It accepts all parameters of execute_js, with the addition of *html_dest*, the path to the output file. For example, *html_dest="./output.html"*
122 |
123 | ```python
124 | save_html(,
125 | html_dest,
126 | library_list,
127 | main_function,
128 | data_dict={},
129 | callbacks=None,
130 | css_list=[],
131 | )
132 | ```
133 |
134 | **Warning:** callbacks do not work in standalone HTML files. This parameter only exists to make *execute_js* and *save_html* interoperable.
135 |
136 | ## Examples
137 |
138 | ### Hello World - Python Callbacks
139 |
140 | In this example, we show how to display "hello world" in multiple languages using Javascript and Python. The Javascript is responsible for updating the front end and requesting a new message from Python. Python returns a random message every time the callback is invoked.
141 |
142 | 
143 |
144 | **Javascript to update the div with a hello world message**
145 | ```Python
146 | helloworld_js = """
147 | function helloworld(div_id, data){
148 | comm = new CommAPI("get_hello", (ret) => {
149 | document.querySelector(div_id).textContent = ret.text;
150 | });
151 | setInterval(() => {comm.call({})}, 1000);
152 | comm.call({});
153 | }
154 | """
155 | ```
156 |
157 | **Defining the Python Callback**
158 | ```Python
159 | import random
160 | def hello_world_random(data):
161 | hello_world_languages = [
162 | "Ola Mundo", # Portuguese
163 | "Hello World", # English
164 | "Hola Mundo", # Spanish
165 | "Geiá sou Kósme", # Greek
166 | "Kon'nichiwa sekai", # Japanese
167 | "Hallo Welt", # German
168 | "Namaste duniya", # Hindi
169 | "Ni hao, shijiè" # Chinese
170 | ]
171 | i = random.randint(0, len(hello_world_languages)-1)
172 | return {'text': hello_world_languages[i]}
173 | ```
174 |
175 | **Invoking the function helloworld in notebook**
176 | ```Python
177 | from notebookjs import execute_js
178 | execute_js(helloworld_js, "helloworld", callbacks={"get_hello": hello_world_random})
179 | ```
180 |
181 | See this [colab notebook](https://colab.research.google.com/drive/1g8WOn9oZ5G_3-Y8DYmpV1MIj59dnd81u?usp=sharing) for a live demo.
182 |
183 | ### Radial Bar Chart - Running D3 code in the Notebook
184 |
185 | Plotting a Radial Bar Chart with data loaded from Python. Adapted from this [bl.ock](https://bl.ocks.org/AntonOrlov/6b42d8676943cc933f48a43a7c7e5b6c). See [Examples/3_RadialBarChart](https://github.com/jorgehpo/notebookJS/blob/main/Examples/3_RadialBarChart/).
186 |
187 | ```Python
188 | # Loading libraries
189 | d3_lib_url = "https://d3js.org/d3.v3.min.js"
190 |
191 | with open("radial_bar.css", "r") as f:
192 | radial_bar_css = f.read()
193 |
194 | with open ("radial_bar_lib.js", "r") as f:
195 | radial_bar_lib = f.read()
196 |
197 | # Loading data
198 | import pandas as pd
199 | energy = pd.read_csv("energy.csv")
200 |
201 | # Plotting the Radial Bar Chart
202 | from notebookjs import execute_js
203 | execute_js(library_list=[d3_lib_url, radial_bar_lib], main_function="radial_bar",
204 | data_dict=energy.to_dict(orient="records"), css_list=[radial_bar_css])
205 | ```
206 |
207 | 
208 |
209 | ### More examples
210 |
211 | Please see the [Examples/](https://github.com/jorgehpo/notebookJS/blob/main/Examples/) folder for more examples.
212 |
213 | ## Reference
214 |
215 | If you use *notebookJS*, please reference our related work:
216 |
217 | "*Interactive Data Visualization in Jupyter Notebooks*. JP Ono, J Freire, CT Silva - Computing in Science & Engineering, 2021"
218 |
219 | Bibtex:
220 | ```
221 | @article{ono2021interactive,
222 | title={Interactive Data Visualization in Jupyter Notebooks},
223 | author={Ono, Jorge Piazentin and Freire, Juliana and Silva, Claudio T},
224 | journal={Computing in Science \& Engineering},
225 | volume={23},
226 | number={2},
227 | pages={99--106},
228 | year={2021},
229 | publisher={IEEE}
230 | }
231 | ```
232 |
--------------------------------------------------------------------------------
/Examples/7_D3_scatterplot/Prices.csv:
--------------------------------------------------------------------------------
1 | GrLivArea,SalePrice
2 | 1710,208500
3 | 1262,181500
4 | 1786,223500
5 | 1717,140000
6 | 2198,250000
7 | 1362,143000
8 | 1694,307000
9 | 2090,200000
10 | 1774,129900
11 | 1077,118000
12 | 1040,129500
13 | 2324,345000
14 | 912,144000
15 | 1494,279500
16 | 1253,157000
17 | 854,132000
18 | 1004,149000
19 | 1296,90000
20 | 1114,159000
21 | 1339,139000
22 | 2376,325300
23 | 1108,139400
24 | 1795,230000
25 | 1060,129900
26 | 1060,154000
27 | 1600,256300
28 | 900,134800
29 | 1704,306000
30 | 1600,207500
31 | 520,68500
32 | 1317,40000
33 | 1228,149350
34 | 1234,179900
35 | 1700,165500
36 | 1561,277500
37 | 2452,309000
38 | 1097,145000
39 | 1297,153000
40 | 1057,109000
41 | 1152,82000
42 | 1324,160000
43 | 1328,170000
44 | 884,144000
45 | 938,130250
46 | 1150,141000
47 | 1752,319900
48 | 2149,239686
49 | 1656,249700
50 | 1452,113000
51 | 955,127000
52 | 1470,177000
53 | 1176,114500
54 | 816,110000
55 | 1842,385000
56 | 1360,130000
57 | 1425,180500
58 | 1739,172500
59 | 1720,196500
60 | 2945,438780
61 | 780,124900
62 | 1158,158000
63 | 1111,101000
64 | 1370,202500
65 | 1710,140000
66 | 2034,219500
67 | 2473,317000
68 | 2207,180000
69 | 1479,226000
70 | 747,80000
71 | 2287,225000
72 | 2223,244000
73 | 845,129500
74 | 1718,185000
75 | 1086,144900
76 | 1605,107400
77 | 988,91000
78 | 952,135750
79 | 1285,127000
80 | 1768,136500
81 | 1230,110000
82 | 2142,193500
83 | 1337,153500
84 | 1563,245000
85 | 1065,126500
86 | 1474,168500
87 | 2417,260000
88 | 1560,174000
89 | 1224,164500
90 | 1526,85000
91 | 990,123600
92 | 1040,109900
93 | 1235,98600
94 | 964,163500
95 | 2291,133900
96 | 1786,204750
97 | 1470,185000
98 | 1588,214000
99 | 960,94750
100 | 835,83000
101 | 1225,128950
102 | 1610,205000
103 | 1732,178000
104 | 1535,118964
105 | 1226,198900
106 | 1818,169500
107 | 1992,250000
108 | 1047,100000
109 | 789,115000
110 | 1517,115000
111 | 1844,190000
112 | 1855,136900
113 | 1430,180000
114 | 2696,383970
115 | 2259,217000
116 | 2320,259500
117 | 1458,176000
118 | 1092,139000
119 | 1125,155000
120 | 3222,320000
121 | 1456,163990
122 | 988,180000
123 | 1123,100000
124 | 1080,136000
125 | 1199,153900
126 | 1586,181000
127 | 754,84500
128 | 958,128000
129 | 840,87000
130 | 1348,155000
131 | 1053,150000
132 | 2157,226000
133 | 2054,244000
134 | 1327,150750
135 | 1296,220000
136 | 1721,180000
137 | 1682,174000
138 | 1214,143000
139 | 1959,171000
140 | 1852,230000
141 | 1764,231500
142 | 864,115000
143 | 1734,260000
144 | 1385,166000
145 | 1501,204000
146 | 1728,125000
147 | 1709,130000
148 | 875,105000
149 | 2035,222500
150 | 1080,141000
151 | 1344,115000
152 | 969,122000
153 | 1710,372402
154 | 1993,190000
155 | 1252,235000
156 | 1200,125000
157 | 1096,79000
158 | 1040,109500
159 | 1968,269500
160 | 1947,254900
161 | 2462,320000
162 | 1232,162500
163 | 2668,412500
164 | 1541,220000
165 | 882,103200
166 | 1616,152000
167 | 1355,127500
168 | 1867,190000
169 | 2161,325624
170 | 1720,183500
171 | 1707,228000
172 | 1382,128500
173 | 1656,215000
174 | 1767,239000
175 | 1362,163000
176 | 1651,184000
177 | 2158,243000
178 | 2060,211000
179 | 1920,172500
180 | 2234,501837
181 | 968,100000
182 | 1525,177000
183 | 1802,200100
184 | 1340,120000
185 | 2082,200000
186 | 1252,127000
187 | 3608,475000
188 | 1217,173000
189 | 1656,135000
190 | 1224,153337
191 | 1593,286000
192 | 2727,315000
193 | 1479,184000
194 | 1431,192000
195 | 1709,130000
196 | 864,127000
197 | 1456,148500
198 | 1726,311872
199 | 3112,235000
200 | 2229,104000
201 | 1713,274900
202 | 1121,140000
203 | 1279,171500
204 | 1310,112000
205 | 848,149000
206 | 1284,110000
207 | 1442,180500
208 | 1696,143900
209 | 1100,141000
210 | 2062,277000
211 | 1092,145000
212 | 864,98000
213 | 1212,186000
214 | 1852,252678
215 | 990,156000
216 | 1392,161750
217 | 1236,134450
218 | 1436,210000
219 | 1328,107000
220 | 1954,311500
221 | 1248,167240
222 | 1498,204900
223 | 2267,200000
224 | 1552,179900
225 | 864,97000
226 | 2392,386250
227 | 1302,112000
228 | 2520,290000
229 | 987,106000
230 | 912,125000
231 | 1555,192500
232 | 1194,148000
233 | 2794,403000
234 | 987,94500
235 | 894,128200
236 | 1960,216500
237 | 987,89500
238 | 1414,185500
239 | 1744,194500
240 | 1694,318000
241 | 1487,113000
242 | 1566,262500
243 | 866,110500
244 | 1440,79000
245 | 1217,120000
246 | 2110,205000
247 | 1872,241500
248 | 1928,137000
249 | 1375,140000
250 | 1668,180000
251 | 2144,277000
252 | 1306,76500
253 | 1625,235000
254 | 1640,173000
255 | 1302,158000
256 | 1314,145000
257 | 2291,230000
258 | 1728,207500
259 | 1604,220000
260 | 1792,231500
261 | 882,97000
262 | 1382,176000
263 | 2574,276000
264 | 1212,151000
265 | 1316,130000
266 | 764,73000
267 | 1422,175500
268 | 1511,185000
269 | 2192,179500
270 | 778,120500
271 | 1113,148000
272 | 1939,266000
273 | 1363,241500
274 | 2270,290000
275 | 1632,139000
276 | 816,124500
277 | 1548,205000
278 | 1560,201000
279 | 864,141000
280 | 2121,415298
281 | 2022,192000
282 | 1982,228500
283 | 1262,185000
284 | 1314,207500
285 | 1468,244600
286 | 1575,179200
287 | 1250,164700
288 | 1734,159000
289 | 858,88000
290 | 900,122000
291 | 1396,153575
292 | 1919,233230
293 | 1716,135900
294 | 1716,131000
295 | 2263,235000
296 | 1644,167000
297 | 1003,142500
298 | 1558,152000
299 | 1950,239000
300 | 1743,175000
301 | 1152,158500
302 | 1336,157000
303 | 2452,267000
304 | 1541,205000
305 | 894,149900
306 | 3493,295000
307 | 2000,305900
308 | 2243,225000
309 | 1406,89500
310 | 861,82500
311 | 1944,360000
312 | 1501,165600
313 | 972,132000
314 | 1118,119900
315 | 2036,375000
316 | 1641,178000
317 | 1432,188500
318 | 2353,260000
319 | 1959,270000
320 | 2646,260000
321 | 1472,187500
322 | 2596,342643
323 | 2468,354000
324 | 2730,301000
325 | 1163,126175
326 | 2978,242000
327 | 803,87000
328 | 1719,324000
329 | 1383,145250
330 | 2134,214500
331 | 1192,78000
332 | 1728,119000
333 | 1056,139000
334 | 1629,284000
335 | 1358,207000
336 | 1638,192000
337 | 1786,228950
338 | 1922,377426
339 | 1536,214000
340 | 1621,202500
341 | 1215,155000
342 | 1908,202900
343 | 841,82000
344 | 1040,87500
345 | 1684,266000
346 | 1112,85000
347 | 1577,140200
348 | 958,151500
349 | 1478,157500
350 | 1626,154000
351 | 2728,437154
352 | 1869,318061
353 | 1453,190000
354 | 1111,95000
355 | 720,105900
356 | 1595,140000
357 | 1200,177500
358 | 1167,173000
359 | 1142,134000
360 | 1352,130000
361 | 1924,280000
362 | 912,156000
363 | 1505,145000
364 | 1922,198500
365 | 987,118000
366 | 1574,190000
367 | 1344,147000
368 | 1394,159000
369 | 1431,165000
370 | 1268,132000
371 | 1287,162000
372 | 1664,172400
373 | 1588,134432
374 | 752,125000
375 | 1319,123000
376 | 1928,219500
377 | 904,61000
378 | 914,148000
379 | 2466,340000
380 | 1856,394432
381 | 1800,179000
382 | 1691,127000
383 | 1301,187750
384 | 1797,213500
385 | 784,76000
386 | 1953,240000
387 | 1269,192000
388 | 1184,81000
389 | 1125,125000
390 | 1479,191000
391 | 2332,426000
392 | 1367,119000
393 | 1961,215000
394 | 882,106500
395 | 788,100000
396 | 1034,109000
397 | 1144,129000
398 | 894,123000
399 | 1812,169500
400 | 1077,67000
401 | 1550,241000
402 | 1288,245500
403 | 1310,164990
404 | 672,108000
405 | 2263,258000
406 | 1572,168000
407 | 1620,150000
408 | 1639,115000
409 | 1680,177000
410 | 2172,280000
411 | 2078,339750
412 | 1276,60000
413 | 1056,145000
414 | 1478,222000
415 | 1028,115000
416 | 2097,228000
417 | 1340,181134
418 | 1400,149500
419 | 2624,239000
420 | 1134,126000
421 | 1056,142000
422 | 1344,206300
423 | 1602,215000
424 | 988,113000
425 | 2630,315000
426 | 1196,139000
427 | 1389,135000
428 | 1644,275000
429 | 907,109008
430 | 1208,195400
431 | 1412,175000
432 | 987,85400
433 | 1198,79900
434 | 1365,122500
435 | 1604,181000
436 | 630,81000
437 | 1661,212000
438 | 1118,116000
439 | 904,119000
440 | 694,90350
441 | 1196,110000
442 | 2402,555000
443 | 1440,118000
444 | 1573,162900
445 | 1258,172500
446 | 1908,210000
447 | 1689,127500
448 | 1888,190000
449 | 1886,199900
450 | 1376,119500
451 | 1183,120000
452 | 813,110000
453 | 1533,280000
454 | 1756,204000
455 | 1590,210000
456 | 1728,188000
457 | 1242,175500
458 | 1344,98000
459 | 1663,256000
460 | 1666,161000
461 | 1203,110000
462 | 1935,263435
463 | 1135,155000
464 | 864,62383
465 | 1660,188700
466 | 1040,124000
467 | 1414,178740
468 | 1277,167000
469 | 1644,146500
470 | 1634,250000
471 | 1710,187000
472 | 1502,212000
473 | 1969,190000
474 | 1072,148000
475 | 1976,440000
476 | 1652,251000
477 | 970,132500
478 | 1493,208900
479 | 2643,380000
480 | 1718,297000
481 | 1131,89471
482 | 1850,326000
483 | 1792,374000
484 | 1826,155000
485 | 1216,164000
486 | 999,132500
487 | 1113,147000
488 | 1073,156000
489 | 1484,175000
490 | 2414,160000
491 | 630,86000
492 | 1304,115000
493 | 1578,133000
494 | 1456,172785
495 | 1269,155000
496 | 886,91300
497 | 720,34900
498 | 3228,430000
499 | 1820,184000
500 | 899,130000
501 | 912,120000
502 | 1218,113000
503 | 1768,226700
504 | 1214,140000
505 | 1801,289000
506 | 1322,147000
507 | 1960,124500
508 | 1911,215000
509 | 1218,208300
510 | 1378,161000
511 | 1041,124500
512 | 1363,164900
513 | 1368,202665
514 | 864,129900
515 | 1080,134000
516 | 789,96500
517 | 2020,402861
518 | 2119,158000
519 | 2344,265000
520 | 1796,211000
521 | 2080,234000
522 | 1294,106250
523 | 1244,150000
524 | 1664,159000
525 | 4676,184750
526 | 2398,315750
527 | 1266,176000
528 | 928,132000
529 | 2713,446261
530 | 605,86000
531 | 2515,200624
532 | 1509,175000
533 | 1362,128000
534 | 827,107500
535 | 334,39300
536 | 1414,178000
537 | 1347,107500
538 | 1724,188000
539 | 864,111250
540 | 1159,158000
541 | 1601,272000
542 | 1838,315000
543 | 2285,248000
544 | 1680,213250
545 | 767,133000
546 | 1496,179665
547 | 2183,229000
548 | 1635,210000
549 | 768,129500
550 | 825,125000
551 | 2094,263000
552 | 1069,140000
553 | 928,112500
554 | 1717,255500
555 | 1126,108000
556 | 2046,284000
557 | 1048,113000
558 | 1092,141000
559 | 1336,108000
560 | 1446,175000
561 | 1557,234000
562 | 1392,121500
563 | 1389,170000
564 | 996,108000
565 | 1674,185000
566 | 2295,268000
567 | 1647,128000
568 | 2504,325000
569 | 1535,214000
570 | 2132,316600
571 | 943,135960
572 | 1728,142600
573 | 864,120000
574 | 1692,224500
575 | 1430,170000
576 | 1109,139000
577 | 1216,118500
578 | 1477,145000
579 | 1320,164500
580 | 1392,146000
581 | 1795,131500
582 | 1429,181900
583 | 2042,253293
584 | 816,118500
585 | 2775,325000
586 | 1573,133000
587 | 2028,369900
588 | 838,130000
589 | 860,137000
590 | 1473,143000
591 | 935,79500
592 | 1582,185900
593 | 2296,451950
594 | 816,138000
595 | 848,140000
596 | 924,110000
597 | 1826,319000
598 | 1368,114504
599 | 1402,194201
600 | 1647,217500
601 | 1556,151000
602 | 1904,275000
603 | 1375,141000
604 | 1915,220000
605 | 1200,151000
606 | 1494,221000
607 | 1986,205000
608 | 1040,152000
609 | 2008,225000
610 | 3194,359100
611 | 1029,118500
612 | 2153,313000
613 | 1032,148000
614 | 1872,261500
615 | 1120,147000
616 | 630,75500
617 | 1054,137500
618 | 1509,183200
619 | 832,105500
620 | 1828,314813
621 | 2262,305000
622 | 864,67000
623 | 2614,240000
624 | 980,135000
625 | 1512,168500
626 | 1790,165150
627 | 1116,160000
628 | 1422,139900
629 | 1520,153000
630 | 2080,135000
631 | 1350,168500
632 | 1750,124000
633 | 1554,209500
634 | 1411,82500
635 | 1056,139400
636 | 1056,144000
637 | 3395,200000
638 | 800,60000
639 | 1387,93000
640 | 796,85000
641 | 1567,264561
642 | 1518,274000
643 | 1929,226000
644 | 2704,345000
645 | 1620,152000
646 | 1766,370878
647 | 981,143250
648 | 1048,98300
649 | 1094,155000
650 | 1839,155000
651 | 630,84500
652 | 1665,205950
653 | 1510,108000
654 | 1716,191000
655 | 1469,135000
656 | 2113,350000
657 | 1092,88000
658 | 1053,145500
659 | 1502,149000
660 | 1458,97500
661 | 1486,167000
662 | 1935,197900
663 | 2448,402000
664 | 1392,110000
665 | 1181,137500
666 | 2097,423000
667 | 1936,230500
668 | 2380,129000
669 | 1679,193500
670 | 1437,168000
671 | 1180,137500
672 | 1476,173500
673 | 1369,103600
674 | 1208,165000
675 | 1839,257500
676 | 1136,140000
677 | 1441,148500
678 | 1774,87000
679 | 792,109500
680 | 2046,372500
681 | 988,128500
682 | 923,143000
683 | 1520,159434
684 | 1291,173000
685 | 1668,285000
686 | 1839,221000
687 | 2090,207500
688 | 1761,227875
689 | 1102,148800
690 | 1419,392000
691 | 1362,194700
692 | 848,141000
693 | 4316,755000
694 | 2519,335000
695 | 1073,108480
696 | 1539,141500
697 | 1137,176000
698 | 616,89000
699 | 1148,123500
700 | 894,138500
701 | 1391,196000
702 | 1800,312500
703 | 1164,140000
704 | 2576,361919
705 | 1812,140000
706 | 1484,213000
707 | 1092,55000
708 | 1824,302000
709 | 1324,254000
710 | 1456,179540
711 | 904,109900
712 | 729,52000
713 | 1178,102776
714 | 1228,189000
715 | 960,129000
716 | 1479,130500
717 | 1350,165000
718 | 2554,159500
719 | 1178,157000
720 | 2418,341000
721 | 971,128500
722 | 1742,275000
723 | 848,143000
724 | 864,124500
725 | 1470,135000
726 | 1698,320000
727 | 864,120500
728 | 1680,222000
729 | 1232,194500
730 | 1776,110000
731 | 1208,103000
732 | 1616,236500
733 | 1146,187500
734 | 2031,222500
735 | 1144,131400
736 | 948,108000
737 | 1768,163000
738 | 1040,93500
739 | 1801,239900
740 | 1200,179000
741 | 1728,190000
742 | 1432,132000
743 | 912,142000
744 | 1349,179000
745 | 1464,175000
746 | 1337,180000
747 | 2715,299800
748 | 2256,236000
749 | 2640,265979
750 | 1720,260400
751 | 1529,98000
752 | 1140,96500
753 | 1320,162000
754 | 1494,217000
755 | 2098,275500
756 | 1026,156000
757 | 1471,172500
758 | 1768,212000
759 | 1386,158900
760 | 1501,179400
761 | 2531,290000
762 | 864,127500
763 | 1301,100000
764 | 1547,215200
765 | 2365,337000
766 | 1494,270000
767 | 1506,264132
768 | 1714,196500
769 | 1750,160000
770 | 1836,216837
771 | 3279,538000
772 | 858,134900
773 | 1220,102000
774 | 1117,107000
775 | 912,114500
776 | 1973,395000
777 | 1204,162000
778 | 1614,221500
779 | 894,142500
780 | 2020,144000
781 | 1004,135000
782 | 1253,176000
783 | 1603,175900
784 | 1430,187100
785 | 1110,165500
786 | 1484,128000
787 | 1342,161500
788 | 1652,139000
789 | 2084,233000
790 | 901,107900
791 | 2087,187500
792 | 1145,160200
793 | 1062,146800
794 | 2013,269790
795 | 1496,225000
796 | 1895,194500
797 | 1564,171000
798 | 1285,143500
799 | 773,110000
800 | 3140,485000
801 | 1768,175000
802 | 1688,200000
803 | 1196,109900
804 | 1456,189000
805 | 2822,582933
806 | 1128,118000
807 | 1428,227680
808 | 980,135500
809 | 1576,223500
810 | 1086,159950
811 | 2138,106000
812 | 1309,181000
813 | 848,144500
814 | 1044,55993
815 | 1442,157900
816 | 1250,116000
817 | 1661,224900
818 | 1008,137000
819 | 1689,271000
820 | 1052,155000
821 | 1358,224000
822 | 1640,183000
823 | 936,93000
824 | 1733,225000
825 | 1489,139500
826 | 1489,232600
827 | 2084,385000
828 | 784,109500
829 | 1434,189000
830 | 2126,185000
831 | 1223,147400
832 | 1392,166000
833 | 1200,151000
834 | 1829,237000
835 | 1516,167000
836 | 1144,139950
837 | 1067,128000
838 | 1559,153500
839 | 987,100000
840 | 1099,144000
841 | 1200,130500
842 | 1482,140000
843 | 1539,157500
844 | 1165,174900
845 | 1800,141000
846 | 1416,153900
847 | 1701,171000
848 | 1775,213000
849 | 864,133500
850 | 2358,240000
851 | 1855,187000
852 | 848,131500
853 | 1456,215000
854 | 1646,164000
855 | 1445,158000
856 | 1779,170000
857 | 1040,127000
858 | 1026,147000
859 | 1481,174000
860 | 1370,152000
861 | 2654,250000
862 | 1426,189950
863 | 1039,131500
864 | 1097,152000
865 | 1148,132500
866 | 1372,250580
867 | 1002,148500
868 | 1646,248900
869 | 1120,129000
870 | 2320,169000
871 | 1949,236000
872 | 894,109500
873 | 1682,200500
874 | 910,116000
875 | 1268,133000
876 | 1131,66500
877 | 2610,303477
878 | 1040,132250
879 | 2224,350000
880 | 1155,148000
881 | 864,136500
882 | 1090,157000
883 | 1717,187500
884 | 1593,178000
885 | 2230,118500
886 | 892,100000
887 | 1709,328900
888 | 1712,145000
889 | 1393,135500
890 | 2217,268000
891 | 1505,149500
892 | 924,122900
893 | 1683,172500
894 | 1068,154500
895 | 1383,165000
896 | 1535,118858
897 | 1796,140000
898 | 951,106500
899 | 2240,142953
900 | 2364,611657
901 | 1236,135000
902 | 858,110000
903 | 1306,153000
904 | 1509,180000
905 | 1670,240000
906 | 902,125500
907 | 1063,128000
908 | 1636,255000
909 | 2057,250000
910 | 902,131000
911 | 1484,174000
912 | 2274,154300
913 | 1268,143500
914 | 1015,88000
915 | 2002,145000
916 | 1224,173733
917 | 1092,75000
918 | 480,35311
919 | 1229,135000
920 | 2127,238000
921 | 1414,176500
922 | 1721,201000
923 | 2200,145900
924 | 1316,169990
925 | 1617,193000
926 | 1686,207500
927 | 1126,175000
928 | 2374,285000
929 | 1978,176000
930 | 1788,236500
931 | 2236,222000
932 | 1466,201000
933 | 925,117500
934 | 1905,320000
935 | 1500,190000
936 | 2069,242000
937 | 747,79900
938 | 1200,184900
939 | 1971,253000
940 | 1962,239799
941 | 2403,244400
942 | 1728,150900
943 | 2060,214000
944 | 1440,150000
945 | 1632,143000
946 | 1344,137500
947 | 1869,124900
948 | 1144,143000
949 | 1629,270000
950 | 1776,192500
951 | 1381,197500
952 | 864,129000
953 | 965,119900
954 | 768,133900
955 | 1968,172000
956 | 980,127500
957 | 1958,145000
958 | 1229,124000
959 | 1057,132000
960 | 1337,185000
961 | 1416,155000
962 | 858,116500
963 | 2872,272000
964 | 1548,155000
965 | 1800,239000
966 | 1894,214900
967 | 1484,178900
968 | 1308,160000
969 | 1098,135000
970 | 968,37900
971 | 1095,140000
972 | 1192,135000
973 | 1626,173000
974 | 918,99500
975 | 1428,182000
976 | 2019,167500
977 | 1382,165000
978 | 869,85500
979 | 1241,199900
980 | 894,110000
981 | 1121,139000
982 | 999,178400
983 | 2612,336000
984 | 1266,159895
985 | 2290,255900
986 | 1734,126000
987 | 1164,125000
988 | 1635,117000
989 | 1940,395192
990 | 2030,195000
991 | 1576,197000
992 | 2392,348000
993 | 1742,168000
994 | 1851,187000
995 | 1500,173900
996 | 1718,337500
997 | 1230,121600
998 | 1050,136500
999 | 1442,185000
1000 | 1077,91000
1001 | 1208,206000
1002 | 944,82000
1003 | 691,86000
1004 | 1574,232000
1005 | 1680,136905
1006 | 1504,181000
1007 | 985,149900
1008 | 1657,163500
1009 | 1092,88000
1010 | 1710,240000
1011 | 1522,102000
1012 | 1271,135000
1013 | 1664,100000
1014 | 1502,165000
1015 | 1022,85000
1016 | 1082,119200
1017 | 1665,227000
1018 | 1504,203000
1019 | 1360,187500
1020 | 1472,160000
1021 | 1506,213490
1022 | 1132,176000
1023 | 1220,194000
1024 | 1248,87000
1025 | 1504,191000
1026 | 2898,287000
1027 | 882,112500
1028 | 1264,167500
1029 | 1646,293077
1030 | 1376,105000
1031 | 1218,118000
1032 | 1928,160000
1033 | 3082,197000
1034 | 2520,310000
1035 | 1654,230000
1036 | 954,119750
1037 | 845,84000
1038 | 1620,315500
1039 | 2263,287000
1040 | 1344,97000
1041 | 630,80000
1042 | 1803,155000
1043 | 1632,173000
1044 | 1306,196000
1045 | 2329,262280
1046 | 2524,278000
1047 | 1733,139600
1048 | 2868,556581
1049 | 990,145000
1050 | 1771,115000
1051 | 930,84900
1052 | 1302,176485
1053 | 1316,200141
1054 | 1977,165000
1055 | 1526,144500
1056 | 1989,255000
1057 | 1523,180000
1058 | 1364,185850
1059 | 1850,248000
1060 | 2184,335000
1061 | 1991,220000
1062 | 1338,213500
1063 | 894,81000
1064 | 2337,90000
1065 | 1103,110500
1066 | 1154,154000
1067 | 2260,328000
1068 | 1571,178000
1069 | 1611,167900
1070 | 2521,151400
1071 | 893,135000
1072 | 1048,135000
1073 | 1556,154000
1074 | 1456,91500
1075 | 1426,159500
1076 | 1240,194000
1077 | 1740,219500
1078 | 1466,170000
1079 | 1096,138800
1080 | 848,155900
1081 | 990,126000
1082 | 1258,145000
1083 | 1040,133000
1084 | 1459,192000
1085 | 1251,160000
1086 | 1498,187500
1087 | 996,147000
1088 | 1092,83500
1089 | 1953,252000
1090 | 1709,137500
1091 | 1247,197000
1092 | 1040,92900
1093 | 1252,160000
1094 | 1694,136500
1095 | 1200,146000
1096 | 936,129000
1097 | 1314,176432
1098 | 1355,127000
1099 | 1088,170000
1100 | 1324,128000
1101 | 1601,157000
1102 | 438,60000
1103 | 950,119500
1104 | 1134,135000
1105 | 1194,159500
1106 | 1302,106000
1107 | 2622,325000
1108 | 1442,179900
1109 | 2021,274725
1110 | 1690,181000
1111 | 1836,280000
1112 | 1658,188000
1113 | 1964,205000
1114 | 816,129900
1115 | 1008,134500
1116 | 833,117000
1117 | 1734,318000
1118 | 1419,184100
1119 | 894,130000
1120 | 1601,140000
1121 | 1040,133700
1122 | 1012,118400
1123 | 1552,212900
1124 | 960,112000
1125 | 698,118000
1126 | 1482,163900
1127 | 1005,115000
1128 | 1555,174000
1129 | 1530,259000
1130 | 1959,215000
1131 | 936,140000
1132 | 1981,135000
1133 | 974,93500
1134 | 2210,117500
1135 | 2020,239500
1136 | 1600,169000
1137 | 986,102000
1138 | 1252,119000
1139 | 1020,94000
1140 | 1567,196000
1141 | 1167,144000
1142 | 952,139000
1143 | 1868,197500
1144 | 2828,424870
1145 | 1006,80000
1146 | 924,80000
1147 | 1576,149000
1148 | 1298,180000
1149 | 1564,174500
1150 | 1111,116900
1151 | 1482,143000
1152 | 932,124000
1153 | 1466,149900
1154 | 1811,230000
1155 | 816,120500
1156 | 1820,201800
1157 | 1437,218000
1158 | 1265,179900
1159 | 1314,230000
1160 | 1580,235128
1161 | 1876,185000
1162 | 1456,146000
1163 | 1640,224000
1164 | 894,129000
1165 | 1258,108959
1166 | 1432,194000
1167 | 1502,233170
1168 | 1694,245350
1169 | 1671,173000
1170 | 2108,235000
1171 | 3627,625000
1172 | 1118,171000
1173 | 1261,163000
1174 | 1250,171900
1175 | 3086,200500
1176 | 2345,239000
1177 | 2872,285000
1178 | 923,119500
1179 | 1224,115000
1180 | 1343,154900
1181 | 1124,93000
1182 | 2514,250000
1183 | 1652,392500
1184 | 4476,745000
1185 | 1130,120000
1186 | 1572,186700
1187 | 1221,104900
1188 | 1699,95000
1189 | 1624,262000
1190 | 1660,195000
1191 | 1804,189000
1192 | 1622,168000
1193 | 1441,174000
1194 | 1472,125000
1195 | 1224,165000
1196 | 1352,158000
1197 | 1456,176000
1198 | 1863,219210
1199 | 1690,144000
1200 | 1212,178000
1201 | 1382,148000
1202 | 864,116050
1203 | 1779,197900
1204 | 1348,117000
1205 | 1630,213000
1206 | 1074,153500
1207 | 2196,271900
1208 | 1056,107000
1209 | 1700,200000
1210 | 1283,140000
1211 | 1660,290000
1212 | 1845,189000
1213 | 1752,164000
1214 | 672,113000
1215 | 960,145000
1216 | 999,134500
1217 | 894,125000
1218 | 1902,112000
1219 | 1314,229456
1220 | 912,80500
1221 | 1218,91500
1222 | 912,115000
1223 | 1211,134000
1224 | 1846,143000
1225 | 2136,137900
1226 | 1490,184000
1227 | 1138,145000
1228 | 1933,214000
1229 | 912,147000
1230 | 1702,367294
1231 | 1507,127000
1232 | 2620,190000
1233 | 1190,132500
1234 | 1224,101800
1235 | 1188,142000
1236 | 1964,130000
1237 | 1784,138887
1238 | 1626,175500
1239 | 1948,195000
1240 | 1141,142500
1241 | 1484,265900
1242 | 1768,224900
1243 | 1689,248328
1244 | 1173,170000
1245 | 2076,465000
1246 | 1517,230000
1247 | 1868,178000
1248 | 1553,186500
1249 | 1034,169900
1250 | 2058,129500
1251 | 988,119000
1252 | 2110,244000
1253 | 1405,171750
1254 | 874,130000
1255 | 2167,294000
1256 | 1656,165400
1257 | 1367,127500
1258 | 1987,301500
1259 | 864,99900
1260 | 1166,190000
1261 | 1054,151000
1262 | 1675,181000
1263 | 1050,128900
1264 | 1788,161500
1265 | 1824,180500
1266 | 1337,181000
1267 | 1452,183900
1268 | 1889,122000
1269 | 2018,378500
1270 | 3447,381000
1271 | 1524,144000
1272 | 1524,260000
1273 | 1489,185750
1274 | 935,137000
1275 | 1357,177000
1276 | 1250,139000
1277 | 1920,137000
1278 | 1395,162000
1279 | 1724,197900
1280 | 2031,237000
1281 | 1128,68400
1282 | 1573,227000
1283 | 1339,180000
1284 | 1040,150500
1285 | 1824,139000
1286 | 2447,169000
1287 | 1412,132500
1288 | 1328,143000
1289 | 1582,190000
1290 | 1659,278000
1291 | 1970,281000
1292 | 1152,180500
1293 | 1302,119500
1294 | 2372,107500
1295 | 1664,162900
1296 | 864,115000
1297 | 1052,138500
1298 | 1128,155000
1299 | 1072,140000
1300 | 5642,160000
1301 | 1246,154000
1302 | 1983,225000
1303 | 1494,177500
1304 | 2526,290000
1305 | 1616,232000
1306 | 1708,130000
1307 | 1652,325000
1308 | 1368,202500
1309 | 990,138000
1310 | 1122,147000
1311 | 1294,179200
1312 | 1902,335000
1313 | 1274,203000
1314 | 2810,302000
1315 | 2599,333168
1316 | 948,119000
1317 | 2112,206900
1318 | 1630,295493
1319 | 1352,208900
1320 | 1787,275000
1321 | 948,111000
1322 | 1478,156500
1323 | 720,72500
1324 | 1923,190000
1325 | 708,82500
1326 | 1795,147000
1327 | 796,55000
1328 | 774,79000
1329 | 816,130500
1330 | 2792,256000
1331 | 1632,176500
1332 | 1588,227000
1333 | 954,132500
1334 | 816,100000
1335 | 1360,125500
1336 | 1365,125000
1337 | 1334,167900
1338 | 1656,135000
1339 | 693,52500
1340 | 1861,200000
1341 | 864,128500
1342 | 872,123000
1343 | 1114,155000
1344 | 2169,228500
1345 | 1913,177000
1346 | 1456,155835
1347 | 960,108500
1348 | 2156,262500
1349 | 1776,283463
1350 | 1494,215000
1351 | 2358,122000
1352 | 2634,200000
1353 | 1716,171000
1354 | 1176,134900
1355 | 3238,410000
1356 | 1865,235000
1357 | 1920,170000
1358 | 892,110000
1359 | 1078,149900
1360 | 1573,177500
1361 | 1980,315000
1362 | 2601,189000
1363 | 1530,260000
1364 | 1738,104900
1365 | 1412,156932
1366 | 1200,144152
1367 | 1674,216000
1368 | 1790,193000
1369 | 1475,127000
1370 | 848,144000
1371 | 1668,232000
1372 | 1374,105000
1373 | 1661,165500
1374 | 2097,274300
1375 | 2633,466500
1376 | 1958,250000
1377 | 1571,239000
1378 | 790,91000
1379 | 1604,117000
1380 | 987,83000
1381 | 1394,167500
1382 | 864,58500
1383 | 2117,237500
1384 | 1762,157000
1385 | 1416,112000
1386 | 1258,105000
1387 | 1154,125500
1388 | 2784,250000
1389 | 2526,136000
1390 | 1746,377500
1391 | 1218,131000
1392 | 1525,235000
1393 | 1584,124000
1394 | 900,123000
1395 | 1912,163000
1396 | 1500,246578
1397 | 2482,281213
1398 | 1687,160000
1399 | 1513,137500
1400 | 1904,138000
1401 | 1608,137450
1402 | 1158,120000
1403 | 1593,193000
1404 | 1294,193879
1405 | 1464,282922
1406 | 1214,105000
1407 | 1646,275000
1408 | 768,133000
1409 | 833,112000
1410 | 1363,125500
1411 | 2093,215000
1412 | 1840,230000
1413 | 1668,140000
1414 | 1040,90000
1415 | 1844,257000
1416 | 1848,207000
1417 | 1569,175900
1418 | 2290,122500
1419 | 2450,340000
1420 | 1144,124000
1421 | 1844,223000
1422 | 1416,179900
1423 | 1069,127500
1424 | 848,136500
1425 | 2201,274970
1426 | 1344,144000
1427 | 1252,142000
1428 | 2127,271000
1429 | 1558,140000
1430 | 804,119000
1431 | 1440,182900
1432 | 1838,192140
1433 | 958,143750
1434 | 968,64500
1435 | 1792,186500
1436 | 1126,160000
1437 | 1537,174000
1438 | 864,120500
1439 | 1932,394617
1440 | 1236,149700
1441 | 1725,197000
1442 | 2555,191000
1443 | 848,149300
1444 | 2007,310000
1445 | 952,121000
1446 | 1422,179600
1447 | 913,129000
1448 | 1188,157900
1449 | 2090,240000
1450 | 1346,112000
1451 | 630,92000
1452 | 1792,136000
1453 | 1578,287090
1454 | 1072,145000
1455 | 1140,84500
1456 | 1221,185000
1457 | 1647,175000
1458 | 2073,210000
1459 | 2340,266500
1460 | 1078,142125
1461 | 1256,147500
--------------------------------------------------------------------------------