I chose to waste time and store the whole bar chart in array of arrays of
chars.I
chose to scale vertically only if the height was to small for the data.
I also added horizatonal centering and padding between bars. I did no
scaling
horizontally so if your width is too small then you lose the data on the
right.
Unfortunantely since I stored all the values individually I had to loop
again to concatenate them. Sorry no comments. I did enjoy having a variable
char for the background characters instead of spaces. I liked to use a '~'
for my debugging.
--------------------------------------
#!/usr/bin/perl
use strict;
use warnings;
my $HISTOGRAM_WIDTH = 18;
my $HISTOGRAM_HEIGHT = 16;
my $HISTOGRAM_CHAR;
my @hist = histogram(11, 2, 31, 24, 7);
print @hist;
sub histogram {
my @data = @_;
$HISTOGRAM_WIDTH ||= 15;
$HISTOGRAM_HEIGHT ||= 15;
$HISTOGRAM_CHAR ||= '*';
my $HISTOGRAM_NONCHAR = ' ';
my $MARGIN;
my $PADDING;
if ($HISTOGRAM_WIDTH <= @data) {
$MARGIN = 0;
$PADDING = 0;
} elsif ($HISTOGRAM_WIDTH < 2 * @data) {
$MARGIN = int(($HISTOGRAM_WIDTH % @data) / 2);
$PADDING = 0;
} elsif ($HISTOGRAM_WIDTH >= 2 * @data) {
$PADDING = int(($HISTOGRAM_WIDTH / @data))-1;
$MARGIN = int(($HISTOGRAM_WIDTH % @data) / 2)+1;
}
my $max_value = 0;
for (@data) {$max_value = $_ if ($_ > $max_value)}
if ($max_value > $HISTOGRAM_HEIGHT) {
for (@data) {$_ = sprintf("%d", $_*$HISTOGRAM_HEIGHT/$max_value)}
}
my $chart_ref = [];
my ($i, $j) = (0, 0);
my @tmp = @data;
while ($j < $HISTOGRAM_HEIGHT) {
if ($i < $MARGIN) {
$chart_ref->[$i][$j] = $HISTOGRAM_NONCHAR;
$i++;
next;
}
my $val = shift @tmp;
$chart_ref->[$i][$j] = (defined $val && $val > $j)
? $HISTOGRAM_CHAR : $HISTOGRAM_NONCHAR;
for (my $p = 0; $p < $PADDING; $p++) {
$i++;
$chart_ref->[$i][$j] = $HISTOGRAM_NONCHAR;
}
next if (++$i < $HISTOGRAM_WIDTH);
$i = 0;
$j++;
@tmp = @data;
}
($i, $j) = (0, 0);
my @retval;
while ($j < $HISTOGRAM_HEIGHT) {
$retval[$j] .= $chart_ref->[$i][$HISTOGRAM_HEIGHT-$j-1];
next if (++$i < $HISTOGRAM_WIDTH);
$retval[$j] .= "\n";
$i = 0;
$j++;
}
return @retval;
}
_________________________________
Dave Kershaw
Database Developer, e-Business Data & Information
VWR International, http://www.vwr.com
Phone: 610-429-5558
|