5

I have data for each and every student, e.g.:

    Student Name         Score
    Jack                  89
    Jill                  70
    Sandy                 40

Now I'm trying to plot these in a bar chart using GD::Graph::Bar, but I see that I can manually declare all the X and Y values from the chart to be plotted.

Since I don't know the names and scores of each of the student (pulled from a text file), I want to be able to do the values automatically,

I was thinking hash keys and values was a good approach. I placed everything in a hash table: %hash(student name)=(score)

Can anyone help me plot this as a bar chart or guide me? Or would you recommend a different approach?

This is the part where I can plot the graph manually by entering the student names:

 my $graph = GD::Graph::bars->new(800, 800);

   @data = ( 
      ["Jack","Jill"],
      ['30','50'],
        );

     $graph->set( 
        x_label           => 'Students',
        y_label           => 'Scores',
        title             => 'Student Vs. Scores',
       y_max_value       => 60,
       y_tick_number     => 8,
       y_label_skip      => 2 
      ) or die $graph->error;

    my $gd = $graph->plot(\@data) or die $graph->error;

    open(IMG, '>file.png') or die $!;
     binmode IMG;
     print IMG $gd->png;
toolic
  • 57,801
  • 17
  • 75
  • 117
Le Ray
  • 367
  • 2
  • 12

3 Answers3

3

Assuming your data file is as follows, using tab delimiters.

Student Name         Score
Jack                  89
Jill                  70
Sandy                 40

You could do something like this, pushing your x axis and y axis values from your data file to arrays.

use strict;
use warnings;
use CGI qw( :standard );
use GD::Graph::bars;

open my $fh, '<', 'data.txt' or die $!;

my (@x, @y);
while (<$fh>) {
   next if $. == 1;            # skip header line
   push @x, (split /\t/)[0];   # push 'Student Names' into @x array
   push @y, (split /\t/)[1];   # push 'Score' into @y array
}
close $fh;

my $graph = GD::Graph::bars->new(800, 800);

$graph->set( 
             x_label => 'Students',
             y_label => 'Scores',
             title   => 'Student Vs. Scores',
) or warn $graph->error;

my @data = (\@x, \@y);
$graph->plot(\@data) or die $graph->error();

print header(-type=>'image/jpeg'), $graph->gd->jpeg;

Giving you for example: enter image description here

If you are wanting to use multiple y axis values, assuming you have another tab delimiter column with for example Score2, you could easily do something like this.

my (@x, @y, @y2);
while (<$fh>) {
   next if $. == 1;
   push @x, (split /\t/)[0];
   push @y, (split /\t/)[1];
   push @y2, (split /\t/)[2];
}

And change your @data array to:

my @data = (\@x, \@y, \@y2);

And your result would be: enter image description here

hwnd
  • 69,796
  • 4
  • 95
  • 132
1

According to the documentation, you need to pass an array of arrays to the plot method of GD::Graph::bars. It sounds like you already have a hash so you need to convert it to an array of arrays. There are a number of ways to do this, but here's an example:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my %hash = (
    Larry => 15,
    Curly => 16,
    Moe   => 20
);

my (@names, @scores);
while (my ($name, $score) = each %hash) {
    push @names, $name;
    push @scores, $score;
}

my @data = (\@names, \@scores);

print Dumper(\@data);

# $VAR1 = [ 
#           [ 
#             'Moe',
#             'Curly',
#             'Larry'
#           ],
#           [ 
#             20,
#             16,
#             15
#           ]
#        ];

However you do it, make sure you preserve the order in the inner arrays.

ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110
  • Hey, I just implemented this method and it worked. I was able to create a nice little chart and tweak it accordingly. Thank you! However, I just ran into a problem. Instead of having a single average for each student, I now have two averages for each student. And I don't think I can use a hash with two values. Any inputs or suggestions would be great. – Le Ray Aug 09 '13 at 18:28
  • 2
    You can do `my %hash = ( Jack => [ 89, 90 ] );` – hwnd Aug 09 '13 at 18:36
  • Actually, I think I got it. I'll just get rid of the hashes, add another array with and add it to my @data = (\@names,\@scores,\@newarray); – Le Ray Aug 09 '13 at 18:37
  • hwnd, will I be able to plot the two values too? – Le Ray Aug 09 '13 at 18:38
  • You can plot both values – hwnd Aug 09 '13 at 18:54
  • I'm assuming your speaking of multiple `x` axis values? – hwnd Aug 09 '13 at 20:06
  • See example I posted below. – hwnd Aug 09 '13 at 20:49
1

I adapted the code from the samples directory in GD::Graph:

use warnings;
use strict;
use GD::Graph::bars;
use GD::Graph::Data;

my %students = (
    Jack    => 89,
    Jill    => 70,
    Sandy   => 40,
);

my @scores;
my @names;
for (keys %students) {
    push @names, $_;
    push @scores, $students{$_};
}

my $data = GD::Graph::Data->new([
    [@names],
    [@scores],
]) or die GD::Graph::Data->error;

my $my_graph = GD::Graph::bars->new();
$my_graph->set(
    x_label         => 'Name',
    y_label         => 'Score',
    title           => 'A Simple Bar Chart',
) or warn $my_graph->error;
$my_graph->plot($data) or die $my_graph->error();
save_chart($my_graph, 'graph');

sub save_chart {
    my $chart = shift or die "Need a chart!";
    my $name = shift or die "Need a name!";
    local(*OUT);

    my $ext = $chart->export_format;

    open(OUT, ">$name.$ext") or
        die "Cannot open $name.$ext for write: $!";
    binmode OUT;
    print OUT $chart->gd->$ext();
    close OUT;
}
toolic
  • 57,801
  • 17
  • 75
  • 117