This repository has the a Python package for generation of JavaScript's D3 code for making plots and charts.
This package is intended to be used in Jupyter notebooks with Python kernels. The commands of the package generate JavaScript code that produces (nice) D3.js plots or charts.
For illustrative examples see the Jupyter notebook "Tests-for-JavaScriptD3".
The (original versions of the) JavaScript snippets used in this package are taken from "The D3.js Graph Gallery".
The design and implementation of this package closely follows the Raku package "JavaScript::D3".
There is another Python package, "ipychart", that makes visualizations with JavaScript in Jupyter, but uses Chart.js instead of D3.js.
Remark: This Markdown file was automatically generated from the notebook: "./docs/Python-JavaScriptD3-README.ipynb".
Make first class -- beautiful, tunable, and useful -- plots and charts with Python using concise specifications.
Here is a list of guiding design principles:
-
Concise plot and charts specifications.
-
Using Mathematica's plot functions for commands signatures inspiration. (Instead of, say, R's "ggplot2".)
- For example, see
ListPlot,BubbleChart.
- For example, see
-
The primary target data structure to visualize is an array of hashes, with all array elements having the one of these sets of keys
<x y><x y group><x y z><x y z group>
-
Multiple-dataset plots are produced via dataset records that have the key "group".
-
Whenever possible deduce the keys from arrays of scalars.
-
The data manipulation functions of the package "pandas" should be nicely fit.
-
Data frames of "pandas" and numerical array from "numpy", should be automatically ingested and transformed as much as reasonable. (In order to get desired visualizations.)
-
The package functions are tested separately:
- As Python functions that produce output for given signatures
- As JavaScript plots that correspond to the corresponding intents
Here is a diagram that summarizes the evaluation path from a Raku plot spec to a browser diagram:
graph TD
Python{{Python}}
IPython{{"Python<br>Jupyter kernel"}}
Jupyter{{Jupyter}}
JS{{JavaScript}}
PythonInput[/Python code input/]
JSOutput[/JavaScript code output/]
CellEval[Cell evaluation]
JSResDisplay[JavaScript code result display]
Jupyter -.-> |1|IPython -.-> |2|Python -.-> |3|JSOutput -.-> |4|Jupyter
Jupyter -.-> |5|JS -.-> |6|JSResDisplay
PythonInput --> CellEval --> Jupyter --> JSResDisplay
Here is the corresponding narration:
-
Enter Python plot command in cell that starts with the magic spec
%% js.- Like
js_d3_list_plot(numpy.random.uniform(1,10,100)).
- Like
-
Jupyter via the Python kernel evaluates the Python plot command.
-
The Python plot command produces JavaScript code.
-
The Jupyter "lets" the web browser to evaluate the obtained JavaScript code.
- Instead of web browser, say, Visual Studio Code can be used.
The evaluation loop spelled out above is possible because of the %%javacript magic cells implementation in the Jupyter.
Here we load some packages that are used to generate, summarize, and modify datasets:
from RandomDataGenerators import *
import pandas
import numpyHere we load some packages that are used to generate, summarize, and modify datasets:
from JavaScriptD3 import *Here we use a JavaScript cell that allows the visualization of with D3.js in Jupyter notebooks:
%%javascript
require.config({
paths: {
d3: 'https://d3js.org/d3.v7.min'
}});
require(['d3'], function(d3) {
console.log(d3);
});<IPython.core.display.Javascript object>
Here is an example of a histogram:
js_d3_histogram(
numpy.random.normal(120, 10, 500),
height=500,
background='white',
title='Normal distribution example',
x_axis_label='random value',
y_axis_label='counts', margins = {"top":120})<IPython.core.display.Javascript object>
Here we make random data:
dfXYG = random_data_frame(400, ["x", "y", "group"],
generators = { "x" : lambda size: numpy.random.uniform(0, 100, size=size),
"y" : lambda size: numpy.random.uniform(0, 10, size=size),
"group" : ["astro", "barista", "crom", "dark"] })
dfXYG.sample(5).dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
| x | y | group | |
|---|---|---|---|
| 372 | 38.825638 | 7.152835 | astro |
| 227 | 8.455611 | 8.212151 | crom |
| 69 | 38.492431 | 6.388774 | crom |
| 121 | 35.694540 | 4.763404 | astro |
| 270 | 98.590092 | 8.886541 | astro |
Here is an example of multi-line list plot:
js_d3_list_line_plot(dfXYG.sort_values( ["x"] ), background='')<IPython.core.display.Javascript object>
Here we make some random data:
df3DGroups = random_data_frame(100, ["x", "y", "z", "group"],
generators = { "x" : lambda size: numpy.random.uniform(0, 20, size=size),
"y" : lambda size: numpy.random.uniform(200, 50, size=size),
"z" : lambda size: numpy.random.normal(20, 12, size=size),
"group" : ["aspirin", "biscuit", "cookie"]
} )
df3DGroups.sample(5).dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
| x | y | z | group | |
|---|---|---|---|---|
| 67 | 13.962933 | 120.956196 | 27.915837 | cookie |
| 22 | 13.797296 | 158.308319 | 31.905862 | aspirin |
| 35 | 1.033294 | 112.486546 | 18.128586 | biscuit |
| 81 | 12.033880 | 53.989824 | 21.682677 | cookie |
| 43 | 14.374499 | 84.026492 | 17.529052 | biscuit |
Here is an example of bubble chart (with tooltips):
js_d3_bubble_chart(df3DGroups,
x_axis_label='x coordinates',
y_axis_label='Normal distribution',
title='Bubble chart over groups',
background='',
margins = {"left":60, "top" : 60},
opacity=0.5, tooltip = True, legends=True)<IPython.core.display.Javascript object>
As it was mentioned above, the design and implementation of this package closely follows the Raku package "JavaScript::D3".
Instead of using D3.js as a "backend" it is possible -- and instructive -- to implement Raku plotting functions that generate JavaScript code for the library Chart.js.
D3.js is lower level than Chart.js, hence in principle Chart.js is closer to the mission of this Python package. I.e. at first I considered having Raku plotting implementations with Chart.js (in a package called "JavaScript::Chart".) But I had hard time making Chart.js plots work consistently within Jupyter.
As it was mentioned above, there is another Python package, "ipychart", that makes visualizations with JavaScript in Jupyter, but uses Chart.js instead of D3.js.
The package provides a CLI script that can be used to generate HTML files with plots or charts.
js_d3_graphics --helpTBD..
Here is a usage example that produces a list line plot:
js_d3_graphics list-line-plot 1 2 2 12 33 41 15 5 -t="Nice plot" --x-label="My X" --y-label="My Y" > out.html && open out.html
Here is an example that produces bubble chart:
js_d3_graphics bubble-chart "1,1,10 2,2,12 33,41,15 5,3,30" -t="Nice plot" --x-label="My X" --y-label="My Y" > out.html && open out.htm
The following org mode file has a TODO list -- the highest priority items are placed first. (TBD...)
The package works by splicing of parametrized JavaScript code snippets and replacing the parameters with concrete values.
In a sense, JavaScript macros are used to construct the final code through text manipulation. (Probably, unsound software-engineering-wise, but it works.)
...
[OV1] Olivia Vane, "D3 JavaScript visualisation in a Python Jupyter notebook", (2020), livingwithmachines.ac.uk.
[SF1] Stefaan Lippens, Custom D3.js Visualization in a Jupyter Notebook, (2018), stefaanlippens.net.
[AAp1] Anton Antonov, [JavasScript::D3 Raku package]((https://raku.land/zef:antononcube/JavaScript::D3), (2022), GitHub/antononcube.
[AAp2] Anton Antonov, Text::Plot Raku package, (2022), GitHub/antononcube.
[NH1] Nicholas H, ipychart Python package, (2019-2022), GitHub/nicohlr.