Maintaining original order and dimensions of a 3D matrix while using sort - matlab

I'm working with a fairly large 3D matrix (32x87x378), and I want to be able to extract every Nth element of a matrix, while keeping them in the same order. Similar to a previous question I asked: Matlab: Extracting Nth element of a matrix, while maintaining the original order of matrix
The method I was given was quite practical (and simple) and works well in most instances. For a random (1x20) matrix, where I wanted every 5th value, beginning with 4 and 5 (so that I am left with a 1x8 matrix (ab) of elements 4,5,9,10,14,15,19,20). It is done as follows:
r = rand(1,20);
n = 5;
ab = r(sort([4:n:numel(r) 5:n:numel(r)]))
My question is, how can this method be used for a 3D matrix r for it's 3rd dimension (or can it?), such as this:
r = rand(2,5,20);
It should be fairly simple, such as this:
n = 5;
ab = r(sort([4:n:numel(r) 5:n:numel(r)],3));
However, this will then give me a 1x80 matrix, as it does not preserve the original dimensions. Is there a way to correct this using the sort function? I'm also open to other suggestions, but I just want to be sure I am not missing anything.
Thanks in advance.

See if this is what you are after -
ab = r(:,:,sort([4:n:size(r,3) 5:n:size(r,3)]))

Related

3D matrix Indexing using 2D matrix

Could anyone shed some light on how this for loop can be replaced by a single command in MATLAB?
for i = 1 : size(w,3)
x=w(:,:,i);
w1(i,:)=x(B(i),:);
end
clear x
Here, w is a 3D (x by y by z) matrix and B (1 by z) is a vector containing rows pertaining to each layer in w. This for loop takes about 150 seconds to execute when w is 500000 layers deep. I tried using,
Q = w(B,:,:);
Q = reshape(Q(1,:),[500000,2])';
This creates a matrix Q of size 500000 X 2 X 500000 and MATLAB threw me an error saying memory out of bound. Any help would be appreciated!
You are creating intermediate variables (such as x) and using a for loop. The core idea of the following approach is to first pre-populate the indices used and then use linear indexing to access all the elements at once. Then, we can reshape to get the desired result.
ind = [B(1)*ones(size(w,2),1) (1:size(w,2)).' 1*ones(size(w,2),1)];
ind = [ind; [B(2)*ones(size(w,2),1) (1:size(w,2)).' 2*ones(size(w,2),1)]];
ind = [ind; [B(3)*ones(size(w,2),1) (1:size(w,2)).' 3*ones(size(w,2),1)]];
lin_ind = sub2ind(size(w), ind(:,1), ind(:,2), ind(:,3));
w1 = reshape(w(lin_ind),size(w,2),size(w,3)).'
On my system, this matches with w1 computed with the loop given in your question. Note that you may need to use a for loop to pre-populate the indices. I wrote three expressions since I was experimenting with small matrices. Actually, the first three lines can be written in such a way that you don't need loops at all and it still works with any size. I will leave that up to you.

MATLAB ttest2 command gives different values when used on same data in a 2D and 3D matrix?

I'm using MATLAB to perform some statistics on some data. I have two 17x206x378 matrices where dimension 1 are subjects from the same group (so 17 subjects in matrix1, 17 in matrix 2). I want to perform ttests so I get 206 p-values. I then want to do this SEPARATELY for each of the 378 elements in the third dimension.
So say u is a 17x206x378 matrix and d is a different 17x206x378 matrix.
I basically started by doing:
[h,p,ci,s] = ttest2(u,d)
Which does in fact give me a p-matrix size 1x206x378 so everything looked great.
Then to do a quick check I just extracted the first of the third dimension elements from each matrix with:
u1=u(:,:,1); d1=d(:,:,1);
and ran test2 on this data via what you would expect:
[h1,p1,ci1,s1] = ttest2(u1,d1);
I again got a 1x206 p1-matrix of results but the values are not the same as those in the 1x206x378 p-matrix. When I plot the values in both the p(:,:,1) and the p1 vectors the resulting plots look very similar but not exactly the same.
Obviously one of these give results that are significant (below .05) in some instances where the other does not and I do not want to report a fake result so 2 questions I suppose?
1) I am under the impression I am doing the ttests on the same data so what exactly is going on here?
2) If I do ultimately want to get 206 p-values for each of the 378 third dimension elements, what is the correct way to do this?
Thanks for your help!
I ran the following code:
u = rand(17,206,378);
d = rand(17,206,378);
u1 = u(:,:,1);
d1 = d(:,:,1);
[h,p,ci,s] = ttest(u,d);
[h1,p1,ci1,s1] = ttest(u1,d1);
sum(abs(p1(1,:)- p(1,:,1)))
And the output was 0, indicating that the corresponding elements of p and p1 are the same. Maybe it's an indexing issue.

For loop inside another for loop to make new set of vectors

I would like to use a for loop within a for loop (I think) to produce a number of vectors which I can use separately to use polyfit with.
I have a 768x768 matrix and I have split this into 768 separate cell vectors. However I want to split each 1x768 matrix into sections of 16 points - i.e. 48 new vectors which are 16 values in length. I want then to do some curve fitting with this information.
I want to name each of the 48 vectors something different however I want to do this for each of the 768 columns. I can easily do this for either separately but I was hoping that there was a way to combine them. I tried to do this as a for statement within a for statement however it doesn't work, I wondered if anyone could give me some hints on how to produce what I want. I have attached the code.
Qne is my 768*768 matrix with all the points.
N1=768;
x=cell(N,1);
for ii=1:N1;
x{ii}=Qnew(1:N1,ii);
end
for iii = 1:768;
x2{iii}=x{iii};
for iv = 1:39
N2=20;
x3{iii}=x2{iii}(1,(1+N2*iv:N2+N2*iv));
%Gx{iv}=(x3{iv});
end
end
Use a normal 2D matrix for your inner split. Why? It's easy to reshape, and many of the fitting operations you'll likely use will operate on columns of a matrix already.
for ii=1:N1
x{ii} = reshape(Qnew(:, ii), 16, 48);
end
Now x{ii} is a 2D matrix, size 16x48. If you want to address the jj'th split window separately, you can say x{ii}(:, jj). But often you won't have to. If, for example, you want the mean of each window, you can just say mean(x{ii}), which will take the mean of each column, and give you a 48-element row vector back out.
Extra reference for the unasked question: If you ever want overlapping windows of a vector instead of abutting, see buffer in the signal processing toolbox.
Editing my answer:
Going one step further, a 3D matrix is probably the best representation for equal-sized vectors. Remembering that reshape() reads out columnwise, and fills the new matrix columnwise, this can be done with a single reshape:
x = reshape(Qnew, 16, 48, N1);
x is now a 16x48x768 3D array, and the jj'th window of the ii'th vector is now x(:, jj, ii).

matlab: addressing of one index without sub2ind

This is very closely related to this other question, but that question wanted to avoid sub2ind because of performance concerns. I am more concerned about the "unelegance" of using sub2ind.
Let's suppose I want to create another MxN matrix which is all zeros except for one entry in each column that I want to assign from the corresponding entry in a vector, and choice of row in each column is based on another vector. For example:
z = zeros(10,4);
rchoice = [3 1 8 7];
newvals = [123 456 789 10];
% ??? I would like to set z(3,1)=123, z(1,2)=456, z(8,3)=789, z(7,4)=10
I can use sub2ind to accomplish this (which I used in an answer to a closely related question):
z(sub2ind(size(z),rchoice,1:4)) = newvals
but is there another alternative? Seems like logical addressing could be used in some way but I'm stumped, because in order to set the elements of a logical matrix to 1, you're dealing with the same element positions as in the matrix you actually want to address.
There's a much simpler way of doing it.
nCols=size(z,2);
z(rchoice,1:nCols)=diag(newvals);
You can just add the number of rows in previous columns to rchoice to get the linear index directly.
nRows = size(z,1); %# in case you don't know this already
nCols2write = length(newvals);
z(rchoice+[0:nRows:(nRows*(nCols2write-1)]) = newvals;

Compact MATLAB matrix indexing notation

I've got an n-by-k sized matrix, containing k numbers per row. I want to use these k numbers as indexes into a k-dimensional matrix. Is there any compact way of doing so in MATLAB or must I use a for loop?
This is what I want to do (in MATLAB pseudo code), but in a more MATLAB-ish way:
for row=1:1:n
finalTable(row) = kDimensionalMatrix(indexmatrix(row, 1),...
indexmatrix(row, 2),...,indexmatrix(row, k))
end
If you want to avoid having to use a for loop, this is probably the cleanest way to do it:
indexCell = num2cell(indexmatrix, 1);
linearIndexMatrix = sub2ind(size(kDimensionalMatrix), indexCell{:});
finalTable = kDimensionalMatrix(linearIndexMatrix);
The first line puts each column of indexmatrix into separate cells of a cell array using num2cell. This allows us to pass all k columns as a comma-separated list into sub2ind, a function that converts subscripted indices (row, column, etc.) into linear indices (each matrix element is numbered from 1 to N, N being the total number of elements in the matrix). The last line uses these linear indices to replace your for loop. A good discussion about matrix indexing (subscript, linear, and logical) can be found here.
Some more food for thought...
The tendency to shy away from for loops in favor of vectorized solutions is something many MATLAB users (myself included) have become accustomed to. However, newer versions of MATLAB handle looping much more efficiently. As discussed in this answer to another SO question, using for loops can sometimes result in faster-running code than you would get with a vectorized solution.
I'm certainly NOT saying you shouldn't try to vectorize your code anymore, only that every problem is unique. Vectorizing will often be more efficient, but not always. For your problem, the execution speed of for loops versus vectorized code will probably depend on how big the values n and k are.
To treat the elements of the vector indexmatrix(row, :) as separate subscripts, you need the elements as a cell array. So, you could do something like this
subsCell = num2cell( indexmatrix( row, : ) );
finalTable( row ) = kDimensionalMatrix( subsCell{:} );
To expand subsCell as a comma-separated-list, unfortunately you do need the two separate lines. However, this code is independent of k.
Convert your sub-indices into linear indices in a hacky way
ksz = size(kDimensionalMatrix);
cksz = cumprod([ 1 ksz(1:end-1)] );
lidx = ( indexmatrix - 1 ) * cksz' + 1; #'
% lindx is now (n)x1 linear indices into kDimensionalMatrix, one index per row of indexmatrix
% access all n values:
selectedValues = kDimensionalMatrix( lindx );
Cheers!