Pareto Chart C#
Introduction
The main purpose of this article is to create a simple Pareto Chart Control. There are a few third-party Pareto Charts available and also a Pareto Chart can be made using Microsoft Chart controls. My intent is to creata a simple Pareto Chart User Control. As a result, yes I have created a Pareto Chart user control using C#. First we start with a description of what a Pareto Chart is.
Pareto Chart
A Pareto Chart is a combination of both a Bar graph and a Line graph. In a Pareto Chart an X-Axis we will plot the Data Names. In the left Y-Axis we will plot the Data Frequency and in the right side Y-Axis we will plot the Data Cumulative Frequency Percentage. A Pareto Chart is used to graphically summarize and display the relative importance of the differences among groups of data.
The rules of a Pareto Chart are to start displaying the highest to the lowest frequency data as a Bar Graph. Display the Cumulative Frequency Percentage data as a Line Graph.
Here we can see some sample data.
S.No |
Name |
Freq |
Cum Freq |
Cum Per % |
1 |
Pareto1 |
53 |
53 |
19.27272727 |
2 |
pareto2 |
45 |
98 |
35.63636364 |
3 |
pareto3 |
43 |
141 |
51.27272727 |
4 |
pareto4 |
37 |
178 |
64.72727273 |
5 |
pareto5 |
35 |
213 |
77.45454545 |
6 |
pareto6 |
27 |
240 |
87.27272727 |
7 |
pareto7 |
23 |
263 |
95.63636364 |
8 |
pareto8 |
12 |
275 |
100 |
|
Sum |
275 |
|
|
The preceding sample data has been used in my program to display the result of the Pareto Chart. From the preceding data we can see.
Fre (Frequency)
Frequency is the total count of data of each sample. For example, a sample pareto1 has 53 data count and pareto2 has 45 and and so on.
Frequency = Total Count of Each Data
Cum Freq (Cumulative Frequency)
Cumulative Frequency is previous frequency plus current frequency. For example the first frequency is 53 so the Cumulative Frequency will be as 53. The next frequency is 45 so the Cumulative Frequency will be 45+53=98 and and so on.
Cumulative Frequency = previous Frequency + Current Frequency (for first data previous frequency will be as zero).
Cum per % (Cumulative Frequency Percentage)
Cumulative Frequency Percentage will be calculated by Cumulative Frequency divded by the Sum of Frequency and multiplied by 100. For example the Sum of Frequency is 275. The first Cumulative Frequency is 53 so 53/275*100=19.27.
Cumulative Frequency percentage =SUM of Frequency / Current Frequency *100
From the following image we can see the sample Excel file that has the preceding sample data with the result and my Pareto Control Chart with the result.
Now we start with our code.
I have created a Pareto Chart as a User Control so that it can be used easily in all projects.
In this article I have attached a Zip file named SHANUParetoChart.zip that contains:
1) "ShanuParetoChart_CTL" folder (containing the Pareto Chart User control Source code).
Note : I will pass the data to the user control as a DataTable. You can allow the same method or change my code as per your requirements.
The DataTable format is the first column as Serial Number. The second column as Data Name and the third column as Frequency. For my sample I will pass the total Data count for every sample as Frequency. The DataTable I have created like this .
- DataTable dt = new DataTable();
- dt.Columns.Add("No");
- dt.Columns.Add("Names");
- dt.Columns.Add("Frequency");
- dt.Columns["Frequency"].DataType = Type.GetType("System.Int32");
2) The "SHANUParetoChart_DEMO" folder (conaining the demo program that includes the paretoChart User Control with a random data sample using a Timer control).
Using the code
1) First we will start with the User Control. To create a user control:
1. Create a new Windows Control Library project.
2. Set the Name of Project and Click Ok (here my user control name is ShanuParetoChart_CTL).
3. Add all the controls that are needed.
4. In code behind declare all the public variables and public property variables.
- private System.Windows.Forms.PictureBox PicBox;
- private System.Windows.Forms.Panel OuterPanel;
- private System.ComponentModel.Container components = null;
- private string m_sPicName = "";
- ContextMenuStrip docmenu = new System.Windows.Forms.ContextMenuStrip();
- ToolStripMenuItem saveimg = new ToolStripMenuItem();
-
- int NoofPlots = 5;
-
- public DataTable dt = new DataTable();
-
- Font f12 = new Font("arial", 12, FontStyle.Bold, GraphicsUnit.Pixel);
- Pen B1pen = new Pen(Color.Black, 1);
- Pen B2pen = new Pen(Color.Black, 2);
-
-
- int[] intDataValue;
- int[] intCumulativeValue;
- Double[] intCumulativeValuePer;
- Double[] XaxisplotWidth;
-
- int First_chartDatarectHeight = 400;
- int First_chartDatarectWidth = 600;
- Font f10 = new Font("arial", 10, FontStyle.Bold, GraphicsUnit.Pixel);
- LinearGradientBrush a2 = new LinearGradientBrush(new RectangleF(0, 0, 100, 19), Color.DarkGreen, Color.Green, LinearGradientMode.Horizontal);
- LinearGradientBrush a3 = new LinearGradientBrush(new RectangleF(0, 0, 100, 19), Color.DarkRed, Color.Red, LinearGradientMode.Horizontal);
- LinearGradientBrush a1 = new LinearGradientBrush(new RectangleF(0, 0, 100, 19), Color.Blue, Color.DarkBlue, LinearGradientMode.Horizontal);
5. In Pictuerbox Paint Method I call a method to draw the Pareto Chart. I have used .NET GDI+ to draw both a Bar and Line Graph in Pareto Chart with the Datatable result. In the paint method I check for the Datatable Data and store the Frequency, Cumulative Frequency and Cumulative Frequency Percentage to an Array.
- public void PicBox_Paint(object sender, PaintEventArgs e)
- {
-
- int opacity = 58;
-
- e.Graphics.DrawString("SHANU Pareto CHART",
- new Font("Arial", 28),
- new SolidBrush(Color.FromArgb(opacity, Color.OrangeRed)),
- 20,
- 10);
- if (dt.Rows.Count <= 2)
- {
- return;
- }
-
- int NoofTrials = dt.Rows.Count;
- int NoofParts = dt.Columns.Count - 1;
- NoofPlots = NoofTrials;
-
-
- intDataValue = new int[NoofTrials];
- intCumulativeValue = new int[NoofTrials];
- intCumulativeValuePer = new Double[NoofTrials];
- if (dt.Rows.Count <= 2)
- {
- return;
- }
- int idataval = 0;
- dt.DefaultView.Sort = "Frequency DESC";
- dt = dt.DefaultView.ToTable();
-
-
- for (int iRow = 0; iRow < dt.Rows.Count; iRow++)
- {
- intDataValue[idataval] = System.Convert.ToInt32(dt.Rows[iRow][2].ToString());
- idataval = idataval + 1;
-
- }
- int sumofFrequence = intDataValue.Sum();
- intCumulativeValue[0] = intDataValue[0];
- intCumulativeValuePer[0] = Convert.ToDouble(intCumulativeValue[0]) / Convert.ToDouble(sumofFrequence) * 100;
- for (int ival = 1; ival < intDataValue.Length; ival++)
- {
-
- intCumulativeValue[ival] = intCumulativeValue[ival - 1] + intDataValue[ival];
- intCumulativeValuePer[ival] = Convert.ToDouble(intCumulativeValue[ival]) / Convert.ToDouble(sumofFrequence) * 100;
-
- }
-
- drawPareto(e.Graphics, sumofFrequence);
-
- }
In the "drawpareto" function chek all data and draw the X-axis, Y-Axis with both frequency and cumulative frequency percentage. Read all the sample frequency data and draw a bar graph using a draw and fill rectangle method.
- public void drawPareto(Graphics e, int sumofFrequence)
- {
- try
- {
- First_chartDatarectHeight = PicBox.Height - 20;
- First_chartDatarectWidth = PicBox.Width - 20;
-
-
-
- e.DrawRectangle(Pens.Black, 10, 10, First_chartDatarectWidth, First_chartDatarectHeight);
-
- int historgramHeigt = First_chartDatarectHeight - 30;
- int historgramWidth = First_chartDatarectWidth - 20;
- int StartXval = 80;
- int startyval = 60;
-
-
- e.DrawLine(B1pen, StartXval, historgramHeigt, historgramWidth, historgramHeigt);
-
-
- e.DrawLine(B1pen, StartXval, startyval, StartXval, historgramHeigt);
-
-
- e.DrawLine(B1pen, historgramWidth, startyval, historgramWidth, historgramHeigt);
-
-
-
- int widthcalculation = (historgramWidth) / NoofPlots + 1;
- if (widthcalculation <= StartXval)
- {
- widthcalculation = StartXval;
- }
- int XvalPosition = widthcalculation;
-
- widthcalculation = widthcalculation - 18;
-
- String[] Xaxisplotdata = new String[NoofPlots];
-
- XaxisplotWidth = new Double[NoofPlots];
-
- RectangleF rectF1 = new RectangleF(30, 10, 100, 122);
-
-
-
- Double yValmaxDataVal = intDataValue.Max();
- Double yValminDataVal = intDataValue.Min();
- Double yValresultMaxMin = yValmaxDataVal - yValminDataVal;
-
-
- Double yValMeasurementYAxisValue = yValmaxDataVal / NoofPlots;
- int yValheightcalculation = historgramHeigt / NoofPlots;
- int yValXvalPosition = yValheightcalculation;
-
- Double plotYvals = yValmaxDataVal + 4;
-
- Double[] YaxisplotHight = new Double[NoofPlots];
- Double[] YaxisplotData = new Double[NoofPlots];
-
- for (int ival = 0; ival < NoofPlots; ival++)
- {
-
- String MeasurementXAxisValue = dt.Rows[ival][1].ToString();
- Xaxisplotdata[ival] = MeasurementXAxisValue;
- e.DrawLine(B1pen, XvalPosition, historgramHeigt, XvalPosition, historgramHeigt + 15);
- rectF1 = new RectangleF(XvalPosition, historgramHeigt + 6, widthcalculation, 34);
- e.DrawString(MeasurementXAxisValue.ToString(), f10, a2, rectF1);
-
-
- XaxisplotWidth[ival] = XvalPosition;
- XvalPosition = XvalPosition + widthcalculation;
-
-
-
-
- if (ival == NoofPlots - 1)
- {
- e.DrawString("0", f10, a2, StartXval - 12, yValXvalPosition + 4);
- }
- else
- {
- e.DrawLine(B1pen, StartXval - 10, yValXvalPosition, StartXval, yValXvalPosition);
- e.DrawString(Math.Round(plotYvals, 0).ToString(), f10, a2, StartXval - 20, yValXvalPosition + 4);
- }
-
-
-
-
-
- YaxisplotData[ival] = plotYvals;
- plotYvals = plotYvals - yValMeasurementYAxisValue;
-
- YaxisplotHight[ival] = yValXvalPosition;
- yValXvalPosition = yValXvalPosition + yValheightcalculation;
-
-
-
- }
-
- int widthcalculation_new = historgramWidth / NoofPlots;
-
- int XvalPosition_Start = Convert.ToInt32(XaxisplotWidth[0]);
-
- int XvalPosition_new = Convert.ToInt32(XaxisplotWidth[1]) - XvalPosition_Start;
-
- widthcalculation = widthcalculation - 18;
-
- int Ystartval = 100;
- int YEndval = 100;
-
- LinearGradientBrush a6;
-
- Font f2 = new Font("arial", 12, FontStyle.Bold, GraphicsUnit.Pixel);
- for (int ival = 0; ival < YaxisplotData.Length - 1; ival++)
- {
- for (int yval = 1; yval < YaxisplotData.Length - 1; yval++)
- {
-
- Double finaldisplayvalue = YaxisplotHight[yval - 1] + ((YaxisplotData[yval - 1] - intDataValue[ival]) * 6);
-
-
- if (intDataValue[ival] <= YaxisplotData[yval - 1] && intDataValue[ival] > YaxisplotData[yval])
- {
-
- Ystartval = Convert.ToInt32(finaldisplayvalue);
- YEndval = historgramHeigt - Convert.ToInt32(finaldisplayvalue);
- }
-
- else if (intDataValue[ival] <= YaxisplotData[yval - 1] && intDataValue[ival] < YaxisplotData[yval])
- {
-
-
- Ystartval = Convert.ToInt32(finaldisplayvalue);
- YEndval = historgramHeigt - Convert.ToInt32(finaldisplayvalue);
- }
- }
-
- if (YEndval > 2)
- {
- }
- else
- {
- Ystartval = historgramHeigt - 2;
- YEndval = 2;
- }
- a6 = new LinearGradientBrush(new RectangleF(StartXval, Ystartval, XvalPosition_new, YEndval), Color.LightBlue, Color.CornflowerBlue, LinearGradientMode.Vertical);
-
- XvalPosition_Start = Convert.ToInt32(XaxisplotWidth[ival]);
- e.DrawRectangle(B1pen, XvalPosition_Start, Ystartval, XvalPosition_new, YEndval);
- e.FillRectangle(a6, XvalPosition_Start, Ystartval - 1, XvalPosition_new - 1, YEndval - 1);
- e.DrawString(intDataValue[ival].ToString(), f2, a2, XvalPosition_Start + 10, Ystartval - 20);
-
-
-
- XvalPosition_new = Convert.ToInt32(XaxisplotWidth[ival + 1]) - XvalPosition_Start;
-
-
- if (ival == YaxisplotData.Length - 2)
- {
- for (int yval = 1; yval < YaxisplotData.Length - 1; yval++)
- {
- if (intDataValue[ival + 1] <= YaxisplotData[yval - 1] && intDataValue[ival] > YaxisplotData[yval])
- {
- Double finaldisplayvalue = YaxisplotHight[yval - 1] + ((YaxisplotData[yval - 1] - intDataValue[ival + 1]) * 6);
-
- Ystartval = Convert.ToInt32(finaldisplayvalue);
- YEndval = historgramHeigt - Convert.ToInt32(finaldisplayvalue);
- }
- else if (intDataValue[ival + 1] <= YaxisplotData[yval - 1] && intDataValue[ival + 1] < YaxisplotData[yval])
- {
- Double finaldisplayvalue = YaxisplotHight[yval - 1] + ((YaxisplotData[yval - 1] - intDataValue[ival + 1]) * 6);
-
- Ystartval = Convert.ToInt32(finaldisplayvalue);
- YEndval = historgramHeigt - Convert.ToInt32(finaldisplayvalue);
- }
- }
-
- if (YEndval > 2)
- {
- }
- else
- {
- Ystartval = historgramHeigt - 2;
- YEndval = 2;
- }
-
-
- XvalPosition_Start = Convert.ToInt32(XaxisplotWidth[ival + 1]);
-
- if (XvalPosition_Start + XvalPosition_new > historgramWidth)
- {
- XvalPosition_new = XvalPosition_new - (XvalPosition_Start + XvalPosition_new - historgramWidth);
- }
-
-
-
- e.DrawRectangle(B1pen, XvalPosition_Start, Ystartval, XvalPosition_new, YEndval);
- e.FillRectangle(a6, XvalPosition_Start, Ystartval - 1, XvalPosition_new - 1, YEndval - 1);
- e.DrawString(intDataValue[ival + 1].ToString(), f2, a2, XvalPosition_Start + 10, Ystartval - 20);
- }
-
-
- }
-
-
- drawParetoCurveLine(e);
-
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.ToString());
- }
- }
The same as in the preceding function I check for all the cumulative frequency percentage data and draw a line graph using the drawline and fillpie methods.
- public void drawParetoCurveLine(Graphics e)
- {
-
- First_chartDatarectHeight = PicBox.Height - 20;
- First_chartDatarectWidth = PicBox.Width - 20;
-
-
- int historgramHeigt = First_chartDatarectHeight - 30;
- int historgramWidth = First_chartDatarectWidth - 20;
- int StartXval = historgramWidth + 12;
- int startyval = 60;
-
- Double yValmaxDataVal = 100;
- Double yValminDataVal = 0;
- Double yValresultMaxMin = yValmaxDataVal - yValminDataVal;
- int NoofPlots = 5;
- Double yValMeasurementYAxisValue = yValmaxDataVal / NoofPlots;
- int yValheightcalculation = historgramHeigt / NoofPlots;
- int yValXvalPosition = startyval;
-
- Double plotYvals = yValmaxDataVal;
-
- Double[] YaxisplotHight = new Double[NoofPlots];
- Double[] YaxisplotData = new Double[NoofPlots];
-
- for (int ival = 0; ival <= NoofPlots - 1; ival++)
- {
-
- if (ival == NoofPlots)
- {
- e.DrawString("0", f10, a2, StartXval - 12, yValXvalPosition + 4);
- }
- else
- {
- e.DrawLine(B1pen, StartXval - 10, yValXvalPosition, StartXval, yValXvalPosition);
- e.DrawString(Math.Round(plotYvals, 0).ToString() + "%", f10, a2, StartXval - 11, yValXvalPosition + 4);
- }
-
- YaxisplotData[ival] = plotYvals;
- plotYvals = plotYvals - yValMeasurementYAxisValue;
-
- YaxisplotHight[ival] = yValXvalPosition;
- yValXvalPosition = yValXvalPosition + yValheightcalculation;
-
-
- }
-
-
- SolidBrush brush = new SolidBrush(Color.Aquamarine);
- Random rnd = new Random();
-
- NoofPlots = intCumulativeValuePer.Length;
-
- int widthcalculation_new = historgramWidth / NoofPlots;
-
- int XvalPosition_Start = Convert.ToInt32(XaxisplotWidth[0]);
-
- int XvalPosition_new = Convert.ToInt32(XaxisplotWidth[1]) - XvalPosition_Start;
-
-
-
- int Ystartval = 100;
- int YEndval = 100;
-
- LinearGradientBrush a6 = new LinearGradientBrush(new RectangleF(StartXval, Ystartval, widthcalculation_new - StartXval, YEndval), Color.GreenYellow, Color.Green, LinearGradientMode.Vertical);
- brush = new SolidBrush(Color.Aquamarine);
- Pen pen = new Pen(Color.Gray);
- Font f2 = new Font("arial", 12, FontStyle.Bold, GraphicsUnit.Pixel);
- Point p1 = new Point();
- Point p2 = new Point();
- int smallLength = (historgramWidth / (intCumulativeValuePer.Length + 1));
- Double smallHeight = 0;
-
- for (int i = 0; i < intCumulativeValuePer.Length - 1; i++)
- {
-
-
- brush.Color = Color.FromArgb(rnd.Next(200, 255), rnd.Next(255),
- rnd.Next(255), rnd.Next(255));
- p1 = p2;
-
- if (i == 0)
- {
- p2.X = p2.X + smallLength + 40;
- }
- else
- {
- p2.X = p2.X + smallLength + 14;
- }
-
- smallHeight = YaxisplotHight[YaxisplotHight.Length - 1] + ((YaxisplotData[YaxisplotHight.Length - 1] - intCumulativeValuePer[i]) * 2);
-
-
- for (int yval = 1; yval < YaxisplotData.Length; yval++)
- {
-
-
-
- Double finaldisplayvalue = YaxisplotHight[yval] + ((YaxisplotData[yval] - intCumulativeValuePer[i]) * 2);
- if (intCumulativeValuePer[i] <= YaxisplotData[yval - 1] && intCumulativeValuePer[i] > YaxisplotData[yval])
- {
-
- Ystartval = Convert.ToInt32(finaldisplayvalue);
- smallHeight = Convert.ToInt32(finaldisplayvalue);
- }
-
-
-
- }
-
-
- p2.Y = Convert.ToInt32(smallHeight);
- if (p1.X != 0 && p1.Y != 0)
- {
- e.DrawLine(pen, p1, p2);
-
- }
- Color pointColor = new Color();
- pointColor = Color.Green;
-
-
- DrawDots(e, p2, pointColor);
- e.DrawString(Math.Round(intCumulativeValuePer[i], 2).ToString(), f2, a2, p2.X, p2.Y - 18);
-
- if (i == 0)
- {
- smallLength = smallLength - 15;
- }
-
- if (i == intCumulativeValuePer.Length - 2)
- {
- p1.X = p2.X;
- p1.Y = p2.Y;
- for (int yval = 1; yval < YaxisplotData.Length; yval++)
- {
-
-
-
- Double finaldisplayvalue = YaxisplotHight[yval] + ((YaxisplotData[yval] - intCumulativeValuePer[i + 1]) * 3);
- if (intCumulativeValuePer[i + 1] <= YaxisplotData[yval - 1] && intCumulativeValuePer[i + 1] > YaxisplotData[yval])
- {
-
- Ystartval = Convert.ToInt32(finaldisplayvalue);
- smallHeight = Convert.ToInt32(finaldisplayvalue);
- }
-
-
- }
-
- p2.Y = Convert.ToInt32(smallHeight);
- if (p1.X != 0 && p1.Y != 0)
- {
-
-
- p2.X = p2.X + smallLength + 14;
- e.DrawLine(pen, p1, p2);
-
- }
- DrawDots(e, p2, pointColor);
- e.DrawString(Math.Round(intCumulativeValuePer[i + 1], 2).ToString(), f2, a2, p2.X, p2.Y - 18);
- } } }
I have added a user friendly feature to save my Pareto Chart as an image. The user can save the Pareto Chart as an image by double-clicking on the Pareto Chart control or by right-clicking and save.
- private void PicBox_DoubleClick(object sender, EventArgs e)
- {
- saveImages();
- }
-
- private void docmenu_Click(object sender, EventArgs e)
- {
- saveImages();
- }
-
- public void saveImages()
- {
- if (dt.Rows.Count <= 0)
- {
- return;
- }
-
- using (var bitmap = new Bitmap(PicBox.Width, PicBox.Height))
- {
- PicBox.DrawToBitmap(bitmap, PicBox.ClientRectangle);
-
-
- SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = "*";
- dlg.DefaultExt = "bmp";
- dlg.ValidateNames = true;
-
- dlg.Filter = "Bitmap Image (.bmp)|*.bmp|Gif Image (.gif)|*.gif|JPEG Image (.jpeg)|*.jpeg|Png Image (.png)|*.png";
- if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
- {
-
- bitmap.Save(dlg.FileName);
- }}
- }
6. After completion save, build and run the project.
2) Now we create a Windows application and test our "ShanuParetoChart_CTL" User Control.
1. Create a new Windows project.
2. Open your form and then from Toolbox right-click then choose items then select your user control DLL and add.
3. Drag the User Control to your Windows form.
4. Create a DataTable and pass the Datatable to our User Control.
- public void loadGridColums()
- {
- dt.Columns.Add("No");
- dt.Columns.Add("Names");
- dt.Columns.Add("Frequency");
- dt.Columns["Frequency"].DataType = Type.GetType("System.Int32");
- }
5.In my demo program I used the Timer for the random sample data. I have used the "button1" as a Toggle button. When clicking Enabled the first time and starting the Timer and the same button is clicked again it stops the Timer. When the timer is started I have generated a random number and pass the different data to the user control and check for the chart result.
- private void frmShanuPaerto_Load(object sender, EventArgs e)
- {
- loadgrid(1);
- shanuParetoChart.Bindgrid(dt);
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- if (button1.Text == "Real Time Data ON")
- {
- timer1.Enabled = true;
- timer1.Start();
- button1.Text = "Real Time Data Off";
- }
- else
- {
- timer1.Enabled = false;
- timer1.Stop();
- button1.Text = "Real Time Data ON";
- }
- }
-
- private void timer1_Tick(object sender, EventArgs e)
- {
- loadgrid(2);
- shanuParetoChart.Bindgrid(dt);
- }
Conclusion
The main purpose of this article is to create a simple user friendly Pareto Chart Control that will be useful for many users to use and use for free in their projects. I hope my Pareto Chart control will be useful for many users from now on.
If you like my article and Pareto Chart Control kindly leave me a comment.
Reference links:
http://www.wikihow.com/Create-a-Pareto-Chart-in-MS-Excel-2010
http://asq.org/learn-about-quality/cause-analysis-tools/overview/pareto.html
http://whatis.techtarget.com/definition/Pareto-chart-Pareto-distribution-diagram
History
Initial release on 2014/08/01.