Custom plots
The methods in the Plot.Create
class are useful to create “standard” plots, but you can achieve a greater degree of customisation by manually creating a plot. This involves creating all the individual plot elements (axes, grid, data elements, etc) and adding them onto an empty Plot
object.
The following example shows how to create a custom plot. Here, we sample some points from a Gaussian distribution, use them to compute a linear transformation, and plot at the same time the distribution of sampled points (as a histogram and box plot), the sampled points themselves, a trendline, and the equation plotted by the trendline. See the pages describing the various plot elements for more information about each plot element.
using VectSharp ;
using VectSharp.Plots ;
using VectSharp.SVG ;
using MathNet.Numerics.Distributions ;
using MathNet.Numerics.Statistics ;
// Sample some points from a Gaussian distribution
double [] samples = Normal . Samples ( 10 , 2 ). Take ( 250 ). ToArray ();
// Used to add some randomness to the data.
Random rnd = new Random ();
// Use the sampled points to compute a linear function.
double [][] data = ( from x in samples select new double [] { x , 2 * x + 3 + rnd . NextDouble () * 10 }). ToArray ();
// Create the coordinate system for the points.
LinearCoordinateSystem2D mainCoordinateSystem = new LinearCoordinateSystem2D ( data );
// Create the plot element that will plot the points.
ScatterPoints < IReadOnlyList < double >> scatterPoints = new ScatterPoints < IReadOnlyList < double >>( data , mainCoordinateSystem )
{
PresentationAttributes = new PlotElementPresentationAttributes ()
{
Stroke = null ,
Fill = Colour . FromRgb ( 0 , 114 , 178 ) // Draw points in blue.
}
};
// Create the plot element that will draw the trendline.
LinearTrendLine trendline = new LinearTrendLine ( data , mainCoordinateSystem );
// Bin the samples to create the histogram.
int binCount = 20 ;
double minSample = samples . Min ();
double maxSample = samples . Max ();
double binWidth = ( maxSample - minSample ) / binCount ;
int [] bins = new int [ binCount ];
foreach ( double sample in samples )
{
bins [ Math . Min (( int )(( sample - minSample ) / binWidth ), binCount - 1 )]++;
}
double [][] binnedData = bins . Select (( x , i ) => new double [] { minSample + binWidth * ( i + 0.5 ), bins [ i ] }). ToArray ();
// Create a new coordinate system for the histogram.
LinearCoordinateSystem2D histogramCoordinateSystem = new LinearCoordinateSystem2D ( mainCoordinateSystem . MinX , mainCoordinateSystem . MaxX , 0 , bins . Max (), 350 , 250 );
// Create the plot element that draws the histogram.
Bars bars = new Bars ( binnedData , histogramCoordinateSystem )
{
Margin = 0.1 ,
PresentationAttributes = new PlotElementPresentationAttributes ()
{
Stroke = null ,
Fill = Colour . FromRgba ( 0 , 0 , 0 , 32 )
}
};
// Compute some statistics for the box plot.
double quart1 = samples . LowerQuartile ();
double median = samples . Median ();
double quart3 = samples . UpperQuartile ();
double mean = samples . Mean ();
double stdDev = samples . StandardDeviation ();
double whisker1 = Math . Max ( mean - 2 * stdDev , minSample );
double whisker2 = Math . Min ( mean + 2 * stdDev , maxSample );
// Create the plot element that will plot the box plot.
BoxPlot box = new BoxPlot ( new double [] { median , 0 }, new double [] { 1 , 0 }, whisker1 - median , quart1 - median , quart3 - median , whisker2 - median , histogramCoordinateSystem )
{
Width = histogramCoordinateSystem . MaxY * 0.05 ,
WhiskersPresentationAttributes = new PlotElementPresentationAttributes () { LineWidth = 2 , Stroke = Colour . FromRgb ( 0 , 114 , 178 ) },
BoxPresentationAttributes = new PlotElementPresentationAttributes () { Stroke = Colour . FromRgb ( 0 , 114 , 178 ), Fill = Colour . FromRgb ( 213 , 240 , 255 ) }
};
// Create the plot element that will display the equation
TextLabel < IReadOnlyList < double >> textLabel = new TextLabel < IReadOnlyList < double >>( "<i>y</i> = " + trendline . Slope . ToString ( "0.##" , System . Globalization . CultureInfo . InvariantCulture ) + " <i>x</i> + " + trendline . Intercept . ToString ( "0.##" , System . Globalization . CultureInfo . InvariantCulture ), new double [] { samples . Min (), bins . Max () * 0.95 }, histogramCoordinateSystem )
{
Alignment = TextAnchors . Left ,
Baseline = TextBaselines . Top ,
PresentationAttributes = new PlotElementPresentationAttributes () { Stroke = null , Font = new Font ( FontFamily . ResolveFontFamily ( FontFamily . StandardFontFamilies . TimesRoman ), 12 ) }
};
// Create the X axis
ContinuousAxis xAxis = new ContinuousAxis ( new double [] { minSample - ( maxSample - minSample ) * 0.05 , 0 }, new double [] { maxSample + ( maxSample - minSample ) * 0.05 , 0 }, histogramCoordinateSystem );
// Ticks for the X axis
ContinuousAxisTicks xTicks = new ContinuousAxisTicks ( new double [] { minSample , 0 }, new double [] { maxSample , 0 }, histogramCoordinateSystem )
{
SizeAbove = _ => 0 ,
SizeBelow = _ => 20 ,
PresentationAttributes = new PlotElementPresentationAttributes () { LineDash = new LineDash ( 3 , 3 , 0 ) },
IntervalCount = 7
};
// Labels for the X axis
ContinuousAxisLabels xLabels = new ContinuousAxisLabels ( new double [] { minSample , 0 }, new double [] { maxSample , 0 }, histogramCoordinateSystem )
{
Rotation = 0 ,
Alignment = TextAnchors . Center ,
Baseline = TextBaselines . Top ,
Position = _ => 25 ,
PresentationAttributes = new PlotElementPresentationAttributes () { Stroke = null },
IntervalCount = 7
};
// Create the Y axis
ContinuousAxis yAxis = new ContinuousAxis ( new double [] { minSample - ( maxSample - minSample ) * 0.05 , 0 }, new double [] { minSample - ( maxSample - minSample ) * 0.05 , bins . Max () }, histogramCoordinateSystem );
// Ticks for the Y axis
ContinuousAxisTicks yTicks = new ContinuousAxisTicks ( new double [] { minSample - ( maxSample - minSample ) * 0.05 , data . Select ( x => x [ 1 ]). Min () }, new double [] { minSample - ( maxSample - minSample ) * 0.05 , data . Select ( x => x [ 1 ]). Max () }, mainCoordinateSystem );
// Labels for the Y axis
ContinuousAxisLabels yLabels = new ContinuousAxisLabels ( new double [] { minSample - ( maxSample - minSample ) * 0.05 , data . Select ( x => x [ 1 ]). Min () }, new double [] { minSample - ( maxSample - minSample ) * 0.05 , data . Select ( x => x [ 1 ]). Max () }, mainCoordinateSystem )
{
Alignment = TextAnchors . Right ,
Baseline = TextBaselines . Middle ,
PresentationAttributes = new PlotElementPresentationAttributes () { Stroke = null },
Position = _ => - 10
};
// Create an empty plot.
Plot plot = new Plot ();
// Add the plot elements to the plot.
plot . AddPlotElements ( xTicks , xLabels , xAxis , yTicks , yLabels , yAxis , bars , box , scatterPoints , trendline , textLabel );
// Render the plot to a Page and save it as an SVG document.
Page pag = plot . Render ();
pag . SaveAsSVG ( "customPlot.svg" );