D3 Troubleshooting
D3 is a great JavaScript library for data visualization. However, like every technology, it can be annoying when you don’t know why things don’t work as expected.
A good way for troubleshooting D3 is by using the browser console.
Let’s run a small project as shown on the D3 tutorial above. It can be as simple as creating an index.html
and linking to the D3.js
library.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
</body>
</html>
Then run the webserver like this:
$ python -m http.server 8888 --bind 127.0.0.1
Open the browser on: http://127.0.0.1:8888/
. For this test I used Firefox 72.
Open the console on the browser. (You can use clear()
to clear the output)
D3 Methods
On the console, type d3
. Then expand the result, which shows all these methods. Now you can investigate which methods are available:
{…}
__esModule: true
active: function active()
arc: function arc()
area: function ry()
areaRadial: function ly()
ascending: function n()
axisBottom: function axisBottom()
axisLeft: function axisLeft()
axisRight: function axisRight()
...
...
it has about 500 methods...
For example, two useful methods are d3.min
and d3.max
.
Let’s create an array:
numbers = [3, 9, 1, 0, 2, 8, 2];
It shows this output:
Array(7) [ 3, 9, 1, 0, 2, 8, 2 ]
Then the min and max can be easily calculated:
d3.min(numbers)
0
d3.max(numbers)
9
This can be helpful when using scales
to define the input domain.
Array methods
Type the name of the array we created
numbers
Expand the result to see the values of the array and the array methods
(7) […]
0: 3
1: 9
2: 1
3: 0
4: 2
5: 8
6: 2
length: 7
<prototype>: []
concat: function concat()
constructor: function Array()
copyWithin: function copyWithin()
entries: function entries()
every: function every()
fill: function fill()
filter: function filter()
find: function find()
findIndex: function findIndex()
flat: function flat()
flatMap: function flatMap()
forEach: function forEach()
includes: function includes()
indexOf: function indexOf()
join: function join()
keys: function keys()
lastIndexOf: function lastIndexOf()
length: 0
map: function map()
pop: function pop()
push: function push()
reduce: function reduce()
reduceRight: function reduceRight()
reverse: function reverse()
shift: function shift()
slice: function slice()
some: function some()
sort: function sort()
splice: function splice()
toLocaleString: function toLocaleString()
toSource: function toSource()
toString: function toString()
unshift: function unshift()
values: function values()
Symbol(Symbol.iterator): function values()
Symbol(Symbol.unscopables): Object { copyWithin: true, entries: true, fill: true, … }
<prototype>: Object { … }
There is a sort
function.
numbers.sort()
Array(7) [ 0, 1, 2, 2, 3, 8, 9 ]
There is also length
.
numbers.length
7
Object Methods
Let’s create an object.
simpsons = { name: "Homer", age: 40, city: "Springfield"};
Type simpsons
Object { name: "Homer", age: 40, city: "Springfield" }
Create an array from this object:
simpsonsArray = d3.values(simpsons)
Array(3) [ "Homer", 40, "Springfield" ]
To see methods available for Object
simpsons, type simpsons
and expand the output.
{…}
age: 40
city: "Springfield"
name: "Homer"
<prototype>: {…}
__defineGetter__: function __defineGetter__()
__defineSetter__: function __defineSetter__()
__lookupGetter__: function __lookupGetter__()
__lookupSetter__: function __lookupSetter__()
__proto__:
constructor: function Object()
hasOwnProperty: function hasOwnProperty()
isPrototypeOf: function isPrototypeOf()
propertyIsEnumerable: function propertyIsEnumerable()
toLocaleString: function toLocaleString()
toSource: function toSource()
toString: function toString()
valueOf: function valueOf()
<get __proto__()>: function __proto__()
<set __proto__()>: function __proto__()
Console.log on callback function
On your D3 script, use console.log
on a callback function to see the data before and after such as:
var someFunction (d) {
console.log("Before something happens");
console.log(d);
somethingHappens(d);
console.log("After something happens");
console.log(d);
}
d3.nest reformats a Date object to string
If you are using d3.nest()
to group data that has a Date
. Nest will force the value into a string.
Given a dataset where you parse the year:
const parseTime = d3.timeParse("%Y");
After loading dataset
, then creating an array of objects:
const data = dataset.map(d => {
return {
year: parseTime(d.year),
count: +d.sale
};
})
The parsed d.year
stored into year
should be like this:
Date Sat Jan 01 2011 00:00:00 GMT-0500 (Eastern Standard Time)
If you want to summarize the data and calculate amount per year:
const salesYear = d3.nest()
.key(d => d.year)
.rollup(amount => d3.sum(amount, d => d.sale))
.entries(data);
However, .key(d => d.year)
forces the Date
object to String
like this:
"Sat Jan 01 2011 00:00:00 GMT-0500 (Eastern Standard Time)"
If you were to plot Sales Amount vs Year
by using a scale that maps Year
to Date
objects. You will get a NaN
. Because now Year
is not a Date
object, but a String
.
const line = d3.line()
.x(d => xScale(d.year))
.y(d => yScale(d.amount));
You need to convert the Date string
back to Date object
.
const line = d3.line()
.x(d => xScale(new Date(d.year)))
.y(d => yScale(d.amount));
D3 examples
There are two websites that have a lot of examples. However, keep in mind some use different versions of D3, and the syntax might change in different versions: