R's poly()
function produces orthogonal polynomials for data fitting. However, I would like to use the results of the regression outside of R (say in C++), and there doesn't seem to be a way to get the coefficients for each orthogonal polynomial.
Note 1: I don't mean the regression coefficients, but the coefficients of the orthogonal polynomials themselves), e.g. the p_i(x)
in
y = a0 + a1*p_1(x) + a2*p_2(x) + ...
Note 2: I know poly(x, n, raw=T)
forces poly to return non-orthogonal polynomials, but I want to regress against orthogonal polynomials, so that's what I'm looking for.
We can say that orthogonal is a synonym of perpendicular. If the inner product (inner product is generalization of dot product) of two polynomials is zero then we call them orthogonal polynomials. In R, we can find the orthogonal product by using poly function as shown in the below examples.
Both, the manual coding (Example 1) and the application of the poly function with raw = TRUE (Example 2) use raw polynomials. However, depending on your situation you might prefer to use orthogonal (i.e. uncorrelated) polynomials.
The orthogonal polynomial is summarized by the coefficients, which can be used to evaluate it via the three-term recursion given in Kennedy & Gentle (1980, pp.343--4), and used in the predict part of the code. poly using … is just a convenience wrapper for polym: coef is ignored.
Hence they are correlated and the regression parameters could be unstable, but it is not automatically the case that they are unreliable. Conversion to orthognonal polynomials may be more reliable. But what makes the coefficient of the original powers of x any more interpretable than the coefficients of the orthogonal polynomials?
The polynomials are defined recursively using the alpha
and norm2
coefficients of the poly
object you've created. Let's look at an example:
z <- poly(1:10, 3)
attributes(z)$coefs
# $alpha
# [1] 5.5 5.5 5.5
# $norm2
# [1] 1.0 10.0 82.5 528.0 3088.8
For notation, let's call a_d
the element in index d
of alpha
and let's call n_d
the element in index d
of norm2
. F_d(x)
will be the orthogonal polynomial of degree d
that is generated. For some base cases we have:
F_0(x) = 1 / sqrt(n_2)
F_1(x) = (x-a_1) / sqrt(n_3)
The rest of the polynomials are recursively defined:
F_d(x) = [(x-a_d) * sqrt(n_{d+1}) * F_{d-1}(x) - n_{d+1} / sqrt(n_d) * F_{d-2}(x)] / sqrt(n_{d+2})
To confirm with x=2.1
:
x <- 2.1
predict(z, newdata=x)
# 1 2 3
# [1,] -0.3743277 0.1440493 0.1890351
# ...
a <- attributes(z)$coefs$alpha
n <- attributes(z)$coefs$norm2
f0 <- 1 / sqrt(n[2])
(f1 <- (x-a[1]) / sqrt(n[3]))
# [1] -0.3743277
(f2 <- ((x-a[2]) * sqrt(n[3]) * f1 - n[3] / sqrt(n[2]) * f0) / sqrt(n[4]))
# [1] 0.1440493
(f3 <- ((x-a[3]) * sqrt(n[4]) * f2 - n[4] / sqrt(n[3]) * f1) / sqrt(n[5]))
# [1] 0.1890351
The most compact way to export your polynomials to your C++ code would probably be to export attributes(z)$coefs$alpha
and attributes(z)$coefs$norm2
and then use the recursive formula in C++ to evaluate your polynomials.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With