Julia:如何使用IndexedTables.jl的聚合来按组计算平均值?

时间:2017-10-15 03:46:40

标签: julia

我正在尝试使用aggregate函数按组计算变量的平均值

using Distributions, PooledArrays

N=Int64(2e9/8); K=100;

pool = [@sprintf "id%03d" k for k in 1:K]
pool1 = [@sprintf "id%010d" k for k in 1:(N/K)]

function randstrarray(pool, N)
    PooledArray(PooledArrays.RefArray(rand(UInt8(1):UInt8(K), N)), pool)
end

using JuliaDB
DT = IndexedTable(Columns([1:N;]), Columns(
  id1 = randstrarray(pool, N),
  v3 =  rand(round.(rand(Uniform(0,100),100),4), N) # numeric e.g. 23.5749
 ));

res = IndexedTables.aggregate(mean, DT, by=(:id1,), with=:v3)

我是如何得到错误的

MethodError: no method matching mean(::Float64, ::Float64)
Closest candidates are:
  mean(!Matched::Union{Function, Type}, ::Any) at statistics.jl:19
  mean(!Matched::AbstractArray{T,N} where N, ::Any) where T at statistics.jl:57
  mean(::Any) at statistics.jl:34
in  at base\<missing>
in #aggregate#144 at IndexedTables\src\query.jl:119
in aggregate_to at IndexedTables\src\query.jl:148

然而

IndexedTables.aggregate(+ , DT, by=(:id1,), with=:v3)

工作正常

3 个答案:

答案 0 :(得分:1)

您需要告诉它如何将两个数字减少为一个。 mean用于数组。所以只需使用匿名函数:

res = IndexedTables.aggregate((x,y)->(x+y)/2, DT, by=(:id1,), with=:v3)

答案 1 :(得分:1)

修改

res = IndexedTables.aggregate_vec(mean, DT, by=(:id1,), with=:v3)

来自帮助:

help?> IndexedTables.aggregate_vec

aggregate_vec(f :: Function,x :: IndexedTable) 使用从向量到标量的函数将具有相等索引的相邻行组合,例如。意味着

旧回答:

(我保留它是因为这是一个愉快的运动(对我而言)如果某些东西不能像我们想要的那样创建帮助器类型和功能。也许它可以在将来帮助某人:)

我不确定你喜欢如何聚合均值。我的想法是计算等效质量点的“重心”。

两点的中心:G =(A + B)/ 2

添加(聚合)第三点C是(2G + C)/ 3(2G,因为G的质量是A的质量+ B的质量)

struct Atractor
     center::Float64
     mass::Int64
end

" two points create new atractor with double mass "
mediocre(a::Float64, b::Float64) = Atractor((a+b)/2, 2)

# pls forgive me function's name! :) 

" aggregate new point to atractor "
function mediocre(a::Atractor, b::Float64)
    mass = a.mass + 1  
    Atractor((a.center*a.mass+b)/mass, mass)
end

测试:

tst_array = rand(Float64, 100);

isapprox(mean(tst_array), reduce(mediocre, tst_array).center)
true  # at least in my tests! :) 

mean(tst_array) == reduce(mediocre, tst_array).center  # sometimes true

对于聚合函数,我们需要更多的工作:

import Base.convert

" we need method for convert Atractor to Float64 because aggregate
  function wants to store result in Float64 "
convert(Float64, x::Atractor) = x.center

现在它(可能是:P)正常工作

res = IndexedTables.aggregate(mediocre, DT, by=(:id1,), with=:v3)
id1     │ 
────────┼────────
"id001" │ 45.9404
"id002" │ 47.0032
"id003" │ 46.0846
"id004" │ 47.2567
...

我希望你看到聚合均值对精度有影响! (有更多的和和除法运算)

答案 2 :(得分:0)

我真的很乐意帮助你,但我花了10分钟安装所有软件包,再过几分钟来运行代码并弄清楚它实际上做了什么(或没有)。如果您提供一个专注于问题的“最小工作示例”,那将是很棒的。实际上,重现问题的唯一要求是看似IndexedTables和两个随机数组。

(对不起,这不是一个完整的答案,但要评论太久了。)

无论如何,如果你阅读IndexedTables.aggregate的文档字符串,你会发现它需要一个带有两个参数的函数,显然会返回一个值::

help?> IndexedTables.aggregate
  aggregate(f::Function, arr::IndexedTable)

  Combine adjacent rows with equal indices using the given 2-argument
  reduction function, returning the result in a new array.

您在发布的错误消息中看到

no method matching mean(::Float64, ::Float64)

由于我不知道您期望计算什么,我现在假设您要计算两个数字的mean值。在这种情况下,您可以为mean()定义另一种方法:

Base.mean(x, y) = (x+y) / 2

这将满足aggregate函数签名要求。但我不确定这是不是你想要的。