This post illustrates how to do the following using ggplot2:
Draw a simple line plot.
Change labels on the axes.
Change the color of the plotted line and increase its thickness.
Change the default background color of the area in which line is plotted.
Load ggplot2 package and create data for plotting
Line plot using ggplot2 with all default options
The output is:
The part aes(x=xcol, y=ycol) in above code specifies the aesthetic mapping, i.e., mapping between data and the things we can perceive on the plot. For instance, in the example above x-position on plot is mapped to data in column xcol and y-position on the plot is mapped to data in ycol (see Section 4.5 in Hadley Wickham's book on ggplot2) for more on aesthetic mappings.
Plot with custom labels for the axes
In the above plot the labels for the x and y axes are the same as the column names in the data. You can modify these labels if you wish. The following code shows how, using functions xlab() and ylab()
The output is:
Change color of the plotted line and increase thickness
You can change certain properties of the plotted line such as its color and thickness. To change its color, use the color property and to change its thickness use the size property. The default size is 0.5 and the default color is black. The following piece of code illustrates how this is done by specifying these as arguments of geom_line() function.
The output is:
Change the default background color of the plot area
The following code illustrates how to modify the background color:
The output is:
Note the use of opts(panel.background=theme_rect(fill='linen')) line to set the background color. A natural question is, which options other than panel.background can be specified using the opts() function? And, what are the possible theme values of these options? The list of options can be found on ggplot2 wiki page. You can also get the list of options and their default values by using theme_get() at the R prompt.
The following screenshot shows partial output from the theme_get()function:
I will end this post by combining all the different modifications we made above into a single script. The script and its output follow:
If you use Google Calendar's quick add feature, you know it is way better than using the form to add events/appointments. Unfortunately, those who have multiple calendars cannot avoid using the form for entering appointments. Quick add currently adds events only to the default calendar. To add an event to any other calendar you have to go to the form to choose that specific calendar. This more or less defeats the purpose of having quick add.
It would be nice to be able to use quick add to add events to a specific calendar. Numerous users of google calendar have requested this feature in forums, at least since 2009. It is not clear to me why Google refuses to add, what appears to be a simple feature, despite so many requests. Is it not possible for them to do it? If so, they should let us know on one of these forums, or on the page where they explain the quick add feature.
Anyhow, they have made it possible for us to let them know that we want the feature by listing this feature on their Google Calendar feature suggestions page. If you care about this feature, please go to the link and vote for it. In the Organization menu, press the suggest it button next to Quick add events to secondary calendars. The screenshot below shows what you see once you have made the suggestion.
EDIT: Added on July 22, 2012
I have not recently checked whether Google calendar has yet added this feature. But if they have not, and you use Mac, and you are tired of waiting around for Google to add this feature, I would strongly recommend using an awesome app called Quickcal to do this. At the time I am writing this, it costs $2.99 and it is certainly worth that amount. It works both with your iCal and with your Google Calendar. I have been using it for many months now and it works great.
In this post I show two ways of creating a simple SVG path using D3.js:
Method 1: the path data is specified in the same way as when using raw SVG (see here for more information).
Method 2: the path data is specified using path data generator method d3.svg.line() provided by D3.js
Method 1
I am assuming that you have the skeleton HTML file with link to d3.js. Add the following script to that HTML file.
Open the file in the browser and you should see:
Method 2
The same path as you see above is drawn here. But instead of specifying the path as a string, as done above, I have used the d3.svg.line() method to generate it from the given set of points.
As we expected, this code also gives:
The following piece of code gives an example of how to create SVG groups using D3.js, add graphical elements to these groups and simultaneously set attributes of elements belonging to a particular group. Scroll below to see the resulting figure.
If you are new to D3.js and have looked at the various D3.js examples on the web to learn it, you have most probably come across a sequence of selectAll(), data(), enter() and append() statements as shown in Example 1 below. To a newcomer to D3.js, it is not obvious how these methods work. At least, initially, I did not find it easy to understand their functioning. If you are also having some trouble with understanding these methods and how they work together, I think the examples and explanations below will be helpful.
"The key function also determines the enter and exit selections: the new data for which there is no corresponding key in the old data become the enter selection, and the old data for which there is no corresponding key in the new data become the exit selection. The remaining data become the default update selection."
Don't worry if the above excerpt makes little sense at this point. Once you look at the examples and the explanation below, the meaning will become clearer. In all the examples below, we have used D3.js to create HTML paragraph elements. But the same principles apply for any other elements you create using D3.js.
Carefully study the following two examples and their outputs. Try to spot the difference between these examples and their outputs.
The only difference in the code in the two examples is the presence of two paragraph elements in Example 2 ("Already existing paragraph 1" and "Already existing paragraph 2") before the d3.js script is called. In the output of Example 2, you can see that numbers 10 and 12 do not show up, even thought they are present in the data array pdata. Why? This is what we look at now. Let us go through the script.
Line selectDIV.selectAll("p") returns an array of all <p>...</p> elements in selectDIV.
In Example 1 this array is empty. In Example 2, this array contains the two already existing paragraph elements.
The line .data(pdata) specifies two things:
(i) the data array and,
(ii) the key function which assigns keys to the elements of the data array. These keys determine the enter selection — you can think of the enter selection as the elements in the data array that will be bound to nodes that you specify with the .enter().append() methods.
You are probably thinking, "wait! I do not see any key function. And, how do the keys determine the enter selection?" Let us address these thoughts.
As the creator of D3.js has said in this post, when a key function is not explicitly specified as an argument to the .data() method, it takes the default value of the index function. That is, the default key of an element in a data array is its index in the data array. For instance, since a key function has not been explicitly specified in Example 1, keys of elements 10, 12, 6, 8 and 15 are 0, 1, 2, 3, and 4 respectively.
The keys determine the enter selection in the following manner: the keys of elements in the specified data array (which, in our examples is pdata) are compared with keys of the elements in the existing selection (the selection returned by .selectAll("p") method). Any element in the specified data array whose key is different from keys of all the the existing elements, becomes a part of the enter selection. If the key of a new element matches the key of one of the existing elements then it is NOT a part of the enter selection.
Neither example above specifies an explicit key function. Hence, each elements key is its index in the array. In Example 1, selectAll("p") returns an empty array; there are no existing elements and consequently set of keys of existing elements is empty. The keys of elements in pdata, i.e., elements 10, 12, 6, 8 and 15 are 0, 1, 2, 3, and 4 respectively. Since all these have keys that are different from the keys of all the existing elements, they all become part of the enter selection.
On the other hand, in Example 2, .selectAll("p") returns an array with two elements with keys 0 and 1 respectively. The keys of elements in pdata, i.e., elements 10, 12, 6, 8 and 15 are 0, 1, 2, 3, and 4 respectively.
Since elements 10 and 12 have the same keys as keys of elements in the existing selection, 10 and 12 do not become part of the enter selection.
Methods .enter.append("p") create as many <p>...</p> elements as the number of elements in the enter selection (see the previous step). The argument of .append() specifies the type of element to be created. Finally, .text() line adds the members of the enter selection to the paragraph elements created by .enter.append("p").
In Example 2, since elements 10 and 12 are not part of the enter selection, no paragraph elements are created for them and they do not show up in the output.
Example 3 below is similar to Example 2. The only difference is that a key function is explicitly specified. Due to this, all elements in the pdata array show up in the output.
Note the second argument in the .data() method in Example 3. We have specified the identity function as the key function. As a result, the keys associated with the elements are the values of the elements: Element 10 has a key of 10, 12 has a key of 12, 6 has a key of 6 and so on. None of these keys match the keys of elements in the existing selection, which consists of paragraph elements <p>Already existing paragraph 1</p> and <p>Already existing paragraph 2</p>. Hence in Example 3, unlike Example 2, since every new data element has a different key than those of the existing elements, it is a part of the enter selection and gets added to the document when .enter.append("p").text(function(d){return d;}) methods are called.
In a follow up to this post I will explain how selecAll().data().exit() sequence works. I have yet to write that. When I do, I will put a link here.
Using D3.js to draw a grid
In a previous post, entitled "Drawing a straight line with D3.js, you saw how to draw a single line. In this post, we will draw a grid, which requires us to draw multiple lines. We will see two ways of doing this:
The first way uses the standard for loop of JavaScript to achieve this (in addition to the technique we discussed in the previous post). It does not use any feature specific to D3.js that we did not talk about in the previous post.
The second way illustrates use of D3.js methods that make it possible to bind given data to graphical elements. In other words, it illustrates use of D3.js methods that add graphical elements automatically based on available data. When you are creating graphics to represent data that you already have, this approach will be more useful.
Drawing a grid using for loops and D3.js
The following code illustrates how to draw a grid using for loops. Create a DIV container in your HTML file, <div id="D3lines"></div> and add the following javascript code:
// Select the DIV container "D3line" then
// add an SVG element to it
var width = 400;
var height = 400;
var lineGraph = d3.select("#D3lines")
.append("svg:svg")
.attr("width", width)
.attr("height", height);
// To draw a line use the "svg:line" element.
// "svg:line" element requires 4 attributes (x1, y1, x2, and y2)
// (x1,y1) are coordinates of the starting point.
// (x2,y2) are coordinates of the end point.
// You also need to specify the stroke color.
// Using for loop to draw multiple horizontal lines
for (var j=25; j <= width-25; j=j+25) {
lineGraph.append("svg:line")
.attr("x1", 25)
.attr("y1", j)
.attr("x2", width-25)
.attr("y2", j)
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2);
};
// Using for loop to draw multiple vertical lines
for (var j=25; j <= height-25; j=j+25) {
lineGraph.append("svg:line")
.attr("x1", j)
.attr("y1", 25)
.attr("x2", j)
.attr("y2", height-25)
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2);
};
This script gives:
Note that in the for loops, the values of the line attributes (x1, y1, x2, y2) are specified when the lines are being generated.
Drawing a grid using given coordinate data to generate the line elements.
The following code illustrates how to use data that has already been created, to generate line elements. Create a DIV container <div id="D3grid_D3way"></div> and add the following script to your HTML file.
// Select the DIV container "D3grid_D3way", then
// add an SVG element to it
var width = 400;
var height = 400;
var gridGraph = d3.select("#D3grid_D3way")
.append("svg:svg")
.attr("width", width) // Set width of the SVG canvas
.attr("height", height); // Set height of the SVG canvas
// the yaxiscoorddata gives the y coordinates
// for horizontal lines ("x1" = 25 and, "x2"=width-25)
var yaxiscoorddata = d3.range(25, height, 25);
// the xaxiscoorddata gives the x coordinates
// for vertical lines ("y1" = 25 and, "y2"=height-25)
var xaxiscoorddata = d3.range(25, width, 25);
// Using the xaxiscoorddata to generate vertical lines.
gridGraph.selectAll("line.vertical")
.data(xaxiscoorddata)
.enter().append("svg:line")
.attr("x1", function(d){return d;})
.attr("y1", 25)
.attr("x2", function(d){return d;})
.attr("y2", height-25)
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2);
// Using the yaxiscoorddata to generate horizontal lines.
gridGraph.selectAll("line.horizontal")
.data(yaxiscoorddata)
.enter().append("svg:line")
.attr("x1", 25)
.attr("y1", function(d){return d;})
.attr("x2", width-25)
.attr("y2", function(d){return d;})
.style("stroke", "rgb(6,120,155)")
.style("stroke-width", 2);
This script gives the same grid as above.
Function d3.range(start, stop, step) generates an array of numbers from start to stop (excluding stop) with the difference between the two successive numbers given by step. For example: d3.range(0,25,5) returns [0, 5, 10, 15, 20].
To understand the magic from line gridGraph.selectAll("line.vertical") onwards, I suggest you see my other post on Understanding selectAll, data, enter, append sequence in D3.js. You can also see Jerome Cukier's post on understanding selections. Jerome gives a good explanation of how data is used to create graphical elements with an example in which he creates bar charts. Go to the post and scroll down to the section entitled, "Bonus: understanding selections".