Custom Transform

A custom transformation allows a user-defined mapping of data to an axis. A custom transform is used when the Transform attribute has the value TRANSFORM_CUSTOM. The custom transform is the value of the CustomTransform attribute set with AbstractChartNode.CustomTransform.

A custom transform implements the Transform interface. This interface has three methods. One, SetupMapping, is called first to allow the transformation to initialize itself. The other two, MapUserToUnit and MapUnitToUser, specify a mapping from the window interval onto [0,1]. These methods must each be the inverse of the other.

Example: Normal Probability Plot

A normal probability plot maps normally distributed data into a straight line, just as a semilog plot maps exponential data into a straight line.

In this example, 400 normally distributed random numbers are grouped into 20 bins. The bin counts are normalized by dividing by the number of samples (400). Cumulative probabilities are computed by summing the probabilities in all of the bins to the left of the current bin.

The custom transformation is the normal cumulative distribution function, Cdf.Normal, and its inverse, InvCdf.Normal, scaled to map the probability range [0.001,0.999] onto [0,1].

Autoscaling is turned off on the probability (y) axis and a fixed set of probability tick marks are specified.

The plot is of the bin centered on the x-axis versus the cumulative probabilities on the y-axis. The points are not in an exactly straight line because with only a finite number of samples, the distribution does not exactly match the normal distribution.

(Download Code)

 

using Imsl.Chart2D;

using Imsl.Stat;

using System.Drawing;

 

public class SampleProbability : FrameChart {

 

    public SampleProbability() {

        Chart chart = this.Chart;

        AxisXY axis = new AxisXY(chart);

 

        double[] ticks = {

            0.001, 0.005, 0.01, 0.02, 0.05, 0.10, 0.20, 0.30,

            0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 0.95, 0.98,

            0.99, 0.995, 0.999};

        double a = ticks[0];

        double b = ticks[ticks.Length-1];

 

        axis.AxisX.AxisTitle.SetTitle("x");

        axis.AxisY.AxisTitle.SetTitle("Probability");

 

        axis.AxisY.Transform = Axis.TRANSFORM_CUSTOM;

        axis.AxisY.CustomTransform = new NormalTransform();

        axis.AxisY.AutoscaleInput = Axis.AUTOSCALE_OFF;

        axis.AxisY.SetWindow(a, b);

        axis.AxisY.TextFormat = "0.000";

        axis.AxisY.SetTicks(ticks);

        axis.AxisY.MinorTick.IsVisible = false;

 

        int nSamples = 400;

        int nBins = 20;

 

        // Setup the bins

        double[] bins = new double[nBins];

        double dx = 6.0/nBins;

        double[] x = new double[nBins];

        for (int k = 0; k < x.Length; k++) {

            x[k] = -3.0 + (k+0.5)*dx;

        }

 

        // Generate random normal deviates and sort into bins

        Random r = new Random(123457);

        for (int k = 0; k < nSamples; k++) {

            double t = r.NextNormal();

            int j = (int)System.Math.Round((t+3.0-0.5*dx)/dx);

            if (j <= 0) {

                bins[0]++;

            } else if (j >= nBins-1) {

                bins[nBins-1]++;

            } else {

                bins[j]++;

            }

        }

 

        // Compute the cumulative distribution

        // y[k] is the sum of bins[j] for j=0,...,k divided by the nSamples.

        double[] y = new double[nBins];

        y[0] = bins[0]/nSamples;

        for (int k = 1; k < nBins; k++) {

            y[k] = y[k-1] + bins[k]/nSamples;

        }

 

        // Plot the data using markers

        Data data = new Data(axis, x, y);

        data.DataType = Data.DATA_TYPE_MARKER;

        data.MarkerType = Data.MARKER_TYPE_FILLED_CIRCLE;

        data.MarkerColor = Color.Blue;

 

    }

 

    public static void Main(string[] argv) {

        System.Windows.Forms.Application.Run(new SampleProbability());

    }

}

 

class NormalTransform : Transform

{

    private double scaleA, scaleB;

 

    // Initializes the mappings between user and coordinate space.

    public void SetupMapping(Axis1D axis1d)

    {

        double[] w = axis1d.GetWindow();

        double t = InvCdf.Normal(w[0]);

        scaleB = 1.0 / (InvCdf.Normal(w[1]) - t);

        scaleA = -scaleB * t;

    }

 

    // Maps a point in [0,1] to a probability.

    public double MapUnitToUser(double unit)

    {

        return Cdf.Normal((unit - scaleA) / scaleB);

    }

 

    // Maps a probablity to the interval [0,1].

    public double MapUserToUnit(double p)

    {

        return scaleA + scaleB * InvCdf.Normal(p);

    }

}



Visual Numerics - Developers of IMSL and PV-WAVE
http://www.vni.com/
PHONE: 713.784.3131
FAX:713.781.9260