//=================================================================
// File: SixSigma.cs
//
// Namespace: System.Web.UI.DataVisualization.Charting.Utilities
//
// Classes: SixSigma
//
// Purpose: To create SixSigma charts.
//
//===================================================================
// Chart Control for ASP.Net
// Copyright ?Microsoft Corporation, all rights reserved
//===================================================================
using System;
using System.Data;
using System.Configuration;
using System.Drawing;
using System.Web.UI.DataVisualization.Charting;
namespace System.Web.UI.DataVisualization.Charting.Utilities
{
///
/// SixSigma is a utility that includes useful functions related to the Six Sigma strategy.
/// This utility mainly focuses on those parts of the strategy that involve charting, and producing
/// those charts.
///
public class SixSigma
{
#region Members
///
/// sBar holds a value if an schart has been created. sBar is used for a XBAR chart.
///
private float mysBar = 0;
///
/// rBar holds a value if an rchart has been created. rBar is used for a XBAR chart.
///
private float myrBar = 0;
///
/// Holds the n value passed into sChart or rChart for use by XBAR.
///
private int mynValue = 0;
private bool myAutoFitLines;
private bool myShowLineLabels;
private Color myLineColor;
private Color myForeColor;
private Font myFont;
#endregion
#region Properties
///
/// Controls whether the AxisY will have an automatic Maximum set so that the chart will always
/// contain the UCL annotation. If left to false, it is up to the user to set a maximum AxisY value
/// such that all annotations appear on the graph.
///
public bool AutoFitLines
{
get { return myAutoFitLines; }
set { myAutoFitLines = value; }
}
///
/// Controls whether text is added to the lines to label what they are.
///
public bool ShowLineLabels
{
get { return myShowLineLabels; }
set { myShowLineLabels = value; }
}
///
/// Specifies the colour of the line annotations added to each chart.
///
public Color LineColor
{
get { return myLineColor; }
set { myLineColor = value; }
}
///
/// Specifies the colour of the text that will label the lines.
///
public Color ForeColor
{
get { return myForeColor; }
set { myForeColor = value; }
}
///
/// Specifies the font of the text that will label the lines.
///
public Font Font
{
get { return myFont; }
set { myFont = value; }
}
#endregion
#region Constructors
///
/// Default Constructor.
///
public SixSigma()
{
myAutoFitLines = false;
myShowLineLabels = false;
myLineColor = Color.Red;
myForeColor = Color.Red;
myFont = new Font("Arial", 8);
}
#endregion
#region Chart Creation Methods
///
/// Creates a C-Chart with lines indicating cBar, UCL and LCL. A C-Chart is a measure of the number of
/// non-conformities per unit, where unit is a fixed rate.
///
/// An array holding the subgroups. Must aline with the nonconform array.
/// An array holding the non-conformity data associated with the subgroups. Must aline with the subgroups.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series cChart(float[] subgroup, float[] nonconform, Chart output)
{
//Assure the input all have the same number of items.
if (subgroup.Length != nonconform.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, nonconform");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize cBar, LCL and UCL.
float cbar = 0;
float LCL = 0;
float UCL = 0;
//Calculate cbar.
for (int i = 0; i < c; i++)
{
cbar += (float)(nonconform[i]);
}
cbar /= c;
//Calculate UCL and LCL.
UCL = (float)(cbar + 3 * System.Math.Sqrt(cbar));
LCL = (float)(cbar - 3 * System.Math.Sqrt(cbar));
//If LCL is less than zero, then it should be zero.
if (LCL < 0)
LCL = 0;
//Create the series for the cchart data.
Series cseries = new Series("cseries");
//Graph subgroups vs. non-conformities.
for (int i = 0; i < c; i++)
cseries.Points.AddXY(subgroup[i], nonconform[i]);
//Set the series type to a line.
cseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(cseries);
//Add the lines to the graph as annotations.
//cBar line.
addLineAnnotation(output.ChartAreas[cseries.ChartArea].AxisX, output.ChartAreas[cseries.ChartArea].AxisY,
cseries.Points[0].XValue, cbar, cseries.Points[c - 1].XValue, 0, output);
//UCL line.
addLineAnnotation(output.ChartAreas[cseries.ChartArea].AxisX, output.ChartAreas[cseries.ChartArea].AxisY,
cseries.Points[0].XValue, UCL, cseries.Points[c - 1].XValue, 0, output);
//LCL line.
addLineAnnotation(output.ChartAreas[cseries.ChartArea].AxisX, output.ChartAreas[cseries.ChartArea].AxisY,
cseries.Points[0].XValue, LCL, cseries.Points[c - 1].XValue, 0, output);
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("CBAR", output.ChartAreas[cseries.ChartArea].AxisX, output.ChartAreas[cseries.ChartArea].AxisY,
cseries.Points[0].XValue, cbar, output);
addTextAnnotation("UCL", output.ChartAreas[cseries.ChartArea].AxisX, output.ChartAreas[cseries.ChartArea].AxisY,
cseries.Points[0].XValue, UCL, output);
addTextAnnotation("LCL", output.ChartAreas[cseries.ChartArea].AxisX, output.ChartAreas[cseries.ChartArea].AxisY,
cseries.Points[0].XValue, LCL, output);
}
//Scale the chart if the user has requested it.
FitChart(UCL, cseries.ChartArea, output);
//Return the series.
return cseries;
}
///
/// Creates a P-Chart with lines indicating pBar, UCL and LCL. A P-Chart is the same as an NP-Chart except
/// with a variable number of items in each subgroup.
///
/// An array holding the subgroups. Must aline with the nonconform array and the number tested.
/// An array holding the non-conformity data associated with the subgroups. Must aline with the subgroups and the number tested.
/// An array holding the number of items in each subgroup. Must aline with the subgroups and the nonconform array.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series pChart(float[] subgroup, float[] nonconform, float[] numbertested, Chart output)
{
//Assure the input all have the same number of items.
if ((subgroup.Length != nonconform.Length) || (subgroup.Length != numbertested.Length) || (nonconform.Length != numbertested.Length))
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, nonconform, numbertested");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize phat, UCL, LCL and pBar.
float[] phat = new float[c];
float[] UCL = new float[c];
float[] LCL = new float[c];
float pbar = 0;
//Calculate phat(i).
for (int i = 0; i < c; i++)
phat[i] = nonconform[i] / numbertested[i];
//Calculate pbar.
for (int i = 0; i < c; i++)
{
pbar += phat[i];
}
pbar /= c;
//Calculate UCL and LCL.
for (int i = 0; i < c; i++)
{
UCL[i] = (float)(pbar + 3 * System.Math.Sqrt((pbar * (1 - pbar)) / numbertested[i]));
LCL[i] = (float)(pbar - 3 * System.Math.Sqrt((pbar * (1 - pbar)) / numbertested[i]));
}
//Create the series for the pchart data.
Series pseries = new Series("pseries");
//Graph subgroup vs. proportion.
for (int i = 0; i < c; i++)
pseries.Points.AddXY(subgroup[i], phat[i]);
//Set the series type to a line.
pseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(pseries);
//Add the lines to the graph as annotations.
//PBAR line.
addLineAnnotation(output.ChartAreas[pseries.ChartArea].AxisX, output.ChartAreas[pseries.ChartArea].AxisY,
pseries.Points[0].XValue, pbar, pseries.Points[c - 1].XValue, 0, output);
//Add the UCL and LCL line segments as individual line annotations.
for (int i = 0; i < c - 1; i++)
{
//UCL line.
addLineAnnotation(output.ChartAreas[pseries.ChartArea].AxisX, output.ChartAreas[pseries.ChartArea].AxisY,
subgroup[i], UCL[i], subgroup[i + 1] - subgroup[i], UCL[i + 1] - UCL[i], output);
//LCL line.
addLineAnnotation(output.ChartAreas[pseries.ChartArea].AxisX, output.ChartAreas[pseries.ChartArea].AxisY,
subgroup[i], LCL[i], subgroup[i + 1] - subgroup[i], LCL[i + 1] - LCL[i], output);
}
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("PBAR", output.ChartAreas[pseries.ChartArea].AxisX, output.ChartAreas[pseries.ChartArea].AxisY,
pseries.Points[0].XValue, pbar, output);
addTextAnnotation("UCL", output.ChartAreas[pseries.ChartArea].AxisX, output.ChartAreas[pseries.ChartArea].AxisY,
pseries.Points[0].XValue, UCL[0], output);
addTextAnnotation("LCL", output.ChartAreas[pseries.ChartArea].AxisX, output.ChartAreas[pseries.ChartArea].AxisY,
pseries.Points[0].XValue, LCL[0], output);
}
//Find the maximum UCL value and store the index of it.
int maxUCL = 0;
for (int i = 1; i < c; i++)
if (UCL[maxUCL] < UCL[i])
maxUCL = i;
//Scale the chart if the user has requested it.
FitChart(UCL[maxUCL], pseries.ChartArea, output);
//Return the series.
return pseries;
}
///
/// Creates an NP-Chart with lines indicating CL, UCL and LCL. An NP chart is the same as a P-chart except
/// that the number of items in each subgroup is the same.
///
/// An array holding the subgroups. Must aline with the nonconform array.
/// An array holding the non-conformity data associated with the subgroups. Must aline with the subgroups.
/// An array holding the number of items in each subgroup. Must aline with the subgroups and the nonconform array.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series npChart(float[] subgroup, float[] nonconform, int numbertested, Chart output)
{
//Assure the input all have the same number of items.
if (subgroup.Length != nonconform.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, nonconform, numbertested");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize UCL, LCL and pBar.
float UCL = 0;
float LCL = 0;
float pbar = 0;
//Calculate pbar.
for (int i = 0; i < c; i++)
{
pbar += nonconform[i];
}
pbar /= (c * numbertested);
//Calculate UCL and LCL.
UCL = (float)(numbertested * pbar + 3 * System.Math.Sqrt(numbertested * pbar * (1 - pbar)));
LCL = (float)(numbertested * pbar - 3 * System.Math.Sqrt(numbertested * pbar * (1 - pbar)));
//If LCL is less than zero, then it should be zero.
if (LCL < 0)
LCL = 0;
//Create the npseries
Series npseries = new Series("npseries");
//Graph subgroup vs. non-conforming values.
for (int i = 0; i < c; i++)
npseries.Points.AddXY(subgroup[i], nonconform[i]);
//Set the type to line series.
npseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(npseries);
//Add the lines to the graph as annotations.
//CL line.
addLineAnnotation(output.ChartAreas[npseries.ChartArea].AxisX, output.ChartAreas[npseries.ChartArea].AxisY,
npseries.Points[0].XValue, pbar * 100, npseries.Points[c - 1].XValue, 0, output);
//UCL line.
addLineAnnotation(output.ChartAreas[npseries.ChartArea].AxisX, output.ChartAreas[npseries.ChartArea].AxisY,
npseries.Points[0].XValue, UCL, npseries.Points[c - 1].XValue, 0, output);
//LCL line.
addLineAnnotation(output.ChartAreas[npseries.ChartArea].AxisX, output.ChartAreas[npseries.ChartArea].AxisY,
npseries.Points[0].XValue, LCL, npseries.Points[c - 1].XValue, 0, output);
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("PBAR", output.ChartAreas[npseries.ChartArea].AxisX, output.ChartAreas[npseries.ChartArea].AxisY,
npseries.Points[0].XValue, pbar * 100, output);
addTextAnnotation("UCL", output.ChartAreas[npseries.ChartArea].AxisX, output.ChartAreas[npseries.ChartArea].AxisY,
npseries.Points[0].XValue, UCL, output);
addTextAnnotation("LCL", output.ChartAreas[npseries.ChartArea].AxisX, output.ChartAreas[npseries.ChartArea].AxisY,
npseries.Points[0].XValue, LCL, output);
}
//Scale the chart if the user has requested it.
FitChart(UCL, npseries.ChartArea, output);
//Return the series.
return npseries;
}
///
/// Creates a U-Chart with UBar, UCL and LCL lines. A U-Chart is used when the desired chart
/// is that of the number of non-conformities per inspection unit, where the inspection unit is variable size.
///
/// An array holding the subgroups. Must aline with the nonconform array.
/// An array holding the non-conformity data associated with the subgroups. Must aline with the subgroups.
/// The number of items in each subgroup.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series uChart(float[] subgroup, float[] nonconform, float[] numbertested, Chart output)
{
//Assure the input all have the same number of items.
if ((subgroup.Length != nonconform.Length) || (subgroup.Length != numbertested.Length) || (nonconform.Length != numbertested.Length))
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, nonconform, numbertested");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize UCL, LCL and uBar.
float[] UCL = new float[c];
float[] LCL = new float[c];
float ubar = 0;
//Initialize temporary variables to hold temp sums for the ubar calculation
float sumNumerator = 0;
float sumDenominator = 0;
//Calculate ubar.
for (int i = 0; i < c; i++)
{
sumNumerator += nonconform[i];
sumDenominator += numbertested[i];
}
ubar = sumNumerator / sumDenominator;
//Calculate the Yvalues
float[] yvalues = new float[c];
for (int i = 0; i < c; i++)
yvalues[i] = nonconform[i] / numbertested[i];
//Calculate UCL and LCL.
for (int i = 0; i < c; i++)
{
UCL[i] = (float)(ubar + 3 * System.Math.Sqrt(ubar / numbertested[i]));
LCL[i] = (float)(ubar - 3 * System.Math.Sqrt(ubar / numbertested[i]));
}
//Create the series for the pchart data.
Series useries = new Series("useries");
//Graph subgroup vs. proportion.
for (int i = 0; i < c; i++)
useries.Points.AddXY(subgroup[i], yvalues[i]);
//Set the series type to a line.
useries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(useries);
//Add the lines to the graph as annotations.
//UBAR line.
addLineAnnotation(output.ChartAreas[useries.ChartArea].AxisX, output.ChartAreas[useries.ChartArea].AxisY,
useries.Points[0].XValue, ubar, useries.Points[c - 1].XValue, 0, output);
//Add the UCL and LCL line segments as individual line annotations.
for (int i = 0; i < c - 1; i++)
{
//UCL line.
addLineAnnotation(output.ChartAreas[useries.ChartArea].AxisX, output.ChartAreas[useries.ChartArea].AxisY,
subgroup[i], UCL[i], subgroup[i + 1] - subgroup[i], UCL[i + 1] - UCL[i], output);
//LCL line.
addLineAnnotation(output.ChartAreas[useries.ChartArea].AxisX, output.ChartAreas[useries.ChartArea].AxisY,
subgroup[i], LCL[i], subgroup[i + 1] - subgroup[i], LCL[i + 1] - LCL[i], output);
}
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("UBAR", output.ChartAreas[useries.ChartArea].AxisX, output.ChartAreas[useries.ChartArea].AxisY,
useries.Points[0].XValue, ubar, output);
addTextAnnotation("UCL", output.ChartAreas[useries.ChartArea].AxisX, output.ChartAreas[useries.ChartArea].AxisY,
useries.Points[0].XValue, UCL[0], output);
addTextAnnotation("LCL", output.ChartAreas[useries.ChartArea].AxisX, output.ChartAreas[useries.ChartArea].AxisY,
useries.Points[0].XValue, LCL[0], output);
}
//Find the maximum UCL value and store the index of it.
int maxUCL = 0;
for (int i = 1; i < c; i++)
if (UCL[maxUCL] < UCL[i])
maxUCL = i;
//Scale the chart if the user has requested it.
FitChart(UCL[maxUCL], useries.ChartArea, output);
//Return the series.
return useries;
}
///
/// Creates a Run Chart of the data and adds an average line. A run chart is a plot of the data without
/// manipulation along with a line indicating where the average of the points is.
///
/// An array holding the subgroups. Must aline with the data array.
/// An array holding the data. Must aline with the subgroup array.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series runChart(float[] subgroup, float[] data, Chart output)
{
//Assure the input all have the same number of items.
if (subgroup.Length != data.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, data");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize a variable to hold the average.
float average = 0;
//Create the series for the runchart data.
Series runseries = new Series("runseries");
//Graph subgroups vs. data.
for (int i = 0; i < c; i++)
runseries.Points.AddXY(subgroup[i], data[i]);
//Set the series type to a line.
runseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(runseries);
//Calculate the average.
average = (float)output.DataManipulator.Statistics.Mean("runseries");
//Add the lines to the graph as annotations.
//average line.
addLineAnnotation(output.ChartAreas[runseries.ChartArea].AxisX, output.ChartAreas[runseries.ChartArea].AxisY,
runseries.Points[0].XValue, average, runseries.Points[c - 1].XValue, 0, output);
//Return the series.
return runseries;
}
///
/// Creates an S-Chart of prepared data that can be evaluated. If the data is deemed to be in statistical control
/// an XBAR chart can be created via XBARChart function.
///
/// An array holding the subgroups. Must aline with the data array. There must be at least 20 subgroups.
/// Array holding the Standard Deviation of each subgroup.
/// Number of measurements per subgroup. Must be between 2 and 9.
/// Variable returning the process standard deviation estimation.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series sChart(float[] subgroup, float[] data, int n, out float processStdDev, Chart output)
{
//Assure the input all have the same number of items.
if (subgroup.Length != data.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, data");
//Assure there are at least 20 subgroups
if (subgroup.Length < 20)
throw new ArgumentOutOfRangeException("There must be at least 20 subgroups.", "subgroup");
//Assure n is between 2 and 9
if (n < 2 || n > 9)
throw new ArgumentOutOfRangeException("n must be between 1 and 9.", "n");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize a variable to hold sbar, UCL and LCL.
float sbar = 0;
float UCL = 0;
float LCL = 0;
//Declare the constants needed for calculations.
float[] B3 ={ 0, 0, 0, 0, 0.03F, 0.118F, 0.185F, 0.239F };
float[] B4 ={ 3.267F, 2.568F, 2.266F, 2.089F, 1.970F, 1.882F, 1.815F, 1.761F };
float[] C4 ={ 0.7979F, 0.8862F, 0.9213F, 0.9400F, 0.9515F, 0.9594F, 0.9650F, 0.9693F };
//Calculate sbar which is the center line of the standard deviation of each subgroup.
for (int i = 0; i < c; i++)
sbar += data[i];
sbar /= c;
//Calculate UCL and LCL.
//It is n-2 as the number is between 2 and 9, but the array starts at 0.
UCL = B4[n - 2] * sbar;
LCL = B3[n - 2] * sbar;
//If LCL is less than zero, then it should be zero.
if (LCL < 0)
LCL = 0;
//Create the series for the sChart data.
Series sseries = new Series("sseries");
//Graph subgroups vs. data.
for (int i = 0; i < c; i++)
sseries.Points.AddXY(subgroup[i], data[i]);
//Set the series type to a line.
sseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(sseries);
//Add the lines to the graph as annotations.
//sbar line.
addLineAnnotation(output.ChartAreas[sseries.ChartArea].AxisX, output.ChartAreas[sseries.ChartArea].AxisY,
sseries.Points[0].XValue, sbar, sseries.Points[c - 1].XValue, 0, output);
//UCL line.
addLineAnnotation(output.ChartAreas[sseries.ChartArea].AxisX, output.ChartAreas[sseries.ChartArea].AxisY,
sseries.Points[0].XValue, UCL, sseries.Points[c - 1].XValue, 0, output);
//LCL line.
addLineAnnotation(output.ChartAreas[sseries.ChartArea].AxisX, output.ChartAreas[sseries.ChartArea].AxisY,
sseries.Points[0].XValue, LCL, sseries.Points[c - 1].XValue, 0, output);
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("SBAR", output.ChartAreas[sseries.ChartArea].AxisX, output.ChartAreas[sseries.ChartArea].AxisY,
sseries.Points[0].XValue, sbar, output);
addTextAnnotation("UCL", output.ChartAreas[sseries.ChartArea].AxisX, output.ChartAreas[sseries.ChartArea].AxisY,
sseries.Points[0].XValue, UCL, output);
addTextAnnotation("LCL", output.ChartAreas[sseries.ChartArea].AxisX, output.ChartAreas[sseries.ChartArea].AxisY,
sseries.Points[0].XValue, LCL, output);
}
//Scale the chart if the user has requested it.
FitChart(UCL, sseries.ChartArea, output);
//Calculate process standard deviation.
processStdDev = (float)((sbar / C4[n - 2]) * System.Math.Sqrt(1 - System.Math.Pow(C4[n - 2], 2)));
//Set the sBar class variable and clear rBar.
this.mysBar = sbar;
this.myrBar = 0;
//Store the n value for use by XBAR.
this.mynValue = n;
//Return the series.
return sseries;
}
///
/// Creates an R-Chart of prepared data that can be evaluated. If the data is deemed to be in statistical control
/// an XBAR chart can be created via XBARChart function.
///
/// An array holding the subgroups. Must aline with the data array. There must be at least 20 subgroups.
/// Array holding the Range (biggest value - smallest value) of each subgroup.
/// Number of measurements per subgroup. Must be between 2 and 9.
/// Variable returning the process standard deviation estimation.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series rChart(float[] subgroup, float[] data, int n, out float processStdDev, Chart output)
{
//Assure the input all have the same number of items.
if (subgroup.Length != data.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, data");
//Assure there are at least 20 subgroups
if (subgroup.Length < 20)
throw new ArgumentOutOfRangeException("There must be at least 20 subgroups.", "subgroup");
//Assure n is between 2 and 9
if (n < 2 || n > 9)
throw new ArgumentOutOfRangeException("n must be between 1 and 9.", "n");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize a variable to hold sbar, UCL and LCL.
float rbar = 0;
float UCL = 0;
float LCL = 0;
//Declare the constants needed for calculations.
float[] D3 ={ 0, 0, 0, 0, 0, 0.076F, 0.136F, 0.184F };
float[] D4 ={ 3.267F, 2.574F, 2.282F, 2.114F, 2.004F, 1.924F, 1.864F, 1.816F };
float[] D2 ={ 1.128F, 1.693F, 2.059F, 2.326F, 2.534F, 2.704F, 2.847F, 2.970F };
//Calculate rbar which is the center line of the range of each subgroup.
for (int i = 0; i < c; i++)
rbar += data[i];
rbar /= c;
//Calculate UCL and LCL.
//It is n-2 as the number is between 2 and 9, but the array starts at 0.
UCL = D4[n - 2] * rbar;
LCL = D3[n - 2] * rbar;
//If LCL is less than zero, then it should be zero.
if (LCL < 0)
LCL = 0;
//Create the series for the sChart data.
Series rseries = new Series("rseries");
//Graph subgroups vs. data.
for (int i = 0; i < c; i++)
rseries.Points.AddXY(subgroup[i], data[i]);
//Set the series type to a line.
rseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(rseries);
//Add the lines to the graph as annotations.
//sbar line.
addLineAnnotation(output.ChartAreas[rseries.ChartArea].AxisX, output.ChartAreas[rseries.ChartArea].AxisY,
rseries.Points[0].XValue, rbar, rseries.Points[c - 1].XValue, 0, output);
//UCL line.
addLineAnnotation(output.ChartAreas[rseries.ChartArea].AxisX, output.ChartAreas[rseries.ChartArea].AxisY,
rseries.Points[0].XValue, UCL, rseries.Points[c - 1].XValue, 0, output);
//LCL line.
addLineAnnotation(output.ChartAreas[rseries.ChartArea].AxisX, output.ChartAreas[rseries.ChartArea].AxisY,
rseries.Points[0].XValue, LCL, rseries.Points[c - 1].XValue, 0, output);
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("RBAR", output.ChartAreas[rseries.ChartArea].AxisX, output.ChartAreas[rseries.ChartArea].AxisY,
rseries.Points[0].XValue, rbar, output);
addTextAnnotation("UCL", output.ChartAreas[rseries.ChartArea].AxisX, output.ChartAreas[rseries.ChartArea].AxisY,
rseries.Points[0].XValue, UCL, output);
addTextAnnotation("LCL", output.ChartAreas[rseries.ChartArea].AxisX, output.ChartAreas[rseries.ChartArea].AxisY,
rseries.Points[0].XValue, LCL, output);
}
//Scale the chart if the user has requested it.
FitChart(UCL, rseries.ChartArea, output);
//Calculate process standard deviation.
processStdDev = (float)(rbar / D2[n - 2]);
//Set the rBar class variable and clear sBar.
this.myrBar = rbar;
this.mysBar = 0;
//Store the n value for use by XBAR.
this.mynValue = n;
//Return the series.
return rseries;
}
///
/// Creates an XBAR chart of the prepared data. sChart or rChart must have already been called to create a XBAR chart.
///
/// An array holding the subgroups. Must aline with the data array.
/// Array holding the Mean of each subgroup. Must aline with the subgroup array.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series XBARChart(float[] subgroup, float[] data, Chart output)
{
//Call the private function with no MRBAR to graph a separate XBAR chart.
return XBARChartImplementation(subgroup, data, output, 0);
}
///
/// Creates an Individuals Chart of the data and places the MRBar, XBAR, UCL and LCL upon it.
///
/// An array holding the subgroups. Must aline with the data array.
/// An array holding the data. Must aline with the subgroup array.
/// Variable returning the process standard deviation estimation.
/// The chart which will have the created series added to it.
/// Series which has been created and added to the output chart.
public Series individualsChart(float[] subgroup, float[] data, out float processStdDev, Chart output)
{
//Assure the input all have the same number of items.
if (subgroup.Length != data.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, data");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize MRBAR.
float mrbar = 0;
//Calculate cbar.
for (int i = 0; i < c-1; i++)
{
mrbar += (float)System.Math.Abs(data[i + 1] - data[i]);
}
mrbar /= c - 1;
//Calculate the process standard deviation.
processStdDev = (float)(mrbar / 1.128);
//Return the series
return XBARChartImplementation(subgroup, data, output, mrbar);
}
///
/// Creates an XBAR chart of the prepared data. This function contains the implementation for the public function with one extra
/// parameter for MRBAR. If MRBAR is specified, then only one chart is created with all the values.
///
/// An array holding the subgroups. Must aline with the data array.
/// Array holding the Mean of each subgroup. Must aline with the subgroup array.
/// The chart which will have the created series added to it.
/// The value of MRBAR if it has been calculated by Individuals Chart.
/// Series which has been created and added to the output chart.
private Series XBARChartImplementation(float[] subgroup, float[] data, Chart output, float mrbar)
{
//Assure the input all have the same number of items.
if (subgroup.Length != data.Length)
throw new ArgumentException("Input arrays must all be the same length.", "subgroup, nonconform, numbertested");
//Assure that either rBar or sBar have a value, and n has a value. If MRBAR has been specified, do not check.
if (mrbar == 0)
if ((this.myrBar == 0 && this.mysBar == 0) || (this.mynValue < 2 || this.mynValue > 9))
throw new ArgumentException("sChart or rChart must be called before XBAR.");
//c holds the total number of items (subgroups).
int c = subgroup.Length;
//Declare and initialize a variable to hold sbar, UCL and LCL.
float xbar = 0;
float UCL = 0;
float LCL = 0;
//Declare the constants needed for calculations.
float[] A2 ={ 1.880F, 1.023F, 0.729F, 0.577F, 0.483F, 0.419F, 0.373F, 0.337F };
float[] A3 ={ 2.659F, 1.954F, 1.628F, 1.427F, 1.287F, 1.182F, 1.099F, 1.032F };
//Calculate xbar, which is the mean of all the subgroup means.
for (int i = 0; i < c; i++)
xbar += data[i];
xbar /= c;
//Calculate UCL and LCL.
//Switch based on if we're calculating from a rChart or a sChart, or if a MRBAR was given.
//It is n-2 as the number is between 2 and 9, but the array starts at 0
if (mrbar != 0)
{
UCL = (float)(xbar + 2.66 * mrbar);
LCL = (float)(xbar - 2.66 * mrbar);
}
else if (this.mysBar != 0)
{
UCL = xbar + A3[this.mynValue - 2] * this.mysBar;
LCL = xbar - A3[this.mynValue - 2] * this.mysBar;
}
else if (this.myrBar != 0)
{
UCL = xbar + A2[this.mynValue - 2] * this.myrBar;
LCL = xbar - A2[this.mynValue - 2] * this.myrBar;
}
//If LCL is less than zero, then it should be zero.
if (LCL < 0)
LCL = 0;
//Create the series for the xbar data.
Series xbarseries = new Series("xbarseries");
//Graph subgroups vs. data.
for (int i = 0; i < c; i++)
xbarseries.Points.AddXY(subgroup[i], data[i]);
//Set the series type to a line.
xbarseries.ChartType = SeriesChartType.Line;
//Add the series to the chart.
output.Series.Add(xbarseries);
//Add the lines to the graph as annotations.
//xbar line.
addLineAnnotation(output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, xbar, xbarseries.Points[c - 1].XValue, 0, output);
//UCL line.
addLineAnnotation(output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, UCL, xbarseries.Points[c - 1].XValue, 0, output);
//LCL line.
addLineAnnotation(output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, LCL, xbarseries.Points[c - 1].XValue, 0, output);
//MBAR line if it has been specified
if (mrbar != 0)
addLineAnnotation(output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, mrbar, xbarseries.Points[c - 1].XValue, 0, output);
//Add Text Annotations (line labels) if the user desired.
if (myShowLineLabels)
{
addTextAnnotation("XBAR", output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, xbar, output);
addTextAnnotation("UCL", output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, UCL, output);
addTextAnnotation("LCL", output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, LCL, output);
if (mrbar!=0)
addTextAnnotation("MRBAR", output.ChartAreas[xbarseries.ChartArea].AxisX, output.ChartAreas[xbarseries.ChartArea].AxisY,
xbarseries.Points[0].XValue, mrbar, output);
}
//Scale the chart if the user has requested it.
FitChart(UCL, xbarseries.ChartArea, output);
//Return the series.
return xbarseries;
}
#endregion
#region Math Methods
///
/// Calculates the mean of an array of numbers.
///
/// The input array of items of which the mean will be calculated.
/// The mean of the data.
public float Mean(float[] data)
{
//Create a temporary variable to hold the sum.
float sum = 0;
//Calculate the sum of the data items.
for (int i = 0; i < data.Length; i++)
sum += data[i];
//Divide them by the total number of items.
sum /= data.Length;
//Return the mean.
return sum;
}
///
/// Calculates the range of an array of numbers.
///
/// The input array of items of which the range will be calculated.
/// The range of the data.
public float Range(float[] data)
{
//Create two variables: one to hold the index of the largest value and one to hold the index of the smallest value.
int maxIndex = 0;
int minIndex = 0;
//Find the largest and smallest values in the array.
for (int i = 1; i < data.Length; i++)
{
if (data[maxIndex] < data[i])
maxIndex = i;
if (data[minIndex] > data[i])
minIndex = i;
}
//Return the Range.
return (data[maxIndex] - data[minIndex]);
}
///
/// Calculates the standard deviation of an array of numbers.
///
/// The input array of items of which the standard deviation will be calculated.
/// The standard deviation of the data.
public float StandardDeviation(float[] data)
{
//Create two variables: one to hold the sum of the deviations, and one to hold the mean of the data.
float sumdeviation = 0;
float mean = 0;
//Calculate the mean.
mean = Mean(data);
//Calculate the sum of the squared deviations.
for (int i = 0; i < data.Length; i++)
sumdeviation += (float)System.Math.Pow(data[i] - mean, 2);
//Return the standard deviation.
return (float)System.Math.Sqrt(sumdeviation / (data.Length - 1));
}
#endregion
#region Private Methods
///
/// Scales the chart so that the UCL line will always be within the chart.
///
/// The maximum UCL value.
/// The name of the chart area we are plotting to.
/// The chart which contains the chart area.
private void FitChart(float UCL, string ChartArea, Chart output)
{
//Check to see if scaling is enabled.
if (myAutoFitLines)
{
//Force the chartarea to recalculate the axis values so that we can find the Y-axis maximum.
output.ChartAreas[ChartArea].RecalculateAxesScale();
//Check if UCL is outside the maximum Y value.
if (UCL > (float)output.ChartAreas[ChartArea].AxisY.Maximum)
//If so set the Y-axis maximum to be the UCL value.
output.ChartAreas[ChartArea].AxisY.Maximum = UCL;
}
}
///
/// Function to add a line annotation to the desired chart in the format which is followed by
/// all the line annotations in this add-on.
///
/// X-Axis that the line annotation is to use for co-ordinates.
/// Y-Axis that the line annotation is to use for co-ordinates.
/// X value for the start of the line.
/// Y value for the start of the line.
/// Width of the line.
/// Height of the line.
/// The chart that the line annotation is being added to.
private void addLineAnnotation(Axis AxisX, Axis AxisY, double X, double Y, double Width, double Height, Chart output)
{
//Create a new line annotation.
LineAnnotation lineAnnotation = new LineAnnotation();
//Set each property to the parameters passed in.
lineAnnotation.AxisX = AxisX;
lineAnnotation.AxisY = AxisY;
lineAnnotation.Y = Y;
lineAnnotation.X = X;
lineAnnotation.Height = Height;
lineAnnotation.Width = Width;
//Turn off relative size to get graph-oriented co-ordinates and set the line color.
lineAnnotation.IsSizeAlwaysRelative = false;
lineAnnotation.LineColor = myLineColor;
//Add the annotation to the chart.
output.Annotations.Add(lineAnnotation);
}
///
/// Function to add text as an annotation to the chart.
///
/// The text that we wish to display.
/// X-Axis that the text annotation is to use for co-ordinates.
/// Y-Axis that the text annotation is to use for co-ordinates.
/// X value for the start of the text.
/// Y value for the start of the text.
/// The chart that the line annotation is being added to.
private void addTextAnnotation(string text, Axis AxisX, Axis AxisY, double X, double Y, Chart output)
{
//Create a new line annotation.
TextAnnotation textAnnotation = new TextAnnotation();
//Set each property to the parameters passed in.
textAnnotation.AxisX = AxisX;
textAnnotation.AxisY = AxisY;
textAnnotation.Y = Y;
textAnnotation.X = X;
textAnnotation.Text = text;
//Turn off relative size to get graph-oriented co-ordinates and set the aesthetic properties.
textAnnotation.IsSizeAlwaysRelative = false;
textAnnotation.ForeColor = myForeColor;
textAnnotation.Font = myFont;
//Add the annotation to the chart.
output.Annotations.Add(textAnnotation);
}
#endregion
}
}