迭代器的元素方差

时间:2017-05-06 21:04:13

标签: statistics julia

什么是一种数值稳定的方法来获取迭代器元素的方差?举个例子,我想做一些像

这样的事情
var((rand(4,2) for i in 1:10))

并返回(4,2)矩阵,该矩阵是每个系数的方差。这会使用Julia的基础var引发错误。有没有可以处理这个的包?或者使用Base Julia功能轻松(并且存储效率)这样做?或者是否需要自己开发?

2 个答案:

答案 0 :(得分:0)

有关数值稳定的版本

,请参阅下面的更新

另一种计算方法:

srand(0)   # reset random for comparing across implementations

moment2var(t) = (t[3]-t[2].^2./t[1])./(t[1]-1)
foldfunc(x,y) = (x[1]+1,x[2].+y,x[3].+y.^2)

moment2var(foldl(foldfunc,(0,zeros(1,1),zeros(1,1)),(rand(4,2) for i=1:10)))

给出:

4×2 Array{Float64,2}:
 0.0848123  0.0643537
 0.0715945  0.0900416
 0.111934   0.084314 
 0.0819135  0.0632765

类似于:

srand(0)   # reset random for comparing across implementations

# naive component-wise application of `var` function
map(var,zip((rand(4,2) for i=1:10)...))

是非迭代器版本(或CS术语中的离线版本)。

此方法基于平均值和平方和的方差计算。 moment2varfoldfunc只是一个辅助函数,但它没有它们就适合单行。

评论:

  • 速度方面,这应该也很不错。也许,StaticArrays并使用正确的foldl迭代器初始化v0' eltype可以节省更多时间。

  • 基准测试在样本输入上比componentwise_meanvar(来自另一个答案)提供了 5x 速度优势(以及更好的内存使用率)。

  • 使用moment2meanvar(t)=(t[2]./t[1],(t[3]-t[2].^2./t[1])./(t[1]-1)‌​)同时给出componentwise_meanvar等均值和方差。

  • 正如@ChrisRackauckas所指出的,当总和的元素数量很大时,这种方法会受到数值不稳定的影响。

---更新方法---

对问题的一点抽象要求在迭代器上执行foldl(和reducefoldr)的方法,返回矩阵,按元素和保持形状。为此,我们可以定义一个辅助函数mfold,该函数采用折叠函数并使其按元素方式折叠矩阵。按如下方式定义:

mfold(f) = (x,y)->[f(t[1],t[2]) for t in zip(x,y)]

对于这个特定的方差问题,我们可以定义分量方面的折叠函数,以及将矩组合成方差的最终函数(如果需要,则表示平均值)。代码:

ff(x,y) = (x[1]+1,x[2]+y,x[3]+y^2)          # fold and collect moments
moment2var(t) = (t[3]-t[2]^2/t[1])/(t[1]-1) # calc variance from moments
moment2meanvar(t) = (t[2]./t[1],(t[3]-t[2].^2./t[1])./(t[1]-1))

我们可以看到moment2meanvar适用于单个向量,如下所示:

julia> moment2meanvar(foldl(ff,(0.0,0.0,0.0),[1.0,2.0,3.0]))
(2.0, 1.0)

现在使用foldm(使用. - 表示法)对其进行矩阵化:

moment2var.(foldl(mfold(ff),fill((0,0,0),(4,2)),(rand(4,2) for i=1:10)))

@ChrisRackauckas注意到这不是数值稳定的,另一种方法(在维基百科中详述)更好。使用foldm这可以实现为:

# better fold function compensating the sums for stability
ff2(x,y) = begin 
  delta=y-x[2]
  mean=x[2]+delta/(x[1]+1)
  return (x[1]+1,mean,x[3]+delta*(y-mean)) 
end

# combine the collected information for the variance (and mean)
m2var(t) = t[3]/(t[1]-1)
m2meanvar(t) = (t[2],t[3]/(t[1]-1))

我们再次:

m2var.(foldl(mfold(ff2),fill((0,0.0,0.0),(4,2)),(rand(4,2) for i=1:10)))

给出相同的结果(可能更准确一点)。

答案 1 :(得分:0)

  

使用Base Julia功能还是一种简单(并且存储效率)的方法吗?

出于好奇,为什么在外部维度上使用DB_CON->exec("INSERT INTO `users` (`email`, `first_name`) VALUES (".$email.", ".firstname."") 的标准解决方案对你不利?

var

显然,我在这里使用julia> var(cat(3,(rand(4,2) for i in 1:10)...),3) 4×2×1 Array{Float64,3}: [:, :, 1] = 0.08847 0.104799 0.0946243 0.0879721 0.105404 0.0617594 0.0762611 0.091195 ,显然效率不高,所以我可以根据您的问题使用Base Julia函数和原始生成器语法。但是如果你直接在一个预先分配的大小数组(4,2,10)上初始化你的随机值,你也可以使这个存储有效,所以这里不是真正的问题。

或者我误解了你的问题?

EDIT - 回应评论的基准

cat

function standard_var(Y, A)
  for i in 1 : length(A)
    Y[:,:,i], = next(A,i);
  end
  var(Y,3)
end

function testit()
  A = (rand(4,2) for i in 1:10000);
  Y = Array{Float64, 3}(4,2,length(A));
  @time componentwise_meanvar(A); # as defined in Chris's answer above
  @time standard_var(Y, A)        # standard variance + using preallocation
  @time var(cat(3, A...), 3);     # standard variance without preallocation
  return nothing
end