Using D3.js in Jupyter notebook

D3.js and Jupyter

A short description how to use D3.js in a Jupyter notebook.

Input data

Lets create a CSV file containing the amounts of 5 crypto currencies over a small period.

In [1]:
csvstring = """
Time;BTC;DOGE;ETH;LTC;REP
2017-05-03 23:17;19,70;;78,88;20,81;
2017-05-04 20:18;21,21;;90,45;24,91;
2017-05-05 19:11;20,1;11,58;91,73;24,06;
2017-05-06 18:56;20,28;12,37;92,78;25,91;
2017-05-09 0:50;21,77;20,34;89,27;28,45;
2017-05-09 2:15;21,99;20,58;88,62;28,49;34,70
2017-05-09 23:59;22,46;17,51;87,45;30,14;32,45
2017-05-11 0:57;23,15;18,83;86,94;32,18;34,36
2017-05-11 22:17;24,17;17,48;87,87;29,62;34,36
2017-05-12 1:55;24,13;17,99;88,05;30,02;36,08
2017-05-13 1:57;22,41;17,48;85,25;27,00;33,72
2017-05-14 15:32;23,67;17,04;89,19;28,90;34,24
2017-05-14 23:34;23,47;17,51;88,60;28,01;33,72
2017-05-15 22:12;22,34;16,16;90,20;24,43;32,58
2017-05-16 20:25;23,12;15,68;88,54;24,19;31,36
2017-05-17 22:01;24,00;19,26;86,28;24,50;30,62
2017-05-18 23:45;24,84;21,05;94,93;27,93;32,62
2017-05-19 22:01;25,59;21,98;118,55;27,03;33,62"""

Write the CSV text fo a file.

In [2]:
!echo "$csvstring" > wallet.csv

Check if the file exists.

In [3]:
!ls -la wallet.csv
-rw-rw-r-- 1 jitsejan jitsejan 833 Aug  2 10:38 wallet.csv

Check the last entry of the CSV file.

In [4]:
!tail -n 1 wallet.csv
2017-05-19 22:01;25,59;21,98;118,55;27,03;33,62

Read the file into a dataframe.

In [5]:
import pandas as pd
wallet_data = pd.read_csv('wallet.csv', sep=';', decimal=",")
wallet_data = wallet_data.fillna(0)
wallet_data.head()
Out[5]:
Time BTC DOGE ETH LTC REP
0 2017-05-03 23:17 19.70 0.00 78.88 20.81 0.0
1 2017-05-04 20:18 21.21 0.00 90.45 24.91 0.0
2 2017-05-05 19:11 20.10 11.58 91.73 24.06 0.0
3 2017-05-06 18:56 20.28 12.37 92.78 25.91 0.0
4 2017-05-09 0:50 21.77 20.34 89.27 28.45 0.0

Add a total column for the currency columns.

In [6]:
wallet_data['total'] = wallet_data.ix[:, wallet_data.columns != 'Time'].sum(axis=1)
/home/jitsejan/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:1: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate_ix
  """Entry point for launching an IPython kernel.
In [7]:
wallet_data
Out[7]:
Time BTC DOGE ETH LTC REP total
0 2017-05-03 23:17 19.70 0.00 78.88 20.81 0.00 119.39
1 2017-05-04 20:18 21.21 0.00 90.45 24.91 0.00 136.57
2 2017-05-05 19:11 20.10 11.58 91.73 24.06 0.00 147.47
3 2017-05-06 18:56 20.28 12.37 92.78 25.91 0.00 151.34
4 2017-05-09 0:50 21.77 20.34 89.27 28.45 0.00 159.83
5 2017-05-09 2:15 21.99 20.58 88.62 28.49 34.70 194.38
6 2017-05-09 23:59 22.46 17.51 87.45 30.14 32.45 190.01
7 2017-05-11 0:57 23.15 18.83 86.94 32.18 34.36 195.46
8 2017-05-11 22:17 24.17 17.48 87.87 29.62 34.36 193.50
9 2017-05-12 1:55 24.13 17.99 88.05 30.02 36.08 196.27
10 2017-05-13 1:57 22.41 17.48 85.25 27.00 33.72 185.86
11 2017-05-14 15:32 23.67 17.04 89.19 28.90 34.24 193.04
12 2017-05-14 23:34 23.47 17.51 88.60 28.01 33.72 191.31
13 2017-05-15 22:12 22.34 16.16 90.20 24.43 32.58 185.71
14 2017-05-16 20:25 23.12 15.68 88.54 24.19 31.36 182.89
15 2017-05-17 22:01 24.00 19.26 86.28 24.50 30.62 184.66
16 2017-05-18 23:45 24.84 21.05 94.93 27.93 32.62 201.37
17 2017-05-19 22:01 25.59 21.98 118.55 27.03 33.62 226.77

Convert the data to a dictionary that Javascript can use.

In [8]:
wallet_data = wallet_data.to_json(orient='records')

Attach the data to the current window by using Javascript.

In [9]:
from IPython.display import Javascript
Javascript("""
           window.walletData={};
           """.format(wallet_data))
Out[9]:

Create the graph

First include the D3.js library.

In [1]:
%%javascript
require.config({
    paths: {
        d3: '//cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min'
    }
});

I copied the content of my gist to recreate the graph inside this notebook. Use the HTML function of IPython to attach inline style to the page.

In [11]:
from IPython.core.display import HTML
HTML("""
<style>
path { 
    stroke-width: 1;
    fill: none;
    stroke-linejoin: round;
    stroke-linecap: round;
}
circle { 
  stroke-width: 1;
}
.axis path,
.axis line {
  fill: none;
  stroke: grey;
  stroke-width: 1;
  shape-rendering: crispEdges;
}
.legend, .label, .hover-text{
    font-size: x-small;
    background-color: white;
}
</style>
""")
Out[11]:

Create the graph by retrieving the data from the window.

In [12]:
%%javascript
require(['d3'], function(d3) {
    //a weird idempotency thing
    $("#chart1").remove();
    //create canvas
    element.append("<svg id='chart1' width='960' height='500'></svg>");
    var svg = d3.select('svg'),
        margin = {
            top: 20,
            right: 50,
            bottom: 100,
            left: 50
        },
        width = +svg.attr('width') - margin.left - margin.right,
        height = +svg.attr('height') - margin.top - margin.bottom,
        g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
    // Graph title
    g.append('text')
        .attr('x', (width / 2))
        .attr('y', 0 - (margin.top / 3))
        .attr('text-anchor', 'middle')
        .style('font-size', '16px')
        .text('Wallet chart');

    // Function to convert a string into a time
    var parseTime = d3.time.format('%Y-%m-%d %H:%M').parse;
    // Function to show specific time format
    var formatTime = d3.time.format('%e %B');
    // Set data
    var data = window.walletData;
    data.forEach(function(d) {
        d.date = parseTime(d.Time);
    });
    // Set the X scale
    var x = d3.time.scale().range([0, width], 0.5);
    // Set the Y scale
    var y = d3.scale.linear().range([height, 0]);
    // Set the color scale
    var color = d3.scale.category10();
    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");
    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");
    var line = d3.svg.line()
        // .interpolate("basis")
        .x(function(d) {
            return x(d.date);
        })
        .y(function(d) {
            return y(d.worth);
        });
    color.domain(d3.keys(data[0]).filter(function(key) {
        return key !== "Time" && key !== "date";
    }));
    var currencies = color.domain().map(function(name) {
        return {
            name: name,
            values: data.map(function(d) {
                return {
                    date: d.date,
                    worth: +d[name]
                };
            })
        };
    });

    x.domain(d3.extent(data, function(d) {
        return d.date;
    }));
    // Set the Y domain
    y.domain([
        d3.min(currencies, function(c) {
            return d3.min(c.values, function(v) {
                return v.worth;
            });
        }),
        d3.max(currencies, function(c) {
            return d3.max(c.values, function(v) {
                return v.worth;
            });
        })
    ]);
    // Set the X axis
    g.append("g")
        .attr("class", "x axis")
        //         .attr("fill", "none")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
    // Set the Y axis
    g.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Value (USD)");
    // Draw the lines
    var currency = g.selectAll(".currency")
        .data(currencies)
        .enter().append("g")
        .attr("class", "currency");

    currency.append("path")
        .attr("class", "line")
        .attr("fill", "none")
        .attr("d", function(d) {
            return line(d.values);
        })
        .style("stroke", function(d) {
            return color(d.name);
        });

    // Add the circles
    currency.append("g").selectAll("circle")
        .data(function(d) {
            return d.values
        })
        .enter()
        .append("circle")
        .attr("r", 2)
        .attr("cx", function(dd) {
            return x(dd.date)
        })
        .attr("cy", function(dd) {
            return y(dd.worth)
        })
        .attr("fill", "none")
        .attr("stroke", function(d) {
            return color(this.parentNode.__data__.name)
        });
    // Add label to the end of the line
    currency.append("text")
        .attr("class", "label")
        .datum(function(d) {
            return {
                name: d.name,
                value: d.values[d.values.length - 1]
            };
        })
        .attr("transform", function(d) {
            return "translate(" + x(d.value.date) + "," + y(d.value.worth) + ")";
        })
        .attr("x", 3)
        .attr("dy", ".35em")
        .text(function(d) {
            return d.name;
        });
    
});
In [ ]: