Logo

SQL Server POLYVAL Function

Updated 2024-03-06 21:33:04.597000

Description

Use the aggregate function POLYVAL for calculating a new y-value given a new x-value using the coefficients of a polynomial p(x) of degree n that fits the x- and y-values supplied to the function. The y-value is calculated using n+1 polynomial coefficients in descending powers:

y=p_1x^n+p_2x^{n-1}+\dots+p_nx^1+p_{n+1}x^0

Syntax

SELECT [wct].[POLYVAL] (<@known_x, float,>
<@known_y, float,>
<@degree, smallint,>
<@new_x, float,>)

Arguments

@known_y

the y-values to be used in the calculation. @known_y must be of the type float or of a type that implicitly converts to float.

@degree

an integer specifying the degree of the polynomial.

@new_x

the new x-value for which you want POLYVAL to calculate the y-value.

Return Type

float

Remarks

The x- and y-values are passed to the function as pairs.

If x is NULL or y is NULL, the pair is not used in the calculation.

Use the POLYFIT or POLYFIT_q functions to get the coefficients.

@degree must remain invariant for the GROUP.

@new_x must remain invariant for the GROUP.

Examples

In this example, we will use the SeriesFloat function to generate a series of x-values equally spaced in the interval [0, 2.5] and then evaluate the error function, ERF, at those points. We will specify an approximating polynomial of 6 degrees. We will then generate values at the midpoint for each interval using the approximating polynomial.

SET NOCOUNT ON;
SELECT SeriesValue as x,
       westclintech.wct.ERF(SeriesValue) as y
INTO #erf
FROM wct.SeriesFloat(0, 2.5, 0.1, NULL, NULL);
SELECT a.seriesvalue as x,
       wct.POLYVAL(e.x, e.y, 6, seriesvalue) as POLYVAL
FROM wct.SERIESFLOAT(0.05, 2.5, 0.1, NULL, NULL) a ,
     #erf E
GROUP BY seriesvalue;
DROP TABLE #erf;

This produces the following result.

xPOLYVAL
0.050.0560409087420763
0.150.167414870029564
0.250.276183548384518
0.350.379669434495585
0.450.475932241043269
0.550.563669129052707
0.650.642120996194105
0.750.710984827030842
0.850.770332105215247
0.950.82053328763204
1.050.862188340489436
1.150.896063337357925
1.250.923033119156717
1.350.944030016087848
1.450.959998631517963
1.550.971856687807761
1.650.980461934089109
1.750.986585115989822
1.850.990889007306113
1.950.993913503622709
2.050.996066777880637
2.150.997622497892672
2.250.998723105806456
2.350.999389159515293
2.450.999534736016589

Since we know that the y-values are actually erf(x), in this example we will compare the values calculated using the approximating polynomial and erf(x).

SET NOCOUNT ON;
SELECT SeriesValue as x,
       westclintech.wct.ERF(SeriesValue) as y
INTO #erf
FROM wct.SeriesFloat(0, 2.5, 0.1, NULL, NULL);
SELECT x,
       POLYVAL,
       [ERF(x)],
       POLYVAL - [ERF(x)] as [DIFFERENCE]
FROM
(
    SELECT a.seriesvalue as x,
           wct.POLYVAL(e.x, e.y, 6, a.seriesvalue) as POLYVAL,
           westclintech.wct.ERF(a.SeriesValue) as [ERF(x)]
    FROM wct.SERIESFLOAT(0.05, 2.5, 0.1, NULL, NULL) a ,
         #erf E
    GROUP BY seriesvalue
) m;
DROP TABLE #erf;

This produces the following result.

xPOLYVALERF(x)DIFFERENCE
0.050.05604090874207630.0563719777970165-0.00033106905494016
0.150.1674148700295640.167995971427363-0.000581101397799128
0.250.2761835483845180.276326390168236-0.000142841783717984
0.350.3796694344955850.3793820535623090.00028738093327535
0.450.4759322410432690.4754817197869230.000450521256346315
0.550.5636691290527070.5633233663251080.000345762727599452
0.650.6421209961941050.6420293273556719.16688384339226E-05
0.750.7109848270308420.711155633653513-0.000170806622671549
0.850.7703321052152470.770668057608351-0.000335952393103467
0.950.820533287632040.820890807273276-0.000357519641236315
1.050.8621883404894360.862436106090095-0.000247765600658978
1.150.8960633373579250.896123842936913-6.05055789882902E-05
1.250.9230331191567170.9229001282564560.000132990900260643
1.350.9440300160878480.9437621961227220.000267819965126148
1.450.9599986315179630.9596950256374570.000303605880506042
1.550.9718566878077610.971622733262010.000233954545751147
1.650.9804619340891090.9803755850233588.63490657508903E-05
1.750.9865851159898220.98667167121918-8.65552293580762E-05
1.850.9908890073061130.991111030056084-0.000222022749971074
1.950.9939135036227090.994179333592187-0.000265829969478215
2.050.9960667778806370.996258096044454-0.000191318163817344
2.150.9976224978926720.997638607037322-1.61091446494455E-05
2.250.9987231058064560.9985372834133170.000185822393138357
2.350.9993891595152930.9991107329678650.000278426547427824
2.450.9995347360165890.9994694198877466.53161288429738E-05

It looks like the approximating polynomial is accurate to about 3 or 4 decimal places. In fact if we graphed the results, they would look like this:

http://westclintech.com/Portals/0/images/doc_math_POLYVAL_img2.jpg

We can see that in this range, [0, 2.5] the fit between the y-values and the f-values (ERF(x) in this case) is quite good. Since POLYVAL creates an approximating polynomial, it is not bound by minima and maxima of x-y pairs. In this example, we will graph the results this SQL, which will using the approximating polynomial from the [0, 2.5] interval on the interval [0, 6].

SET NOCOUNT ON;
 
SELECT SeriesValue as x
,westclintech.wct.ERF(SeriesValue) as y
INTO #erf
FROM wct.SeriesFloat(0,2.5,0.1,NULL,NULL);
 
SELECT a.seriesvalue as x
,wct.POLYVAL(e.x, e.y, 6, seriesvalue) as POLYVAL
FROM wct.SERIESFLOAT(0.0,6.0,0.1,NULL,NULL) a
,#erf E
GROUP BY seriesvalue;
 
DROP TABLE #erf;

This produces the following graph.

http://westclintech.com/Portals/0/images/doc_math_POLYVAL_img3.jpg

As you can see, the polynomial approximation quickly diverges from the function in the region above 2.5. Let's look at how POLYVAL works with dates. In this example we will look at some interest rate values over a time-horizon and calculate the 3rd degree polynomial approximation. The x-values are dates and the y-values are the decimal values of the interest rate (.01 = 1%).

SET NOCOUNT ON;
SELECT cast(x as datetime) as x,
       y
INTO #a
FROM
(
    VALUES
        ('2012-Apr-30', 0.0028),
        ('2013-Apr-30', 0.0056),
        ('2014-Apr-30', 0.0085),
        ('2016-Apr-30', 0.0164),
        ('2018-Apr-30', 0.0235),
        ('2021-Apr-30', 0.0299),
        ('2031-Apr-30', 0.0382),
        ('2041-Apr-30', 0.0406)
) m (x, y);
SELECT wct.POLYVAL(cast(x as float), y, 3, cast(Cast('2012-Oct-31' as datetime) 
          as float)) as POLYVAL
from #a;
DROP TABLE #a;

This produces the following result.

POLYVAL
0.00378351662681808

In this example we will use POLYVAL, the SeriesInt function and the EOMONTH function from XLeratorDB/financial to calculate the rates for each year from 1 through 30 out to 4 decimal places.

SET NOCOUNT ON;
SELECT cast(x as datetime) as x,
       y
INTO #a
FROM
(
    VALUES
        ('2012-Apr-30', 0.0028),
        ('2013-Apr-30', 0.0056),
        ('2014-Apr-30', 0.0085),
        ('2016-Apr-30', 0.0164),
        ('2018-Apr-30', 0.0235),
        ('2021-Apr-30', 0.0299),
        ('2031-Apr-30', 0.0382),
        ('2041-Apr-30', 0.0406)
) m (x, y);
SELECT convert(varchar, wct.EOMONTH('04/30/2011', SeriesValue), 106) as [MATURITY 
          DATE],
       ROUND(wct.POLYVAL(cast(x as float), y, 3, cast(wct.EOMONTH('04/30/2011', 
                 SeriesValue) as float)), 4) as [INTEREST RATE]
from #a,
     wct.SeriesInt(12, 360, 12, NULL, NULL)
GROUP BY wct.EOMONTH('04/30/2011', SeriesValue);
DROP TABLE #a;

This produces the following result.

MATURITY DATEINTEREST RATE
30 Apr 20120.0015
30 Apr 20130.0059
30 Apr 20140.0099
30 Apr 20150.0136
30 Apr 20160.017
30 Apr 20170.02
30 Apr 20180.0228
30 Apr 20190.0252
30 Apr 20200.0274
30 Apr 20210.0293
30 Apr 20220.031
30 Apr 20230.0325
30 Apr 20240.0338
30 Apr 20250.0349
30 Apr 20260.0358
30 Apr 20270.0366
30 Apr 20280.0373
30 Apr 20290.0378
30 Apr 20300.0382
30 Apr 20310.0385
30 Apr 20320.0388
30 Apr 20330.039
30 Apr 20340.0391
30 Apr 20350.0393
30 Apr 20360.0394
30 Apr 20370.0395
30 Apr 20380.0397
30 Apr 20390.0399
30 Apr 20400.0402
30 Apr 20410.0405