关于形状不可知的切片的ndarrays

时间:2014-03-20 15:14:56

标签: matlab multidimensional-array

在这篇文章中,我使用术语 slice 来引用 n - 维数组B_i的子数组A对于某个维度size(B_i, d)d为1。 Asize(A, d)个此类切片组成,沿着维d连接。

例如,如果ndims(A)为6而d为3,那么表单的表达式为

A(:, :, i, :, :, :)
i1:size(A, d)

代表组成d的所有切片(沿着维度A)。

A(:, :, i, :, :, :)这样的表达式的问题在于它不能被概括为符号到沿着与不同于6的维度的数组中的3不同的维度的切片。例如,到沿着维度2获取A个切片,需要一个不同的表达式A(:, i, :, :, :, :)。这意味着这样的表达式在代码中是无用的,这些代码与要从中提取切片的某个数组的形状无关。

下面的函数是我的matlab-noob尝试实现与形状无关的切片。 (名称slice已经被采用,因此我调用函数hslice,简称hyperslice。)函数的策略是将输入数组重新整形为合适的三维数组,沿着重新整形的数组 second 维度的所需切片,并将结果重新整形为具有原始输入数组中切片的形状。

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

是否有内置或至少更有效的方法来实现相同的结果(以形状无关的方式)?

2 个答案:

答案 0 :(得分:17)

呀。您可以使用解除引用的单元格数组和"逗号分隔的列表"之间的等价,以及您可以使用字符':'作为动态构造任意维度A(:, :, :, i, :, ...)调用的索引。

function out = slice(A, ix, dim)

subses = repmat({':'}, [1 ndims(A)]);
subses{dim} = ix;
out = A(subses{:});

这将完全概括,并将完全相同的"切片"索引操作作为原始静态A(:, :, i, :, ...)表达式,除了将这些字符串设置为设置它的开销。

或者,如果您愿意,可以使用sprintfA(:, :, i, :, ...)构造为字符串,然后在其上调用eval()。但我想尽可能避免eval

请注意,您的原始实现正在使用快速操作,并且应该执行得很好,与此速度一样快。我发布此信息是因为我认为它非常易读,可以回答您最初提出的问题,并且可以应用于其他有用的内容。

分配给切片

您也可以使用相同的单元格下标技术作为左值来分配数组的切片。但是,您不能直接重用切片函数,因为它返回数组的提取子集,而不是左值引用。所以你可以创建一个非常类似于赋值的函数。

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;

答案 1 :(得分:3)

您可以尝试permutesetdiff将该维度移至一致的位置:

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一起去了!