如何在 C# 中重新创建调用 TREND() 的 Excel 公式?

发布于 2024-12-05 05:59:53 字数 2280 浏览 1 评论 0原文

我正在构建一个 .net 页面来模仿电子表格。该工作表包含此公式

=ROUND(TREND(AA7:AE7,AA$4:AE$4,AF$4),1)

有人可以提供与 TREND() 等效的 C# 公式吗?或者,如果任何人都可以提供围绕它的快捷方式,那也很好;我对那里的数学不太熟悉,不知道是否有更简单的方法。

如果有帮助的话,这里有一些示例数字。

AA7:AE7 6 8 10 12 14

10.2 13.6 17.5 20.4 23.8

AA$4:AE$4 600 800 1000 1200 1400

AF$4 650

编辑:这是我想到的,它似乎生成了与我的电子表格相同的数字。

public static partial class Math2
{
    public static double[] Trend(double[] known_y, double[] known_x, params double[] new_x)
    {
        // return array of new y values
        double m, b;
        Math2.LeastSquaresFitLinear(known_y, known_x, out m, out b);

        List<double> new_y = new List<double>();
        for (int j = 0; j < new_x.Length; j++)
        {
            double y = (m * new_x[j]) + b;
            new_y.Add(y);
        }

        return new_y.ToArray();
    }

    // found at http://stackoverflow.com/questions/7437660/how-do-i-recreate-an-excel-formula-which-calls-trend-in-c
    // with a few modifications
    public static void LeastSquaresFitLinear(double[] known_y, double[] known_x, out double M, out double B)
    {
        if (known_y.Length != known_x.Length)
        {
            throw new ArgumentException("arrays are unequal lengths");
        }

        int numPoints = known_y.Length;

        //Gives best fit of data to line Y = MC + B
        double x1, y1, xy, x2, J;

        x1 = y1 = xy = x2 = 0.0;
        for (int i = 0; i < numPoints; i++)
        {
            x1 = x1 + known_x[i];
            y1 = y1 + known_y[i];
            xy = xy + known_x[i] * known_y[i];
            x2 = x2 + known_x[i] * known_x[i];
        }

        M = B = 0;
        J = ((double)numPoints * x2) - (x1 * x1);

        if (J != 0.0)
        {
            M = (((double)numPoints * xy) - (x1 * y1)) / J;
            //M = Math.Floor(1.0E3 * M + 0.5) / 1.0E3; // TODO this is disabled as it seems to product results different than excel
            B = ((y1 * x2) - (x1 * xy)) / J;
            // B = Math.Floor(1.0E3 * B + 0.5) / 1.0E3; // TODO assuming this is the same as above
        }
    }

}

I'm building a .net page to mimic a spreadsheet. The sheet contains this formula

=ROUND(TREND(AA7:AE7,AA$4:AE$4,AF$4),1)

Can someone provide the C# equivalent of TREND() ? Alternatively if anyone can provide a shortcut around it that's fine too; I'm not familiar enough with the math there to know if there's an easier way.

Here are some sample numbers if it helps.

AA7:AE7
6 8 10 12 14

or
10.2 13.6 17.5 20.4 23.8

AA$4:AE$4
600 800 1000 1200 1400

AF$4
650

edit: here's what I came up with and it seems to be producing the same numbers as my spreadsheet.

public static partial class Math2
{
    public static double[] Trend(double[] known_y, double[] known_x, params double[] new_x)
    {
        // return array of new y values
        double m, b;
        Math2.LeastSquaresFitLinear(known_y, known_x, out m, out b);

        List<double> new_y = new List<double>();
        for (int j = 0; j < new_x.Length; j++)
        {
            double y = (m * new_x[j]) + b;
            new_y.Add(y);
        }

        return new_y.ToArray();
    }

    // found at http://stackoverflow.com/questions/7437660/how-do-i-recreate-an-excel-formula-which-calls-trend-in-c
    // with a few modifications
    public static void LeastSquaresFitLinear(double[] known_y, double[] known_x, out double M, out double B)
    {
        if (known_y.Length != known_x.Length)
        {
            throw new ArgumentException("arrays are unequal lengths");
        }

        int numPoints = known_y.Length;

        //Gives best fit of data to line Y = MC + B
        double x1, y1, xy, x2, J;

        x1 = y1 = xy = x2 = 0.0;
        for (int i = 0; i < numPoints; i++)
        {
            x1 = x1 + known_x[i];
            y1 = y1 + known_y[i];
            xy = xy + known_x[i] * known_y[i];
            x2 = x2 + known_x[i] * known_x[i];
        }

        M = B = 0;
        J = ((double)numPoints * x2) - (x1 * x1);

        if (J != 0.0)
        {
            M = (((double)numPoints * xy) - (x1 * y1)) / J;
            //M = Math.Floor(1.0E3 * M + 0.5) / 1.0E3; // TODO this is disabled as it seems to product results different than excel
            B = ((y1 * x2) - (x1 * xy)) / J;
            // B = Math.Floor(1.0E3 * B + 0.5) / 1.0E3; // TODO assuming this is the same as above
        }
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城°AllureLove 2024-12-12 05:59:53

考虑 TREND 基于 Excel 函数 LINEST。
如果您点击此链接,https://support.office.com/en-us/article/LINEST-function-84d7d0d9-6e50-4101-977a-fa7abf772b6d,它将解释 LINEST 背后的功能。

此外,您还会找到它使用的基本公式。

第一个公式

第二个公式

Consider TREND is based upon the Excel Function, LINEST.
If you follow this link, https://support.office.com/en-us/article/LINEST-function-84d7d0d9-6e50-4101-977a-fa7abf772b6d, it will explain the functionality behind LINEST.

In addition, you'll find the base formula that it uses.

First Formula
.

Second formulat

孤千羽 2024-12-12 05:59:53

这篇文章非常有帮助,因为我们需要在 C# 中重新创建它。感谢上面 Jeff 的回答,我使用以下内容重新创建了该公式:

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

public static class MathHelper
{
    /// <summary>
    /// Gets the value at a given X using the line of best fit (Least Square Method) to determine the equation
    /// </summary>
    /// <param name="points">Points to calculate the value from</param>
    /// <param name="x">Function input</param>
    /// <returns>Value at X in the given points</returns>
    public static float LeastSquaresValueAtX(List<PointF> points, float x)
    {
        float slope = SlopeOfPoints(points);
        float yIntercept = YInterceptOfPoints(points, slope);

        return (slope * x) + yIntercept;
    }

    /// <summary>
    /// Gets the slope for a set of points using the formula:
    /// m = ∑ (x-AVG(x)(y-AVG(y)) / ∑ (x-AVG(x))²
    /// </summary>
    /// <param name="points">Points to calculate the Slope from</param>
    /// <returns>SlopeOfPoints</returns>
    private static float SlopeOfPoints(List<PointF> points)
    {
        float xBar = points.Average(p => p.X);
        float yBar = points.Average(p => p.Y);

        float dividend = points.Sum(p => (p.X - xBar) * (p.Y - yBar));
        float divisor = (float)points.Sum(p => Math.Pow(p.X - xBar, 2));

        return dividend / divisor;            
    }

    /// <summary>
    /// Gets the Y-Intercept for a set of points using the formula:
    /// b = AVG(y) - m( AVG(x) )
    /// </summary>
    /// <param name="points">Points to calculate the intercept from</param>
    /// <returns>Y-Intercept</returns>
    private static float YInterceptOfPoints(List<PointF> points, float slope)
    { 
        float xBar = points.Average(p => p.X);
        float yBar = points.Average(p => p.Y);

        return yBar - (slope * xBar);        
    }       
}

由于 Point 使用整数来定义其值,因此我选择使用 PointF,因为在我们的应用程序中,可以有很多小数位。请原谅任何数学术语,因为我花更多的时间编写代码而不是开发这样的算法,尽管如果我在某个地方弄错了一个术语,我希望有人纠正我。

这肯定比等待 Excel Interop 在后台加载以使用工作簿的趋势方法要快。

This post has been very helpful as we've needed to recreate this in C#. Thanks to Jeff's answer above I've recreated that formula using the following:

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

public static class MathHelper
{
    /// <summary>
    /// Gets the value at a given X using the line of best fit (Least Square Method) to determine the equation
    /// </summary>
    /// <param name="points">Points to calculate the value from</param>
    /// <param name="x">Function input</param>
    /// <returns>Value at X in the given points</returns>
    public static float LeastSquaresValueAtX(List<PointF> points, float x)
    {
        float slope = SlopeOfPoints(points);
        float yIntercept = YInterceptOfPoints(points, slope);

        return (slope * x) + yIntercept;
    }

    /// <summary>
    /// Gets the slope for a set of points using the formula:
    /// m = ∑ (x-AVG(x)(y-AVG(y)) / ∑ (x-AVG(x))²
    /// </summary>
    /// <param name="points">Points to calculate the Slope from</param>
    /// <returns>SlopeOfPoints</returns>
    private static float SlopeOfPoints(List<PointF> points)
    {
        float xBar = points.Average(p => p.X);
        float yBar = points.Average(p => p.Y);

        float dividend = points.Sum(p => (p.X - xBar) * (p.Y - yBar));
        float divisor = (float)points.Sum(p => Math.Pow(p.X - xBar, 2));

        return dividend / divisor;            
    }

    /// <summary>
    /// Gets the Y-Intercept for a set of points using the formula:
    /// b = AVG(y) - m( AVG(x) )
    /// </summary>
    /// <param name="points">Points to calculate the intercept from</param>
    /// <returns>Y-Intercept</returns>
    private static float YInterceptOfPoints(List<PointF> points, float slope)
    { 
        float xBar = points.Average(p => p.X);
        float yBar = points.Average(p => p.Y);

        return yBar - (slope * xBar);        
    }       
}

Since Point uses Integers to define its values I've elected to use PointF since in our applications, there can be many decimal places. Pardon any math terminology that is off as I spend more time writing code than developing algorithms like this although I'd love anyone to correct me should I have mistated a term somewhere.

This is certainly faster than waiting for Excel Interop to load up in the background to use the workbook's Trend method.

萌化 2024-12-12 05:59:53

感谢您用 JavaScript 重新创建的代码。

function LeastSquaresFitLinear(known_y, known_x, offset_x)
{
    if (known_y.length != known_x.length) return false; //("arrays are unequal lengths");
    var numPoints = known_y.length;

    var x1=0, y1=0, xy=0, x2=0, J, M, B;
    for (var i = 0; i < numPoints; i++)
    {
        known_x[i] -= offset_x;
        x1 = x1 + known_x[i];
        y1 = y1 + known_y[i];
        xy = xy + known_x[i] * known_y[i];
        x2 = x2 + known_x[i] * known_x[i];
    }

    J = (numPoints * x2) - (x1 * x1);
    if (J != 0.0)
    {
        M = ((numPoints * xy) - (x1 * y1)) / J;
        B = ((y1 * x2) - (x1 * xy)) / J;
    }
    return [M,B];
}

Thanks for the code, recreated in javascript.

function LeastSquaresFitLinear(known_y, known_x, offset_x)
{
    if (known_y.length != known_x.length) return false; //("arrays are unequal lengths");
    var numPoints = known_y.length;

    var x1=0, y1=0, xy=0, x2=0, J, M, B;
    for (var i = 0; i < numPoints; i++)
    {
        known_x[i] -= offset_x;
        x1 = x1 + known_x[i];
        y1 = y1 + known_y[i];
        xy = xy + known_x[i] * known_y[i];
        x2 = x2 + known_x[i] * known_x[i];
    }

    J = (numPoints * x2) - (x1 * x1);
    if (J != 0.0)
    {
        M = ((numPoints * xy) - (x1 * y1)) / J;
        B = ((y1 * x2) - (x1 * xy)) / J;
    }
    return [M,B];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文