MS Chart中的插值实现

7

我需要在Windows应用程序的Ms-Chart中实现插值和外推。

为了进行插值,我正在使用"MathNet"库。但我仍然不知道如何实现它。

我已经尝试了以下插值实现:

using MathNet.Numerics.Interpolation.Algorithms;

NevillePolynomialInterpolation objIterpolate = new NevillePolynomialInterpolation(Xpoints, Ypoints);

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

我正在将XValues数组作为我的图表的第一个参数传递到NevillePolynomialInterpolation()中,而YValues数组则作为第二个参数。我还将newValue作为需要进行插值计算的XValue。

请问有人能建议一下我是否在正确的轨道上,或者提供正确实施插值的方法吗?


就我所见,你做得很对,尽管我不确定为什么你需要取插值结果的绝对值?你能详细说明一下你为什么关注这种方法吗? - Anders Gustafsson
我不确定我所做的是对还是错。我已经记录下了你的建议。谢谢... - Nitin Vijay
也许你可以插入一个屏幕截图,展示一下你的原始和插值数据点的示例? - Anders Gustafsson
1个回答

1
我已经创建了一个简短的示例,如果我下面粘贴的代码对您有用,请告诉我。
我不太习惯MathDotNet库,但XML文档足够详细,因此学习曲线并不陡峭,只是另一个.NET库中的许多其他内容之一。
否则,您仍然可以访问库网站查看其文档,除了我不确定是否涵盖插值的几个示例外,您可能会发现与阅读XML文档得到的相同内容。您还可以检查github并查看您要处理的插值的实现。
当然,如果您坚持按照此处描述的算法进行实现,则也可以尝试从头开始实现:http://en.wikipedia.org/wiki/Neville%27s_algorithm 无论如何,我假设您想利用MathDotNet库执行Neville多项式插值,并在同一图表区域上显示原始和插值数据。
关于其他信息,可以在这里找到一些(仍然不要期望太多): 关于MS Chart,它就像处理任何其他Winforms控件一样,只需查看文档,如果有什么棘手的问题,请指出哪些是困难的,我会尽力让您清楚明白。
到目前为止,坦率地说,我有点困惑于您不理解的内容,是MS Chart、MathDotNet还是两者都有问题?哪一个是你的问题?
无论如何,没有什么特别复杂的,只需将X和Y点传递给MathDotNet库(只要Xs和Ys的底层实现实现了类似数组T[]的IEnumerable<T>即可)。
然后库会为您完成所有的数学计算,您只需要使用给定的插值引擎的Interpolate(...)方法(您必须理解这里的Interpolation意味着一种插值引擎)。
我假设在你的代码片段中:XPointsYPoints 都是 IEnumerable<T> 集合(因为你提到它们是数组),其中 TDoubleSingle 或任何适合你的 .NET 数字原语类型。请注意,此处不需要解释,保留 HTML 标签即可。
// 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;
            }
        }
    }
}

随时可以就此提出更多问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接