Greg
Greg

Reputation: 161

Extract several columns from 3d matrix

I currently have an array A which has dimensions N x t x t. I want to create a 2D matrix, N x t, of the form:

B = [ A[:,1,1] A[:,2,2],...,A[:,t,t]]

Obviously, 2 ways I could do this are writing it out in full (impractical since t is large) and loops (potentially slow). Is there a way to do this without loops. I figured it would work if I did:

B = A[:,[1:end],[1:end]]

but that just gave me back the original matrix.

Upvotes: 4

Views: 1727

Answers (3)

j_kubik
j_kubik

Reputation: 6181

Here is my take on that subject:

mask = repmat(logical(permute(eye(size(A,2)), [3 1 2])), size(A,1), 1);
newA = reshape(A(mask), size(A,1), []);

Just generate mask, apply it and reshape result to the right shape.

Edit:

Or even simpler:

newA = A(:, logical(eye(size(A,2))))

or along the same lines,

newA = A(:, diag(true(size(A,2),1)));

which is a tad faster.

Upvotes: 3

Rasman
Rasman

Reputation: 5359

Basically, you need to start thinking about how to reorganize your matrix.

From

A = randn([5 3 3]);

Let's look at

A(:,:)

Basically you want columns 1, 5, 9. Thinking about it, knowing t = 3, from the present column you want to increment by t + 1. The formula is basically:

((1:3)-1)*(3+1)+1 %(or (0:2)*(3+1) + 1)

Which plugged in A yields your solution

A(:,((1:3)-1)*(3+1)+1)

In a general format, you can do:

A(:,((1:t)-1)*(t+1)+1)

EDIT:

Amro basically just put me to shame. The idea is still the same, it just becomes more readable thanks to end

Therefore use:

A(:,1:t+1:end)

Upvotes: 8

Rody Oldenhuis
Rody Oldenhuis

Reputation: 38032

MATLAB is pretty good with vectors and matrices, but when it comes to "general arrays" you'll often have to switch to "general methods". When you're used to the ease with which you manipulate matrices/vectors, this will seem really clumsy, backward and not very convenient at all (there's actually very defendable reasons for that, but that's a discussion for another time :).

The version below loops through each page via arrayfun (which is only faster than a normal loop for large matrices) and calls diag on each page:

% "flip" the array, so that 3rd dimension becomes the 1st (rows), and 
% the 1st dimension becomes the 3rd, so that each "page" is a regular 
% matrix. You want the diagonals of all these matrices.
b = permute(a, [3 2 1]);

% Now "loop" through the pages, and collect the matrix diagonal for each page
c = arrayfun(@(ii)diag(b(:,:,ii)), 1:size(b,3), 'UniformOutput', false);

% The above will output a cell-array, which you can cast back to 
% a numeric matrix through this operation: 
A = [c{:}];

Upvotes: 1

Related Questions