vectorized block assign with overlopping values - matlab

So I ran into this bug today
A_TEST(dest,:)=A_TEST(source,:)+A_TEST(dest,:);
A_TEST(:,dest)=A_TEST(:,source)+A_TEST(:,dest);
If dest is non-unique, this fails (which makes sense). So my quick fix is to do a for loop over dest
for (k=1:numel(dest))
A(dest(k),:)=A(source(k),:)+A(dest(k),:);
A(:,dest(k))=A(:,source(k))+A(:,dest(k));
end
And matlab is bad at such for loops. How would one vectorize this call?

With the following, I show how to do it with rows.
To do it with columns, it's a similar approach but different code, I'll explain why.
To summarize, you have a matrix A, with n rows and p columns.
You have a list of integers in the range [1,n], src, and idem for dst.
I'm assuming that they both have the same size, and might contain more than n elements (so that there are repetitions in both potentially).
Grouping the srcs by dsts, it's clear that the operation you're talking about is equivalent to a linear recombination of rows. This is equivalent to a pre-multiplication by a n x n matrix in which element (i,j) = k means "the recombination corresponding to the destination row i contains row j with multiplicity k".
This is what the following code does:
function A = madScience( A, src, dst )
n = size(A,1);
M = eye(n);
ix = sub2ind( [n,n], dst, src );
[ux,~,mlt] = unique( ix );
nux = length(ux);
mlt = accumarray( mlt(:), 1, [nux,1] );
M(ux) = M(ux) + mlt';
A = M*A;
end
Note 1: The two codes that you give in your post are NOT equivalent; you would need two separate for loops to make them equivalent.
Note 2: The same operation on columns is equivalent to a post multiplication by a matrix in which element (i,j) = k means "the recombination corresponding to column j contains column i with multiplicity k".
Note 3: If A is square, then both operations can be performed with the same matrix M as (M*A) * M' (the parenthesis are important).

Related

Comparing matrices of different size in matlab and storing values that are close

I have two matrices A and B. A(:,1) corresponds to an x-coordinate, A(:,2) corresponds to a y-coordinate, and A(:,3) corresponds to a certain radius. All three values in a row describe the same circle. Now let's say...
A =
[1,4,3]
[8,8,7]
[3,6,3]
B =
[1,3,3]
[1, 92,3]
[4,57,8]
[5,62,1]
[3,4,6]
[9,8,7]
What I need is to be able to loop through matrix A and determine if there are any rows in matrix B that are "similar" as in the x value is within a range (-2,2) of the x value of A (Likewise with the y-coordinate and radius).If it satisfies all three of these conditions, it will be added to a new matrix with the values that were in A. So for example I would need the above data to return...
ans =
[1,4,3]
[8,8,7]
Please help and thank you in advance to anyone willing to take the time!
You can use ismembertol.
result = A(ismembertol(A,B,2,'ByRows',1,'DataScale',1),:)
Manual method
A = [1,4,3;
8,8,7;
3,6,3];
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]; % example matrices
t = 2; % desired threshold
m = any(all(abs(bsxfun(#minus, A, permute(B, [3 2 1])))<=t, 2), 3);
result = A(m,:);
The key is using permute to move the first dimension of B to the third dimension. Then bsxfun computes the element-wise differences for all pairs of rows in the original matrices. A row of A should be selected if all the absolute differences with respect to any column of B are less than the desired threshold t. The resulting variable m is a logical index which is used for selecting those rows.
Using pdist2 (Statistics and Machine Learning Toolbox)
m = any(pdist2(A, B, 'chebychev')<=t, 2);
result = A(m,:);
Ths pdist2 function with the chebychev option computes the maximum coordinate difference (Chebychev distance, or L∞ metric) between pairs of rows.
With for loop
It should work:
A = [1,4,3;
8,8,7;
3,6,3]
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]
index = 1;
for i = 1:size(A,1)
C = abs(B - A(i,:));
if any(max(C,[],2)<=2)
out(index,:) = A(i,:);
index = index + 1
end
end
For each row of A, computes the absolute difference between B and that row, then checks if there exists a row in which the maximum is less than 2.
Without for loop
ind = any(max(abs(B - permute(A,[3 2 1])),[],2)<=2);
out = A(ind(:),:);

how to check the values of each variables from a resultant matrix in matlab?

I have sum of 3 cell arrays
A=72x1
B=72x720
C=72x90
resultant=A+B+C
size of resultant=72x64800
now when I find the minimum value with row and column indices I can locate the row element easily but how can I locate the column element in variables?
for example
after dong calculations for A,B,C I added them all and got a resultant in from of <72x(720x90)> or can say a matrix of integers of size <72x64800> then I found the minimum value of resultant with row and column index using the code below.
[minimumValue,ind]=min(resultant(:));
[row,col]=find(result== minimumValue);
then row got 14 and column got 6840 value..
now I can trace row 14 of all A,B,C variables easily but how can I know that the resultant column 6480 belongs to which combination of A,B,C?
Instead of using find, use the ind output from the min function. This is the linear index for minimumValue. To do that you can use ind2sub:
[r,c] = ind2sub(size(resultant),ind);
It is not quite clear what do you mean by resultant = A+B+C since you clearly don't sum them if you get a bigger array (72x64800), on the other hand, this is not a simple concatenation ([A B C]) since this would result in a 72x811 array.
However, assuming this is a concatenation you can do the following:
% get the 2nd dimension size of all matrices:
cols = cellfun(#(x) size(x,2),{A,B,C})
% create a vector with reapiting matrices names for all their columns:
mats = repelem(['A' 'B' 'C'],cols);
% get the relevant matrix for the c column:
mats(c)
so mats(c) will be the matrix with the minimum value.
EDIT:
From your comment I understand that your code looks something like this:
% arbitrary data:
A = rand(72,1);
B = rand(72,720);
C = rand(72,90);
% initializing:
K = size(B,2);
N = size(C,2);
counter = 1;
resultant = zeros(72,K*N);
% summing:
for k = 1:K
for n = 1:N
resultant(:,counter) = A + B(:,k) + C(:,n);
counter = counter+1;
end
end
% finding the minimum value:
[minimumValue,ind] = min(resultant(:))
and from the start of the answer you know that you can do this:
[r,c] = ind2sub(size(resultant),ind)
to get the row and column of minimumValue in resultant. So, in the same way you can do:
[Ccol,Bcol] = ind2sub([N,K],c)
where Bcol and Ccol is the column in B and C, respectively, so that:
minimumValue == A(r) + B(r,Bcol) + C(r,Ccol)
To see how it's working imagine that the loop above fills a matrix M with the value of counter, and M has a size of N-by-K. Because we fill M with a linear index, it will be filled in a column-major way, so the row will correspond to the n iterator, and the column will correspond to the k iterator. Now c corresponds to the counter where we got the minimum value, and the row and column of counter in M tells us the columns in B and C, so we can use ind2sub again to get the subscripts of the position of counter. Off course, we don't really need to create M, because the values within it are just the linear indices themselves.

How to vectorize this Matlab loop

I need some help to vectorize the following operation since I'm a little confused.
So, I have a m-by-2 matrix A and n-by-1 vector b. I want to create a n-by-1 vector c whose entries should be the values of the second column of A whose line is given by the line where the correspondent value of b would fall...
Not sure if I was clear enough. Anyway, the code below does compute c correctly so you can understand what is my desired output. However, I want to vectorize this function since my real n and m are in the order of many thousands.
Note that values of bare non-integer and not necessarily equal to any of those in the first column of A (these ones could be non-integers too!).
m = 5; n = 10;
A = [(0:m-1)*1.1;rand(1,m)]'
b = (m-1)*rand(n,1)
[bincounts, ind] = histc(b,A(:,1))
for i = 1:n
c(i) = A(ind(i),2);
end
All you need is:
c = A(ind,2);

Partial sum of a vector

For a vector v (e.g. v=[1,2,3,4,5]), and two index vectors (e.g. a=[1,1,1,2,3] and b=[3,4,5,5,5], with all a(i)<b(i)), I would like to construct w=sum(v(a:b)), which gives the values
w = zeros(length(a),1);
for i = 1:length(a)
w(i)=sum(v(a(i):b(i)));
end
It is slow when length(a) is large. Can I compute w without the for loop?
Yes! The nth element of cumsum(v) is the sum of the first n elements in v, so just take that and subtract the sum of the elements that you don't want to include:
v=[1,2,3,4,5]
a=[1,1,1,2,3]
b=[3,4,5,5,5]
C=cumsum(v)
C(b)-C(a)+v(a)
%// or alternatively
C=cumsum([0 v])
C(b+1)-C(a)
The following code works, but it is of course much less readable:
% assume v is a column vector
units = 1:length(v); units = units'; %units is a column vector
units_matrix = repmat(units, [1 length(a)]);
a_matrix = repmat(a, [length(v) 1]); % assuming a is is a row vector
b_matrix = repmat(b, [length(v) 1]);
weights = (units_matrix>=a_matrix) & (units_matrix<=b_matrix);
v_matrix = repmat(v, [1 length(a)]);
w = sum(v_matrix.*weights);
Explanation:
v_matrix contains copies of v. The summation will be done along
the column, so we need to prepare the other needed information in
vectorized form. units_matrix contains the indexes in v along the
columns. The columns are identical. a_matrix and b_matrix, in
each of their column, contains the indexes that are relevant for each
partial summation. All rows are identical. weights is a logical
matrix where, for each column, the indexes contained in
units_matrix between the corresponding a and b are 1 (true),
and the rest is 0. The element-wise multiplication thus filters the
"right" values, and all the indexes outside the range (again, for
each different column) is multiplied by zero. w is then he result
of the sum function, i.e. a row vector (every column of the
"filtered" matrix is summed).

what the mean of the following MATLAB code

please help me to understand this code:
x(:,i) = mean( (y(:,((i-1)*j+1):i*j)), 2 )';
i can't find it in my book. thanks.
The code you posted can be made more readable using temporary variables:
a = (i-1)*j+1;
b = i*j;
val = y(:,a:b);
x(:,i) = mean( val, 2 )'; %# =mean( val' )
What exactly you do not understand? For meaning of mean , : and ' consult matlab help.
It would help if you said exactly what you don't understand, but here are a few tips:
if you have something like a(r,c), that means matrix a, row r, column c (always in this order). In other words, you should have two elements inside the brackets separated by a comma where the first represents the row, the second the column.
If you have : by itself in one of the sides of the comma, that means "all". Thus, if you had a(r,:), then you would have matrix a, row r, all columns.
If : is not alone in one of the sides of the comma, then it will mean "to". So if you have a(r, z:y), that means matrix a, row r, columns z to y.
Mean = average. The format of the function in Matlab is M = mean(A,dim). A will be the matrix you take the average (or mean) of, M will be the place where the results are going to go. If dim = 1, you will get a row vector with each element being the average of a column. If dim = 2 (as it is in your case), then you should get a column vector, with each element being the average of a row. Be careful, though, because at the end of your code you have ', which means transpose. That means that your column vector will be transformed into a row vector.
OK, so your code:
x(:,i) = mean( (y(:,((i-1)*j+1):i*j)), 2 )';
Start with the bit inside, that is
y(:,((i-1)*j+1):i*j)
So that is saying
matrix y(r,c)
where
r (row) is :, that is, all rows
c (column) is ((i-1)j+1):ij, that is, columns going from (i-1)j+1 until ij
Your code will then get the matrix resulting from that, which I called y(r,c), and will do the following:
mean( (y(r,c), 2 )
so get the result from above and take the mean (average) of each row. As your code has the ' afterwards, that is, you have:
mean( (y(r,c), 2 )'
then it will get the column vector and transform into a row vector. Each element of this row will be the average of a row of y(r,c).
Finally:
x(:,i) = mean( (y(r,c), 2 )';
means that the result of the above will be put in column i of matrix x.
Shouldn't this be x(i,:) instead?
The i-th column of the array x is the average of the i-th group of j columns of the array y.
For example, if i is 1 and j is 3, the 1st column of x is the average of the first three columns of y.