Equivalent for (:) for slices of higher dimension matrices - matlab

Context
You have a 3D variable A
rng('default')
A = randi(100,5,7,3);
Problem
You want to get a column vector with all the values of a given slice along the third dimension of A, e.g.
Tmp = A(:,:,2);
out = Tmp(:);
Question
Is there a built in way to do this directly without having to use a temporary variable or a function (i.e. with a combination of brackets, ...)? The closest to what I look for I have found yet would be
out = reshape(A(:,:,2),[],1)
Which I find a bit "heavy". I was looking for something like (A(:,:,2))(:) but it doesn't work in MATLAB.
The fact that they added the input all in functions like any would suggest that there is not but I figured I'd still ask

Related

Applying (with as few loops as possible) a function to given elements/voxels (x,y,z) taken from subfields of multiple structs (nifti's) in MATLAB?

I have a dataset of n nifti (.nii) images. Ideally, I'd like to be able to get the value of the same voxel/element from each image, and apply a function to the n data points. I'd like to do this for each voxel/element across the whole image, so that I can reconvert the result back into .nii format.
I've used the Tools for NIfTI and ANALYZE image toolbox to load my images:
data(1)=load_nii('C:\file1.nii');
data(2)=load_nii('C:\file2.nii');
...
data(n)=load_nii('C:\filen.nii');
From which I obtain a struct object with each sub-field containing one loaded nifti. Each of these has a subfield 'img' corresponding to the image data I want to work on. The problem comes from trying to select a given xyz within each img field of data(1) to data(n). As I discovered, it isn't possible to select in this way:
data(:).img(x,y,z)
or
data(1:n).img(x,y,z)
because matlab doesn't support it. The contents of the first brackets have to be scalar for the call to work. The solution from googling around seems to be a loop that creates a temporary variable:
for z = 1:nz
for x = 1:nx
for y = 1:ny
for i=1:n;
points(i)=data(i).img(x,y,z);
end
[p1(x,y,z,:),~,p2(x,y,z)] = fit_data(a,points,b);
end
end
end
which works, but takes too long (several days) for a single set of images given the size of nx, ny, nz (several hundred each).
I've been looking for a solution to speed up the code, which I believe depends on removing those loops by vectorisation, preselecting the img fields (via getfield ?)and concatenating them, and applying something like arrayfun/cellfun/structfun, but i'm frankly a bit lost on how to do it. I can only think of ways to pre-select which themselves require loops, which seems to defeat the purpose of the exercise (though a solution with fewer loops, or fewer nested loops at least, might do it), or fun into the same problem that calls like data(:).img(x,y,z) dont work. googling around again is throwing up ways to select and concatenate fields within a struct, or a given field across multiple structs. But I can't find anything for my problem: select an element from a non-scalar sub-field in a sub-struct of a struct object (with the minimum of loops). Finally I need the output to be in the form of a matrix that the toolbox above can turn back into a nifti.
Any and all suggestions, clues, hints and help greatly appreciated!
You can concatenate images as a 4D array and use linear indexes to speed up calculations:
img = cat(4,data.img);
p1 = zeros(nx,ny,nz,n);
p2 = zeros(nx,ny,nz);
sz = ny*nx*nz;
for k = 1 : sz
points = img(k:sz:end);
[p1(k:sz:end),~,p2(k)] = fit_data(a,points,b);
end

How to make a vector like vec(x) in MATLAB?

I am trying to replicate this formula:
I have gathered all variables in my workspace. However estimating vec(Theta') does not seem to work and so I am a little bit stuck.
Theta = A*B-C;
vTheta = vec(Theta');
A, B and C are defined. The problem is that MATLAB does not seem to know the function vec to do what I would like to do with Theta as in the formula.
How to fix this?
I don't know where you got that equation from, but vec is a function in R, maybe it's related to that? If you want to convert a matrix Theta into a vector, do
Theta(:)
Edit: If you need to transpose the matrix first, MATLAB might not let you do Theta'(:). Instead do it in two steps:
tmp = Theta'; tmp(:)
As written above the Colon Operator is the way vectorize defined variable.
Yet, sometime we want to vectorize a sub set of a variable.
Let's say we have a matrix - mA and we'd like to vectorize a sub section of it - mA(2:3, 4:7).
One way is to define a new variable and vectorize it:
vA = mA(2:3, 4:7);
vA = vA(:);
Yet, what if we only wanted to use this inside another expression and only once?
Could we escape the need to generate explicit variable?
Well, unfortunately MATLAB doesn't have the view() functionality like in Julia.
Yet if you want to avoid explicitly naming new variable (I'm not sure if MATLAB's JIT Engine can also void the memory allocation as Julia) you can do:
reshape(mA(2:3, 4:7), [], 1)
This will always yield a column vector.
You can also use:
reshape(mA(2:3, 4:7), 1, [])
To generate row vector.
For instance you can do:
reshape(mA(2:3, 4:7), 1, []) * reshape(mA(2:3, 4:7), [], 1, )
This will be the sum of squared values of those elements.

Creating functions in Matlab

Hi, I am trying to write a function as per the question. I have tried to create four sub-matrices which are the reverse of each other and then multiply to give the products demanded by the question. My attempt:
function T = custom_blocksT(n,m)
T(1:end,end-1:1);
T(1:end,end:-1:1)*2;
T(1:end,end:-1:1)*3;
T(1:end,end:-1:1)*4;
What I'm unsure of is
(i) What do the the indivual sub-matrices(T(1:end,end-1:1);)need to be equal to? I was thinking of(1:3)?
(ii) I tried to create a generic sub-matrix which can take any size matrix input using end was this correct or can't you do that? I keep getting this error
Undefined function or variable 'T'.
Error in custom_blocksT (line 2)
T(1:end,end-1:1);
I have searched the Matlab documentation and stacked overflow, but the problem is I'm not quite sure what I'm supposed to be looking for in terms of solving this question.
If someone could help me I would be very thankfull.
There are many problems with your function:
function T = custom_blocksT(n,m)
T(1:end,end-1:1);
T(1:end,end:-1:1)*2;
T(1:end,end:-1:1)*3;
T(1:end,end:-1:1)*4;
end
This is an extremely basic question, I highly recommend you find and work through some very basic MATLAB tutorials before continuing, even before reading this answer to be honest.
That said here is what you should have done and a bit of what you did wrong:
First, you are getting the error that T dos not exist because it doesn't. The only variables that exist in your function are those that you create in the function or those that are passed in as parameters. You should have passed in T as a parameter, but instead you passed in n and m which you don't use.
In the question, they call the function using the example:
custom_blocks([1:3;3:-1:1])
So you can see that they are only passing in one variable, your function takes two and that's already a problem. The one variable is the matrix, not it's dimensions. And the matrix they are passing in is [1:3;3:-1:1] which if you type in the command line you will see gives you
[1 2 3
3 2 1]
So for your first line to take in one argument which is that matrix it should rather read
function TOut = custom_blocks(TIn)
Now what they are asking you to do is create a matrix, TOut, which is just different multiples of TIn concatenated.
What you've done with say TIn(1:end,end-1:1)*2; is just ask MATLAB to multiple TIn by 2 (that's the only correct bit) but then do nothing with it. Furthermore, indexing the rows by 1:end will do what you want (i.e. request all the rows) but in MATLAB you can actually just use : for that. Indexing the columns by end-1:1 will also call all the columns, but in reverse order. So in effect you are flipping your matrix left-to-right which I'm sure is not what you wanted. So you could have just written TIn(:,:) but since that's just requesting the entire matrix unchanged you could actually just write TIn.
So now to multiply and concatenate (i.e. stick together) you do this
TOut = [TIn, TIn*2; TIn*3, TIn*4]
The [] is like a concatenate operation where , is for horizontal and ; is for vertical concatenation.
Putting it all together:
function TOut = custom_blocks(TIn)
TOut = [TIn, TIn*2; TIn*3, TIn*4];
end

Use linkage with custom distance

I would like to use the linkage function in matlab with a custom distance.
My distance function is in the form:
Distance = pdist(matrix,#mydistance);
so given a
matrix = rand(132,18)
Distance will be a vector [1x8646];
D_matrix = squareform(Distance,'tomatrix');
is a matrix 132x132 contaning all the pairwise distances between te rows of matrix
How can I embed mydistance in linkage?
You can use a call to linkage like this:
Z = linkage(Data,'single','#mydistance')
where 'single' can also be any of the other cluster merge methods as described here: http://www.mathworks.com/help/stats/linkage.html.
In other words, just put your function handle in a string and pass it as the 3rd argument to linkage. You cannot use the 'savememory' function in linkage while using a custom distance function, however. This is causing me some frustration with my 300,000 x 6 dataset. I think the solution will be to project it to some space where euclidean distance is defined and meaningful but we'll see how that goes.
Besides using
tree = linkage(Data,'single','#mydistance')
like Imperssonator suggests, you can also use
dissimilarity = pdist(Data,#mydistance);
tree = linkage(dissimilarity,'single');
The latter has the benefit of allowing Data to be an object array with #mydistance using objects as arguments.

What's the best way to iterate through columns of a matrix?

I want to apply a function to all columns in a matrix with MATLAB. For example, I'd like to be able to call smooth on every column of a matrix, instead of having smooth treat the matrix as a vector (which is the default behaviour if you call smooth(matrix)).
I'm sure there must be a more idiomatic way to do this, but I can't find it, so I've defined a map_column function:
function result = map_column(m, func)
result = m;
for col = 1:size(m,2)
result(:,col) = func(m(:,col));
end
end
which I can call with:
smoothed = map_column(input, #(c) (smooth(c, 9)));
Is there anything wrong with this code? How could I improve it?
The MATLAB "for" statement actually loops over the columns of whatever's supplied - normally, this just results in a sequence of scalars since the vector passed into for (as in your example above) is a row vector. This means that you can rewrite the above code like this:
function result = map_column(m, func)
result = [];
for m_col = m
result = horzcat(result, func(m_col));
end
If func does not return a column vector, then you can add something like
f = func(m_col);
result = horzcat(result, f(:));
to force it into a column.
Your solution is fine.
Note that horizcat exacts a substantial performance penalty for large matrices. It makes the code be O(N^2) instead of O(N). For a 100x10,000 matrix, your implementation takes 2.6s on my machine, the horizcat one takes 64.5s. For a 100x5000 matrix, the horizcat implementation takes 15.7s.
If you wanted, you could generalize your function a little and make it be able to iterate over the final dimension or even over arbitrary dimensions (not just columns).
Maybe you could always transform the matrix with the ' operator and then transform the result back.
smoothed = smooth(input', 9)';
That at least works with the fft function.
A way to cause an implicit loop across the columns of a matrix is to use cellfun. That is, you must first convert the matrix to a cell array, each cell will hold one column. Then call cellfun. For example:
A = randn(10,5);
See that here I've computed the standard deviation for each column.
cellfun(#std,mat2cell(A,size(A,1),ones(1,size(A,2))))
ans =
0.78681 1.1473 0.89789 0.66635 1.3482
Of course, many functions in MATLAB are already set up to work on rows or columns of an array as the user indicates. This is true of std of course, but this is a convenient way to test that cellfun worked successfully.
std(A,[],1)
ans =
0.78681 1.1473 0.89789 0.66635 1.3482
Don't forget to preallocate the result matrix if you are dealing with large matrices. Otherwise your CPU will spend lots of cycles repeatedly re-allocating the matrix every time it adds a new row/column.
If this is a common use-case for your function, it would perhaps be a good idea to make the function iterate through the columns automatically if the input is not a vector.
This doesn't exactly solve your problem but it would simplify the functions' usage. In that case, the output should be a matrix, too.
You can also transform the matrix to one long column by using m(:,:) = m(:). However, it depends on your function if this would make sense.