How to sum aggregate matrix rows in the following way? - matlab

I have a matrix of size n-by-3. For some rows of this matrix, the first two columns are identical. I need to keep only one copy of these first-two-element combinations, where the third column will have the sum of 3rd column from rows with identical first-two-columns.
Here's an example of what I want to do:
M = [...
1 2 1
1 2 3
1 2 2
1 2 4
2 3 1
2 3 4
2 3 0];
The final matrix that I need is
R = [...
1 2 1+3+2+4
2 3 1+4+0];
How can this be done? I don't see how I can use the unique command for this.

You may use unique in combination with accumarray. Let's call the initial n x 3 array A:
[C, ~, ic] = unique(A(:,1:2), 'rows');
B = [C, accumarray(ic, A(:,3))];
Explanation:
unique outputs not only unique elements of array (rows in our case thanks to the argument rows), but also two arrays of indexes. The first one is the indexes of the first unique elements in A; I discard it since I don't use it. The second one can be used to reconstruct original array from the output array: A(:, 1:2) = C(ic,:).
accumarray is the generalization of histogram computation, it makes the sum of elements in 2nd argument array for each unique index in the first argument array. In your case, you make the sum over the 3rd column of the original array only.
And that's all in two simple commands!

Related

Find the row/column of all maximum values in each row of a matrix

I want search indexes maximum value/s in each row. If row has more than one maximum, so I want to save both indexes.
For example:
X = [5 6 8
1 2 3
4 4 0];
And I need indexes
inds = [1 3
2 3
3 1
3 2];
I wanted to use function max but this function only saves one index.
You can use max to compute the max for each row and then compare the elements in each row to it's row-wise max using bsxfun and eq. Then you can find the row/column positions of these maxima. We use a transpose in there (.') to ensure that we get the ordering of the output that you expect.
[c,r] = find(bsxfun(#eq, d, max(d, [], 2)).')
output = [r,c];
Another way to do it, would be to use max and repmat.
First you find the maximum of each row using
rowMaximum=max(X,[],2);
Then you replicate the maximum so that it has the same dimension as your input and compare it to the input
logicalMaximum=repmat(rowMaximum,1,size(X,2))==X;
And the last thing you wanna do is converting this logical array into your desired indexes
[columns,rows]=find(logicalMaximum);
result=[rows,columns];

Matlab find unique column-combinations in matrix and respective index

I have a large matrix with with multiple rows and a limited (but larger than 1) number of columns containing values between 0 and 9 and would like to find an efficient way to identify unique row-wise combinations and their indices to then build sums (somehwat like a pivot logic). Here is an example of what I am trying to achieve:
a =
1 2 3
2 2 3
3 2 1
1 2 3
3 2 1
uniqueCombs =
1 2 3
2 2 3
3 2 1
numOccurrences =
2
1
2
indizies:
[1;4]
[2]
[3;5]
From matrix a, I want to first identify the unique combinations (row-wise), then count the number occurrences / identify the row-index of the respective combination.
I have achieved this through generating strings with num2str and strcat, but this method appears to be very slow. Along these thoughts I have tried to find a way to form a new unique number through concatenating the values horizontally, but Matlab does not seem to support this (e.g. from [1;2;3] build 123). Sums won't work because they would remove the possibility to identify unique combinations. Any suggestions on how to best achieve this? Thanks!
To get the unique rows you can use unique with the 'rows' option enabled:
[C, ix, ic] = unique(a, 'rows', 'stable');
C contains the unique rows; ix the indexes of the first occurrences of those rows in C; ic basically contains the information you want. To access it you can loop over the indexes of ix and save them in a cell array:
indexes = cell(1, length(ix));
for k = 1:length(ix)
indexes{k} = find(ic == ix(k));
end
indexes will be a cell array containing the indexes you were looking for. For example:
indexes{1}
% ans =
%
% 1
% 4
And to count the occurrences of a particular combination you can just use numel. For example:
numel(indexes{1})
% ans =
%
% 2

sorting an array and keep the index in MATLAB

I have an array called A, it has 2 Rows and 56 columns ( as shown in the attached image)
the first row represents the values while the second row represents the index for each value.
I want to sort the first row but keep the index for each value (I tried to use Sort function but it doesnt keep the index of each value in the second row).
for example, let's suppose I have this matrix
input
x=[9 2 4 3 ;3 1 8 2 ]
i want the output looks like this
y=[2 3 4 9; 1 2 8 3 ];
There are a couple of ways to achieve this:
[y1,I] = sort(x(1,:))
y2 = x(2,I)
y = [ y1 ; y2 ]
This basically sort the first row of your data, and save the sorting index in I, and then use those index to get the 'sorted' second row, and then just join them
or
y = sortrows(x')'
The ' operator transposes the matrix, which allows you to use sortrows and then use it again to reshape your output matrix.
You can use indices of sorted elements
[S,I]=sort(x(1,:));
result = [S;x(2,I)]
The first row is sorted and indices of the sorted elements is used to order the second row.
result
2 3 4 9
1 2 8 3
You can get the indices directly from sorting the first row. These can be used as an argument in x itself:
x=[9 2 4 3 ;3 1 8 2 ];
%get the indices of the first row:
[~,indices] = sort(x(1,:))
%return x in the order "indices" for each row:
y = [x(1,indices);x(2,indices)]

How to vectorize sub2ind?

I have a 2 dimensional array L, and I am trying to create a vector of linear indices ind for each row of this array.
L=
1 5 25 4 0 0
2 3 3 45 5 6
45 5 6 0 0 0
I am using lenr to store the number of non zero elements in each row (starting from column 1).
lenr=
4
6
3
Then I have 1x45 array RULES. Indices stored in L refer to elements in RULES. Since I want to vectorize the code, I decided to create linear indices and then run RULES(ind).
This works perfectly:
ind=sub2ind(size(L),1,lenr(1));
while this doesn't work:
ind=sub2ind(size(L),1:3,1:lenr(1:3));
Any ideas?
UPDATE:
This is what I initially tried to vectorize the code, but it did not works and that's why I checked linear indices:
rul=repmat(RULES,3);
result = rul((L(1:J,1:lenr(1:J))));
If I correctly interpret your edit, you want to create a variable result that contains the elements of RULES indicated by the non-zero elements of L. Note that in general, the best way to vectorize sub2ind is to not use sub2ind.
If you want result to be a linear array, you can simply write
%// transpose so that result is ordered by row
L_transposed = L.';
result = RULES(L_transposed(L_transposed>0));
If, instead, you want result to be an array of the same size as L, with all numbers in L replaced by the corresponding element in RULES, it's even simpler:
result = L;
result(result>0) = RULES(result>0);

Creating new matrix out of one column by order of two other columns

So I have a matrix A, take as an example
4 6 3.5
3 6 -1
A = 5 2 0.7
4 3 1.2
I now want to use Matlab to make a matrix B out of the last column of A in a very specific way. The rows of B should be ordered by the first column of A (in ascending order) and the columns of B should be ordered (ascending) by the second column of A. This can give empty elements in B, which should be assigned NaN. Applied to the example above this gives
NaN NaN -1
B = NaN 1.2 3.5
0.7 NaN NaN
Note that the number of rows and columns in B depend on the number of unique elements in the first and second column of A respectively.
I have tried a few different things trying to be clever with Matlab indexing but so far no success..
You can use this method
[~,J1,K1] = unique(A(:,1));
[~,J2,K2] = unique(A(:,2));
sz = [numel(J1) numel(J2)];
B = nan(sz);
B(sub2ind(sz, K1, K2)) = A(:,3);
first use unique to gather the unique items and their indices in the original column. The size of B is determined by number of unique elements in first and second column of A.
Now use linear indexing (obtained using sub2ind) to put the values in third column in the right place.