Draw a normal distribution curve in C#

[normal distribution]

A normal distribution is a probability distribution that occurs arise when you analyze seemingly “random” occurrences. You can define a normal distribution in terms of its mean and standard deviation.

Mean (μ)

This is the basically the average of the values in the data set.

Standard deviation (σ)

This is a measurement of how tall and peaked or short and wide the curve it.

Its is also helpful to define the curve’s variance, which is simply σ2. With those three values defined, you can graph a normal distribution by using the following equation.

[normal distribution]

The notation F(x|μ,σ) means you’re calculating a function of x given fixed values for μ and σ.

The following code shows the function used by the example.

// The normal distribution function.
private float F(float x, float one_over_2pi, float mean,
    float stddev, float var)
    return (float)(one_over_2pi *
        Math.Exp(-(x - mean) * (x - mean) / (2 * var)));

This is simply a matter of plugging in the values for x, the mean, the standard deviation, and the variance.

That’s all there is to the example as far as calculating the distribution function is concerned. Most of the example’s code deals with drawing it. The code is fairly long so it’s presented in pieces below.

The following code shows how the program starts drawing the curve.

private void btnDraw_Click(object sender, EventArgs e)
    float mean = float.Parse(txtMean.Text);
    float stddev = float.Parse(txtStdDev.Text);
    float var = stddev * stddev;

    // Make a bitmap.
    Bitmap bm = new Bitmap(
    using (Graphics gr = Graphics.FromImage(bm))
        gr.SmoothingMode = SmoothingMode.AntiAlias;

        // Define the mapping from world
        // coordinates onto the PictureBox.
        const float wxmin = -5.1f;
        const float wymin = -0.2f;
        const float wxmax = -wxmin;
        const float wymax = 1.1f;
        const float wwid = wxmax - wxmin;
        const float whgt = wymax - wymin;
        RectangleF world = new RectangleF(wxmin, wymin, wwid, whgt);
        PointF[] device_points =
            new PointF(0, picGraph.ClientSize.Height),
            new PointF(picGraph.ClientSize.Width,
            new PointF(0, 0),
        Matrix transform = new Matrix(world, device_points);

This code gets the mean and standard deviation entered by the user. It calculates the variance.

Next the code creates a Bitmap to hold the drawing. It makes an associated Graphics object to do the drawing.

To make drawing easier, the program will draw in a coordinate system that’s natural for it, so the code defines constants to represent the world coordinates where it will draw. For this example, I used -5.1 ≤ x ≤ 5.1, -0.2 ≤ y ≤ 1.1. Those values work well for distributions with means near 0 and relatively small standard deviations.

Next the code creates a RectangleF defining the world coordinates and an array if PointF that indicates where the upper left, upper right, and lower left corners of the world coordinates should be mapped to fit on the Graphics object that will be used for drawing. It uses the RectangleF and array if PointF to make a Matrix representing a transformation from world coordinates onto a Graphics object. You’ll see how that’s used shortly.

The following code shows the next few steps.

        // Make a thin Pen to use.
        using (Pen pen = new Pen(Color.Red, 0))
            using (Font font = new Font("Arial", 8))
                // Draw the X axis.
                gr.Transform = transform;
                pen.Color = Color.Black;
                gr.DrawLine(pen, wxmin, 0, wxmax, 0);
                    for (int x = (int)wxmin; x <= wxmax; x++)
                    gr.DrawLine(pen, x, -0.05f, x, 0.05f);
                    gr.DrawLine(pen, x + 0.25f, -0.025f,
                                     x + 0.25f, 0.025f);
                    gr.DrawLine(pen, x + 0.50f, -0.025f,
                                     x + 0.50f, 0.025f);
                    gr.DrawLine(pen, x + 0.75f, -0.025f,
                                     x + 0.75f, 0.025f);

This code defines a Pen with thickness 0. When you use transformations, any lines you draw are also transformed. For this example, I don’t want the lines to be transformed because they would be stretched out of shape. In this case the world coordinates are 10.2 units wide so if I drew a line with thickness 1, it would be scaled so it was about 10% of the width of the drawing area (horizontally). For this example, the line would be almost 50 pixels wide.

If you use a Pen with thickness 0, the program draws it one pixel wide no matter how the Graphics object is transformed.

After defining the thin pen, the code creates a Font to use in labeling the axes. It then sets the Graphics object’s Transform property to the Matrix that maps from world coordinates to the Bitmap. After this, any drawing commands will be transformed appropriately.

The code then loops over some x values to draw the X axis.

The following code shows how the program labels the axis.

                // Label the X axis.
                gr.Transform = new Matrix();
                gr.TextRenderingHint =
                List<PointF> ints = new List<PointF>();
                for (int x = (int)wxmin; x <= wxmax; x++)
                    ints.Add(new PointF(x, -0.07f));
                PointF[] ints_array = ints.ToArray();

                using (StringFormat sf = new StringFormat())
                    sf.Alignment = StringAlignment.Center;
                    sf.LineAlignment = StringAlignment.Near;
                    int index = 0;
                    for (int x = (int)wxmin; x <= wxmax; x++)
                        gr.DrawString(x.ToString(), font,
                            ints_array[index++], sf);

If the program simply drew text at this point, it would be transformed by the Graphics object’s current transformation so the text would be stretched weirdly. To avoid that, the code resets the Graphics object’s Transform property to a new identity transformation. Any drawing after this point, including drawing text, will not be transformed.

Of course that means the program can’t use world coordinates to draw the text because they wouldn’t be mapped to the correct locations. To solve that problem the code creates an array of PointF holding the locations where it would like to draw the text. It then uses the transformation Matrix object’s TransformPoints method to transform the points into Graphics object coordinates.

Next the program creates a StringFormat object to align the text properly and loops through the points drawing X values at the appropriate positions.

The code that draws the Y axis is similar so it isn’t shown here.

The following code shows the rest of the Draw button’s event handler, which draws the distribution curve.

                // Draw the curve.
                gr.Transform = transform;
                List<PointF> points = new List<PointF>();
                float one_over_2pi = (float)(1.0 /
                    (stddev * Math.Sqrt(2 * Math.PI)));

                float dx = (wxmax - wxmin) /
                for (float x = wxmin; x <= wxmax; x += dx)
                    float y = F(x, one_over_2pi, mean, stddev, var);
                    points.Add(new PointF(x, y));
                pen.Color = Color.Red;
                gr.DrawLines(pen, points.ToArray());
            } // Font
        } // Pen

        picGraph.Image = bm;

This code makes a List<PointF>. It then divides the width of the world coordinates by the width of the output bitmap so it can plot a value with x equal to each pixel in the bitmap. It loops through those x values and uses the function F to adds points to the list. It finishes by converting the list into an array and drawing lines to connect the points.

Download Example   Follow me on Twitter   RSS feed   Donate

About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, drawing, graphics, mathematics and tagged , , , , , , , , , , , , , . Bookmark the permalink.

7 Responses to Draw a normal distribution curve in C#

  1. Pingback: Draw a scaled normal distribution in C# - C# HelperC# Helper

  2. manish kumar says:

    whay its not working with SD = 6336.17094374367 and mean = 29720.81

    • RodStephens says:

      This example is designed to work with small values near x = 0. Your mean is so far to the right that all of the values near x = 0 are extremely close to 0. In other words, it is drawing the graph, but you can’t see it because the Y values are around 0.000000001.

      The other problem is that the standard deviation is so large that the curve is basically very flat and short. The peak is around 0.000006 and spreads out very widely.

      You can get a reasonable curve if you do this:

      – Comment out the code that draws the axis tic marks
      – Comment out the code that labels the axes
      – Use the following code to set the world coordinate values:

      float wxmin = mean - 15000f;
      float wymin = -0.00001f;
      float wxmax = mean + 15000f;
      float wymax = 0.00007f;
      float wwid = wxmax - wxmin;
      float whgt = wymax - wymin;

      If you want tic marks and labels on the axes, you’ll need to figure out the spacing. For example, you might label values every 0.00001 along the Y axis and every 5,000 along the X axis.

  3. AYODEJI ABON says:

    what is the c# library for your picgraph class

    • RodStephens says:

      The picGraph control is a PictureBox on the form. This example only uses normal drawing methods, although it does use the following two non-typical namespaces:

      using System.Drawing.Drawing2D;
      using System.Drawing.Text;

      Download the example program to see all of the details.

  4. Cris says:

    First, thank you for your code, second, what’s the license for your program?

    Thank you

    • RodStephens says:

      Feel free to use any of the programs described on the site or in my books. I request but do not require an acknowledgement. I also recommend that you include the URL or other citation in your code so you can find it later if you need it.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.