statisticstime-seriesjulia

How to do a Yule-Walker analysis in Julia


Is there a method in Julia to perform a Yule-Walker analysis similar to the equivalent method in Python statsmodels.regression.linear_model.yule_walker ?


Solution

  • In the end, as recommended, I have implemented it myself, and the code is like this. I have tested it for some cases, and the results are the same as those of the Python function.

    function yule_walker(
        x::Vector{Float64};
        order::Int64=1,
        method="adjusted",
        df::Union{Nothing,Int64}=nothing,
        inv=false,
        demean=true,
    )
        method in ("adjusted", "mle") ||
            throw(ArgumentError("ACF estimation method must be 'adjusted' or 'MLE'"))
    
        x = copy(x)
        if demean
            x .-= mean(x)
        end
        n = isnothing(df) ? length(x) : df
    
        adj_needed = method == "adjusted"
    
        if ndims(x) > 1 || size(x, 2) != 1
            throw(ArgumentError("Expecting a vector to estimate AR parameters"))
        end
    
        r = zeros(Float64, order + 1)
        r[1] = sum(x .^ 2) / n
        for k = 1:order
            r[k+1] = sum(x[1:end-k] .* x[k+1:end]) / (n - k * adj_needed)
        end
        R = Toeplitz(r[1:(end - 1)], conj(r[1:(end - 1)]))
    
        rho = 0
        try
            rho = R \ r[2:end]
        catch err
            if occursin("Singular matrix", string(err))
                @warn "Matrix is singular. Using pinv."
                rho = pinv(R) * r[2:end]
            else
                throw(err)
            end
        end
    
        sigmasq = r[1] - dot(r[2:end], rho)
        sigma = isnan(sigmasq) || sigmasq <= 0 ? NaN : sqrt(sigmasq)
    
        if inv
            return rho, sigma, inv(R)
        else
            return rho, sigma
        end
    end
    

    It's tested for these cases,

    tol = 1e-5
    @testset "yule_walker" begin
        rho, sigma = yule_walker([1.0, 2, 3]; order=1)
        @test rho == [0.0]
    
        rho, sigma = yule_walker([1.0, 2, 3]; order=2)
        @test rho == [0.0, -1.5]
    
        x = [0.9901178, -0.74795127, 0.44612542, 1.1362954, -0.04040932]
        rho, sigma = yule_walker(x; order=3, method="mle")
        @test rho ≈ [-0.9418963, -0.90335955, -0.33267884]
        @test sigma ≈ 0.44006365345695164
    
        rho, sigma = yule_walker(x; order=3)
        @test isapprox(rho, [0.10959317, 0.05242324, 1.06587676], atol=tol)
        @test isapprox(sigma, 0.15860522671108127, atol=tol)
    
        rho, sigma = yule_walker(x; order=5, method="mle")
        @test isapprox(
            rho, [-1.24209771, -1.56893346, -1.16951484, -0.79844781, -0.27598787], atol=tol
        )
        @test isapprox(sigma, 0.3679474002175471, atol=tol)
    
        x = [
            0.9901178,
            -0.74795127,
            0.44612542,
            1.1362954,
            -0.04040932,
            0.28625813,
            0.88901716,
            -0.1079814,
            -0.33231995,
            0.4607741,
        ]
    
        rho, sigma = yule_walker(x; order=3, method="mle")
        @test isapprox(
            rho, [-0.4896151627237206, -0.5724647370433921, 0.09083516892540627], atol=tol
        )
        @test isapprox(sigma, 0.4249693094713215, atol=tol)
    
        x = [
            0.9901178,
            -0.74795127,
            0.44612542,
            1.1362954,
            -0.04040932,
            0.28625813,
            0.88901716,
            -0.1079814,
            -0.33231995,
            0.4607741,
            0.7729643,
            -1.0998684,
            1.098167,
            1.0105597,
            -1.3370227,
            1.239718,
            -0.01393661,
            -0.4790918,
            1.5009186,
            -1.1647809,
        ]
    
        rho, sigma = yule_walker(x; order=3, method="mle")
        @test isapprox(rho, [-0.82245705, -0.57029742, 0.12166898], atol=tol)
        @test isapprox(sigma, 0.5203501608988023, atol=tol)
    
        rho, sigma = yule_walker(x; order=3)
        @test isapprox(rho, [-0.93458149, -0.68653741, 0.10161722], atol=tol)
        @test isapprox(sigma, 0.4269012058667671, atol=tol)
    
        rho, sigma = yule_walker(x; order=5, method="mle")
        @test isapprox(
            rho, [-0.83107755, -0.56407764, 0.20950143, 0.1232321, 0.10249279], atol=tol
        )
        @test isapprox(sigma, 0.5172269743102993, atol=tol)
    
        rho, sigma = yule_walker(x; order=5)
        @test isapprox(
            rho, [-0.96481241, -0.65359486, 0.31587079, 0.28403115, 0.1913565], atol=tol
        )
        @test isapprox(sigma, 0.41677565377507053, atol=tol)
    end;