Login

Basic Charting with Perl

In programming, data manipulation is an everyday task. Programmers must be able to take raw data and perform some operation, or a set of operations, on that data to modify it. However, presenting data is also very important, both in programming and in other tasks. This article will cover one of the ways you can present data in Perl

One way of presenting data is in a chart. There are a variety of chart types, each of which has its particular strength. Perl, not surprisingly, offers a wealth of modules for creating charts to present data. In this article, we’ll examine a collection of modules, appropriately named Chart, that allows for the creation of charts. We’ll take a look at using the module to create some basic charts.

Getting Started

There’s very little to explain about charts in general that you don’t already know, so let’s jump right into the module by creating a basic chart.

The Chart module is, of course, available from CPAN, so obtaining it is simple:

$ cpan Chart::Base

Remember that Chart is actually a collection of modules, rather than a single module. Chart’s usage reflects this. Instead of importing a single base module, as one might expect, we must actually import the module associated with the particular chart we want to create. Chart supports a number of chart types, but let’s start with something simple. Say we want to graph average monthly temperatures for a particular city. A line graph would work well for this. So, let’s use the Chart::Lines module:

use Chart::Lines;

Next, we have to instantiate a Chart::Lines object. Every operation is conducted through this object. The only parameters required are the desired width and height of the chart image. Let’s make the chart 600 pixels wide and 400 pixels high:

my$chart= new Chart::Lines(600,400);

Once a Chart::Lines object is instantiated, we can generate a complete chart with only a single method call. All we need is the name of the resulting image, and a reference to the data to be represented by the chart. The name of the image is a string, and the reference is to an array.

Let’s assume that we already have data inside an array named @data, and that we want to create a chart named temps.png. To do this, we’d call the png method, like this:

$chart->png('temps.png',@data);

As you can see, there’s not much to it. Of course, now we need some data.

{mospagebreak title=Adding Data}

In order for the graph to be created, it needs some real data. We’ll be working with average monthly temperatures. Let’s use data from Portland, Oregon, and let’s just use the high values for now. (This data will be taken from The Weather Channel).

As has already been said, data is stored in an array. The array is actually a multidimensional array, however. The first element of the array should be a reference to an array of labels. In our case, the labels would be the month names. Subsequent elements should contain references to arrays containing the actual values corresponding with those labels. Each of these data elements is called a dataset.

So, if we want to graph the average monthly high temperatures for Portland, @data should be defined like this:

If we call the png method now, then an image will be created named temps.png, containing the resulting chart. The image should look like this:

Now we should plot the average monthly low temperatures. If we do this, though, then the @data definition is going to start looking really messy. Fortunately, there is another way to add data which, in some cases, may make things easier and neater. Using the add_dataset method, it is possible to add datasets directly, without the need to create arrays that will only be used once. You just need to pass in the values as arguments. Let’s go ahead and get rid of @data by using the add_dataset method, and then add data for low temperatures:

Note how the add_dataset method works for adding labels too. Also, when we call the png method, we only pass one argument.

The resulting graph looks like this:

The advantage of this approach is that datasets can easily be added as needed, without the complexities of manipulating a multidimensional array.

{mospagebreak title=Modifying the Graph}

Our graph isn’t perfect, though. It has a number of pretty obvious flaws that need to be addressed in order for the graph to look good. For example, nothing is labeled, so it’s unclear what the graph even represents.

So, we’ll need to modify our graph’s properties a bit, which is actually fairly easy to do. The set method is used, whose arguments operate like a hash whose keys are the property names to be modified and whose values are the new property values.

Let’s start by giving the graph a title, so that viewers will know what they’re looking at. The “title” element of the properties hash corresponds to the graph’s title. Let’s give the graph the title “Average Monthly Temperatures for Portland, OR:”

At this point, rather than stylizing an increasingly messy method call, let’s create a hash. That way, each element can be set separately, and the hash can just be passed to the method by name. This will make the new properties easier to display in this article, anyway. Here’s the rewritten code:

We should probably change the minimum and maximum values on the y-axis too. Right now, since the range of values is so small, it looks like there is an abnormally large spike in the temperature. This can be fixed by setting a more appropriate minimum. Zero would work as a minimum, and it would put the temperatures in context. In order to set the minimum value, the “min_val” property is used:

$property{'min_val'}=0;

Since we’re setting it at zero, however, it’s also possible to set “include_zero”:

$property{'include_zero'}='true';

The maximum value can also be adjusted to provide more room at the top. The “max_val” property is used for this:

$property{'max_val'}=90;

Also, along the y-axis, the numbers are floating point numbers. The graph would look nicer (and there would be more room) if we were to make these numbers integers. This can be done by setting the “integer_ticks_only” property:

$property{'integer_ticks_only'}='true';

We should also set a proper interval for the ticks using the “skip_int_ticks” property:

$property{'skip_int_ticks'}=5;

{mospagebreak title=Modifying the Graph, Continued}

The graph now looks a lot better than it did originally:

However, there are two changes I can think of which should be made before the chart is perfect. First, the lines need to be given names other than “Dataset 1” and “Dataset 2.” In order to name these lines, the “legend_labels” element must be set. The value of this element must be a reference to an array. The elements of this array correspond to the names of the datasets. Let’s name the first line “High” and the second line “Low”:

$property{'legend_labels'}=['High','Low'];

The default color of the first line is appropriate, since red is associated with heat. However, the default color of the second line is not appropriate. Blue would be a much more appropriate color, since blue is associated with coldness.

Fortunately, the color of anything in the graph can be changed using the “colors” element of the properties hash. The value of this element must be a hash. Within the colors hash, the colors of individual items can be changed. In order to change the color associated with a dataset, which is what we want to do, the “datasetN” property of this hash is set, where N is the zero-based number of the dataset we want to change. Let’s go ahead and change the color of the second line, as well as explicitly specify the color of the first line:

$property{'colors'}={'dataset0'=>[255,0,0],'dataset1'=>[0,0,255]};

Now the graph is perfect. It represents the data appropriately, which is the goal. The viewer can look at the graph and instantly tell what the graph represents, and he can see the general trend of the data.

Before we end, however, there is one more interesting thing I’d like to show you. Remember how each graph type corresponded to the name of a class? Well, let’s say that we don’t like the average monthly temperature represented by a line graph. Let’s say that we’d rather have a bar graph. In order for us to change the graph from a line graph to a bar graph, we’d only have to substitute Chart::Lines for Chart::Bars in two places:

use Chart::Bars;my$chart= new Chart::Bars(600,400);

The result is a bar graph which conveys the exact same data, but in another format:

However, we only had to change two lines in the source code to achieve this. This consistency across multiple chart formats makes Chart particularly appealing. If you create a program that creates a chart of a particular type, and later want to change the type, then it’s very easy to do so.

Of course, there is more to Chart, including a lot more properties that can be changed to meet your needs, but, as you can see, the basics are very simple, making Chart an indispensable tool for data representation in Perl.