Accumarray with non-scalar subscripts in Matlab? - matlab

I have a matrix A in Matlab of dimension m x 3, e.g. m=18
A=[ 2| 1 1;
3| 1 2;
-8| 1 3;
-------
-5| 1 1;
2| 1 2;
6| 1 3;
-------
7| 2 1;
3| 2 2;
1| 2 3;
5| 2 4;
-------
3| 2 1;
-8| 2 2;
1| 2 3;
0| 2 4;
-------
1| 2 1;
2| 2 2;
7| 2 3;
9| 2 4]
The characteristics of A are the following:
It is composed by t submatrices. In the example t=5.
Each submatrix t has dimension b x 3 with b<=m and b can take any value in {3,4,5,...,m} (clearly, in a way such that the sum of all rows is m). In the example, the first and the second submatrices have dimension 3 x 3, the last three submatrices have dimension 4 x 3.
All submatrices of the same dimension are stacked one after the other. In the example, firstly we have the submatrices 3 x 3 and then the submatrices 4 x 3.
I want to compute the vector B of dimension f x 1 where f=size(unique(A(:,2:end),'rows','stable'),1), (in the example f=7), such that B(i,1) is obtained by summing the elements j of A(:,1) having A(j,2:end) equal to the i-th row of unique(A(:,2:end),'rows','stable'), i.e.
B=[2-5;
3+2;
-8+6
7+3+1;
3-8+2;
1+1+7;
5+0+9]

Use the third output of unique when examining A over the rows and with the 'stable' flag, then use this as input into accumarray. The third output assigns a unique ID for each unique occurrence of a row vector seen in your matrix A, which is very suitable for use in accumarray:
[~,~,f] = unique(A(:,2:end), 'rows', 'stable');
B = accumarray(f, A(:,1));
We get:
B =
-3 %// 2-5
5 %// 3+2
-2 %// -8+6
11 %// 7+3+1
-3 %// 3-8+2
9 %// 1+1+7
14 %// 5+0+9

Related

Sort matlab matrix rows based on function

Let's say I have a Nx3 matrix M.
Consider, for example, a function Map(v) that receives as input a 1x3 array and returns a double.
How can I sort the rows of M so that the rows with smallest Map should come first?
That is, the row i should be the first if Map(M(i,:)) ≤ Map(M(j,:)) for all 1 ≤ j ≤ N and j ≠ i ?
example:
M = |1 2 3|
|4 5 6|
|7 8 9|
Map(v){
if (v(1) == 1 && v(2) == 2 && v(3) == 3) return 2
if (v(1) == 4 && v(2) == 5 && v(3) == 6) return 3
if (v(1) == 7 && v(2) == 8 && v(3) == 9) return 1
}
So the sorting should leave M like this:
M = |7 8 9|
|1 2 3|
|4 5 6|
% initialize an N-by-3 matrix where N=10...
M = [
1 0 3;
4 2 2;
0 1 0;
7 5 6;
3 3 8;
2 1 4;
0 0 6;
3 1 2;
5 0 0;
1 6 2
];
% sum its rows...
M_sum = sum(M,2);
% sort the vector of sums in ascending order...
[~,idx] = sort(M_sum);
% reorder the matrix rows based on the sorting result...
M = M(idx,:);

Reduce matrix by adding every n

Not sure how to explain it so here it goes as an example:
A=[1 0 0 1 4 4 4 4
0 0 0 0 2 3 2 2
0 0 0 0 0 0 0 1
2 3 4 5 2 3 4 1 ]
result:
b=[ 1 1 13 12
5 9 5 6];
Each of the elements is computed by adding a N size submatrix inside the original, in this case N=2.
so b(1,1) is A(1,1)+A(1,2)+A(2,1)+A(2,2), and b(1,4) is A(1,7)+A(2,7)+A(1,8)+A(2,8).
Visually and more clearly:
A=[|1 0| 0 1| 4 4| 4 4|
|0 0| 0 0| 2 3| 2 2|
____________________
|0 0| 0 0| 0 0| 0 1|
|2 3| 4 5| 2 3| 4 1| ]
b is the sum of the elements on those squares, in this example of size 2.
I can imagine how to make it with loops, but its just feels vectorizable. Any ideas of how it could be done?
Assume that the matrix A has sizes that are multipliers of N.
If you have the Image processing toolbox blockproc could be an option as well:
B = blockproc(A,[2 2],#(x) sum(x.data(:)))
Method 1:
Using mat2cell and cellfun
n = 2;
AC = mat2cell(A,repmat(n,size(A,1)/n,1),repmat(n,size(A,2)/n,1));
out = cellfun(#(x) sum(x(:)), AC)
Method 2:
Using permute and reshape
n = 2;
[rows,cols] = size(A);
out = reshape(sum(sum(permute(reshape(A,n,rows/n,n,[]),[1 3 2 4]))),rows/n,[]);
PS: Here is a close question related to this one, which you might find useful. That question is to find mean while this one is to find sum.
Here are two alternative methods:
Method #1 - im2col
Another method using the image processing toolbox is to use im2col with the distinct flag and sum over all of the resulting columns. You would then need to reshape the matrix back to the right size:
n = 2;
B = im2col(A, [n n], 'distinct');
C = reshape(sum(B, 1), size(A,1)/n, size(A,2)/n);
We get for C:
>> C
C =
1 1 13 12
5 9 5 6
Method #2 - accumarray and kron
We can generate an index matrix with kron which we can use as bins into accumarray and invoke sum as the custom function. We would again have to reshape the matrix back to the right size:
n = 2;
M = reshape(1:prod([size(A,1)/n, size(A,2)/n]), size(A,1)/n, size(A,2)/n);
ind = kron(M, ones(n));
C = reshape(accumarray(ind(:), A(:), [], #sum), size(A,1)/n, size(A,2)/n);
Again we get for C:
C =
1 1 13 12
5 9 5 6

Element-wise binary value concatenation of two matrices

Element by element, I want to concatenate binary values from different matrices to one matrix.
For example,
|1 0 0| |0 1 0| |10 01 00|
|0 1 1| |1 1 0| = |01 11 10|
|1 0 1| |0 0 1| |10 00 11|
How can this be done?
I would store the output matrix as a decimal matrix and convert to binary strings when accessing elements.
To concatenate the elements, treat each input matrix as a binary digit
A1 = [1 0 0; 0 1 1; 1 0 1];
A2 = [0 1 0; 1 1 0; 0 0 1];
output = A1 * 2^1 + A2 * 2^0;
output_str = arrayfun(#dec2bin, output, 'UniformOutput', false);
output will be a double matrix
[2, 1, 0;
1, 3, 2;
2, 0, 3]
output_str will be a cell array of strings
['10', '1', '0';
'1', '11', '10';
'10', '0', '11']
Here is one alternative without bin2dec or dec2bin conversion
out = arrayfun(#(x,y) strcat(num2str(x),num2str(y)),A1,A2,'Uni',0);
Input:
A1 = [1 0 0; 0 1 1; 1 0 1];
A2 = [0 1 0; 1 1 0; 0 0 1];
output:
>> out
out =
'10' '01' '00'
'01' '11' '10'
'10' '00' '11'
If you want it as numeric instead of string, you might do this:
outp = cellfun(#(x) str2double(x), out);
output:
>> outp
outp =
10 1 0
1 11 10
10 0 11

How do I turn a 2x3 matrix into a 3x3 matrix

I'm sorry if this is a stupid question but I just can't find the answer I need.
I have the following matrix:-
A |6 6 0|
|9 0 0|
Each column represents co-ordinates on a grid.
Now to find the inverse of "A" I need to create this into a 3x3 square matrix, to do this I add 001 as the 3rd row...
B |6 6 0|
|9 0 0|
|0 0 1|
I do this simply because it is what I have seen in the online examples.
My question is, what is the method to calculate/add the 3rd row of a 2x3 matrix in this situation?
It is not possible to take the inverse of a matrix that is not squared..
I assume that would like to just extend the matrix in order to make i squared, the reason why you use the [0 0 1] is to make the matrix consistent..
Actually you matrix represent two equations with three variables..
A:
6*x_1 + 6*x_2 + 0*x_3 = 0
9*x_1 + 0*x_2 + 0*x_3 = 0
this is not consistent
but by adding the last row you get
B:
6*x_1 + 6*x_2 + 0*x_3 = 0
9*x_1 + 0*x_2 + 0*x_3 = 0
0*x_1 + 0*x_2 + 1*x_3 = 0
this matrix exists on echelon form
[1 0 0]
[0 1 0]
[0 0 1]
so by adding the last row you are not changing the matrix
you would properly get same result just by reduce it to
[6 6]
[9 0]
Here is a simple way to do it:
s = size(A);
B = eye(max(s));
B(1:s(1),1:s(2)) = A

How to subtract each item of a matrix from each coressponding row of another matrix

A = [1 2 3; 7 6 5]
B = [3 7];
A-B = [1-3 2-3 3-3; 7-7 6-7 5-7];
ans =[-2 -1 0; 0 -1 -2]
This is the operation I want to have done. How could I do it by matrix functions other than the iterative solutions?
You do this most conveniently with bsxfun, which automatically expands the arrays to match in size (so that you don't need to use repmat). Note that I need to transpose B so that it's a 2-by-1 array.
A = [1 2 3; 7 6 5]
B = [3 7];
result = bsxfun(#minus,A,B')
result =
-2 -1 0
0 -1 -2
I think that Jonas answer is the best. But just for the record, here is the solution using an explicit repmat:
A = [1 2 3; 7 6 5];
B = [3 7];
sz = size(A);
C = A - repmat(B', [1 sz(2:end)]);
Not only is Jonas' answer simpler, it is actually faster by a factor of 2 for large matrices on my machine.
It's also interesting to note that in the case where A is an n-d array, both these solutions do something quite reasonable. The matrix C will have the following property:
C(k,:,...,:) == A(k,:,...,:) - B(k)
In fact, Jonas' answer will run, and very likely do what you want, in the case where B is m-d, as long as the initial dimensions of A and B' have the same size. You can change the repmat solution to mimic this ... at which point you are starting to reimplement bsxfun!
Normally you can't. Iterative solutions will be necessary, because the problem is poorly defined. Matrix addition/subtraction is only defined for matrices of the same dimensions.
ie:
A = | 1 2 3 |
| 7 6 5 |
B = | 3 7 |
It makes no sense to subtract a 1x2 matrix from a 2x3 matrix.
However, if you multiplied B by some intermediate matrix to make the result a 2x3 matrix, that would work, ie:
B' * Y = | 3 3 3 |
| 7 7 7 |
eg:
B' = diag(B)
= | 3 0 |
| 0 7 |
B' * Y = | 3 3 3 |
| 7 7 7 |
Y = | 1 1 1 |
| 1 1 1 |
Therefore, A-B'*Y gives a valid, non-iterative solution.
A-(B'*Y) = | 1 2 3 | - | 3 3 3 |
| 7 6 5 | | 7 7 7 |
= A - (diag(B) * Y )
The only "cheat" here is the use of the diag() function, which converts a vector to a strictly-diagonal-matrix. There is a way to manually decompose a set of matrix/vector multiplication operations to manually re-create the diag() function, but that would be more work than my solution above itself.
Good luck!