I am looking for a function to find the most repeated (i.e. modal) rows of a matrix in MATLAB. Something like:
>> A = [0, 1; 2, 3; 0, 1; 3, 4]
A =
0 1
2 3
0 1
3 4
Then running:
>> mode(A, 'rows')
would return [0, 1], ideally with a second output giving the indexes where this row occurred (i.e. [1, 3]'.)
Does anyone know of such a function?
You can use UNIQUE to get unique row indices, and then call MODE on them.
[uA,~,uIdx] = unique(A,'rows');
modeIdx = mode(uIdx);
modeRow = uA(modeIdx,:) %# the first output argument
whereIdx = find(uIdx==modeIdx) %# the second output argument
The answer may not be right. Try A = [2, 3; 0, 1; 3, 4; 0, 1].
It should be the following:
[a, b, uIdx] = unique(A,'rows');
modeIdx = mode(uIdx);
modeRow = a(modeIdx,:) %# the first output argument
whereIdx = find(ismember(A, modeRow, 'rows')) %# the second output argument
Related
I have a matrix
A = [1,2;3,4];
I would like to generate a new matrix B, which contains all permutations over the columns for each row.
B = [1,2;2,1;3,4;4,3]
Is there a one-liner solution?
I could only think of a solution incorporating cell arrays, thus I'm not sure, if that is "efficient" at all. Also, have a look at the limitations of perms.
% Input.
A = [1, 2; 3, 4]
% Expected output.
B = [1, 2; 2, 1; 3, 4; 4, 3]
% Calculate output.
C = sortrows(cell2mat(cellfun(#(x) perms(x), mat2cell(A, ones(1, size(A, 1)), 2), 'UniformOutput', false)))
A =
1 2
3 4
B =
1 2
2 1
3 4
4 3
C =
1 2
2 1
3 4
4 3
I found a solution to my own question.
n = 2; % size of permutations
perm_index = perms(1:n); % index of the matrix to perm
perm_length = size(perm_index,1);
data = [3,4;5,6];
data_length = size(data,1);
output_length = perm_length* data_length;
output = reshape(data(:,perm_index), output_length,n);
%Final output
output = [4,3;6,5;3,4;5,6]
I couldn't find any one-liner solution. Hope this one is simpler enough:
A = [1, 2, 3; 4, 5, 6];
B = [];
for i=1:size(A,1)
B = [B ; perms(A(i, :))];
end
Read about the function nchoosek
A = [1 2 3 4] ;
B = nchoosek(A,2)
Each of my statements work individually, however, I'm having problems with my loop. I have substation data such as this:
1 4
1 5
1 6
2 2
2 8
2 9
3 1
3 5
3 8
I then try to use the following loop to sum the data in the second column grouped by the number in the first column, and then store it in a matrix.
for region = 1:Nnuts3
idx = find(substations(:,1)==Nnuts3);
output = sum(substations(idx,2),1);
mat(Nnuts3,1) = output;
end
Each of the statements in here works fine as an individual line of code when I remove Nnuts3 and place in a number, but it does not work as a whole loop.
What am I doing wrong? I merely want to sum the data using the index in the first row as a condition, and then store the output.
You should use the function accumarray which is specifically designed for your problem:
data = [1, 4; 1, 5; 1, 6; 2, 2; 2, 8; 2, 9; 3, 1; 3, 5; 3, 8];
result = accumarray(data(:,1),data(:,2),[],#sum) %accumarray(index,data,[],#function)
We obtain:
[[1:max(data(:,1))]',result(:)] =
1 15
2 19
3 14
PS: result = accumarray(data(:,1),data(:,2)) %accumarray(index,data) will give you the same result, but in my opinion it's more clear to precise the desired "grouping" function.
It looks like you were not using the variable region on the line idx = find(substations(:,1) == region);
The following works for me:
clear all;
substations = [1, 4; 1, 5; 1, 6; 2, 2; 2, 8; 2, 9; 3, 1; 3, 5; 3, 8];
Nnuts3 = 8; % or whatever it needs to be
for region = 1:Nnuts3
idx = find(substations(:,1) == region);
output = sum(substations(idx,2), 1);
mat(region,1) = output;
end
Giving the output:
mat =
15.0000e+000
19.0000e+000
14.0000e+000
0.0000e+000
0.0000e+000
0.0000e+000
0.0000e+000
0.0000e+000
I hope that helps :)
Just came across this problem might be interesting in many applications, for example,
I have a vector A = [2; 5; 10], the values in vector A is sorted and unique.
I have got a matrix (2D or 3D), for example, B = [2, 8, 10; 2, 5, 5; 9, 1, 10];
Want to get a matrix C = [1, 0, 1; 1, 1, 1; 0, 0, 1].
It means if the element in B is also an element of A, we set it to one; otherwise, set the value to zero.
I did this in a for-loop, but for a large 3D matrix, it takes a long time to finish the loop.
Just wondering if there is a smarter method to do this without 'for' loop.
C = zeros(size(B));
for i = 1:size(A,1)
a = A(i);
C(B==a) = 1;
end
This is exactly what ismember does:
A = [2; 5; 10];
B = [2, 8, 10; 2, 5, 5; 9, 1, 10];
C = ismember(B,A)
C =
1 0 1
1 1 1
0 0 1
From the documentation:
ismember(A,B) returns an array containing 1 (true) where the data in A
is found in B. Elsewhere, it returns 0 (false).
It is almost the same question as this one Matrices intersection!
The difference is this: If the intersection of the element (i, j) of all matrices is the same number then do not output -1 but output this number. An example is the following:
A1 = [2, 2, 0;
2, 2, 0;
0, 2, 0];
A2 = [2, 0, 4;
4, 3, 0;
0, 0, 1];
A3 = [2, 0, 0;
1, 0, 3;
3, 4, 3];
I want to get the follow matrix:
B = [2, 2, 4;
-1, -1, 3;
3, -1, -1];
Version 1
out1 = -1.*(A1~=A2).*(A1~=A3).*(A2~=A3)
max_mat = max(cat(3,A1,A2,A3),[],3)
out1(~out1) = max_mat(~out1)
Output
out1 =
2 2 4
-1 -1 3
3 -1 -1
Version 2: Maybe a faster version
Assumption - If out of the three elements in the corresponding positions across A1, A2 and A3, only two are same, then take the max of those three elements for the final matrix, B.
Code
%%// Concatenate all three A matrices
A=cat(3,A1,A2,A3,A1);
%%// Logical matrix with ones where all three elements are different from each other
out1 = -1.*all(diff(A,[],3)~=0,3)
%%// Get the max values, to be stored where -1 all three corresponding elements
%%// are not different from each other
max_mat = max(A,[],3)
%%// Get the final output
out1(~out1) = max_mat(~out1)
This produces the same output as the previous version.
Version 3
Assumption - If out of the three elements in the corresponding positions across A1, A2 and A3, only two are same, then take the element that is different from the other two for the final matrix, B.
Code
A=cat(3,A1,A2,A3,A1);
AA = A(:,:,1:3);
t1 = bsxfun(#ne,AA,mode(AA,3));
out1 = max(AA.*t1,[],3) + all(~t1,3).*A1;
out1(all(diff(A,[],3)~=0,3))=-1;
This produces the same output as the previous versions.
I would do this
A = A1+A2+A3;
B = (A1==A2)&(A1==A3);
C = (A1==0)+(A2==0)+(A3==0);
D = ones(3)*-1;
D(B==1) = A1(B==1);
D(C==2) = A(C==2);
B records the position of the elements whose number is same for all the matrices.
C records the position of the elements where two of the matrices have 0.
Then we can modify the elements of D, whose values are set -1 initially, using the information in matrices B and C.
A1 = [2, 2, 0;
2, 2, 0;
0, 2, 0];
A2 = [2, 0, 4;
4, 3, 0;
0, 0, 1];
A3 = [2, 0, 0;
1, 0, 3;
3, 4, 3];
A=cat(3,A1,A2,A3);
%identify all fields with identical values on the 3rd dimension
[X,Y]=find(sum(abs(diff(A,1,3)),3)==0);
%delete all but the first repetition, then use the previous code
A(X,Y,2:end)=0;
L=(sum(A~=0,3)>1);
L*-1+(1-L).*sum(A,3)
/update: Had to fix the code, now it should be correct.
I have a matrix M that looks similar to this:
M = [ 1, 2, 3, 0, 0;
1, 2, 0, 0, 0;
2, 3, 4, 5, 0;
4, 5, 6, 0, 0;
1, 2, 3, 4, 5;
]
I'm trying to get a column vector with the rightmost non-zero value of each row in A, but ONLY for the rows that have the first column == 1.
I'm able to calculate a filter for the rows:
r = M( :, 1 ) == 1;
> r = [ 1; 1; 0; 0; 1 ]
And I have a set of indices for "the rightmost non-zero value of each row in M":
> c = [ 3, 2, 4, 3, 5 ]
How do I combine these in a slicing of A in order to get what I'm looking for? I'm looking for something like:
A( r, c )
> ans = [ 3; 2; 5 ]
But doing this gets me a 3x3 matrix, for some reason.
The shortest way I can think of is as follows:
% Get the values of the last non-zero entry per row
v = M(sub2ind(size(M), 1:size(M,1), c))
% Filter out the rows that does not begin with 1.
v(r == 1)
This seems to work (I assume other operations defining r,c have been performed):
M(sub2ind(size(A),find(r==1).',c(r==1))).'
Short interpretation of the problem and solution:
M( r, c )
gives a 3 x 5 matrix (not 3 x 1 as desired) due to mixing of logical and subscript indices. The logical indices in r pick out rows in A with r==1. Meanwhile row array c picks out elements from each row according to the numeric index:
ans =
3 2 0 3 0
0 2 0 0 0
3 2 4 3 5
What you really want are indices into the rightmost nonzero elements in each row starting with 1. The solution uses linear indices (numeric) to get the correct elements from the matrix.
I think this should do the trick. I wonder if there is more elegant way of doing this though.
% get only rows u want, i.e. with first row == 1
M2 = M(r,:);
% get indices of
% "the rightmost non-zero value of each row in M"
% for the rows u want
indicesOfinterest = c(r==1);
noOfIndeciesOfinterest = numel(indicesOfinterest);
% desired output column vector
output = zeros(noOfIndeciesOfinterest, 1);
% iterate through the indeces and select element in M2
% from each row and column indicated by the indice.
for idx = 1:noOfIndeciesOfinterest
output(idx) = M2(idx, indicesOfinterest(idx));
end
output % it is [3; 2 ; 5]
You can use
arrayfun(#(x) M(x,c(x)), find(r))
But unless you need r and c for other purposes, you can use
arrayfun(#(x) M(x,find(M(x,:),1,'last')), find(M(:,1)==1))
Here is a way to do it using linear indexing:
N = M';
lin_index = (0:size(N,1):prod(size(N))-1) + c;
v = N(lin_index);
v(r)