//================================================================= // 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 } }