Я ищу способ разбить входной вектор на группы предопределенного размера, причем последняя группа будет меньше, если есть остаток. Я предпочитаю вывод в виде ячейки, но я не возражаю, если это любой другой класс, если он обеспечивает доступ к подгруппам с использованием последующего индексирования.
Ниже приведены примеры ожидаемого поведения:
% Even split
v = 1:6;
grpSz = 2;
% OUT: {[1,2], [3,4], [5,6]}
% Remainder
v = 1:5;
grpSz = 3;
% OUT: {[1,2,3], [4,5]}
% Single group
v = 1:5;
grpSz = 6;
% OUT: {[1,2,3,4,5]}
Ниже приведены несколько полезных величин:
nG = ceil(numel(v)/grpSz)
r = mod(numel(v), grpSz)
На данный момент я использую mat2cell :
function out = evenSplitter(v, grpSz)
nV = numel(v);
nG = ceil(nV/grpSz);
r = mod(nV, grpSz);
out = mat2cell(v, 1, [repmat(grpSz, 1, nG-1), ~r*grpSz+r]);
... который работает, но выглядит немного неуклюжим. Кто-нибудь предложит более элегантное решение?
Одно решение с использованием splitapply:
v = 1:5; % input array
grpSz = 2; % maximal group size
out = splitapply(@(x){x},v,ceil((1:numel(v))/grpSz)) % split v
Этот метод работает для всех приведенных примеров.
splitapply
разделить данные на группы и применить функцию. Анонимная функция @(x){x}
просто помещает каждый элемент группы в ячейку. И ceil((1:numel(v))/grpSz)
создайте массив, который указывает, какие элементы связаны с какими группами.
Например, если v = 1:5
и grpSz = 2
, ceil((1:numel(v))/grpSz)
создаст следующий массив [1 1 2 2 3]
.
Другое решение, вдохновленное (и в значительной степени эквивалентное) ответу obchardon:
function out = evenSplitter(v, grpSz)
idx = ceil((1:numel(v))/grpSz); % Group ID of each element
out = accumarray( idx.', v, [idx(end) 1], @(x){x} ); % idx(end) == nG
Здесь используется accumarray , который может быть быстрее, чем splitapply.
Это может быть быстрее, чем accumarray
для больших входных данных:
out = arrayfun(@(k) v(k:min(k+grpSz-1, end)), 1:grpSz:numel(v), 'UniformOutput', false);
Я думал сделать это с
arrayfun
иUniformOutput
, но не мог собрать воедино... Хорошая работа!