Following several comments by readers and after stumbling across an AWESOME service for hosting and publishing books, I have begun making the D3 Tips and Tricks manual available on Leanpub.
The content is almost identical, but the format is significantly different since it is written and converted from markdown into pdf, epub and mobi formats (full disclosure, I haven't tried the epub / mobi versions).
The attraction is the service that Leanpub provide. They convert and host the book and when there are additions or revisions, they make them available to readers.
What's in it for you?
Well, the price is still free :-), but if you find the content useful and want to support anyone, please consider contributing 50 cents which will go to Leanpub as a flat fee that they have when a book is sold.
I think the format is superior. While I had more control using LibreOffice and exporting as a pdf, Leanpub provides a more consistent look / feel that is cleaner IMHO. You be the judge and feel free to let me know.
You can get to the new version of the book from the downloads page or directly from Leanpub if you like.
Thursday 24 January 2013
Wednesday 16 January 2013
Applying a colour gradient to an area fill in d3.js
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
The previous example of a varying gradient on a line is neat, but hopefully you're already thinking “Hang on, can't that same thing be applied to an area fill?”.
Damn! You're catching on.
To do this there's only a few things we need to change;
First of all the CSS for the line needs to be amended to refer to the area. So this...
.line { fill: none; stroke: url(#line-gradient); stroke-width: 2px; }…gets changed to this...
.area { fill: url(#area-gradient); stroke-width: 0px; }We've defined the styles for the area this time, but instead of the stroke being defined by the separate script, now it's the area. While we've changed the url name, it's actually the same piece of code, with a different id (because it seemed wrong to be talking about an area when the label said line). We've also set the stroke width to zero, because we don't want any lines around our filled area.
Now we want to take the block of code that defined our line...
var valueline = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); });… and we need to replace it with the standard block that defined an area fill.
var area = d3.svg.area() .x(function(d) { return x(d.date); }) .y0(height) .y1(function(d) { return y(d.close); });So we're not going to be drawing a line at all. Just the area fill.
Next as I mentioned earlier, we change the id for the linearGradient block from “line-gradient” to “area-gradient”
.attr("id", "area-gradient")
And lastly, we remove the block of code that drew the line and replace it with a block that draws an area. So change this....
svg.append("path") .attr("class", "line") .attr("d", valueline(data));… for this;
svg.append("path") .datum(data) .attr("class", "area") .attr("d", area);And then sit back and marvel at your creation;
For a slightly 'nicer' looking example, you could
check out a variation of one of Mike Bostocks originals here;
http://bl.ocks.org/4433087.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Tuesday 15 January 2013
Applying a colour gradient to a graph line in d3.js
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
I just know that you were impressed with the changing dots in a scatter plot based on the value in the previous example. But could we could one better?
How about we try to reproduce the same effect but by varying the colour of the plotted line.
This is a neat feature and a useful example of the flexibility of d3.js and svg in general.
I used the appropriate bits of code from Mike Bostock's Threshold Encoding example here http://bl.ocks.org/3970883. And I should take the opportunity to heartily recommend browsing through his collection of examples on bl.ocks.org (http://bl.ocks.org/mbostock).
Here then is a plotted line that is red below 400, green above 620 and black in between.
How cool is that?
Enough beating around the bush, how is the magic line produced?
Well starting with our simple line graph there are only two blocks of code to go in. One is CSS in the <style> area and the second is a tricky little piece of code that deals with gradients.
So, first the CSS.
.line { fill: none; stroke: url(#line-gradient); stroke-width: 2px; }This block can go in the <style> area towards the end.
There's the fairly standard fill of none and a stroke width of 2 pixels, but the stroke: url(#line-gradient); is something different.
In this case the stroke (which we remember is the colour of the line) us being set at a link within the page which is set by the anchor #line-gradient. As we will see shortly this is in our second block of code, so the colour is being defined in a separate portion of the script.
And now the JavaScript Gradient code;
svg.append("linearGradient") .attr("id", "line-gradient") .attr("gradientUnits", "userSpaceOnUse") .attr("x1", 0).attr("y1", y(0)) .attr("x2", 0).attr("y2", y(1000)) .selectAll("stop") .data([ {offset: "0%", color: "red"}, {offset: "40%", color: "red"}, {offset: "40%", color: "black"}, {offset: "62%", color: "black"}, {offset: "62%", color: "lawngreen"}, {offset: "100%", color: "lawngreen"} ]) .enter().append("stop") .attr("offset", function(d) { return d.offset; }) .attr("stop-color", function(d) { return d.color; });There's our anchor on the second line!
But let's not get ahead of ourselves. This block should be placed after the x and y domains are set, but before the line is drawn.
Seems a bit strange doesn't it? This block is all about defining the actions of an element, but the element in this case is a gradient and the gradient acts on the line.
So, our first line adds our linear gradient. Gradients consist of continuously smooth colour transitions along a vector from one colour to another We can have a linear one or a radial one and depending on which you select, there are a few options to define. There is some great information on gradients here http://www.w3.org/TR/SVG/pservers.html (more than I ever thought existed).
The second line (.attr("id", "line-gradient")) sets our anchor for the CSS that we saw earlier.
The third fourth and fifth lines define the bounds of the area over which the gradient will act. Since the coordinates x1, y1, x2, y2 will describe an area. The values for y1 (0) and y2 (1000) are used more fore convenience to align with our data (which has a maximum value around 630 or so. For more information on the gradientUnits attribute I found this page useful https://developer.mozilla.org/en-US/docs/SVG/Attribute/gradientUnits. We'll come back to the coordinates in a moment.
The next block selects all the 'stop' elements for the gradients. These stop elements define where on the range covered by our coordinates the colours start and stop. These have to be defined as either percentages or numbers (where the numbers are really just percentages in disguise (i.e. 45% =0.45)).
The best way to consider the stop elements is in conjunction with the gradientUnits. The image following may help.
In this case our coordinated describe a vertical line from 0 to 1000. Our colours transition from red (0) to red (400) at which point they change to black (400) and this will continue until it gets to black (620). Then this changes to green (620) and from there, any value above that will be green.
Now, it might seem a little convoluted to be doubling up on the colours and values, but the reason is that the gradient functions are have a lot more to them than we're using and we'll have a look at the possibilities once the explanation of the code is done.
So after defining the stop elements, we enter and append the elements to the gradient (.enter().append("stop")) with attributes for offset and color that we defined in the stop elements area.
Now, the IS cool, but by now, I hope that you have picked that a gradient function really does mean a gradient, and not just a straight change from one colour to another.
So, let's try changing the stop element offsets to the following (and making the stroke-width slightly larger to see more clearly what's going on);
.data([ {offset: "0%", color: "red"}, {offset: "30%", color: "red"}, {offset: "45%", color: "black"}, {offset: "55%", color: "black"}, {offset: "60%", color: "lawngreen"}, {offset: "100%", color: "lawngreen"} ])And here we go...
Ahh... A real gradient.
I have tended to find that I need to have a good
think about how I set the offsets and bounds when doing this sort of
thing since it can get quite complicated quite quickly :-).
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Monday 14 January 2013
Select items with an IF statement in d3.js
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
The filtering – selection section above is a good way to adapt what you see on a graph, but so is a more familiar friend the 'if' statement.
An if statement will act to carry out a task in a particular way dependent on a condition that you specify.
Here's an example, what if we wanted to show our scatter plot as normal, but all those with a 'close' value less than 400 should be coloured red. Sound familiar? Yes, I know it's similar to the example above, with the subtle difference that it is leaving the circles above 400 in place (more on that to follow).
So, starting with the simple scatter plot example all we have to do is include the if statement in the block of code that draws the circles. Here's the entire block with the additions highlighted;
svg.selectAll("dot") .data(data) .enter().append("circle") .attr("r", 3.5) .style("fill", function(d) { // <== Add these if (d.close <= 400) {return "red"} // <== Add these else { return "black" } // <== Add these ;}) // <== Add these .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); });Our first added line introduces the style modifier and the rest of the code acts to provide a return for the 'fill' attribute.
The second line introduces our if statement. They're all pretty easy to follow between languages. Just look out for maintaining the correct syntax and you should be fine. In this case we're asking if the value of d.close is less than or equal to 400 and if it is it will return the “red” statement for our fill.
The third line covers our rear and make sure that if the colour isn't going to be red, it's going to be black. The last line just closes the style and function statements.
The result?
Aww..... nice.
I've placed a copy of the file that uses the if statement into the downloads section on d3noob.org with the general examples as if-statement.html.
Could it be any cooler? I'm glad you asked.
What if we wanted to have all the points where close was less than 400 red and all those where close was greater than 620 green? Oh yeah! Now we're talking.
So with one small change to the if statement;
.style("fill", function(d) { if (d.close <= 400) {return "red"} else if (d.close >= 620) {return "lawngreen"} // <== Right here else { return "black" } ;})Check it out...
Nice.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Sunday 13 January 2013
Selecting / filtering a subset of objects in d3.js
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
OK, Imagine a scenario where you want to select (or should we say filter) a particular range of objects from a larger set for display in some way.
For example, what if we wanted to use our scatter plot example to show the line as normal, but we are particularly interested in the points where the values of the points fall below 400. And when it does we want them highlighted with a circle as we have done with all the point previously.
So that we end up with something that looks a little like this...
Err... Yes, for those among you who are of the observant persuasion, I have deliberately coloured them red as well (red for DANGER!).
This is a fairly simple example, but serves to illustrate the principle adequately.
From our simple scatter plot example we only need to add in two lines to the block of code that draws the circles as follows;
svg.selectAll("dot") .data(data) .enter().append("circle") .filter(function(d) { return d.close < 400 }) // <== This line .style("fill", "red") // <== and this one .attr("r", 3.5) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); });The first added line uses the '.filter' function to act on the data points and according to the arguments passed to it in this case, only return those where the value of d.close is less than 400 (return d.close < 400).
The second added line is our line that simply
colours the circles red (.style("fill",
"red")).
That's all there is to it. Pretty simple, but the
filter function can be very powerful when used wisely.
I've placed a copy of the file for selecting /
filtering into the downloads section on d3noob.org with the general
examples as filter-selection.html.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Saturday 12 January 2013
Adding tooltips to a d3.js graph
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
Tooltips have a marvelous duality. They are on one hand a pretty darned useful thing that aids in giving context and information where required and on the other hand, if done with a bit of care, they can look very stylish :-).
Technically, they represent a slight move from what we have been playing with so far into a mildly more complex arena of 'transitions' and 'events'. You can take this one of two ways. Either accept that it just works and implement it as shown, or you will know what s going on and feel free to deride my efforts as those of a rank amateur :-).
The source for the implementation was taken from Mike Bostock's example here; http://bl.ocks.org/1087001. This was combined with a few other bit's and pieces (the trickiest being working out how to format the displayed date correctly and inserting a line break in the tooltip (which I found here; https://groups.google.com/forum/?fromgroups=#!topic/d3-js/GgFTf24ltjc (well done to all those participating in that discussion)). I make the assumption that any or all errors that occur in the implementation will be mine, whereas, any successes will be down to the original contributors.
So, just in case there is some degree of confusion, a tooltip (one word or two?) is a discrete piece of information that will pop into view when the mouse is hovered over somewhere specific. Most of us have seen and used them, but I suppose we all tend to call them different things such as 'infotip', 'hint' or 'hover box' I don't know if there's a right name for them, but here's an example of what we're trying to achieve;
You can see the mouse has hovered over one of the scatter plot circles and a tip has appeared that provides the user with the exact date and value for that point.
Now, you may also notice that there's a certain degree of 'fancy' here as the information is bound by a rectangular shape whit rounded corners and a slight opacity. The other piece of 'fancy' which you don't see in a pdf is that when these tool tips appear and disappear, they do so in an elegant fade-in, fade-out way. Purty.
Now, before we get started describing how the code goes together, let's take a quick look at the two technique specifics that I mentioned earlier, 'transitions' and 'events'.
Transitions
From the main d3.js web page (d3js.org) transitions are described as gradually interpolating styles and attributes over time. So what I take that to mean is that if you want to change an object, you can do so be simply specifying the attribute / style end point that you want it to end up with and the time you want it to take and go!
Of course, it's not quite that simple, but luckily, smarter people than I have done some fantastic work describing different aspects of transitions so please see the following for a more complete description of the topic;
- Mike Bostock's Bar chart tutorial (http://mbostock.github.com/d3/tutorial/bar-2.html)
- Christophe Viau's 'Try D3 Now!' tutorial (http://christopheviau.com/d3_tutorial/)
Hopefully observing the mouseover and mouseout transitions in the tooltips example will whet your appetite for more!
Events
The other technique is related to mouse 'events'. This describes the browser watching for when 'something' happens with the mouse on the screen and when it does, it takes a specified action. A (probably non-comprehensive) list of the types of events are the following;
- mousedown: Triggered by an element when a mouse button is pressed down over it
- mouseup: Triggered by an element when a mouse button is released over it
- mouseover: Triggered by an element when the mouse comes over it
- mouseout: Triggered by an element when the mouse goes out of it
- mousemove: Triggered by an element on every mouse move over it.
- click: Triggered by a mouse click: mousedown and then mouseup over an element
- contextmenu: Triggered by a right-button mouse click over an element.
- dblclick: Triggered by two clicks within a short time over an element
How many of these are valid to use within d3 I'm not sure, but I'm willing to bet that there are probably more than those here as well. Please go to http://javascript.info/tutorial/mouse-events for a far better description of the topic if required.
Get tipping
So, bolstered with a couple of new concepts to consider, let's see how they are enacted in practice.
If we start with our simple-scatter plot graph there are 4 areas in it that we will want to modify (it may be easier to check the tooltips.html file in the example files in the downloads section on d3noob.org).
The first area is the CSS. The following code should be added just before the </style> tag;
div.tooltip { position: absolute; text-align: center; width: 60px; height: 28px; padding: 2px; font: 12px sans-serif; background: lightsteelblue; border: 0px; border-radius: 8px; pointer-events: none; }These styles are defining how our tooltip will appear . Most of them are fairly straight forward. The position of the tooltip is done in absolute measurements, not relative. The text is centre aligned, the height, width and colour of the rectangle is 28px, 60px and lightsteelblue respectively. The 'padding' is an interesting feature that provides a neat way to grow a shape by a fixed amount from a specified size.
We set the boarder to 0px so that it doesn't show up and a neat style (attribute?) called border-radius provides the nice rounded corners on the rectangle.
Lastly, but by no means least, the 'pointer-events: none' line is in place to instruct the mouse event to go "through" the element and target whatever is "underneath" that element instead (Read more here; https://developer.mozilla.org/en-US/docs/CSS/pointer-events). That means that even if the tooltip partly obscures the circle, the code will stll act as if the mouse is over only the circle.
The second addition is a simple one-liner that should (for forms sake) be placed under the 'parseData' variable declaration;
var formatTime = d3.time.format("%e %B");This line formats the date when it appears in our tooltip. Without it, the time would default to a disturbingly long combination of temporal details. In the case here we have declared that we want to see the day of the month (%e) and the full month name(%B).
The third block of code is the function declaration for 'div'.
var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0);We can place that just after the 'valueline' definition in the JavaScript. Again there's not too much here that's surprising. We tell it to attach 'div' to the body element, we set the class to the tooltip class (from the CSS) and we set the opacity to zero. It might sound strange to have the opacity set to zero, but remember, that's the natural state of a tooltip. It will live unseen until it's moment of revelation arrives and it pops up!
The final block of code is slightly more complex and could be described as a mutant version of the neat little bit of code that we used to do the drawing of the dots for the scatter plot. That's because the tooltips are all about the scatter plot circles. Without a circle to 'mouseover', the tooltip never appears :-).
So here's the code that includes the scatter plot drawing (it's included since it's pretty much integral);
svg.selectAll("dot") .data(data) .enter().append("circle") .attr("r", 5) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); }) .on("mouseover", function(d) { div.transition() .duration(200) .style("opacity", .9); div .html(formatTime(d.date) + "<br/>" + d.close) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); });
Before we start going through the code, the
example file for tooltips that is on d3noob.org includes a brief
series of comments for the lines that are added or changed from the
scatter plot, so if you want to compare what is going on in context,
that is an option.
The first six lines of the code are a repeat of
the scatter plot drawing script. The only changes are that we've
increased the radius of the circle from 3.5 to 5 (just to make it
easier to mouse over the object) and we've removed the semicolon from
the 'cy' attribute line since the code now has to carry on.
So the additions are broken into to areas that
correspond to the two events. 'mouseover' and 'mouseout'. When the
mouse moves over any of the circles in the scatter plot, the
mouseover code is executed on the 'div' element. When the mouse is
moved off the circle a different set of instructions are executed.
It would be a mistake to think of tooltips in the
plural because there aren't a whole series of individual tooltips
just waiting to appear for their specific circle. There is only one
tooltip that will appear when the mouse moves over a circle. And
depending on what circle it's over, the properties of the tooltip
will alter slightly (in terms of its position and contents).
on.mouseover
The .on("mouseover"
line initiates the introduction of the tooltip. Then we declare the
element we will be introducing ('div') and that we will be applying a
transition to it's introduction (.transition()).
The next two lines describe the transition. It will take 200
milliseconds (.duration(200))
and will result in changing the elements opacity to .9
(.style("opacity",
.9);).
Given that the natural state of our tooltip is an opacity of 0, this
make sense for something appearing, but it doesn't go all the way to
a solid object and it retains a slight transparency just to make it
look less permanent.
The following three lines format our tooltip. The
first one adds an html element that contains our x and y information
(the date and the d.close value). Now this is done in a slightly
strange way. Other tooltips that I have seen have used a '.text'
element instead of a '.html' one, but I have used '.html' in this
case because I wanted to include the line break tag “<br/>”
to separate the date and value. I'm sure there are other ways to do
it, but this worked for me. The other interesting part of this line
is that this is where we call our time formatting function that we
described earlier. The next two lines position the tooltip on the
screen and to do this they grab the x and y coordinates of the mouse
when the event takes place (with the d3.event.pageX and
d3.event.pageY snippets) and apply a correction in the case of the y
coordinate to raise the tooltip up by the same amount as its height
(28 pixels).
on.mouseout
The .on("mouseout"
section is slightly simpler in that it doesn't have to do any fancy
text / html / coordinate stuff. All it has to do is to fade out the
'div' element. And that is done by simply reversing the opacity back
to 0 and setting the duration for the transition to 500 milliseconds
(being slightly longer than the fade-in makes it look slightly cooler
IMHO).
Right, there you go. As a description it's ended
up being a bit of a wall of text I'm afraid. But hopefully between
the explanation and the example code you will get the idea. Please
take the time to fiddle with the settings described here to find the
ones that work for you and in the process you will reinforce some of
the principles that help D3 do it's thing.
I've placed a copy of the file for drawing the
tooltips into the downloads section on d3noob.org with the general
examples as tooltips.html.
Edit: 2014-05-02: After an interesting question below I have added a new post on including an HTML link in out tooltips and it can be found here.
Edit: 2014-05-02: After an interesting question below I have added a new post on including an HTML link in out tooltips and it can be found here.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Friday 11 January 2013
Change a line chart into a scatter plot with d3.js
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
Confession time.
I didn't actually intend to add in a section with
a scatter plot in it for its own sake because I thought it would be;
- tricky
- not useful
- all of the above
I was wrong on all counts.
I did want to have a scatter plot, because
I wanted to display tool tips, but this is too neat to ignore.
It was literally a 5 minute job, 3 minutes of
which was taken up by going to the d3 gallery on the wiki
(https://github.com/mbostock/d3/wiki/Gallery)
and ogling at the cool stuff there before snapping out of it and
going to the scatter plot example (http://bl.ocks.org/3887118).
All you need to so is take the simple graph
example file and slot the following block in between the 'Add the
valueline path' and the 'add the x axis' blocks.
svg.selectAll("dot") .data(data) .enter().append("circle") .attr("r", 3.5) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); });And you will get...
Now I deliberately put the dots after the line in
the drawing section, because I thought they would look better, but
you could put the block of code before the line drawing block to get
the following effect;
(just trying to reinforce the concept that 'order'
matters when drawing objects :-)).
You could of course just remove the line block all
together...
But in my humble opinion it looses something.
So what do the individual lines in the scatter
plot block of JavaScript do?
The first line (svg.selectAll("dot"))
essentially provides a suitable grouping label for the svg circle
elements that will be added. The next line associates the range of
data that we have to the group of elements we are about to add in.
Then we add a circle for each data point
(.enter().append("circle"))
with a radius of 3.5 pixels (.attr("r",
3.5))
and appropriate x (.attr("cx",
function(d)
{
return
x(d.date);
}))
and y (.attr("cy",
function(d)
{
return
y(d.close);
});)
coordinates.
There is lots more that we could be doing with
this piece of code (check out the scatter plot example
(http://bl.ocks.org/3887118))
including varying the colour or size or opacity of the circles
depending on the data and all sorts of really neat things, but for
the mean time, there we go. scatter plot!
I've placed a copy of the file for drawing the
scatter plot into the downloads section on d3noob.org with the
general examples as simple-scatterplot.html.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Thursday 10 January 2013
Format a date / time axis with specified values in d3.js
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
OK then. We've been very clever in rotating our text, but you will notice that D3 has used it's own good judgement as to what format the days / date will be represented as.
Not that there's anything wrong with it, but what if we want to put a specific format of date / time nomenclature as axis labels?
No problem. D3 has your back.
This is actually a pretty easy thing to do, but there are plenty of options for the formatting, so the only really tricky part is deciding what to put where.
But before we start doing anything we are going to have to expand our bottom margin even more than we did with the rotate the axis labels feature.
var margin = {top: 30, right: 40, bottom: 70, left: 50},That should see us right.
Right, now the simple part :-). changing the format of the label is as simple as inserting the tickFormat command into the xAxis declaration a little like this;
var xAxis = d3.svg.axis().scale(x) .orient("bottom").ticks(10) .tickFormat(d3.time.format("%Y-%m-%d")); //<== insert the tickFormat functionSo, what the tickFormat is allowing the setting of formatting for the tick labels. The d3.time.format portion of the code is specifying the exact format of those ticks. This formatting is described using the same arguments that were explained in the section on formatting date time values starting on page 21 (or of course the source here; https://github.com/mbostock/d3/wiki/Time-Formatting). That means that the examples we see here (%Y-%m-%d) should display the year as a four digit number then a hyphen then the month as a two digit number, then another hyphen, then a two digit number corresponding to the day.
Let's take a look at the result;
There we go! You should be able to see this file in the downloads section on d3noob.org with the general examples as formatted-date-time-axis-labels.html.
So how about we try something a little out of the ordinary (extreme)?
How about the full weekday name (%A), the day (%d), the full month name (%B) and the year (%Y) as a four digit number?
.tickFormat(d3.time.format("%A %d %B %Y"));We will also need some extra space for the bottom margin, so how about 140?
var margin = {top: 30, right: 40, bottom: 140, left: 50},and...
Oh yeah... When axis ticks go bad...
But seriously, that does work as a pretty good
example of the flexibility available.
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Wednesday 9 January 2013
How to rotate the text labels for the x Axis of a d3.js graph
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
The observant reader will recall the problem we had observed earlier when increasing the number of ticks on our x axis to 10. The effect had been to produce a large number of x axis ticks (actually 19) but they had run together and become unreadable.
We postulated at the time that an answer to the problem might be to rotate the text to provide more space. Well, it's about time we solved that problem.
The answer I found most usable was provided by Aaron Ward on Google Groups
(https://groups.google.com/forum/#!msg/d3-js/CRlW0ISbOy4/1sgrE5uS5ysJ)
Now, I'll put a bit of a caveat on this solution to the rotating axis label problem. It looks like it's worked well, but I've only carried out this investigation to the point where I've got something that looks like it's a solution. There may be better or more elegant ways of carrying out the same task, so let Google be your friend if it doesn't appear to be working out for you.
So, starting out with our simple graph example, we should increase the number of ticks on the x axis to 10 to get it to highlight our problem as it appears in the previous image.
The first substantive change would be a little housekeeping. Because we are going to be rotating the text at the bottom of the graph, we are going to need some extra space to fit in our labels. So we should change our bottom margin appropriately.
var margin = {top: 30, right: 40, bottom: 50, left: 50},I found that 50 pixels was sufficient.
The remainder of our changes occur in the block that draws the x axis.
svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "end") .attr("dx", "-.8em") .attr("dy", ".15em") .attr("transform", "rotate(-65)" );It's pretty standard until the .call(xAxis) portion of the code. Here we remove the semicolon that was there so that the block continues with its function.
Then we select all the text elements that comprise
the x avis with the .selectAll("text").
From this point we are operating on the text elements associated with
the x axis. So in effect the following 4 'actions' taken are applied
to the text labels.
The .style("text-anchor",
"end")
line ensures that the text label has the end of the label 'attached
to the axis tick. This has the effect of making sure that the text
rotates about the end of the date. This makes sure that the text all
ends up a uniform distance from the axis ticks.
The 'dx' and 'dy' attribute lines move the end of
the text just far enough away from the axis tick so that they don't
crowd it and not too far away so that it appears disassociated. This
took a little bit of fiddling to get right and you will notice that
I've used the 'em' units to get an adjustment if the size of the font
differs.
The final action is kind of the money shot.
The transform attribute applies itself to each
text label and rotates each line by -65 degrees. I selected -65
degrees just because it looked OK. There was no deeper reason.
The end result then looks like the following;
This was a surprisingly difficult problem to find
a solution to that I could easily understand (well done Aaron). So
that makes me think that there are some far deeper mysteries to it
that I don't fully appreciate that could trip this solution up. But
in lieu of that, enjoy!
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Tuesday 8 January 2013
Using multiple axes for a d3.js graph
The following post is a portion of the D3 Tips and Tricks document which it free to download. To use this post in context, consider it with the others in the blog or just download the pdf and / or the examples from the downloads page:-)
--------------------------------------------------------
Alrighty... Let's imagine that we want to show our wonderful graph with two lines on it much like we already have, but that the data that the lines is made from is significantly different in magnitude from the original data (in the example below, the data for the second line has been reduced by approximately a factor of 10 from our original data).
date close open
1-May-12 58.13 3.41
30-Apr-12 53.98 4.55
27-Apr-12 67.00 6.78
26-Apr-12 89.70 7.85
25-Apr-12 99.00 8.92
24-Apr-12 130.28 9.92
23-Apr-12 166.70 10.13
20-Apr-12 234.98 12.23
19-Apr-12 345.44 13.45
18-Apr-12 443.34 16.04
17-Apr-12 543.70 18.03
16-Apr-12 580.13 21.02
13-Apr-12 605.23 22.34
12-Apr-12 622.77 20.15
11-Apr-12 626.20 21.26
10-Apr-12 628.44 31.04
9-Apr-12 636.23 35.04
5-Apr-12 633.68 41.02
4-Apr-12 624.31 43.05
3-Apr-12 629.32 46.03
2-Apr-12 618.63 51.03
30-Mar-12 599.55 53.42
29-Mar-12 609.86 57.82
28-Mar-12 617.62 59.01
27-Mar-12 614.48 56.03
26-Mar-12 606.98 58.01
1-May-12 58.13 3.41
30-Apr-12 53.98 4.55
27-Apr-12 67.00 6.78
26-Apr-12 89.70 7.85
25-Apr-12 99.00 8.92
24-Apr-12 130.28 9.92
23-Apr-12 166.70 10.13
20-Apr-12 234.98 12.23
19-Apr-12 345.44 13.45
18-Apr-12 443.34 16.04
17-Apr-12 543.70 18.03
16-Apr-12 580.13 21.02
13-Apr-12 605.23 22.34
12-Apr-12 622.77 20.15
11-Apr-12 626.20 21.26
10-Apr-12 628.44 31.04
9-Apr-12 636.23 35.04
5-Apr-12 633.68 41.02
4-Apr-12 624.31 43.05
3-Apr-12 629.32 46.03
2-Apr-12 618.63 51.03
30-Mar-12 599.55 53.42
29-Mar-12 609.86 57.82
28-Mar-12 617.62 59.01
27-Mar-12 614.48 56.03
26-Mar-12 606.98 58.01
Now this isn't a problem in itself. D3 will still make a reasonable graph of the data, but because of the difference in range, the detail of the second line will be lost.
So what I'm proposing is that we have a second y axis on the right hand side of the graph that relates to the red line.
The adoption I've done here is based on the great examples put forward by Ben Christensen here: http://benjchristensen.com/2012/05/02/line-graphs-using-d3-js/.
Now... You'll want to concentrate a bit here since there are quite a few different bits to change and adapt, but don't despair, they're all quite logical and make sense.
First things first, there won't be space on the right hand side of or graph to show the extra axis, so we should make our right hand margin a little larger.
var margin = {top: 30, right: 40, bottom: 30, left: 50},I went for 40 and it seems to fit pretty well.
Then (and here's where the main point of difference for this graph comes in) you want to amend the code to separate out the two scales for the two lines in the graph. This is actually a lot easier than it sounds, since it consists mainly of finding anywhere that mentions 'y' and replacing it with 'y0' and then adding in a reciprocal piece of code for 'y1'.
The idea here is that we will be creating two references for the y axis. One for each column of data. Then when we draw the lines the scales will automatically scale the data correctly (and separately) to our canvas and we will draw two different y axes with the different scales. Believe it or not, it's sounds a lot harder than it is.
Let's get started.
Firstly, change the variable declaration for 'y' to 'y0' and add in 'y1'.
var x = d3.time.scale().range([0, width]); var y0 = d3.scale.linear().range([height, 0]); var y1 = d3.scale.linear().range([height, 0]);Then change our yAxis declaration to be specific for 'y0' and specifically 'left'. And add in a declaration for the right hand axis;
var yAxisLeft = d3.svg.axis().scale(y0) // <== Add in 'Left' and 'y0' .orient("left").ticks(5); var yAxisRight = d3.svg.axis().scale(y1) // This is the new declaration for the 'Right', 'y1' .orient("right").ticks(5); // and includes orientation of the axis to the right.Note the orientation change for the right hand axis.
Now change our valueline declarations so that they refer to the 'y0' and 'y1' scales.
var valueline = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y0(d.close); }); // <== y0 var valueline2 = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y1(d.open); }); // <== y1There are a few different ways for the scaling to work, but we'll stick with the fancy max method we used in the dual line example (although technically it's not required).
y0.domain([0, d3.max(data, function(d) { return Math.max(d.close); })]); y1.domain([0, d3.max(data, function(d) { return Math.max(d.open); })]);Again, here's the 'y0' and 'y1' changed and added and the maximums for 'd.close' and 'd.open' are separated out).
The final piece of the puzzle is to draw the new axis, but we also want to make a slight change to the original y axis. Since we have two lines and two axes, we need to know which belongs to which, so we can colour code the text in the axes to match the lines;
svg.append("g") .attr("class", "y axis") .style("fill", "steelblue") .call(yAxisLeft); svg.append("g") .attr("class", "y axis") .attr("transform", "translate(" + width + " ,0)") .style("fill", "red") .call(yAxisRight);In the above code you can see where we have added in a 'style' change for the yAxisLeft to make it 'steelblue' and a complementary change in the new section for yAxisRight to make that text red.
The yAxisRight section obviously needs to be added in, but the only significant difference is the transform / translate attribute that moves the axis to the right hand side of the graph.
And after all that, here's the result...
Now, let's not kid ourselves that it's a thing of beauty, but we should console our aesthetic concerns with the warm glow of understanding how the function works :-).
The above description (and heaps of other stuff) is in the D3 Tips and Tricks document that can be accessed from the downloads page of d3noob.org.
Subscribe to:
Posts (Atom)