Matlab:将向量分割为固定大小的重叠块

8

我有一个向量,想将其分割成大小为cs,移位为sh的重叠子向量。假设输入向量为:

v=[1 2 3 4 5 6 7 8 9 10 11 12 13]; % A=[1:13]

假设有一个chunksize为4(cs=4),移位为2(sh=2),则结果应如下:

[1 2 3 4]
[3 4 5 6]
[5 6 7 8]
[7 8 9 10]
[9 10 11 12]

请注意,输入向量不一定能被 chunksize 整除,因此某些子向量会被丢弃。有没有快速的方法来计算它,而不需要使用例如 for 循环? 在一个相关的帖子中,我找到了如何在考虑非重叠子向量时完成这项工作。

5个回答

6
您可以按照以下方式使用函数bsxfun来进行操作:
v=[1 2 3 4 5 6 7 8 9 10 11 12 13]; % A=[1:13]
cs=4;
sh=2;

A = v(bsxfun(@plus,(1:cs),(0:sh:length(v)-cs)'));

这是它的工作方式。 bsxfun 对2个数组应用一些基本函数并执行类似repmat的操作,如果输入的大小不匹配。在这种情况下,我生成第一块的索引,并添加每个块的偏移量。由于一个输入是行向量,另一个输入是列向量,因此结果是一个矩阵。最后,当使用矩阵对向量进行索引时,结果是一个矩阵,这正是您所期望的。
而且这只是一个代码行(几乎)总是有趣的 :)。

使用 cs = 5; sh = 2; 进行尝试,将会得到三个而不是两个(当 sh=2 时应该是两个)重叠的条目。 - tim
@bjoern,正如我在评论中提到的那样,你的解决方案确实给出了不同的结果。然而,这并不一定意味着它是正确的。 - Dennis Jaheruddin
好的,现在我明白你在说什么了。我之前百分之百确定sh是描述重叠条目数量的,但现在我看到你们两个都把它当作另一种度量方式。对不起,这是我的错,可能真的漏掉了那个部分。而且我现在才看到描述并不唯一,因为发布的示例是含糊不清的。 - tim
一行代码的 bsxfun(几乎)总是值得 +1! - Luis Mendo

3

您有信号处理工具箱吗?那么命令是buffer。首先查看裸输出:

buffer(v, 4, 2)

ans =
     0     1     3     5     7     9    11
     0     2     4     6     8    10    12
     1     3     5     7     9    11    13
     2     4     6     8    10    12     0

这显然是正确的想法,只需要稍微调整一下,就可以得到你想要的输出:

[y z] = buffer(v, 4, 2, 'nodelay');
y.'

ans =
     1     2     3     4
     3     4     5     6
     5     6     7     8
     7     8     9    10
     9    10    11    12

话虽如此,考虑将向量列式留下来,因为这更能匹配大多数使用情况。例如,每个窗口的平均值只是矩阵的mean,因为列式是默认的。


我喜欢使用那些隐藏在工具箱里的小宝石。问题是,它可能会让你在最后一帧留下部分数据,但这取决于你想要实现什么。 - Sam Delaney

2

我认为最简单的方法是使用循环。 尽管矢量化的解决方案可能更快,但如果结果被正确预分配,循环也应该表现不错。

v = 1:13
cs = 4;
sh = 2;

myMat = NaN(floor((numel(v) - cs) / sh) + 1,cs);
count = 0;

for t = cs:sh:numel(v)
   count = count+1;
   myMat(count,:) = v(t-cs+1:t);
end

1
我不明白为什么这是必要的,特别是因为 Tin 要求一个没有 for 循环的解决方案。 - tim
1
@bjoern:我同意Dennis的观点,他的解决方案更简单。Arrayfun本质上也是一个for循环,在这种情况下甚至更慢。 - Robert Seifert
1
丹尼斯,我认为你的代码里还有错误 -> v(t-3:t); 这个是什么意思?我的意思是它缺少了一些关于 cs 的内容,现在你每次都获取 3 个条目,这是错误的。 - tim
1
这也是我最初更改以进行更正的内容,但仍然存在问题,cs = 5; sh = 2; 在矩阵中给出了三个重叠的条目而不是两个。 - tim
1
@DennisJaheruddin:我认为你计算块数的方式不正确,请看我的回答。你只根据sh来预分配myMat的行数,而实际上它还应该包括cs。对于cs = 10sh = 2,它应该只生成2行,而在你的情况下,它预先分配了5行。 - Bas Swinckels
显示剩余4条评论

1
你可以用ndgrid来完成这个任务:
>> v=1:13; cs=4; sh=2;
>> [Y,X]=ndgrid(1:(cs-sh):(numel(v)-cs+1),0:cs-1)
>> chunks = X+Y
chunks =
     1     2     3     4
     3     4     5     6
     5     6     7     8
     7     8     9    10
     9    10    11    12

关于冒号运算符的第二种语法j:i:k),好处在于如果你计划舍弃额外的条目(例如,1:2:6给出[1 3 5]),则不必精确计算k,它会自动转到j+m*i,其中m = fix((k-j)/i)
不同的测试:
>> v=1:14; cs=5; sh=2; % or v=1:15 or v=1:16
>> [Y,X]=ndgrid(1:(cs-sh):(numel(v)-cs+1),0:cs-1); chunks = X+Y
chunks =
     1     2     3     4     5
     4     5     6     7     8
     7     8     9    10    11
    10    11    12    13    14

并且将形成一个新行,其中包含v=1:17。这是否处理了所有需要的情况?


0
这个怎么样?首先,我根据cssh生成起始索引,以便从完整的向量中切出单个向量,然后删除所有索引,其中idx+cs将超过向量长度,然后通过arrayfun切出单个子向量,然后将它们转换为矩阵:
v=[1 2 3 4 5 6 7 8 9 10 11 12 13]; % A=[1:13]
cs=4;
sh=2;

idx = 1:(cs-sh):length(v);
idx = idx(idx+cs-1 <= length(v))
A = arrayfun(@(i) v(i:(i+cs-1)), idx, 'UniformOutput', false);
cell2mat(A')

例如对于 cs=5; sh=3;,这将会给出:

idx =

     1     3     5     7


ans =

     1     2     3     4     5
     3     4     5     6     7
     5     6     7     8     9
     7     8     9    10    11

根据cs; sh的来源,您可能希望引入一个简单的错误检查,以便cs > 0;以及sh < cs。如果您想在中间留下一些值,则理论上可能会出现sh < 0编辑:修复了一个非常小的错误,现在应该可以运行不同组合的sh和cs了。

为什么要这样做呢?它也可以处理不同的数字。idx 只是为了给我子向量的起始索引,因此我肯定需要 cs-sh 作为步长 - 编辑:我尝试使用不同的向量和不同数量的 cssh,效果非常好。 - tim
对于 cs=5; sh=3,我会假设起始索引为 1 4 7 而不是 1 3 5 7。如果是这种情况,可以使用 idx=1:sh:length(v) - Dennis Jaheruddin
1
抱歉,但我必须告诉你,那是错误的。请重新考虑一下。1 3 5 7 是正确的索引 - 只需查看我的输出矩阵,它显然是正确的(长度为5(即5列),您可以清楚地看到,这是块大小cs = 5),并且有三个重叠的条目(每行的最后三个条目是下一行的前三个条目)。 - tim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接