Как воссоздать формулу Excel, которая вызывает TREND() в C#?
Я строю.net страницу, чтобы имитировать электронную таблицу. Лист содержит эту формулу
=ROUND(TREND(AA7:AE7,AA$4:AE$4,AF$4),1)
Может кто-нибудь предоставить C# эквивалент TREND()
? В качестве альтернативы, если кто-то может предоставить ярлык вокруг него, это тоже хорошо; Я не достаточно знаком с математикой, чтобы понять, есть ли более простой способ.
Вот несколько примеров номеров, если это поможет.
AA7: AE76 8 10 12 14
или же10.2 13.6 17.5 20.4 23.8
AA $ 4: AE $ 4600 800 1000 1200 1400
AF $ 4650
редактировать: вот то, что я придумал, и, кажется, производит те же цифры, что и моя таблица.
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://stackru.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
}
}
}
2 ответа
Рассмотрим TREND на основе функции Excel, LINEST. Если вы перейдете по этой ссылке, https://support.office.com/en-us/article/LINEST-function-84d7d0d9-6e50-4101-977a-fa7abf772b6d, это объяснит функциональность LINEST.
Кроме того, вы найдете базовую формулу, которую он использует.
,
Этот пост был очень полезен, так как нам нужно было воссоздать его в C#. Благодаря ответу Джеффа выше, я воссоздал эту формулу, используя следующее:
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 книги.
Спасибо за код, воссозданный на 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];
}