Question

I have some data from a database (SQLite), mapping a value (an integer) to a date. A date is a string with this format: YYYY-MM-DD hh:mm. The dates are not uniformly distributed. I want do draw a line graph with the dates on X and the values on Y. What is the easiest way to do this with Perl?

I tried DBIx::Chart but I could not make it recognize my dates. I also tried GD::Graph, but as the documentation says:

GD::Graph does not support numerical x axis the way it should. Data for X axes should be equally spaced

Was it helpful?

Solution

You can drive gnuplot using Chart::Gnuplot.

Alternatively, if SVG is an acceptable output format, there is SVG::TT::Graph.

OTHER TIPS

You can use Chart::Clicker's Axis::DateTime:

#!/usr/local/bin/perl -w
use strict;
use Chart::Clicker;
use Chart::Clicker::Axis::DateTime;
use Chart::Clicker::Data::Series;
use Chart::Clicker::Data::DataSet;
use Chart::Clicker::Renderer::Point;

my $cc = Chart::Clicker->new;
my $series = Chart::Clicker::Data::Series->new(
    values    => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ],
    keys      => [
        1256147117, 1256148117, 1256149117, 1256150117, 1256151117, 1256152117,
        1256153117, 1256154117, 1256155117, 1256156117
    ],
);
my $ctx = $cc->get_context('default');
$ctx->domain_axis(Chart::Clicker::Axis::DateTime->new(position => 'bottom', orientation     => 'horizontal'));
my $ds = Chart::Clicker::Data::DataSet->new(series => [ $series ]);
$cc->add_to_datasets($ds);
$cc->write_output('foo.png');

You have to convert your times into Unix timestamps, but it DWIMs.

I tried DBIx::Chart but I could not make it recognize my dates.

Have you tried to translate your dates to Unix-Timestamps and use them as X dates?

This did the trick for me:

my $ctx = $chart->get_context('default');
$ctx->domain_axis(
    Chart::Clicker::Axis::DateTime->new(
        position        => 'bottom',
        orientation     => 'horizontal',
        ticks           => 5 ,
        format          => "%Y-%m-%d"
    )
);

The easiest way I've found to do this with Perl is to use the Perl to run a gnuplot process. You can use set timefmt x "%Y-%m-%d" and it will automatically parse the data in the format you have. Gnuplot also supports a variety of output formats.

I would recommend normalizing the dates to integers. The brute force way would be of course using epoch seconds, but that might not look too nice on a graph, so normalize by a linear transform into some decent range (I can provide details of how if you wish).

Do you need your graph to be generated in real time, or is for a one-off report? If the latter, then you can use DateTime modules to generate Excel values and graph them in Excel (or its open-source counterpart.)

use DateTime::Format::MySQL;
use DateTime::Format::Excel;

my $dt = DateTime::Format::MySQL->parse_datetime( '2003-01-16 23:12:01' );
print $dt, "\n";
my $daynum = DateTime::Format::Excel->format_datetime($dt);
print $daynum, "\n";

Time ago I did something like this using Asymptote. It is an incredible package, but it's not easy to use.

The following won't work in SQLLite as recursive queries are not supported, but it will work in Oracle.

You can create a continuous time series for Oracle dBs with a subquery like this:

(SELECT   TRUNC (SYSDATE - x / 1440, 'MI') ts
             FROM   (SELECT       LEVEL x
                           FROM   DUAL
                     CONNECT BY   LEVEL <= 60))

Then link that time series to your chart data using a LEFT JOIN like this:

    SELECT   TO_CHAR (A.TS, 'DD/MM/YYYY HH24:MI') TS
        , b.DATAPOINT1, b.DATAPOINT2
   FROM   (SELECT   TRUNC (SYSDATE - x / 1440, 'MI') ts
             FROM   (SELECT       LEVEL x
                           FROM   DUAL
                     CONNECT BY   LEVEL <= 60)) a
        , MY_CHART_DATA b
  WHERE   a.ts = b.ts(+) ORDER BY   a.TS;

The example above will plot the last 60 minutes and will work** with DBIx::Chart. To change the resolution to hours change 'MI' to 'HH24', or for every 24 hours you can omit the date format parameter all together.

To change the range simply set the CONNECT BY LEVEL value to the number of time series points you need to plot at your specified resolution.

**Note: DBIx::Chart will barf if all datapoint values are zero (ie. undefined). Can't help you there, sorry.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top