【发布时间】:2016-02-11 16:25:26
【问题描述】:
我通常需要使用给定的聚合函数(即总和、平均值等)来总结具有不规则时序的时间序列。但是,我目前的解决方案似乎效率低下且速度慢。
取聚合函数:
function aggArray = aggregate(array, groupIndex, collapseFn)
groups = unique(groupIndex, 'rows');
aggArray = nan(size(groups, 1), size(array, 2));
for iGr = 1:size(groups,1)
grIdx = all(groupIndex == repmat(groups(iGr,:), [size(groupIndex,1), 1]), 2);
for iSer = 1:size(array, 2)
aggArray(iGr,iSer) = collapseFn(array(grIdx,iSer));
end
end
end
请注意,array 和 groupIndex 都可以是二维的。 array 中的每一列都是要聚合的独立系列,但应将 groupIndex 的列放在一起(作为一行)以指定一个周期。
那么当我们给它带上一个不规则的时间序列时(注意第二个周期比它长一个基周期),时序结果很差:
a = rand(20006,10);
b = transpose([ones(1,5) 2*ones(1,6) sort(repmat((3:4001), [1 5]))]);
tic; aggregate(a, b, @sum); toc
Elapsed time is 1.370001 seconds.
使用分析器,我们可以发现grpIdx 行大约需要执行时间的 1/4 (.28 s),iSer 循环大约需要总时间的 3/4 (1.17 s) ( 1.48 秒)。
将此与周期无关的情况进行比较:
tic; cumsum(a); toc
Elapsed time is 0.000930 seconds.
有没有更有效的方法来聚合这些数据?
计时结果
获取每个响应并将其放入一个单独的函数中,这是我在 Windows 7 和 Intel i7 上使用 Matlab 2015b 的 timeit 得到的计时结果:
original | 1.32451
felix1 | 0.35446
felix2 | 0.16432
divakar1 | 0.41905
divakar2 | 0.30509
divakar3 | 0.16738
matthewGunn1 | 0.02678
matthewGunn2 | 0.01977
澄清groupIndex
2D groupIndex 的一个示例是为一组涵盖 1980-2015 年的每日数据指定年号和周号:
a2 = rand(36*52*5, 10);
b2 = [sort(repmat(1980:2015, [1 52*5]))' repmat(1:52, [1 36*5])'];
因此,“年-周”期间由一行groupIndex 唯一标识。这可以通过调用unique(groupIndex, 'rows') 并获取第三个输出来有效处理,因此请随意忽略这部分问题。
【问题讨论】:
-
对于每个组,您的代码必须做一堆 O(n) 的废话,其中 n 是整个数据矩阵的大小。
grIdx = all(groupIndex == repmat(groups(iGr,:), [size(groupIndex,1), 1]), 2);这条线不会很快。我遇到了类似的问题:我有一个数据矩阵和一个列向量,表示(数据矩阵的)行是哪个组的成员。对于每个组,我想提取该组的数据并进行一些计算。我最终用 C++ 编写了一个 mex 函数,它返回一个元胞数组,显示哪个组在哪些行上有数据。 -
如果 groupIndex 只是一个列向量,我可能会发布一些 mex c++ 代码,您可能会觉得有用。它需要一个 groupIndex 向量,并为每个组显示该组所在的 groupIndex 行。
-
@MatthewGunn 这将是一个开始。但它不会取代内部的 for 循环,不是吗?我认为
grIdx行是问题的一个明确部分,但是大部分执行时间都花在了iSer循环上。 -
只要每个组至少有两个观察值,您就可以将其替换为: aggArray(iGr,:) = collapseFn(array(grIdx,:))像平均值等函数......但是,它没有那么健壮
-
我一直在这样做,直到我开始遇到奇怪的错误并添加了它。可能值得为此添加一个 if 语句。我必须检查分析器所说的内容。
标签: matlab time-series