使用accumarray和用户定义函数的Octave组统计计算返回第三列的值

时间:2016-01-22 17:02:47

标签: arrays matlab grouping octave

要清楚以下不是我原来的问题,它的数据要大得多,而且这个代码是在更大的应用程序和代码库的上下文中。我已经将我的工作简化为最简单的例子,现在是玩具或教学大小,以便清晰,开发和单元测试,因为这对于这些目的以及在stackexchange上共享有很大帮助。我在R中有经验,但在八度音程中没有经验(Matlab)。这是octave版本4.0.0的代码。我似乎停留在翻译组计算,如R的tapply()或by(),以及编写和调用用户定义的函数(加上一些额外的处理,而不是那些内置函数),但现在用八度语言编写。

起始状态是一个数组a,如图所示:

a = [5 1 8 0; 2 1 9 0; 2 3 3 0; 5 3 9 0]

a =

5   1   8   0
2   1   9   0
2   3   3   0
5   3   9   0

我需要做的过程基本上就是这样:按第1列分组,在第3列中找到min统计信息,返回存储在同一行第2列中的值,并将值写入第4列。我想要没有要使用的可选包。内置的accumarray和min函数让我非常接近但我没有找到所需的语法。 Matlab似乎有很多版本的参数传递语法在不同版本上开发,请注意我的代码需要在Octave 4.0.0中运行。

期望的最终状态是相同的数组a,但第4列更新如下所示:

a =

5   1   8   1
2   1   9   3
2   3   3   3
5   3   9   1

在我失败的所有尝试中,我最好的几个几乎没有错过的代码片段和最有趣的事情(未显示,因为有很多页面的尝试不起作用)是:

[x,y] = min(a(a(:,1)==5,3),[],1)
x =  8
y =  1

请注意,y是组内行的索引,但不是数组中的行,只要我稍后进行计算以将索引从group-relative转换为global-relative,并且在那里读取a(y,2)的值,这是每行的正确答案值。

>> [x,y] = min(a(a(:,1)==2,3),[],1)
x =  3
y =  2
>> [~,y] = min(a(a(:,1)==2,3),[],1);
>> y
y =  2

请注意,y是我需要的所有min(),因为它是感兴趣的行的索引。

>> accumarray(a(:,1), a(:,3), [], @([~,y]=min([],[],1)))
parse error:

syntax error

请注意,对于某种语法,我需要在其第一个参数中传递给min(),该值由accumarray的参数1和2确定。

我最终需要在min()返回行索引y之后在组计算中发生类似的事情:

a(y,4) = a(y,2); % y is the desired row index found by min() within each group

所以,我尝试编写一个以可能更简单的语法命名的函数:

>> function idx = ccen(d)
[~,y]=min(d,[],1);
idx=a(y,2);
end
>> accumarray(a(:,1), a(:,3), [], @ccen)
error: 'a' undefined near line 3 column 5
error: called from
ccen at line 3 column 4
accumarray at line 345 column 14

对我来说,令我惊讶的是,功能ccen无法访问。现在我该怎么办?谢谢你的阅读。

1 个答案:

答案 0 :(得分:1)

在MATLAB / Octave中声明函数时,无法访问在作用域外声明的任何变量(默认情况下)。这意味着即使您有a的声明,当您创建该函数时,a也无法在函数范围内访问。

您可以做的是修改ccen,以便为函数提供a,以便在调用函数时可以访问变量。之后,在致电ccen时围绕您对accumarray的来电包裹匿名函数。然而,匿名函数 do 可以捕获未明确声明为函数输入变量的变量范围:

首先:

function idx = ccen(a, d) %// Change
    [~,y]=min(d,[],1);
    idx=a(y,2);
end

现在......

out = accumarray(a(:,1), a(:,3), [], @(x) ccen(a,x)); %// Change last parameter

此调用是可以接受的,因为匿名函数在创建时捕获a。请注意匿名函数中的x如何从accumarray调用中传入。您只需将其作为第二个参数转发给ccen并保持a不变。这并没有改变函数的运行方式......它只是解决了范围问题。

我在Octave中得到以下内容:

octave:10> a = [5 1 8 0; 2 1 9 0; 2 3 3 0; 5 3 9 0]
a =

   5   1   8   0
   2   1   9   0
   2   3   3   0
   5   3   9   0

octave:11> function idx = ccen(a,d)
> [~,y]=min(d,[],1);
> idx=a(y,2);
> end
octave:12> out = accumarray(a(:,1), a(:,3), [], @(x) ccen(a,x))
out =

   0
   1
   0
   0
   1