C# 线性插值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12838007/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:38:07  来源:igfitidea点击:

C# Linear Interpolation

c#interpolation

提问by user1548411

I'm having issues interpolating a data file, which i have converted from .csv into an X array and Y array where X[0] corresponds to point Y[0] for example.

我在插入数据文件时遇到问题,我已将其从 .csv 转换为 X 数组和 Y 数组,其中 X[0] 对应于点 Y[0] 例如。

I need to interpolate between the values to give me a smooth file at the end. I am using a Picoscope to output the function which means each line is equally spaced in time, so only using Y values, which is why I'm trying to do this in a strange way when you see my code.

我需要在值之间进行插值,以在最后给我一个平滑的文件。我正在使用 Picoscope 输出函数,这意味着每行在时间上等距,所以只使用 Y 值,这就是为什么当您看到我的代码时我试图以一种奇怪的方式执行此操作。

The kind of values it has to deal with are:

它必须处理的值类型是:

X     Y
0     0
2.5   0
2.5   12000
7.5   12000
7.5   3000
10    3000
10    6000
11    6625.254
12    7095.154

So where 2 Y values next to each other are the same its a straight line between them but when they vary like from X = 10 on wards it becomes a sine wave in this example.

因此,当相邻的 2 个 Y 值相同时,它们之间是一条直线,但是当它们在病房上从 X = 10 变化时,它在本例中变为正弦波。

I have been looking at the equations for interpolation etc and other posts on here but i haven't done that sort of maths for years and sadly i can't figure it out any more, because there's 2 unknowns and i can't think how to program that into c#.

我一直在查看插值等式和此处的其他帖子,但我多年来没有做过那种数学运算,遗憾的是我无法再弄清楚了,因为有 2 个未知数,我不知道怎么做将其编程到 c# 中。

What i have is:

我所拥有的是:

int a = 0, d = 0, q = 0;
bool up = false;
double times = 0, change = 0, points = 0, pointchange = 0; 
double[] newy = new double[8192];
while (a < sizeoffile-1 && d < 8192)
{
    Console.Write("...");
    if (Y[a] == Y[a+1])//if the 2 values are the same add correct number of lines in to make it last the correct time
    {
        times = (X[a] - X[a + 1]);//number of repetitions
        if ((X[a + 1] - X[a]) > times)//if that was a negative number try the other way around
            times = (X[a + 1] - X[a]);//number of repetitions 
        do
        {
            newy[d] = Y[a];//add the values to the newy array which replaces y later on in the program
            d++;//increment newy position
            q++;//reduce number of reps in this loop
        }
        while (q < times + 1 && d < 8192);
        q = 0;//reset reps
    }
    else if (Y[a] != Y[a + 1])//if the 2 values are not the same interpolate between them
    {
        change = (Y[a] - Y[a + 1]);//work out difference between the values
        up = true;//the waveform is increasing
        if ((Y[a + 1] - Y[a]) > change)//if that number was a negative try the other way around
        {
            change = (Y[a + 1] - Y[a]);//work out difference bwteen values
            up = false;//the waveform is decreasing
        }
        points = (X[a] - X[a + 1]);//work out amount of time between given points
        if ((X[a + 1] - X[a]) > points)//if that was a negative try other way around
            points = (X[a + 1] - X[a]);///work out amount of time between given points
        pointchange = (change / points);//calculate the amount per point in time the value changes by
        if (points > 1)//any lower and the values cause errors
        {
            newy[d] = Y[a];//load first point
            d++;
            do
            {
                if (up == true)//
                    newy[d] = ((newy[d - 1]) + pointchange);
                else if (up == false)
                    newy[d] = ((newy[d - 1]) - pointchange);
                d++;
                q++;
            }
            while (q < points + 1 && d < 8192);
            q = 0;
        }
        else if (points != 0 && points > 0)
        {
            newy[d] = Y[a];//load first point
            d++;
        }
    }
    a++;
}

and this creates a close waveform but it is still very steppy.

这会创建一个接近的波形,但它仍然非常阶梯式。

So can anyone see why this isn't very accurate? How to improve its accuracy? Or a different way of doing this using arrays?

那么有人能看出为什么这不是很准确吗?如何提高它的准确率?或者使用数组执行此操作的不同方式?

Thanks for looking :)

感谢您的关注:)

回答by Mike Perrenoud

Try this method for me:

帮我试试这个方法:

static public double linear(double x, double x0, double x1, double y0, double y1)
{
    if ((x1 - x0) == 0)
    {
        return (y0 + y1) / 2;
    }
    return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}

Effectively you should be able to take your arrays and use it like this:

实际上,您应该能够获取数组并像这样使用它:

var newY = linear(X[0], X[0], X[1], Y[0], Y[1]);

I pulled the code from here, but verified that the algorithm matched the theory here, and so I thinkit's right. However, you probably should consider using polynomial interpolation if this is still steppy, please note the theory link, it shows that linear interpolation produces steppy waves.

我从这里提取了代码,但验证了算法与这里的理论相匹配,所以我认为它是正确的。但是,如果这仍然是阶梯状的,您可能应该考虑使用多项式插值,请注意理论链接,它表明线性插值会产生阶梯状波。

So, the first link I gave, where I grabbed this code from, also has a polynomial algorithm:

所以,我给出的第一个链接,我从中抓取了这段代码,也有一个多项式算法:

static public double lagrange(double x, double[] xd, double[] yd)
{
    if (xd.Length != yd.Length)
    {
        throw new ArgumentException("Arrays must be of equal length."); //$NON-NLS-1$
    }
    double sum = 0;
    for (int i = 0, n = xd.Length; i < n; i++)
    {
        if (x - xd[i] == 0)
        {
            return yd[i];
        }
        double product = yd[i];
        for (int j = 0; j < n; j++)
        {
            if ((i == j) || (xd[i] - xd[j] == 0))
            {
                continue;
            }
            product *= (x - xd[i]) / (xd[i] - xd[j]);
        }
        sum += product;
    }
    return sum;
}

To use this one you're going to have to decide how you want to step up your xvalues, so let's say we wanted to do it by finding the midpoint between the current iteration and the next:

要使用这个,你将不得不决定如何提高你的x价值,所以假设我们想通过找到当前迭代和下一个迭代之间的中点来做到这一点:

for (int i = 0; i < X.Length; i++)
{
    var newY = lagrange(new double[] { X[i]d, X[i+1]d }.Average(), X, Y);
}

Please note that there will be more to this loop, like ensuring there is an i+1and such, but I wanted to see if I could give you a start.

请注意,这个循环还有更多内容,比如确保有一个i+1等等,但我想看看我是否可以给你一个开始。

回答by andrei.ciprian

Theoretical base at Wolfram

Wolfram 的理论基础

The solution below computes the averages of Y values for given points with same X, just as the Matlab polyfit function does

下面的解决方案计算具有相同 X 的给定点的 Y 值的平均值,就像 Matlab polyfit 函数所做的那样

Linq and .NET framework version > 3.5 are mandatory for this swift API.Comments inside the code.

对于此 swift API,Linq 和 .NET 框架版本 > 3.5 是必需的。代码中的注释。

using System;
using System.Collections.Generic;
using System.Linq;


/// <summary>
/// Linear Interpolation using the least squares method
/// <remarks>http://mathworld.wolfram.com/LeastSquaresFitting.html</remarks> 
/// </summary>
public class LinearLeastSquaresInterpolation
{
    /// <summary>
    /// point list constructor
    /// </summary>
    /// <param name="points">points list</param>
    public LinearLeastSquaresInterpolation(IEnumerable<Point> points)
    {
        Points = points;
    }
    /// <summary>
    /// abscissae/ordinates constructor
    /// </summary>
    /// <param name="x">abscissae</param>
    /// <param name="y">ordinates</param>
    public LinearLeastSquaresInterpolation(IEnumerable<float> x, IEnumerable<float> y)
    {
        if (x.Empty() || y.Empty())
            throw new ArgumentNullException("null-x");
        if (y.Empty())
            throw new ArgumentNullException("null-y");
        if (x.Count() != y.Count())
            throw new ArgumentException("diff-count");

        Points = x.Zip(y, (unx, uny) =>  new Point { x = unx, y = uny } );
    }

    private IEnumerable<Point> Points;
    /// <summary>
    /// original points count
    /// </summary>
    public int Count { get { return Points.Count(); } }

    /// <summary>
    /// group points with equal x value, average group y value
    /// </summary>
                                                    public IEnumerable<Point> UniquePoints
{
    get
    {
        var grp = Points.GroupBy((p) => { return p.x; });
        foreach (IGrouping<float,Point> g in grp)
        {
            float currentX = g.Key;
            float averageYforX = g.Select(p => p.y).Average();
            yield return new Point() { x = currentX, y = averageYforX };
        }
    }
}
    /// <summary>
    /// count of point set used for interpolation
    /// </summary>
    public int CountUnique { get { return UniquePoints.Count(); } }
    /// <summary>
    /// abscissae
    /// </summary>
    public IEnumerable<float> X { get { return UniquePoints.Select(p => p.x); } }
    /// <summary>
    /// ordinates
    /// </summary>
    public IEnumerable<float> Y { get { return UniquePoints.Select(p => p.y); } }
    /// <summary>
    /// x mean
    /// </summary>
    public float AverageX { get { return X.Average(); } }
    /// <summary>
    /// y mean
    /// </summary>
    public float AverageY { get { return Y.Average(); } }

    /// <summary>
    /// the computed slope, aka regression coefficient
    /// </summary>
    public float Slope { get { return ssxy / ssxx; } }

    // dotvector(x,y)-n*avgx*avgy
    float ssxy { get { return X.DotProduct(Y) - CountUnique * AverageX * AverageY; } }
    //sum squares x - n * square avgx
    float ssxx { get { return X.DotProduct(X) - CountUnique * AverageX * AverageX; } }

    /// <summary>
    /// computed  intercept
    /// </summary>
    public float Intercept { get { return AverageY - Slope * AverageX; } }

    public override string ToString()
    {
        return string.Format("slope:{0:F02} intercept:{1:F02}", Slope, Intercept);
    }
}

/// <summary>
/// any given point
/// </summary>
 public class Point 
 {
     public float x { get; set; }
     public float y { get; set; }
 }

/// <summary>
/// Linq extensions
/// </summary>
public static class Extensions 
{
    /// <summary>
    /// dot vector product
    /// </summary>
    /// <param name="a">input</param>
    /// <param name="b">input</param>
    /// <returns>dot product of 2 inputs</returns>
    public static float DotProduct(this IEnumerable<float> a, IEnumerable<float> b)
    {
        return a.Zip(b, (d1, d2) => d1 * d2).Sum();
    }
    /// <summary>
    /// is empty enumerable
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="a"></param>
    /// <returns></returns>
    public static bool Empty<T>(this IEnumerable<T> a)
    {
        return a == null || a.Count() == 0;
    }
}

Use it like:

像这样使用它:

var llsi = new LinearLeastSquaresInterpolation(new Point[] 
    {
        new Point {x=1, y=1}, new Point {x=1, y=1.1f}, new Point {x=1, y=0.9f}, 
        new Point {x=2, y=2}, new Point {x=2, y=2.1f}, new Point {x=2, y=1.9f}, 
        new Point {x=3, y=3}, new Point {x=3, y=3.1f}, new Point {x=3, y=2.9f}, 
        new Point {x=10, y=10}, new Point {x=10, y=10.1f}, new Point {x=10, y=9.9f},
        new Point {x=100, y=100}, new Point{x=100, y=100.1f}, new Point {x=100, y=99.9f}
    });

Or:

或者:

var llsi = new LinearLeastSquaresInterpolation(
    new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
    new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 });