Matlab 根据给定位置选择矩阵条目
我有以下矩阵(MxN,其中M≤ N) : 我想从每行中分别选择以下列条目(每行一个): 这意味着我们将元素保留在(1,3)、(2,1)和(3,4)中,数组的其余部分应为零 对于上面的示例,我将获得以下输出:Matlab 根据给定位置选择矩阵条目,matlab,performance,matrix,optimization,matrix-indexing,Matlab,Performance,Matrix,Optimization,Matrix Indexing,我有以下矩阵(MxN,其中M≤ N) : 我想从每行中分别选择以下列条目(每行一个): 这意味着我们将元素保留在(1,3)、(2,1)和(3,4)中,数组的其余部分应为零 对于上面的示例,我将获得以下输出: 0 0 0.2785 0 0.9058 0 0 0 0 0 0 0.9706 我目前使用循环生成,当矩阵大小较大时,循环速度会变慢 有人能推荐一种
0 0 0.2785 0
0.9058 0 0 0
0 0 0 0.9706
我目前使用循环生成,当矩阵大小较大时,循环速度会变慢
有人能推荐一种更高效的方法吗?您可以使用
sub2ind
函数将条目索引转换为线性索引
使用线性索引时,matlab将矩阵视为长列向量
org_mat=[0.8147 0.9134 0.2785 0.9649
0.9058 0.6324 0.5469 0.1576
0.1270 0.0975 0.9575 0.9706];
entries=[3,1,4];
linear_entries=sub2ind(size(org_mat),1:length(entries),entries);
new_mat=zeros(size(org_mat));
new_mat(linear_entries)=org_mat(linear_entries);
这应该比sub2ind快:
m = [0.8147, 0.9134, 0.2785, 0.9649;
0.9058, 0.6324, 0.5469, 0.1576;
0.1270, 0.0975, 0.9575, 0.9706];
n=[3,1,4];
linear = (1:length(n)) + (n-1)*size(m,1);
new_m = zeros(size(m));
new_m(linear) = m(linear);
关于绩效的其他回答/评论中有一些讨论。这是一种情况,一个简单(构造良好的)
for
循环将很好地完成工作,基本上不会对性能产生影响
% For some original matrix 'm', and column indexing array 'idx':
x = zeros( size(m) ); % Initialise output of zeros
for ii = 1:numel(idx) % Loop over indices
% Assign the value at the column index for this row
x( ii, idx(ii) ) = m( ii, idx(ii) );
end
这段代码可读性强,速度快。为了证明“快速”的合理性,我已经为所有4种当前答案的方法编写了以下基准测试代码,运行在MatlabR2017B上。以下是输出图
- 对于“小”矩阵,最多2^5列和2^4行:
- 对于“大”矩阵,最多2^15列和2^14行(使用和不使用
解决方案的绘图相同,因为这会破坏缩放):bsxfun
bsxfun
然后sub2ind
然后手动索引然后循环),但y轴是10^(-5)秒,因此使用哪种方法基本上无关紧要
第二个图显示,对于大型矩阵,这些方法基本上是等效的,除了bsxfun
,这很糟糕(这里没有显示,但它需要更多内存)
我会选择更清晰的循环,它允许您更灵活,并且您会确切地记得两年后它在代码中所做的事情
基准代码:
function benchie()
K = 5; % Max loop variable
T = zeros( K, 4 ); % Timing results
for k = 1:K
M = 2^(k-1); N = 2^k; % size of matrix
m = rand( M, N ); % random matrix
idx = randperm( N, M ); % column indices
% Define anonymous functions with no inputs for timeit, and run
f1 = @() f_sub2ind( m, idx ); T(k,1) = timeit(f1);
f2 = @() f_linear( m, idx ); T(k,2) = timeit(f2);
f3 = @() f_loop( m, idx ); T(k,3) = timeit(f3);
f4 = @() f_bsxfun( m, idx ); T(k,4) = timeit(f4);
end
% Plot results
plot( (1:K)', T, 'linewidth', 2 );
legend( {'sub2ind', 'linear', 'loop', 'bsxfun'} );
xlabel( 'k, where matrix had 2^{(k-1)} rows and 2^k columns' );
ylabel( 'function time (s)' )
end
function f_sub2ind( m, idx )
% Using the in-built sub2ind to generate linear indices, then indexing
lin_idx = sub2ind( size(m), 1:numel(idx), idx );
x = zeros( size(m) );
x( lin_idx ) = m( lin_idx );
end
function f_linear( m, idx )
% Manually calculating linear indices, then indexing
lin_idx = (1:numel(idx)) + (idx-1)*size(m,1);
x = zeros( size(m) );
x( lin_idx ) = m( lin_idx );
end
function f_loop( m, idx )
% Directly indexing in a simple loop
x = zeros( size(m) );
for ii = 1:numel(idx)
x( ii, idx(ii) ) = m( ii, idx(ii) );
end
end
function f_bsxfun( m, idx )
% Using bsxfun to create a logical matrix of desired elements, then masking
% Since R2016b, can use 'x = ( (1:size(m,2)) == idx(:) ) .* m;'
x = bsxfun(@eq, 1:size(m,2), idx(:)).*m;
end
无bsxfun
无参与方
设m
为输入矩阵,idx
为带列索引的向量。您可以从idx
构建逻辑掩码,并用m
乘以元素,如下所示:
result = bsxfun(@eq, 1:size(m,2), idx(:)).*m;
TL;DR-这是我的建议:
nI = numel(idx);
sz = size(m);
x = sparse( 1:nI, idx, m(sub2ind( size(m), 1:numel(idx), idx )), sz(1), sz(2), nI);
文章的其余部分讨论了为什么它工作得更好
看到期望的输出矩阵主要由零组成,这实际上需要使用!这不仅可以提高性能(尤其是对于较大的矩阵),还应该对内存更加友好 我将向其中添加两个函数: 本质上的区别在于,我们不是将输出预分配为一个零数组,而是作为一个稀疏数组。基准1的结果如下:
result = bsxfun(@eq, 1:size(m,2), idx(:)).*m;
。。。这就提出了一个问题-为什么稀疏
方法要快一个数量级
为了回答这个问题,我们应该查看基准测试函数中的实际运行时分布,我们可以从中获得。要获得更多信息,我们可以使用profile('-memory',on')
。在运行基准测试的较短版本2(仅针对最高值k
运行)后,我们得到:
因此,我们可以得出以下几点结论:
sparse
!)来说是一个大加号sub2ind
和loop
方法看起来是一样的,我们仍然可以在这两种方法中建立一个“赢家”(参见下图中的紫色框)sub2ind!sub2ind
32毫秒,而循环为41毫秒mlint
警告我们的那样,稀疏循环方法的速度并不令人惊讶:
解释
代码分析器检测可能较慢的稀疏数组的索引模式。更改稀疏数组的非零模式的赋值可能会导致此错误,因为此类赋值会导致相当大的开销
建议的行动
如果可能,使用以下方法构建稀疏数组,并且不要使用索引分配(例如C(4)=B)来构建稀疏数组:
sparse
的内存节省和sub2ind
的矢量化,似乎是运行时间仅为3ms的最佳方法李>
1制作图表的代码:
function q51605093()
K = 15; % Max loop variable
T = zeros( K, 4 ); % Timing results
for k = 1:K
M = 2^(k-1); N = 2^k; % size of matrix
m = rand( M, N ); % random matrix
idx = randperm( N, M ); % column indices
% Define anonymous functions with no inputs, for timeit, and run
f = cell(4,1);
f{1} = @() f_sub2ind( m, idx );
f{2} = @() f_loop( m, idx );
f{3} = @() f_sp_loop( m, idx );
f{4} = @() f_sp_sub2ind( m, idx );
T(k,:) = cellfun(@timeit, f);
if k == 5 % test equality during one of the runs
R = cellfun(@feval, f, 'UniformOutput', false);
assert(isequal(R{:}));
end
end
% Plot results
figure();
semilogy( (1:K).', T, 'linewidth', 2 ); grid on; xticks(0:K);
legend( {'sub2ind', 'loop', 'sp\_loop', 'sp\_sub2ind'}, 'Location', 'NorthWest' );
xlabel( 'k, where matrix had 2^{(k-1)} rows and 2^k columns' );
ylabel( 'function time (s)' )
end
function x = f_sub2ind( m, idx )
% Using the in-built sub2ind to generate linear indices, then indexing
lin_idx = sub2ind( size(m), 1:numel(idx), idx );
x = zeros( size(m) );
x( lin_idx ) = m( lin_idx );
end
function x = f_loop( m, idx )
% Directly indexing in a simple loop
x = zeros( size(m) );
for ii = 1:numel(idx)
x( ii, idx(ii) ) = m( ii, idx(ii) );
end
end
function x = f_sp_loop( m, idx )
nI = numel(idx);
sz = size(m);
x = spalloc( sz(1), sz(2), nI ); % Initialize a sparse array.
for indI = 1:nI
x( indI, idx(indI) ) = m( indI, idx(indI) ); % This generates a warning (inefficient)
end
end
function x = f_sp_sub2ind( m, idx )
nI = numel(idx);
sz = size(m);
x = sparse( 1:nI, idx, m(sub2ind( size(m), 1:numel(idx), idx )), sz(1), sz(2), nI);
end
2用于分析的代码:
function q51605093_MB()
K = 15; % Max loop variable
M = 2^(K-1); N = 2^K; % size of matrix
m = rand( M, N ); % random matrix
idx = randperm( N, M ); % column indices
% Define anonymous functions with no inputs, for timeit, and run
f = cell(4,1);
f{1} = f_sub2ind( m, idx );
f{2} = f_loop( m, idx );
f{3} = f_sp_loop( m, idx );
f{4} = f_sp_sub2ind( m, idx );
% assert(isequal(f{:}));
end
... the rest is the same as above
这意味着(特别是R2016b之后)这是迄今为止最优雅的解决方案,
result=((1:size(m,2))==idx(:).*m
@Wolfies和最慢的:-p刚刚添加到我的中,它对于小矩阵来说非常具有可比性,所以我倾向于整洁,但是对于大矩阵来说它非常差!我真的很欣赏这个简单的解决方案,谢谢@Luis,我也指出这是一个有用的答案。考虑到稀疏矩阵不像普通矩阵那样“友好”,为了完整性,可能值得在最后加入一个full
调用。这将添加回内存分配中,但在稀疏分配之后使用它可能更有效
function q51605093()
K = 15; % Max loop variable
T = zeros( K, 4 ); % Timing results
for k = 1:K
M = 2^(k-1); N = 2^k; % size of matrix
m = rand( M, N ); % random matrix
idx = randperm( N, M ); % column indices
% Define anonymous functions with no inputs, for timeit, and run
f = cell(4,1);
f{1} = @() f_sub2ind( m, idx );
f{2} = @() f_loop( m, idx );
f{3} = @() f_sp_loop( m, idx );
f{4} = @() f_sp_sub2ind( m, idx );
T(k,:) = cellfun(@timeit, f);
if k == 5 % test equality during one of the runs
R = cellfun(@feval, f, 'UniformOutput', false);
assert(isequal(R{:}));
end
end
% Plot results
figure();
semilogy( (1:K).', T, 'linewidth', 2 ); grid on; xticks(0:K);
legend( {'sub2ind', 'loop', 'sp\_loop', 'sp\_sub2ind'}, 'Location', 'NorthWest' );
xlabel( 'k, where matrix had 2^{(k-1)} rows and 2^k columns' );
ylabel( 'function time (s)' )
end
function x = f_sub2ind( m, idx )
% Using the in-built sub2ind to generate linear indices, then indexing
lin_idx = sub2ind( size(m), 1:numel(idx), idx );
x = zeros( size(m) );
x( lin_idx ) = m( lin_idx );
end
function x = f_loop( m, idx )
% Directly indexing in a simple loop
x = zeros( size(m) );
for ii = 1:numel(idx)
x( ii, idx(ii) ) = m( ii, idx(ii) );
end
end
function x = f_sp_loop( m, idx )
nI = numel(idx);
sz = size(m);
x = spalloc( sz(1), sz(2), nI ); % Initialize a sparse array.
for indI = 1:nI
x( indI, idx(indI) ) = m( indI, idx(indI) ); % This generates a warning (inefficient)
end
end
function x = f_sp_sub2ind( m, idx )
nI = numel(idx);
sz = size(m);
x = sparse( 1:nI, idx, m(sub2ind( size(m), 1:numel(idx), idx )), sz(1), sz(2), nI);
end
function q51605093_MB()
K = 15; % Max loop variable
M = 2^(K-1); N = 2^K; % size of matrix
m = rand( M, N ); % random matrix
idx = randperm( N, M ); % column indices
% Define anonymous functions with no inputs, for timeit, and run
f = cell(4,1);
f{1} = f_sub2ind( m, idx );
f{2} = f_loop( m, idx );
f{3} = f_sp_loop( m, idx );
f{4} = f_sp_sub2ind( m, idx );
% assert(isequal(f{:}));
end
... the rest is the same as above