Is there a non-iterative equivalent of this expression in MATLAB? - matlab

The expression is:
for i=1:n
X(:,i) = [P{i}(:)];
end
where X is a DxN matrix and P is a cell-array.

reshape(cat(3,P{:}),[numel(P{1}) n])
Of course, the above solution is just for fun. I would recommend profiling both solutions and only using this one if it has a significant performance advantage.
Maintenance and readability are also very important factors to consider when writing code.

If you obtained the cell array via mat2cell, you may be wanting to arrange blocks of an image into the columns of an array X. This can be achieved in a single step using the command IM2COL
%# rearrange the large array so that each column of X
%# corresponds to the 4 pixels of each 2-by-2 block
X = im2col(largeArray,[2 2],'distinct');

You might be able to get away with:
P{1} = [ 1 2; 3 4];
P{2} = [ 7 8; 9 10];
P{3} = [ 11 12; 13 14];
X = [P{:}]
X =
1 2 7 8 11 12
3 4 9 10 13 14
Then some sort of reshape() to get to where you want to be.

Related

visualizing 2D-graphs of n-dim. array via surf() in matlab

I want to show 2dim. Surface plots for different combinations of 2 parameters of a 3- or higher-dimensional array in matlab. The data for the non-shown dimensions are integrated (i.e. summed in the remaining dimensions). I am using surf(), and for parameter combinations other than (1,2) (eg. (1,3), (2,3) ...) I have to rearrange the data matrices in order to make it work.
I am looking for an alternative command (or shorter code) which does this work.
Here's the code:
a=zeros(3,3,2);
a(:,:,1) = [1 2 3 ;4 5 6; 7 8 9; 10 11 12]; % // data matrix
a(:,:,2) = -[1 2 3 ;4 5 6; 7 8 9; 10 11 12]*2; % // data matrix
ai=[[1 2 3 4]' [5 6 7 0]' [8 9 0 0]']; % // parameter vector
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
aux13 = sum(a,2);
for i = 1:2; mat13(:,i) = aux13(:,:,i);
surf(ai(1:2,3),ai(1:4,1),mat13)
aux23 = sum(a,1);
for i = 1:2; mat23(i,:) = aux23(:,:,i);
surf(ai(1:3,2),ai(1:2,3),mat23)
In other words, I am looking for a way to use surf for matrices mat13 and mat23 without the aux13, aux23 variables and the for loop.
First your example doesn't run because you declare a=zeros(3,3,2); as a matrix [3x3x2] but you immediately try to populate it as a [4x3x2] matrix, so I had to adjust your first line to: a=zeros(4,3,2);
If I run your code with that adjustment, your auxiliary variable and for loops are to reform/reshape a matrix stripped of it's singleton dimension. Matlab provide a handy function for that : squeeze.
For example, your variable aux13 is of dimension [4x1x2], then mat13=squeeze(aux13); achieve the same thing than your for loop. Your matrix mat13 is now of dimension [4x2].
Since no for loop is needed, you can completely bypass your auxiliary variable by calling squeeze directly on the result of your summation: mat13=squeeze( sum(a,2) );
Full example, the code below does exactly the same than your code sample:
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
mat13 = squeeze( sum(a,2) ) ;
surf(ai(1:2,3),ai(1:4,1),mat13)
mat23 = squeeze( sum(a,1) ) ;
mat23 = mat23.' ; %'// <= note the "transpose" operation here
surf(ai(1:3,2),ai(1:2,3),mat23)
Note that I had to transpose mat23 to make it match the one in your example.
sum(a,1) is [1x3x2] => squeeze that and you obtain a [3x2] matrix but your code arrange the same values in a [2x3] matrix, so the use of the transpose. The transpose operator has a shorthand notation .'.
I used it in the example in a separate line just to highlight it. Once understood you can simply write the full operation in one line:
mat23 = squeeze(sum(a,1)).' ;
The way you write your loops isn't exactly MATLAB syntax. Below is the correct loop syntax shown.
On line 2 and 3, you are trying to load (4x3)-matrices into (3x3)-matrices. That is why you get a subscript error. You could resolve it by making the zeros-matrix bigger. Here's some Syntax fixed:
a=zeros(4,3,2);
a(:,:,1) = [1 2 3 ;4 5 6; 7 8 9; 10 11 12]; % // data matrix
a(:,:,2) = -[1 2 3 ;4 5 6; 7 8 9; 10 11 12]*2; % // data matrix
ai=[[1 2 3 4]' [5 6 7 0]' [8 9 0 0]']; % // parameter vector
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
aux13 = sum(a,2);
for i = 1:2 mat13(:,i) = aux13(:,:,i);
surf(ai(1:2,3),ai(1:4,1),mat13)
end
aux23 = sum(a,1);
for i = 1:2 mat23(i,:) = aux23(:,:,i);
surf(ai(1:3,2),ai(1:2,3),mat23)
end
Now, what are you exactly trying to do inside those loops?

Apply custom filter with a custom window matlab

I'm trying to solve the following problem:
I'v a kernel made of 0's and 1's ,
e.g a crosslike kernel
kernel =
0 1 0
1 1 1
0 1 0
and I need to apply it to a given matrix like
D =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
for semplicity let's assume to start from element D(2,2), wich is 11, to avoid padding (that I can do with padarray).
I should superimpose the kernel and extract only elements where kernel==1, i.e
[2,5,11,10,7] then apply on them a custom filter like median or average and replacing central element with the result.
Then I would like to pass through all other elements (neglect edge elements for semplicity) and do the same.
Now I'm using tempS= ordfilt2(Z,order,kernel,'symmetric');
that performs exactly that operation with median filter. But I would like to use a different criterion (i.e. the average or some weird operation )
Use blockproc. This also handles border effects automatically (see the documentation). For example, to compute the median of the values masked by the kernel:
mask = logical(kernel);
R = blockproc(D, [1 1], #(d) median(d.data(mask)), ...
'bordersize', [1 1], 'trimborder', 0);
The first [1 1] indicated the step. The second [1 1] indicates how many elements to take around the central one.
With your example D, the result is
R =
2 3 3 3
9 7 8 10
5 9 10 6
4 7 6 1
This should do what you want:
D = rand(10,20);
kernel = [0,1,0;1,1,1;0,1,0];
[dy,dx] = find(kernel==1);
% should be calculated from kernel
dy = dy-2;
dx = dx-2;
% start and stop should calculated by using kernel size
result = zeros(size(D));
for y = 2:(size(D,1)-1)
for x = 2:(size(D,2)-1)
elements = D(sub2ind(size(D),y+dy,x+dx));
result(y,x) = weirdOperation(elements);
end
end
Nevertheless this will perform very poorly in terms of speed. You should consider use builtin functions. conv2 or filter2 for linear filter operations. ordfilt2 for order-statistic funtionality.

Extract fixed amounts of data from array based on a list of indices

I have the array
a=1:20
and a series of indices which indicate where I want to start pulling data out:
i=[4,12]
For each index i, I want that index and the next four (well, x, really) elements in a column or row. I'll avoid getting to close to the end of the array, so that special case can be disregarded.
If I was hard-coding this, I could use:
a([4:8;12:16])
and this would achieve my result.
But i may have many different values.
Any thoughts on how I can transform a list of indices into a matrix of ranges, or other ways to solve this problem?
EDIT
I am using Matlab 2007; it would be preferable if the solution relied solely on Matlab's internals and toolboxes. bsxfun is not present until 2007a.
Let i be your indicesx and x the number of elements you want in addition to the elements in i, then you can use
i = [4 6 8];
x = 4;
bsxfun(#plus, 0:x, i(:))
to get a matrix of indices:
ans =
4 5 6 7 8
6 7 8 9 10
8 9 10 11 12
If you do not have access to bsxfun you can use repmat instead:
i = [4 6 8];
x = 4;
repmat(i(:), 1, x+1) + repmat(0:x, length(i), 1)
Here is a solution without bsxfun but with repmat inspired by the previous answer.
i = [4 6 8];
x = 4;
p = repmat(1:x,length(i),1);
q = repmat(i',1,x);
p+q

Averaging every n elements of a vector in matlab

I would like to average every 3 values of an vector in Matlab, and then assign the average to the elements that produced it.
Examples:
x=[1:12];
y=%The averaging operation;
After the operation,
y=
[2 2 2 5 5 5 8 8 8 11 11 11]
Therefore the produced vector is the same size, and the jumping average every 3 values replaces the values that were used to produce the average (i.e. 1 2 3 are replaced by the average of the three values, 2 2 2). Is there a way of doing this without a loop?
I hope that makes sense.
Thanks.
I would go this way:
Reshape the vector so that it is a 3×x matrix:
x=[1:12];
xx=reshape(x,3,[]);
% xx is now [1 4 7 10; 2 5 8 11; 3 6 9 12]
after that
yy = sum(xx,1)./size(xx,1)
and now
y = reshape(repmat(yy, size(xx,1),1),1,[])
produces exactly your wanted result.
Your parameter 3, denoting the number of values, is only used at one place and can easily be modified if needed.
You may find the mean of each trio using:
x = 1:12;
m = mean(reshape(x, 3, []));
To duplicate the mean and reshape to match the original vector size, use:
y = m(ones(3,1), :) % duplicates row vector 3 times
y = y(:)'; % vector representation of array using linear indices

vectorized array creation from a list of start/end indices

I have a two-column matrix M that contains the start/end indices of a bunch of intervals:
startInd EndInd
1 3
6 10
12 12
15 16
How can I generate a vector of all the interval indices:
v = [1 2 3 6 7 8 9 10 12 15 16];
I'm doing the above using loops, but I'm wondering if there's a more elegant vectorized solution?
v = [];
for i=1:size(M,1)
v = [v M(i,1):M(i,2)];
end
Here's a vectorized solution I like to use for this particular problem, using the function cumsum:
v = zeros(1, max(endInd)+1); % An array of zeroes
v(startInd) = 1; % Place 1 at the starts of the intervals
v(endInd+1) = v(endInd+1)-1; % Add -1 one index after the ends of the intervals
v = find(cumsum(v)); % Perform a cumulative sum and find the nonzero entries
cell2mat(arrayfun(#colon,M(:,1)',M(:,2)','UniformOutput',false))
I don't have IMFILL, but on my machine this method is faster than the other suggestions and I think would beat the IMFILL method due to the use of find.
It can be made even faster if M is set up transposed (and we adjust the third and fourth arguments of arrayfun).
There's probably an even better solution I'm somehow not seeing, but here's a version using IMFILL
startInd = [1,6,12,15];
endInd = [3,10,12,16];
%# create a logical vector with starts and ends set to true to prepare for imfill
tf = false(endInd(end),1);
tf([startInd,endInd]) = true;
%# fill at startInd+1 wherever startInd is not equal endInd
tf = imfill(tf,startInd(startInd~=endInd)'+1); %' SO formatting
%# use find to get the indices
v = find(tf)' %' SO formatting
v =
1 2 3 6 7 8 9 10 12 15 16
Very weird solution IMHO creating temporary strings and using EVAL. Can be also one-liner.
tmp = cellstr(strcat(num2str(M(:,1)),{':'},num2str(M(:,2)),{' '}));
v = eval(['[' cell2mat(tmp') ']']);
I know it will probably not work on large matrix. Just for fun.