Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

JavaScriptD3 Python package

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".


Mission statement

Make first class -- beautiful, tunable, and useful -- plots and charts with Python using concise specifications.


Design and philosophy

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".)

  • 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

How does it work?

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
Loading

Here is the corresponding narration:

  1. Enter Python plot command in cell that starts with the magic spec %% js.

    • Like js_d3_list_plot(numpy.random.uniform(1,10,100)).
  2. Jupyter via the Python kernel evaluates the Python plot command.

  3. The Python plot command produces JavaScript code.

  4. 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.


Usage examples

Setup

Here we load some packages that are used to generate, summarize, and modify datasets:

from RandomDataGenerators import *
import pandas
import numpy

Here 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>

Histogram

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>

List line plot

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)
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
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>

Bubble chart

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)
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
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>

Alternatives

Raku package

As it was mentioned above, the design and implementation of this package closely follows the Raku package "JavaScript::D3".

Different backend

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.


Command Line Interface (CLI)

The package provides a CLI script that can be used to generate HTML files with plots or charts.

js_d3_graphics --help

TBD..

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

TODO

The following org mode file has a TODO list -- the highest priority items are placed first. (TBD...)


Implementation details

Splicing of JavaScript snippets

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.)

...


References

Articles

[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.

Packages

[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.