D3 Notes

Working through the tutorials from https://d3js.org/

Note: '%%HTML' causes CORS exception. To fix this simply use '%%writefile [filename]' and then the following python code to load in an Iframe. (optionally you can load the file '.js' locally as well)

from IPython.display import IFrame
IFrame("[filename]", width=850, height=150)

Tutorial #1 - Make a simple bar Chart

tutorial here

source code


In [1]:
%%writefile tutorial.bar.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>

.chart div {
  font: 10px sans-serif;
  background-color: steelblue;
  text-align: right;
  padding: 3px;
  margin: 1px;
  color: white;
}

</style>
<div class="chart"></div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>

var data = [4, 8, 15, 16, 23, 42];

var x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, 420]);

d3.select(".chart")
  .selectAll("div")
    .data(data)
  .enter().append("div")
    .style("width", function(d) { return x(d) + "px"; })
    .text(function(d) { return d; });

</script>


Writing tutorial.bar.html

In [5]:
from IPython.display import IFrame
IFrame("tutorial.bar.html", width=850, height=150)


Out[5]:

Optional - Copy to local directory

wget https://d3js.org/d3.v3.js

#or 

wget https://d3js.org/d3.v3.min.js

more examples

Then change:

<script src="https://d3js.org/d3.v3.min.js"></script>

or

<script src="//d3js.org/d3.v3.min.js"></script>

to

<script src="d3.v3.min.js"></script>

See below


In [27]:
%%HTML
<!DOCTYPE html>
<meta charset="utf-8">
<style>

.chart div {
  font: 10px sans-serif;
  background-color: steelblue;
  text-align: right;
  padding: 3px;
  margin: 1px;
  color: white;
}

</style>
<div class="chart"></div>
<script src="d3.v3.min.js"></script>
<script>

var data = [4, 8, 15, 16, 23, 42];

var x = d3.scale.linear()
    .domain([0, d3.max(data)])
    .range([0, 420]);

d3.select(".chart")
  .selectAll("div")
    .data(data)
  .enter().append("div")
    .style("width", function(d) { return x(d) + "px"; })
    .text(function(d) { return d; });

</script>


Tutorial #2 - Load External Data

tutorial

source code

Load data from and external '.tsv' file

from source

"To use this data in a web browser, we need to download the file from a web server and then parse it, which converts the text of the file into usable JavaScript objects. Fortunately, these two tasks can be performed by a single function, d3.tsv.

Loading data introduces a new complexity: downloads are asynchronous. When you call d3.tsv, it returns immediately while the file downloads in the background. At some point in the future when the download finishes, your callback function is invoked with the new data, or an error if the download failed. In effect your code is evaluated out of order:"

// 1. Code here runs first, before the download starts.

d3.tsv("data.tsv", function(error, data) {
  // 3. Code here runs last, after the download finishes.
});

// 2. Code here runs second, while the file is downloading.

In [6]:
%%writefile tutorial.bar2.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>

.chart rect {
  fill: steelblue;
}

.chart text {
  fill: white;
  font: 10px sans-serif;
  text-anchor: end;
}

</style>
<svg class="chart"></svg>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

var width = 420,
    barHeight = 20;

var x = d3.scale.linear()
    .range([0, width]);

var chart = d3.select(".chart")
    .attr("width", width);

d3.tsv("data.tsv", type, function(error, data) {
  x.domain([0, d3.max(data, function(d) { return d.value; })]);

  chart.attr("height", barHeight * data.length);

  var bar = chart.selectAll("g")
      .data(data)
    .enter().append("g")
      .attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });

  bar.append("rect")
      .attr("width", function(d) { return x(d.value); })
      .attr("height", barHeight - 1);

  bar.append("text")
      .attr("x", function(d) { return x(d.value) - 3; })
      .attr("y", barHeight / 2)
      .attr("dy", ".35em")
      .text(function(d) { return d.value; });
});

function type(d) {
  d.value = +d.value; // coerce to number
  return d;
}

</script>


Writing tutorial.bar2.html

In [7]:
%%writefile data.tsv
name	value
Locke	4
Reyes	8
Ford	15
Jarrah	16
Shephard	23
Kwon	42


Writing data.tsv

In [8]:
from IPython.display import IFrame
IFrame("tutorial.bar2.html", width=850, height=150)


Out[8]:

Tutorial 3 - Rotating Columns

tutorial

source code

Important notes

Fixed width vs calculated width

from source

"We previously multiplied the var barHeight by the index of each data point (0, 1, 2, …) to produce fixed-height bars. The resulting chart’s height thus depended on the size of the dataset. But here the opposite behavior is desired: the chart width is fixed and the bar width variable. So rather than fix the barHeight, now we compute the barWidth by dividing the available chart width by the size of the dataset, data.length."

...

d3.tsv("data.tsv", type, function(error, data) {
  y.domain([0, d3.max(data, function(d) { return d.value; })]);
var barWidth = width / data.length;

...

Loading Labels

...

chart.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("Frequency");

...

"Unit-appropriate number formatting also improves legibility by tailoring the display to your data. Since our chart displays relative frequency, percentages are more appropriate than the default behavior which shows a number between 0 and 1. A format string as the second argument to axis.ticks will customize the tick formatting, and the scale will automatically choose a precision appropriate to the tick interval."

...

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(10, "%");

...


In [14]:
%%writefile tutorial.bar3.vertical.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>

.bar {
  fill: steelblue;
}

.bar:hover {
  fill: brown;
}

.axis--x path {
  display: none;
}

</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;

var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
    y = d3.scaleLinear().rangeRound([height, 0]);

var g = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.tsv("data.tutorial3.tsv", function(d) {
  d.frequency = +d.frequency;
  return d;
}, function(error, data) {
  if (error) throw error;

  x.domain(data.map(function(d) { return d.letter; }));
  y.domain([0, d3.max(data, function(d) { return d.frequency; })]);

  g.append("g")
      .attr("class", "axis axis--x")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  g.append("g")
      .attr("class", "axis axis--y")
      .call(d3.axisLeft(y).ticks(10, "%"))
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", "0.71em")
      .attr("text-anchor", "end")
      .text("Frequency");

  g.selectAll(".bar")
    .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.letter); })
      .attr("y", function(d) { return y(d.frequency); })
      .attr("width", x.bandwidth())
      .attr("height", function(d) { return height - y(d.frequency); });
});

</script>


Overwriting tutorial.bar3.vertical.html

In [13]:
%%writefile data.tutorial3.tsv
letter	frequency
A	.08167
B	.01492
C	.02782
D	.04253
E	.12702
F	.02288
G	.02015
H	.06094
I	.06966
J	.00153
K	.00772
L	.04025
M	.02406
N	.06749
O	.07507
P	.01929
Q	.00095
R	.05987
S	.06327
T	.09056
U	.02758
V	.00978
W	.02360
X	.00150
Y	.01974
Z	.00074


Writing data.tutorial3.tsv

In [26]:
from IPython.display import IFrame
IFrame("tutorial.bar3.vertical.html", width=970, height=530)


Out[26]:

Tutorial 4 - Selecting Elements, Entering and Exiting

Selecting Elements

Entering Elements

To size, move, etc, simply enter the

from source

"...By appending to the enter selection, we can create new circles for any missing data."

var svg = d3.select("svg");

var circle = svg.selectAll("circle")
    .data([32, 57, 112, 293]);

var circleEnter = circle.enter().append("circle");

Modify attributes

circleEnter.attr("cy", 60);
circleEnter.attr("cx", function(d, i) { return i * 100 + 30; });
circleEnter.attr("r", function(d) { return Math.sqrt(d); });

Exiting Elements

from source

"... you have too many existing elements, and you want to remove some of them. Again you can select nodes and remove them manually, but the exit selection computed by a data join is more powerful."

var circle = svg.selectAll("circle")
    .data([32, 57]);

...

circle.exit().remove();

In [31]:
# source: https://bost.ocks.org/mike/constancy/
from IPython.display import IFrame
IFrame("https://bost.ocks.org/mike/constancy/", width=970, height=530)


Out[31]:

In [35]:
%pwd


Out[35]:
u'/home/james/Documents/Git/MyGitHub/pynotes-general/D3Notes'

In [40]:
from IPython.display import IFrame
IFrame("https://bost.ocks.org/mike/join/", width=970, height=530)


Out[40]:

Nice Venn diagram, nothing D3 specific though


In [39]:
%%HTML
<style>

@import url(../style.css?aea6f0a);

circle {
  fill: none;
  fill-opacity: .2;
  stroke: black;
  stroke-width: 1.5px;
}

</style>

<svg width="720" height="240">
  <g transform="translate(0,128)">
    <g transform="translate(300)">
      <circle r="110" style="fill: rgb(49, 130, 189);"></circle>
      <text y="-120" dy=".35em" text-anchor="middle" style="font-weight: bold;">Data</text>
      <text x="-50" dy=".35em" text-anchor="middle">Enter</text>
    </g>
    <text x="360" dy=".35em" text-anchor="middle">Update</text>
    <g transform="translate(420)">
      <circle r="110" style="fill: rgb(230, 85, 13);"></circle>
      <text y="-120" dy=".35em" text-anchor="middle" style="font-weight: bold;">Elements</text>
      <text x="50" dy=".35em" text-anchor="middle">Exit</text>
    </g>
  </g>
</svg>


Data Enter Update Elements Exit

In [ ]: