在MATLAB中拆分矢量

And*_*rič 16 arrays matlab vector

我试图优雅地分割矢量.例如,

vec = [1 2 3 4 5 6 7 8 9 10]
Run Code Online (Sandbox Code Playgroud)

根据0和1的另一个相同长度的向量,其中1表示向量应该被分割的位置 - 或者更确切地说:

cut = [0 0 0 1 0 0 0 0 1 0]
Run Code Online (Sandbox Code Playgroud)

给我们一个类似于以下的单元格输出:

[1 2 3] [5 6 7 8] [10]
Run Code Online (Sandbox Code Playgroud)

Div*_*kar 12

解决方案代码

您可以使用cumsum&accumarray获得有效的解决方案 -

%// Create ID/labels for use with accumarray later on
id = cumsum(cut)+1   

%// Mask to get valid values from cut and vec corresponding to ones in cut
mask = cut==0        

%// Finally get the output with accumarray using masked IDs and vec values 
out = accumarray(id(mask).',vec(mask).',[],@(x) {x})
Run Code Online (Sandbox Code Playgroud)

标杆

以下是在列出的三种最常用方法上使用大量输入来解决此问题时的一些性能数字 -

N = 100000;  %// Input Datasize

vec = randi(100,1,N); %// Random inputs
cut = randi(2,1,N)-1;

disp('-------------------- With CUMSUM + ACCUMARRAY')
tic
id = cumsum(cut)+1;
mask = cut==0;
out = accumarray(id(mask).',vec(mask).',[],@(x) {x});
toc

disp('-------------------- With FIND + ARRAYFUN')
tic
N = numel(vec);
ind = find(cut);
ind_before = [ind-1 N]; ind_before(ind_before < 1) = 1;
ind_after = [1 ind+1]; ind_after(ind_after > N) = N;
out = arrayfun(@(x,y) vec(x:y), ind_after, ind_before, 'uni', 0);
toc

disp('-------------------- With CUMSUM + ARRAYFUN')
tic
cutsum = cumsum(cut);
cutsum(cut == 1) = NaN;  %Don't include the cut indices themselves
sumvals = unique(cutsum);      % Find the values to use in indexing vec for the output
sumvals(isnan(sumvals)) = [];  %Remove NaN values from sumvals
output = arrayfun(@(val) vec(cutsum == val), sumvals, 'UniformOutput', 0);
toc
Run Code Online (Sandbox Code Playgroud)

运行时

-------------------- With CUMSUM + ACCUMARRAY
Elapsed time is 0.068102 seconds.
-------------------- With FIND + ARRAYFUN
Elapsed time is 0.117953 seconds.
-------------------- With CUMSUM + ARRAYFUN
Elapsed time is 12.560973 seconds.
Run Code Online (Sandbox Code Playgroud)

特殊情况:如果你可能有自己的运行 1,你需要修改下面列出的一些东西 -

%// Mask to get valid values from cut and vec corresponding to ones in cut
mask = cut==0  

%// Setup IDs differently this time. The idea is to have successive IDs.
id = cumsum(cut)+1
[~,~,id] = unique(id(mask))

%// Finally get the output with accumarray using masked IDs and vec values 
out = accumarray(id(:),vec(mask).',[],@(x) {x})
Run Code Online (Sandbox Code Playgroud)

样本运行与这种情况 -

>> vec
vec =
     1     2     3     4     5     6     7     8     9    10
>> cut
cut =
     1     0     0     1     1     0     0     0     1     0
>> celldisp(out)
out{1} =
     2
     3
out{2} =
     6
     7
     8
out{3} =
    10
Run Code Online (Sandbox Code Playgroud)


Ton*_*ony 7

对于这个问题,一个方便的功能是cumsum,它可以创建切割数组的累积和.生成输出单元阵列的代码如下:

vec = [1 2 3 4 5 6 7 8 9 10];
cut = [0 0 0 1 0 0 0 0 1 0];

cutsum = cumsum(cut);
cutsum(cut == 1) = NaN;  %Don't include the cut indices themselves
sumvals = unique(cutsum);      % Find the values to use in indexing vec for the output
sumvals(isnan(sumvals)) = [];  %Remove NaN values from sumvals
output = {};
for i=1:numel(sumvals)
    output{i} = vec(cutsum == sumvals(i)); %#ok<SAGROW>
end
Run Code Online (Sandbox Code Playgroud)

如另一个答案所示,您可以使用arrayfun创建带有结果的单元格数组.要在此处应用,您可以for使用以下行替换循环(以及输出的初始化):

output = arrayfun(@(val) vec(cutsum == val), sumvals, 'UniformOutput', 0);
Run Code Online (Sandbox Code Playgroud)

这很好,因为它不会最终增长输出单元阵列.

这个例程的关键特性是变量cutsum,最终看起来像这样:

cutsum =
     0     0     0   NaN     1     1     1     1   NaN     2
Run Code Online (Sandbox Code Playgroud)

然后我们需要做的就是使用它来创建索引以将数据拉出原始vec数组.我们从零循环到最大并拉出匹配值.请注意,此例程处理可能出现的某些情况.例如,它在cut数组的最开始和最后处理1个值,它优雅地处理cut数组中的重复值,而不在输出中创建空数组.这是因为使用了unique创建要搜索的值集cutsum,以及我们在sumvals数组中抛出NaN值的事实.

你可以使用-1而不是NaN作为切割位置的信号标志来使用,但我喜欢NaN的可读性.-1值可能更有效,因为你所要做的就是截断sumvals数组中的第一个元素.我只是偏爱使用NaN作为信号标志.

这个输出是一个单元格数组,结果如下:

output{1} =
     1     2     3
output{2} =
     5     6     7     8
output{3} =
    10
Run Code Online (Sandbox Code Playgroud)

我们需要处理一些奇怪的情况.考虑一下情况:

vec = [1 2 3 4 5 6 7 8 9 10 11 12 13 14];
cut = [1 0 0 1 1 0 0 0 0 1  0  0  0  1];
Run Code Online (Sandbox Code Playgroud)

那里有重复的1个,以及开头和结尾的1个.此例程正确处理所有这些,没有任何空集:

output{1} = 
     2     3
output{2} =
     6     7     8     9
output{3} = 
    11    12    13
Run Code Online (Sandbox Code Playgroud)


ray*_*ica 6

你可以用组合做到这一点findarrayfun:

vec = [1 2 3 4 5 6 7 8 9 10];
N = numel(vec);
cut = [0 0 0 1 0 0 0 0 1 0];
ind = find(cut);
ind_before = [ind-1 N]; ind_before(ind_before < 1) = 1;
ind_after = [1 ind+1]; ind_after(ind_after > N) = N;
out = arrayfun(@(x,y) vec(x:y), ind_after, ind_before, 'uni', 0);
Run Code Online (Sandbox Code Playgroud)

我们得到:

>> celldisp(out)

out{1} =

     1     2     3         

out{2} =

     5     6     7     8    

out{3} =

    10
Run Code Online (Sandbox Code Playgroud)

那么这是如何工作的呢?好吧,第一行定义了你的输入向量,第二行定义了这个向量中有多少个元素,第三行表示你的cut向量,它定义了我们需要在向量中切割的位置.接下来,我们使用find确定非零的位置,cut其中对应于向量中的分割点.如果您注意到,分割点确定了我们需要停止收集元素并开始收集元素的位置.

但是,我们需要考虑向量的开始以及结束. ind_after告诉我们需要开始收集价值ind_before的位置,并告诉我们需要停止收集价值的位置.要计算这些起始位置和结束位置,只需取结果find并分别加1和减1.

每个相应的位置ind_afterind_before告诉我们在哪里开始和停止一起收集值.为了适应向量的开头,ind_after需要在开头插入索引1,因为索引1是我们应该在开始时收集值的地方.同样,N需要在最后插入,ind_before因为这是我们需要停止在数组末尾收集值的地方.

现在对于ind_afterind_before,存在退化情况,其中切割点可以在向量的末尾或开始处.如果是这种情况,则减1或加1将生成超出界限的开始和停止位置.我们在第4行和第5行代码中检查这一点,并将它们设置为1或N取决于我们是在数组的开头还是结尾.

的代码使用的最后一行arrayfun和遍历每对ind_afterind_before切片到我们的载体.每个结果都放在一个单元格数组中,我们的输出如下.


我们可以通过在开头和结尾放置1 cut并在其间放置一些值来检查退化情况:

vec = [1 2 3 4 5 6 7 8 9 10];
cut = [1 0 0 1 0 0 0 1 0 1];
Run Code Online (Sandbox Code Playgroud)

使用此示例和上面的代码,我们得到:

>> celldisp(out)

out{1} =

     1

out{2} =

     2     3         

out{3} =

     5     6     7

out{4} =

     9         

out{5} =

    10
Run Code Online (Sandbox Code Playgroud)


Not*_*hat 6

还有另一种方式,但这次没有任何循环或累积......

lengths = diff(find([1 cut 1])) - 1;    % assuming a row vector
lengths = lengths(lengths > 0);
data = vec(~cut);
result = mat2cell(data, 1, lengths);    % also assuming a row vector
Run Code Online (Sandbox Code Playgroud)

diff(find(...))结构为我们提供了从每个标记到下一行的距离-我们与追加界桩[1 cut 1]赶上其触摸结束零的任何运行.但是,每个长度都包含其标记,因此我们减去1来解释它,并删除任何仅覆盖连续标记的长度,这样我们就不会在输出中得到任何不需要的空单元格.

对于数据,我们屏蔽掉与标记相对应的任何元素,因此我们只有我们想要分区的有效部分.最后,随着数据准备分裂以及分割它的长度,这正是mat2cell它的用途.

另外,使用@Divakar的基准代码 ;

-------------------- With CUMSUM + ACCUMARRAY
Elapsed time is 0.272810 seconds.
-------------------- With FIND + ARRAYFUN
Elapsed time is 0.436276 seconds.
-------------------- With CUMSUM + ARRAYFUN
Elapsed time is 17.112259 seconds.
-------------------- With mat2cell
Elapsed time is 0.084207 seconds.
Run Code Online (Sandbox Code Playgroud)

...只是在说' ;)