如何在朱莉娅中获得滚动 window 回归

How to get a rolling window regression in julia

假设我有一只股票的价格,我想以滚动方式找到具有给定 window 大小的回归线的斜率。我怎样才能在 Julia 中完成它?我希望它非常快,因此不想使用 for 循环。

通常,您不应该担心 Julia 中的 for 循环,因为它们没有 R 或 Python for 循环的开销。因此,您只需要担心渐近复杂性,而不用担心解释器开销引入的潜在大常数因子。

然而,与使用朴素的 O(n²) 切片和回归方法相比,使用卷积可以(渐近地)更有效地完成此操作。 DSP.jl 包提供卷积功能。以下是一个没有截距的示例(它计算滚动贝塔);通过修改公式应该可以支持拦截。

using DSP

# Create some example x (signal) and y (stock prices)
# such that strength of signal goes up over time
const x = randn(100)
const y = (1:100) .* x .+ 100 .* randn(100)

# Create the rolling window
const window = Window.rect(20)

# Compute linear least squares estimate (X^T X)^-1 X^T Y
const xᵗx = conv(x .* x, window)[length(window):end-length(window)+1]
const xᵗy = conv(x .* y, window)[length(window):end-length(window)+1]
const lls = xᵗy ./ xᵗx  # desired beta

# Check result against naïve for loop
const βref = [dot(x[i:i+19], y[i:i+19]) / dot(x[i:i+19], x[i:i+19]) for i = 1:81]
@assert isapprox(βref, lls)

编辑添加:为了支持截距,即 X = [x 1],所以 X^T X = [dot(x, x) sum(x); sum(x) w] 其中 w 是 window 大小,二维矩阵的逆公式可以用来得到(X^T X)^-1 = [w -sum(x); -sum(x) dot(x, x)]/(w * dot(x, x) - sum(x)^2)。因此,[β, α] = [w dot(x, y) - sum(x) * sum(y), dot(x, x) * sum(y) - sum(x) * dot(x, y)] / (w * dot(x, x) - sum(x)^2)。这可以转化为以下卷积代码:

# Compute linear least squares estimate with intercept
const w = length(window)
const xᵗx = conv(x .* x, window)[w:end-w+1]
const xᵗy = conv(x .* y, window)[w:end-w+1]
const ᵗx = conv(x, window)[w:end-w+1]
const ᵗy = conv(y, window)[w:end-w+1]
const denom = w .* xᵗx - ᵗx .^ 2
const α = (xᵗx .* ᵗy .- ᵗx .* xᵗy) ./ denom
const β = (w .* xᵗy .- ᵗx .* ᵗy) ./ denom

# Check vs. naive solution
const ref = vcat([([x[i:i+19] ones(20)] \ y[i:i+19])' for i = 1:81]...)
@assert isapprox([β α], ref)

请注意,对于具有不同 window 形状的加权最小二乘法,需要进行一些小的修改以解开上面代码中可互换使用的 length(window)sum(window)

因为我不需要 x 变量,所以我创建了一个数字系列。使用 RollingFunctions 包,我能够通过以下函数获得滚动回归。

using RollingFunctions
function rolling_regression(price,windowsize)
 sum_x = sum(collect(1:windowsize))
 sum_x_squared = sum(collect(1:windowsize).^2)
 sum_xy = rolling(sum,price,windowsize,collect(1:windowsize))
 sum_y = rolling(sum,price,windowsize)
 b = ((windowsize*sum_xy) - (sum_x*sum_y))/(windowsize*sum_x_squared - sum_x^2)
 c = [repeat([missing],windowsize-1);b]
end