For loop continuing past set end point (matlab) - matlab

I have an array of matrices which are all different lengths. I want to compare the distance of each item in matrix 1 to the items in matrix 2 and so on. The for loops I've written below work well except when it reaches a matrix which is length 2. The loop continues to xx = 3 and then calls an error ("Index in position 1 exceeds array bounds. Index must not exceed 2.") because there is no current_mat(3,:). Why is it doing this only for matrices of length 2? I'm relatively new to matlab, so apologies if this is a simple question. Here are some toy data that give the same error I am seeing with a larger dataset.
matrix_1 = ones(16,3)
matrix_2 = ones(14,3)
matrix_3 = ones(2,3)
matrix_4 = ones(10,3)
my_array = {matrix_1; matrix_2; matrix_3; matrix_4}
for ii = 1:length(my_array)-1;
current_mat = my_array{ii};
compare_mat = my_array{ii+1};
for xx = 1:length(current_mat);
xx_info = current_mat(xx,:);
end
end

The issue is that when given a matrix input length returns the longest dimension of the matrix, not the number of rows. In the case of your matrix_3 this is 3 although it appears that you expect 2. So xx goes from 1 to 3 and in line 11 you attempt to access a row that doesn't exist when xx=3. Better would be to explicitly loop across the m dimension. You can do this with size which returns the number of rows and columns in the matrix:
matrix_1 = ones(16,3)
matrix_2 = ones(14,3)
matrix_3 = ones(2,3)
matrix_4 = ones(10,3)
my_array = {matrix_1; matrix_2; matrix_3; matrix_4}
for ii = 1:length(my_array)-1;
current_mat = my_array{ii};
compare_mat = my_array{ii+1};
[m,n] = size(current_mat); % <-- use size here, not length
for xx = 1:m;
xx_info = current_mat(xx,:);
end
end
or, if you wish to look at the columns:
matrix_1 = ones(16,3)
matrix_2 = ones(14,3)
matrix_3 = ones(2,3)
matrix_4 = ones(10,3)
my_array = {matrix_1; matrix_2; matrix_3; matrix_4}
for ii = 1:length(my_array)-1;
current_mat = my_array{ii};
compare_mat = my_array{ii+1};
[m,n] = size(current_mat); % <-- use size here, not length
for xx = 1:n;
xx_info = current_mat(:,xx);
end
end

This code should work for you. You didn't specifically specify the length of columns (or rows) as the determinant,
matrix_1 = ones(16,3);
matrix_2 = ones(14,3);
matrix_3 = ones(2,3);
matrix_4 = ones(10,3);
my_array = {matrix_1; matrix_2; matrix_3; matrix_4};
for ii = 1:length(my_array)-1
current_mat = my_array{ii};
compare_mat = my_array{ii+1};
for xx = 1:size(current_mat,2) % length of columns
xx_info = current_mat(:,xx); % Can compare across columns, since no of columns are consistent across multiple matrices
end
end

Related

How to find maximum value within multiple intervals?

I have two arrays, der_pos and der_neg, which contain indices of an array interpolated. I want to get all the indices for the maximum values of interpolated in the intervals:
der_pos(1):der_neg(1)
der_pos(2):der_neg(2)
etc...
E.g.:
interpolated = [1,5,3,2,7,10,8,14,4]
der_pos = [1,6]
der_neg = [4,9]
So, I would like to obtain the indices:
[2,8]
Because:
in the interval der_pos(1):der_neg(1) → 1:4 → interpolated(1:4) = [1,5,3,2] the max is 5 which is at index 2.
in the interval der_pos(2):der_neg(2) → 6:9 → interpolated(6:9) = [10,8,14,4] the max is 14 which is at index 8.
I managed to do it using a for loop:
interpolated = [1,5,3,2,7,10,8,14,4];
der_pos = [1,6];
der_neg = [4,9];
indices = zeros(1,length(der_pos));
for i = [1:length(der_pos)]
[peak, index] = max(interpolated(der_pos(i):der_neg(i)));
indices(i) = index + der_pos(i) - 1;
endfor
indices % gives [2,8]
But is there a more concise way of doing this?
Here's a sample code. The function findpeaks returns all the peak. The loop then keeps indices of peaks that are in the wanted range.
I added a test to avoid errors in case of no found peak (index will be -1), and to keep the first peak if two peaks are found. You can use a cell if you want to keep all peaks in an interval.
interpolated = [1,5,3,2,7,10,8,14,4];
der_pos = [1 6 7 ];
der_neg = [4 9 8];
[~,i]=findpeaks(interpolated);
indices= -1+zeros(size(der_pos,2),1);
for loopi = 1:length(i)
val = i(i>=der_pos(loopi)&i<=der_neg(loopi));
if ~isempty(val)
indices(loopi) = val(1);
end
end
Here's a way:
interpolated = [1,5,3,2,7,10,8,14,4]; % data
der_pos = [1,6]; % data
der_neg = [4,9]; % data
m = bsxfun(#ge, 1:numel(interpolated), der_pos(:)) .* ...
bsxfun(#le, 1:numel(interpolated), der_neg(:)); % each row is a mask that contains
% 1 for values in the interval and 0 for values outside the interval
m(~m) = NaN; % replace 0 by NaN
[val, result] = max(bsxfun(#times, m, interpolated(:).'), [], 2); % val is the maximum
% of each row, and result is its column index. The maximum is 1 for rows that
% contain at least a 1, and NaN for rows that only contain NaN
result(isnan(val)) = 0; % If the maximum value was NaN the result is set to 0
% (or maybe use NaN), to indicate that the interval was empty
This gives 0 for empty intervals. For example, der_pos = [1,6,8]; der_neg = [4,9,6]; produce result = [2;8;0].
The intervals may overlap. For example, der_pos = [1,6,3]; der_neg = [4,9,7]; produce result = [2;8;6].

average bins along a dimension of a nd array in matlab

To compute the mean of every bins along a dimension of a nd array in matlab, for example, average every 10 elements along dim 4 of a 4d array
x = reshape(1:30*30*20*300,30,30,20,300);
n = 10;
m = size(x,4)/10;
y = nan(30,30,20,m);
for ii = 1 : m
y(:,:,:,ii) = mean(x(:,:,:,(1:n)+(ii-1)*n),4);
end
It looks a bit silly. I think there must be better ways to average the bins?
Besides, is it possible to make the script applicable to general cases, namely, arbitray ndims of array and along an arbitray dim to average?
For the second part of your question you can use this:
x = reshape(1:30*30*20*300,30,30,20,300);
dim = 4;
n = 10;
m = size(x,dim)/10;
y = nan(30,30,20,m);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
for ii = 1 : m
idx1{dim} = ii;
idx2{dim} = (1:n)+(ii-1)*n;
y(idx1{:}) = mean(x(idx2{:}),dim);
end
For the first part of the question here is an alternative using cumsum and diff, but it may not be better then the loop solution:
function y = slicedmean(x,slice_size,dim)
s = cumsum(x,dim);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
idx1{dim} = slice_size;
idx2{dim} = slice_size:slice_size:size(x,dim);
y = cat(dim,s(idx1{:}),diff(s(idx2{:}),[],dim))/slice_size;
end
Here is a generic solution, using the accumarray function. I haven't tested how fast it is. There might be some room for improvement though.
Basically, accumarray groups the value in x following a matrix of customized index for your question
x = reshape(1:30*30*20*300,30,30,20,300);
s = size(x);
% parameters for averaging
dimAv = 4;
n = 10;
% get linear index
ix = (1:numel(x))';
% transform them to a matrix of index per dimension
% this is a customized version of ind2sub
pcum = [1 cumprod(s(1:end-1))];
sub = zeros(numel(ix),numel(s));
for i = numel(s):-1:1,
ixtmp = rem(ix-1, pcum(i)) + 1;
sub(:,i) = (ix - ixtmp)/pcum(i) + 1;
ix = ixtmp;
end
% correct index for the given dimension
sub(:,dimAv) = floor((sub(:,dimAv)-1)/n)+1;
% run the accumarray to compute the average
sout = s;
sout(dimAv) = ceil(sout(dimAv)/n);
y = accumarray(sub,x(:), sout, #mean);
If you need a faster and memory efficient operation, you'll have to write your own mex function. It shouldn't be so difficult, I think !

Optimized matrix operation of matrix with repeating elements in Matlab

I want to get exp() of a large matrix (A) with values that repeat at different indices. To speed-up the exp() operation I only perform it on the unique values of A and then reassemble the matrix. However the reassembly of the matrix is quite slow. The following code provides a working example:
% defintion of a grid
gridSp = 5:5:35*5;
X = repmat(gridSp,35,1);
Z = repmat(gridSp',1,35);
% calculation of the distances
locMat = [X(:) Z(:)];
dist=sqrt(bsxfun(#minus,locMat(:,1),locMat(:,1)').^2 +...
bsxfun(#minus,locMat(:,2),locMat(:,2)').^2);
sizeDist = size(dist);
uniqueDist = unique(dist,'stable');
[~, Locb] = ismember(dist,uniqueDist);
nn_A = exp(1i*2*pi*rand(sizeDist(1),100));
H_A = zeros(size(nn_A));
freq = linspace(10^-3,10,100);
psdA = 4096*length(freq).*10.*4.*22.6./((1 + 6.*freq*22.6).^(5/3));
for jj=1:100
b = exp(-8.8*uniqueDist*sqrt((freq(jj)/15).^2 + 10^-7));
b = b.*psdA(jj);
A = b(Locb);
droptol = max(A(:))*10^-10;
if min(A(:))<droptol
A = sparse(A);
HH_A = ichol(A,struct('type','ict','shape','lower','droptol',droptol));
else
HH_A = chol(A,'lower');
end
H_A(:,jj) = HH_A*nn_A(:,jj);
end
Especially the reassembly of the matrix
A = b(Locb);
and the conversion of the matrix to sparse
A = sparse(A);
in the last for-loop take up a lot of time. Is there a quicker way to do this? Interestingly:
B = A + A;
is much faster than
A = b(Locb);
I have to perfom these operations far more often than the 100 iterations in the example.
Here a condensed version of the code up on request (below).
% defintion of a grid
gridSp = 5:5:28*5;
X = repmat(gridSp,35,1);
Z = repmat(gridSp',1,35);
% calculation of the distances
locMat = [X(:) Z(:)];
dist=sqrt(bsxfun(#minus,locMat(:,1),locMat(:,1)').^2 +bsxfun(#minus,locMat(:,2),locMat(:,2)').^2);
uniqueDist = unique(dist,'stable');
[~, Locb] = ismember(dist,uniqueDist);
for jj=1:100
b = exp(jj.*uniqueDist);
A = b(Locb);
end
In your example, the dimension of dist is just 980 x 980 in which case you would be better off to just perform a dense matrix operation, i.e.
for jj=1:100
A=exp(jj*dist);
end
which is 2 times faster than
for jj=1:100
b = exp(jj.*uniqueDist);
A = b(Locb);
end
for your given example.

Matlab iterative polyfit

I have x and y data that has n number of points in each of the arrays.
I want to use polyfit on portions of the data.
I want to divide the data into a certain number of divisions(numDivisions).
My idea would be to do something along the lines of
n= size(x)%number of data points
numDivisions = 4;%number of times to divide the data
div = zeros(numDivisions,1)%number of points per division
p = zeros(numDivisions,4);% second number is degree of polynomial+1
S = zeros(numDivisions,1);
mu = zeros(numDivisions,1);
E = zeros(numDivisions,1);
for i = 1:numDivisions
div(i) = round(n(1,1)*i/numDivisions) %assign markers for divisions of points
end
for i = 1:size(div)
if i == 1
start = 1;
endpoint = div(i);
[p(i), S(i), mu(i)] = polyfit(x(start:endpoint), y(start:endpoint), 3);
else
[p(i), S(i), mu(i)] = polyfit(x(div(i-1):div(i)), y(div(i-1):div(i)), 3);
end
end
The goal would be to have an array of p values from the polyfits.
However, when I run it I get this error:
In an assignment A(I) = B, the number of elements in B
and I must be the same.
Error in (line 33)
[p(i), S(i), mu(i)] =
polyfit(x(start:endpoint),
y(start:endpoint), 3);

Matlab index with specific values using loop

I know how to write the program with a given number of entries, but not when if the number of entries are n:. Basically I think I need a loop but I can't get it to work properly.
This is a code that works fine when having 3 entries. % is what I put in. I want to create a vector with specific position and values, this is a example how the vector would look like:
X = [1;0;0;0;0;0;0;0;0;0;0;0;0;0;99;0;0;0;0;99]
and the code for it:
n1 = input('Determine value for case:'); %n1 = 1
n2 = input('Determine value for case:'); %n2 = 15
n3 = input('Determine value for case:'); %n3 = 20
X = zeros(20,1);
X(n1) = input('Determine position '); %1
X(n2) = input('Determine position '); %99
X(n3) = input('Determine position '); %99
But for n entries, I need a loop I figured. (the vector may still be 20x1)
for n = 1:entries (%entries are 3, so 3 loops)
n = n+1
n = input('Determine value for case :');
X =zeros(20,1);
X(n) = input('Determine position:')
end
But I can't just get it to work
thanks in advance
Not sure exactly what you are trying to achieve, but I think that you can fix your loop as follows:
entries = 3;
X =zeros(20,1);
for ni = 1:entries
n = input('Determine value for case :');
X(n) = input('Determine position:');
end