【问题标题】:How do I recreate an Excel formula which calls TREND() in C#?如何重新创建在 C# 中调用 TREND() 的 Excel 公式?
【发布时间】:2011-09-15 21:29:57
【问题描述】:

我正在构建一个 .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

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
        }
    }

}

【问题讨论】:

    标签: c# excel trend


    【解决方案1】:

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

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

    .

    【讨论】:

    • 我发现了一个最小平方拟合函数,它接受一组 {x,y} 并返回 M 和 B。然后我可以使用 M 和 B 和一组新的 x 值来生成 y 值作为趋势结果返回。这一切都正确吗?
    • 说实话,我对 LINEST 或 TREND 函数不是很熟悉。使用这些时会发生很多事情,并且从文档中看,它们似乎有点不可靠(垃圾进垃圾出)。我认为您需要了解 Excel 函数的作用,然后简单地尝试在 C# 中重现结果。据我所知,这绝非易事。
    • 链接不再有效
    【解决方案2】:

    这篇文章非常有帮助,因为我们需要在 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 在后台加载以使用工作簿的 Trend 方法更快。

    【讨论】:

      【解决方案3】:

      感谢代码,用 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];
      }
      

      【讨论】:

        猜你喜欢
        • 2015-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-06
        • 2017-04-13
        • 1970-01-01
        相关资源
        最近更新 更多