О форме-агностическом разрезе ndarrays
В этом сообщении я использую термин slice для обозначения подмассива B_i
n-мерного массива A
, для которого size(B_i, d)
равно 1 для некоторой размерности d
. A
состоит из size(A, d)
таких срезов, объединенных вдоль размерности d
.
Например, если ndims(A)
равно 6, а d
равно 3, то выражения вида
A(:, :, i, :, :, :)
для i
в 1:size(A, d)
представляют все срезы (по размеру d
), составляющие A
.
Проблема с выражением типа A(:, :, i, :, :, :)
заключается в том, что она не может быть символически обобщена на срезы по размеру, отличному от 3 в массивах, имеющих ряд измерений, отличных от 6. Например, чтобы получить A
срезы вдоль размерности 2, нужно было бы другое выражение, A(:, i, :, :, :, :)
. Это означает, что такие выражения бесполезны в коде, который агностик относительно формы некоторого массива, из которого нужно извлечь фрагменты.
Ниже приведена моя попытка использования Matlab-noob для реализации аппликации фигур-агностиков. (Имя slice
уже выполнено, поэтому я назвал функцию hslice
, сокращенную для hyperslice
.) Стратегия функции состоит в том, чтобы преобразовать входной массив в подходящий 3-мерный массив, взять желаемый фрагмент вдоль измененного массива второго размера и изменить форму результата, чтобы иметь форму среза из исходного входного массива.
function out = hslice(ndarray, d, i)
sz = size(ndarray);
pfx = sz(1:d-1); % dimensions before d
sfx = sz(d+1:end); % dimensions after d
tmp = reshape(ndarray, prod(pfx), sz(d), prod(sfx));
out = reshape(tmp(:, i, :), [pfx 1 sfx]);
end
Есть ли встроенный или, по крайней мере, более эффективный способ достижения одного и того же результата (в форме-агностическом)?
Ответы
Ответ 1
Да. Вы можете использовать эквивалентность между разделяемыми ячейками ячеек и "списками, разделенными запятыми", и тем фактом, что вы можете использовать char ':' в качестве индекса для динамического создания этого вызова A(:, :, :, i, :, ...)
для произвольных измерений.
function out = slice(A, ix, dim)
subses = repmat({':'}, [1 ndims(A)]);
subses{dim} = ix;
out = A(subses{:});
Это будет полностью обобщено и будет выполнять точно такую же операцию индексации "среза", что и исходное статическое выражение A(:, :, i, :, ...)
, кроме накладных расходов, чтобы свернуть эти строки, чтобы настроить его.
Или, если вы этого захотите, вы можете просто использовать sprintf
для создания этого A(:, :, i, :, ...)
в качестве строки, а затем вызвать eval()
на нем. Но мне нравится избегать eval
, если это вообще возможно.
Обратите внимание, что ваша оригинальная реализация использует быстрые операции и должна выполняться просто отлично, примерно так же быстро, как эта. Я просто размещаю это, потому что я считаю, что он очень читабельен, отвечает на ваш вопрос как первоначально поставленный, и он может быть применен к другим полезным материалам.
Назначение срезам
Вы также можете использовать тот же метод индексов в ячейках, что и lvalue для назначения в срезах массива. Однако вы не можете повторно использовать функцию среза, так как он возвращает извлеченное подмножество массива, а не ссылку на lvalue. Таким образом, вы можете сделать очень схожую функцию, которая выполняет само присваивание.
function A = slice_assign(A, ix, dim, B)
%SLICE_ASSIGN Assign new values to a "slice" of A
subses = repmat({':'}, [1 ndims(A)]);
subses{dim} = ix;
A(subses{:}) = B;
На практике вам также может понадобиться функция, которая просто возвращает вычисленные индексы в массиве ячеек, поэтому вы можете переносить их и использовать их повторно для назначения и ссылки.
function out = slice_subs(A, ix, dim)
subses = repmat({':'}, [1 ndims(A)]);
subses{dim} = ix;
out = subses;
Ответ 2
Вы можете попробовать permute
и setdiff
переместить это измерение в согласованное положение:
function out = hslice(ndarray, d, i)
subdims = setdiff(1:ndims(ndarray),d);
sz = size(ndarray);
outsz = sz(subdims);
order = [d subdims];
ndarray = permute(ndarray,order);
out = reshape(ndarray(i,:),outsz);
end
Например:
d = 3; i = 2;
nd = randi(23,3,3,3,2);
out = hslice(nd,d,i); % out = squeeze(nd(:,:,i,:)) for d=3
НО, данные переписываются до нарезки здесь, а не с кодом в вопросе. Итак, я бы действительно пошел с OP!