Skip to content

Commit f942a4b

Browse files
authored
use qr, not pinv (#291)
* use qr, not pinv * document weights usage in fit * adjust range call for 1.0 testing
1 parent d72dd38 commit f942a4b

File tree

2 files changed

+32
-4
lines changed

2 files changed

+32
-4
lines changed

src/common.jl

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,21 @@ fromroots(A::AbstractMatrix{T}; var::SymbolLike = :x) where {T <: Number} =
6969
fit(x, y, deg=length(x) - 1; [weights], var=:x)
7070
fit(::Type{<:AbstractPolynomial}, x, y, deg=length(x)-1; [weights], var=:x)
7171
72-
Fit the given data as a polynomial type with the given degree. Uses linear least squares. When weights are given, as either a `Number`, `Vector` or `Matrix`, will use weighted linear least squares. The default polynomial type is [`Polynomial`](@ref). This will automatically scale your data to the [`domain`](@ref) of the polynomial type using [`mapdomain`](@ref)
72+
Fit the given data as a polynomial type with the given degree. Uses
73+
linear least squares to minimize the norm of `V⋅c - y`, where `V` is
74+
the Vandermonde matrix and `c` are the coefficients of the polynomial
75+
fit.
76+
77+
This will automatically scale your data to the [`domain`](@ref) of the
78+
polynomial type using [`mapdomain`](@ref). The default polynomial type
79+
is [`Polynomial`](@ref).
80+
81+
When weights are given, as either a `Number`, `Vector` or `Matrix`,
82+
this will use weighted linear least squares. That is, the norm of
83+
`W ⋅ (y - V ⋅ x)` is minimized. (As of now, the weights are specified
84+
using their squares: for a number use `w^2`, for a vector `wᵢ^2`, and for a matrix
85+
specify `W'*W`. This behavior may change in the future.)
86+
7387
"""
7488
function fit(P::Type{<:AbstractPolynomial},
7589
x::AbstractVector{T},
@@ -112,17 +126,21 @@ function _fit(P::Type{<:AbstractPolynomial},
112126
if weights !== nothing
113127
coeffs = _wlstsq(vand, y, weights)
114128
else
115-
coeffs = pinv(vand) * y
129+
coeffs = qr(vand) \ y
116130
end
117131
R = float(T)
118132
return P(R.(coeffs), var)
119133
end
120134

121135

122136
# Weighted linear least squares
137+
# TODO: Breaking change for 2.0: use non-squared weights
123138
_wlstsq(vand, y, W::Number) = _wlstsq(vand, y, fill!(similar(y), W))
124-
_wlstsq(vand, y, W::AbstractVector) = _wlstsq(vand, y, Diagonal(W))
125-
_wlstsq(vand, y, W::AbstractMatrix) = (vand' * W * vand) \ (vand' * W * y)
139+
function _wlstsq(vand, y, w::AbstractVector)
140+
W = Diagonal(sqrt.(w))
141+
qr(W * vand) \ (W * y)
142+
end
143+
_wlstsq(vand, y, W::AbstractMatrix) = qr(vand' * W * vand) \ (vand' * W * y)
126144

127145
"""
128146
roots(::AbstractPolynomial; kwargs...)

test/StandardBasis.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,16 @@ end
326326

327327
# test default (issue #228)
328328
fit(1:3, rand(3))
329+
330+
# weight test (PR #291)
331+
# This should change with 2.0
332+
# as for now we specify w^2.
333+
x = range(0, stop=pi, length=30)
334+
y = sin.(x)
335+
wts = 1 ./ sqrt.(1 .+ x)
336+
# cs = numpy.polynomial.polynomial.polyfit(x, y, 4, w=wts)
337+
cs = [0.0006441172319036863, 0.985961582190304, 0.04999233434370933, -0.23162369757680354, 0.036864056406570644]
338+
@test maximum(abs, coeffs(fit(x, y, 4, weights=wts.^2)) - cs) <= sqrt(eps())
329339
end
330340

331341
@testset "Values" begin

0 commit comments

Comments
 (0)