Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interpolation implementation in MS Chart

I require to implement Interpolation and Extrapolation in Ms-Chart in Windows Application.

For Interpolation I am using "MathNet" Library. but still I am unaware to implement this.

I have try to implement the Interpolation as below.

using MathNet.Numerics.Interpolation.Algorithms;

NevillePolynomialInterpolation objIterpolate = new NevillePolynomialInterpolation(Xpoints, Ypoints);

    double NewYValue;
    NewYValue = Math.Abs(objIterpolate.Interpolate(newValue); 

I am passing in XPoints in NevillePolynomialInterpolation() as a First Parameter which is XValues array of my chart. and Ypoints as a YValues array of my chart.

I am passing newValue as a XValue for getting interpolated value.

Can anybody suggest, Is it right way or suggest the correct way to implement Interpolation.

like image 838
Nitin Vijay Avatar asked Nov 08 '12 05:11

Nitin Vijay


1 Answers

I managed to create a short example so let me know if my code I pasted below works for you.

I am not really used to MathDotNet library however the XML Documentation is pretty enough so the learning curve is not really steep just another .NET library among so many others.

Otherwise you can still go on the library website to have a look at their documentation, apart of couple of examples that I'm not sure cover the interpolation, you would probably find the same thing as you got by reading the XML documentation. You can also check the github and the see implementation of the interpolation you are looking to deal with.

Of course you can also try to implement from scratch if you stick to the algorithm described right here: http://en.wikipedia.org/wiki/Neville%27s_algorithm

Anyway I supposed you want to leverage MathDotNet library to perform a Neville polynomial interpolation and display the raw and interpolated data on the same Chart Area.

About additional information some can be found here (still do not expect that much):

  • MS Chart:

    • http://www.4guysfromrolla.com/articles/072209-1.aspx
    • https://code.msdn.microsoft.com/Samples-Environments-for-b01e9c61
    • https://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.chart(v=vs.110).aspx
  • MathDotNet: http://numerics.mathdotnet.com/Regression.html

About the MS Chart, it's all about like dealing with any other Winforms Control, just check the documentation, if there is something tricky point out what is hard for you and I will try to make it clear for you.

So far and to be completely honest I am struggling a bit about what you do not understand, is it the MS Chart, MathDotNet, both? Which one is a problem for you?

Anyway there is nothing really fancy, just passing your X and Y Points to the MathDotNet library (as long as the underlying implementations of Xs and Ys are implementing IEnumerable<T> like the arrays T[] it's fine).

Then the library is doing all the maths for you and you just have to use the Interpolate(...) methods of the Interpolation given (you have to understand that Interpolation here means a kind of Interpolation Engine, sort of).

I assumed that in your code snippet: XPoints and YPoints are both IEnumerable<T> collections (since you mentionned they are arrays) where T is a type of Double, Single or whatever the sort of .NET Number Primitive that fits you well.

// Copyright: Nothing At All License
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using MathNet.Numerics.Random;

namespace HelpSO
{
    public static class Program
    {
        [STAThread]
        public static void Main(params String[] arguments)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            var mainForm = new MainForm();

            Application.Run(mainForm);
        }
    }

    /// <summary>
    /// Main Form.
    /// </summary>
    public class MainForm : Form
    {
        /// <summary>
        /// Initializes the chart and cosmetics, make-up, glamour, etc..
        /// </summary>
        /// <returns>The chart.</returns>
        private static Chart InitializeChart()
        {
            var chart = new Chart()
            {
                Dock = DockStyle.Fill,      
            };

            const String defaultChartAreaName = @"Default";
            const String defaultLegendName = @"Default";
            const String defaultTitleName = @"Default";

            var chartArea = chart.ChartAreas.Add(defaultChartAreaName);

            var labelFont = new Font(@"Tahoma", 8f);

            var axisX = chartArea.AxisX;
            var axisY = chartArea.AxisY;

            axisX.Title = @"X";
            axisY.Title = @"Y";

            axisX.LabelStyle.Format = axisX.LabelStyle.Format = "F4";

            axisX.TitleFont = axisY.TitleFont = labelFont;
            axisX.LabelStyle.Font = axisY.LabelStyle.Font = labelFont;

            axisX.TitleAlignment = axisY.TitleAlignment = StringAlignment.Far;
            axisX.MajorGrid.Enabled = axisY.MajorGrid.Enabled = true;
            axisX.MinorGrid.Enabled = axisY.MinorGrid.Enabled = true;
            axisX.MinorGrid.LineDashStyle = axisY.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
            axisX.MinorGrid.LineColor = axisY.MinorGrid.LineColor = Color.Gainsboro;

            var legend = chart.Legends.Add(defaultLegendName);
            legend.TitleSeparator = LegendSeparatorStyle.ThickGradientLine;
            legend.BorderColor = Color.Black;
            legend.Title = "Legend";

            var title = chart.Titles.Add(defaultTitleName);
            title.Text = @"My Awesome interpolated data";
            title.Font = new Font(title.Font.FontFamily, 12f);

            MainForm.InitializeChartSeries(chart);

            return chart;
        }

        /// <summary>
        /// Initializes the chart series and related data (raw and interpolated).
        /// </summary>
        /// <param name="chart">Chart.</param>
        private static void InitializeChartSeries(Chart chart)
        {
            const String rawDataSeriesName = @"Raw Data";
            const String interpolatedDataSeriesName = @"Interpolated Data";

            var rawDataSeries = chart.Series.Add(rawDataSeriesName);
            var interpolatedDataSeriesSeries = chart.Series.Add(interpolatedDataSeriesName);

            rawDataSeries.ChartType = SeriesChartType.FastLine;
            interpolatedDataSeriesSeries.ChartType = SeriesChartType.Spline;

            rawDataSeries.BorderWidth = interpolatedDataSeriesSeries.BorderWidth = 2;

            var rawDataPoints = DataFactory.GenerateDummySine(10, 1, 0.25);
            var interpolatedDataPoints = DataFactory.Interpolate(rawDataPoints, 10);

            rawDataSeries.Points.DataBind(rawDataPoints, @"X", @"Y", String.Empty);
            interpolatedDataSeriesSeries.Points.DataBind(interpolatedDataPoints, @"X", @"Y", String.Empty);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HelpSO.MainForm"/> class.
        /// </summary>
        public MainForm()
        {
            this.StartPosition = FormStartPosition.CenterScreen;

            var chart = MainForm.InitializeChart();

            this.Controls.Add(chart);
        }
    }

    /// <summary>
    /// Data Factory.
    /// </summary>
    public static class DataFactory
    {
        /// <summary>
        /// Generates a dummy sine.
        /// </summary>
        /// <returns>The dummy sine.</returns>
        /// <param name="count">Count.</param>
        /// <param name="amplitude">Amplitude.</param>
        /// <param name="noiseAmplitude">Noise amplitude.</param>
        public static IList<Point2D<Double, Double>> GenerateDummySine(UInt16 count, Double amplitude, Double noiseAmplitude)
        {
            if (count < 2)
            {
                throw new ArgumentOutOfRangeException(@"count");
            }
            else
            {
                var dummySinePoints = new List<Point2D<Double, Double>>();

                var random = new Random();

                var xStep = 1.0 / count;

                for (var x = 0.0; x < 1.0; x += xStep) 
                {
                    var y = amplitude * Math.Sin(2f * Math.PI * x) + random.NextDouble() * noiseAmplitude;

                    var dummySinePoint = new Point2D<Double, Double>(x, y);

                    dummySinePoints.Add(dummySinePoint);
                }

                return dummySinePoints;
            }
        }

        /// <summary>
        /// Interpolate the specified source.
        /// </summary>
        /// <param name="source">Source.</param>
        /// <param name="countRatio">Count ratio.</param>
        public static IList<Point2D<Double, Double>> Interpolate(IList<Point2D<Double, Double>> source, UInt16 countRatio)
        {
            if (countRatio == 0)
            {
                throw new ArgumentOutOfRangeException(@"countRatio");
            }
            else if (source.Count < 2)
            {
                throw new ArgumentOutOfRangeException(@"source");
            }
            else
            {

                var rawDataPointsX = source.Select(item => item.X);
                var rawDataPointsY = source.Select(item => item.Y);

                // Could be done within one loop only... so far I'm pretty busy will update that example later
                var xMin = rawDataPointsX.Min();
                var xMax = rawDataPointsX.Max();

                // Different Kinds of interpolation here... it's all up to you o pick up the one that's gonna match your own situation
                // var interpolation = MathNet.Numerics.Interpolation.NevillePolynomialInterpolation.Interpolate(rawDataPointsX, rawDataPointsY);
                var interpolation = MathNet.Numerics.Interpolation.CubicSpline.InterpolateNatural(rawDataPointsX, rawDataPointsY);

                var listCopy = source.ToList();

                var xStep = (xMax - xMin) / (source.Count * countRatio);

                for (var x = xMin; x <= xMax; x += xStep)
                {
                    var y = interpolation.Interpolate(x);

                    var point2D = new Point2D<Double, Double>(x, y);

                    listCopy.Add(point2D);
                }

                return listCopy;
            }
        }
    }

    // C# lacks, for ***now***, generic constraints for primitive "numbers"
    public struct Point2D<TX, TY>
        where TX : struct, IComparable, IFormattable, IConvertible, IComparable<TX>, IEquatable<TX>
        where TY : struct, IComparable, IFormattable, IConvertible, IComparable<TY>, IEquatable<TY>
    {
        public static Point2D<TX, TY> Empty = new Point2D<TX, TY>();

        public Point2D(TX x, TY y)
        {
            this._x = x;
            this._y = y;
        }

        // C# 6 I miss you here: sad
        private readonly TY _y;
        public TY Y
        {
            get
            {
                return this._y;
            }
        }

        // and there too :-(
        private readonly TX _x;
        public TX X
        {
            get
            {
                return this._x;
            }
        }
    }
}

Feel free to ask more questions about it.

like image 191
Natalie Perret Avatar answered Oct 02 '22 12:10

Natalie Perret