ChartDirector 7.0 (Perl Edition)
Histogram with Bell Curve
Source Code Listing
#!/usr/bin/perl
# The ChartDirector for Perl module is assumed to be in "../lib"
use File::Basename;
use lib (dirname($0)."/../lib") =~ /(.*)/;
use perlchartdir;
#
# This example demonstrates creating a histogram with a bell curve from raw data. About half of the
# code is to sort the raw data into slots and to generate the points on the bell curve. The
# remaining half of the code is the actual charting code.
#
# Generate a random guassian distributed data series as the input data for this example.
my $r = new RanSeries(66);
my $samples = $r->getGaussianSeries(200, 100, 10);
#
# Classify the numbers into slots. In this example, the slot width is 5 units.
#
my $slotSize = 5;
# Compute the min and max values, and extend them to the slot boundary.
my $m = new ArrayMath($samples);
my $minX = int($m->min() / $slotSize) * $slotSize;
my $maxX = int($m->max() / $slotSize) * $slotSize + $slotSize;
# We can now determine the number of slots
my $slotCount = int(($maxX - $minX) / $slotSize + 0.5);
my $frequency = [(0) x $slotCount];
# Count the data points contained in each slot
for(my $i = 0; $i < scalar(@$samples); ++$i) {
my $slotIndex = int(($samples->[$i] - $minX) / $slotSize);
$frequency->[$slotIndex] = $frequency->[$slotIndex] + 1;
}
#
# Compute Normal Distribution Curve
#
# The mean and standard deviation of the data
my $mean = $m->avg();
my $stdDev = $m->stdDev();
# The normal distribution curve (bell curve) is a standard statistics curve. We need to vertically
# scale it to make it proportion to the frequency count.
my $scaleFactor = $slotSize * scalar(@$samples) / $stdDev / sqrt(2 * 3.1416);
# In this example, we plot the bell curve up to 3 standard deviations.
my $stdDevWidth = 3.0;
# We generate 4 points per standard deviation to be joined with a spline curve.
my $bellCurveResolution = int($stdDevWidth * 4 + 1);
my $bellCurve = [(0) x $bellCurveResolution];
for(my $i = 0; $i < $bellCurveResolution; ++$i) {
my $z = 2 * $i * $stdDevWidth / ($bellCurveResolution - 1) - $stdDevWidth;
$bellCurve->[$i] = exp(-$z * $z / 2) * $scaleFactor;
}
#
# At this stage, we have obtained all data and can plot the chart.
#
# Create a XYChart object of size 600 x 360 pixels
my $c = new XYChart(600, 360);
# Set the plotarea at (50, 30) and of size 500 x 300 pixels, with transparent background and border
# and light grey (0xcccccc) horizontal grid lines
$c->setPlotArea(50, 30, 500, 300, $perlchartdir::Transparent, -1, $perlchartdir::Transparent,
0xcccccc);
# Display the mean and standard deviation on the chart
$c->addTitle(sprintf("Mean = %s, Standard Deviation = %s", $c->formatValue($mean, "{value|1}"),
$c->formatValue($stdDev, "{value|2}")), "Arial");
# Set the x and y axis label font to 12pt Arial
$c->xAxis()->setLabelStyle("Arial", 12);
$c->yAxis()->setLabelStyle("Arial", 12);
# Set the x and y axis stems to transparent, and the x-axis tick color to grey (0x888888)
$c->xAxis()->setColors($perlchartdir::Transparent, $perlchartdir::TextColor,
$perlchartdir::TextColor, 0x888888);
$c->yAxis()->setColors($perlchartdir::Transparent);
# Draw the bell curve as a spline layer in red (0xdd0000) with 2-pixel line width
my $bellLayer = $c->addSplineLayer($bellCurve, 0xdd0000);
$bellLayer->setXData2($mean - $stdDevWidth * $stdDev, $mean + $stdDevWidth * $stdDev);
$bellLayer->setLineWidth(2);
# Draw the histogram as bars in blue (0x6699bb) with dark blue (0x336688) border
my $histogramLayer = $c->addBarLayer($frequency, 0x6699bb);
$histogramLayer->setBorderColor(0x336688);
# The center of the bars span from minX + half_bar_width to maxX - half_bar_width
$histogramLayer->setXData2($minX + $slotSize / 2.0, $maxX - $slotSize / 2.0);
# Configure the bars to touch each other with no gap in between
$histogramLayer->setBarGap($perlchartdir::TouchBar);
# Use rounded corners for decoration
$histogramLayer->setRoundedCorners();
# ChartDirector by default will extend the x-axis scale by 0.5 unit to cater for the bar width. It
# is because a bar plotted at x actually occupies (x +/- half_bar_width), and the bar width is
# normally 1 for label based x-axis. However, this chart is using a linear x-axis instead of label
# based. So we disable the automatic extension and add a dummy layer to extend the x-axis scale to
# cover minX to maxX.
$c->xAxis()->setIndent(0);
$c->addLineLayer2()->setXData($minX, $maxX);
# For the automatic y-axis labels, set the minimum spacing to 40 pixels.
$c->yAxis()->setTickDensity(40);
# Output the chart
$c->makeChart("histogram.png");