或等效地,"相当于NumPy在Matlab中的省略号索引"
说我有一些高维数组:
x = zeros(3, 4, 5, 6);
我想编写一个采用大小为(3, ...)
的数组的函数,并进行一些计算。在NumPy中,我可以这样写:
def fun(x):
return x[0]*x[1] + x[2]
然而,MATLAB中的等价物不起作用,因为使用一个整数进行索引会将数组展平为1d
function y = fun_bad(x)
y = x(1)*x(2) + x(3)
我可以使用
为多达三维数组做这项工作function y = fun_ok3d(x)
y = x(1,:,:)*x(2,:,:) + x(3,:,:)
如果我想让它适用于多达10维的数组,我可以写
function y = fun_ok10d(x)
y = x(1,:,:,:,:,:,:,:,:,:)*x(2,:,:,:,:,:,:,:,:,:) + x(3,:,:,:,:,:,:,:,:,:)
我怎样才能避免在这里编写愚蠢数量的冒号,只是让它适用于任何维度?是否有一些x(1,...)
语法暗示了这一点?
NumPy可以在索引表达式中使用...
(Ellipsis
)字面值,根据需要多次使用" :
" ,这将解决这个问题。
答案 0 :(得分:18)
':'
我不知道如何指定
:
根据需要多次
同时保持形状。但是你可以指定
:
任意次
在运行时定义的次数。使用此方法,您可以保留形状,前提是索引数与维数一致。
这是使用从单元格数组生成的comma-separated list来完成的,并利用the string ':'
can be used as an index instead of :
:
function y = fun(x)
colons = repmat({':'}, 1, ndims(x)-1); % row cell array containing the string ':'
% repeated the required number of times
y = x(1,colons{:}).*x(2,colons{:}) + x(3,colons{:});
这种方法可以很容易地推广到沿任何维度的索引,而不仅仅是第一个:
function y = fun(x, dim)
% Input argument dim is the dimension along which to index
colons_pre = repmat({':'}, 1, dim-1);
colons_post = repmat({':'}, 1, ndims(x)-dim);
y = x(colons_pre{:}, 1, colons_post{:}) ...
.*x(colons_pre{:}, 2, colons_post{:}) ...
+ x(colons_pre{:}, 3, colons_post{:});
您可以使用num2cell
沿第一维拆分数组,然后将操作应用于生成的子数组。当然这会占用更多内存;而noted by @Adriaan则更慢。
function y = fun(x)
xs = num2cell(x, [2:ndims(x)]); % x split along the first dimension
y = xs{1}.*xs{2} + xs{3};
或者,为了沿任何维度索引:
function y = fun(x, dim)
xs = num2cell(x, [1:dim-1 dim+1:ndims(x)]); % x split along dimension dim
y = xs{1}.*xs{2} + xs{3};
答案 1 :(得分:15)
MATLAB在使用单个冒号时展平所有尾随尺寸,因此您可以使用它来从 N -D数组到2D数组,您可以reshape
返回到计算后的原始 N 尺寸。
如果你想使用第一个维度,你可以使用一段相对简单的短代码:
function y = MyMultiDimensional(x)
x_size = size(x); % Get input size
yflat = x(1,:) .* x(2,:) + x(3,:); % Calculate "flattened" 2D function
y = reshape(yflat, [1 x_size(2:end)]); % Reshape output back to original size
end
如果您希望自己的功能沿着总共 N 中的 n -th维度行事,您可以permute
将该维度放在前面:
function y = MyMultiDimensional(x,n)
x_size = size(x); % Get input size
Order = 1:numel(x_size);
Order(n)=[]; % Remove n-th dimension
Order2 = [n, Order]; % Prepend n-th dimension
xPermuted = permute(x,Order2); % permute the n-th dimension to the front
yTmp = xPermuted (1,:) .* xPermuted (2,:) + xPermuted (3,:); % Calculate "flattened" 2D function
y = reshape(yTmp, x_size(Order)); % Reshape output back to original size
end
我计算了Luis'和我的方法的两种方法的结果:
function timeMultiDim()
x = rand(1e1,1e1,1e1,1e1,1e1,1e1,1e1,1e1);
function y = Luis1(x)
colons = repmat({':'}, 1, ndims(x)-1); % row cell array containing the string ':'
% repeated the required number of times
y = x(1,colons{:}).*x(2,colons{:}) + x(3,colons{:});
end
function y = Luis2(x)
xs = num2cell(x, [2:ndims(x)]); % x split along the first dimension
y = xs{1}.*xs{2} + xs{3};
end
function y = Adriaan(x)
x_size = size(x); % Get input size
yflat = x(1,:) .* x(2,:) + x(3,:); % Calculate "flattened" 2D function
y = reshape(yflat, [1 x_size(2:end)]); % Reshape output back to original size
end
n=1;
function y = Adriaan2(x,n)
x_size = size(x); % Get input size
Order = 1:numel(x_size);
Order(n)=[]; % Remove n-th dimension
Order2 = [n, Order]; % Prepend n-th dimension
xPermuted = permute(x,Order2); % permute the n-th dimension to the front
yTmp = xPermuted (1,:) .* xPermuted (2,:) + xPermuted (3,:); % Calculate "flattened" 2D function
y = reshape(yTmp, x_size(Order)); % Reshape output back to original size
end
t1 = timeit(@() Luis1(x));
t2 = timeit(@() Luis2(x));
t3 = timeit(@() Adriaan(x));
t4 = timeit(@() Adriaan2(x,n));
format long g;
fprintf('Luis 1: %f seconds\n', t1);
fprintf('Luis 2: %f seconds\n', t2);
fprintf('Adriaan 1: %f seconds\n', t3);
fprintf('Adriaan 2: %f seconds\n', t4);
end
Luis 1: 0.698139 seconds
Luis 2: 4.082378 seconds
Adriaan 1: 0.696034 seconds
Adriaan 2: 0.691597 seconds
所以,去一个单元格很糟糕,需要花费5倍以上的时间,reshape
和':'
几乎没有分开,所以要归结为偏好。