comparing rows of a logical matrix in matlab? - matlab

I have sentences cross words [4 cross 5] matrix as follows:
out=
0 1 1 0 1
1 1 1 0 0
0 0 1 1 0
1 1 0 1 1
I want to create a 1D cell array based upon above matrix which should tell in which two sentences same words appear with value = 1 i.e. let's take line 1 and line 2 in which at 2 points column 2 and column 3 the logical matrix has both 1's in row 1 and row 2 this information should be stored in 1D cell array.
suppose above example it's output would be as:
output{1,1} = []
output{1,2} = [2 3]
output{1,3} = [3]
.....
output{n,n} = [....]
where {1,1} tells sentence 1 relation with sentence 1 on the basis of value=1 of words, {1,2} tells sentence 1 relation with sentence 2 on the basis of value=1 of words and so on...

Try this:
x = [ 0 1 1 0 1
1 1 1 0 0
0 0 1 1 0
1 1 0 1 1];
[ii, jj] = ndgrid(1:size(x,1));
y = arrayfun(#(m,n) find(x(m,:) & x(n,:)), ii, jj, 'uniformoutput', 0);
Result:
y{1,1} =
2 3 5
y{2,1} =
2 3
y{3,1} =
3
y{4,1} =
2 5
[...]
If you want the diagonal elements to be empty:
y = arrayfun(#(m,n) find(x(m,:) & x(n,:) & m~=n), ii, jj, 'uniformoutput', 0)
Result:
y{1,1} =
[]
y{2,1} =
2 3
y{3,1} =
3
y{4,1} =
2 5
[...]

If you want something written quick... and since we are dealing with cell arrays, the easiest thing would be to have a pair of for loops. Assuming that you have your matrix defined in out:
out_cell = cell(size(out,1),size(out,1));
for idx = 1 : size(out,1)
for idx2 = 1 : idx
vals = find(out(idx,:) & out(idx2,:));
out_cell{idx,idx2} = vals;
out_cell{idx2,idx} = vals;
end
end
For each pair of rows, idx and idx2, we look to see if any columns match in terms of having a 1, then place the indices into its respective cell location in the 2D cell array. Note that because will encounter pairs of rows that are duplicates (i.e. checking row 3 and row 5, compared to row 5 and row 3), there is no need for the second for loop to iterate all over the other rows. We only need to check up to the current row that is described by the outer loop and we simply write the same values with the row indices swapped... so this is a "symmetric" matrix.
If you would like the diagonal elements to be empty, simply change the inner for loop so that it goes for idx2 = 1 : idx-1, instead of for idx2 = 1 : idx.
With your example, we get:
>> out_cell
out_cell =
[1x3 double] [1x2 double] [ 3] [1x2 double]
[1x2 double] [1x3 double] [ 3] [1x2 double]
[ 3] [ 3] [1x2 double] [ 4]
[1x2 double] [1x2 double] [ 4] [1x4 double]
>> celldisp(out_cell)
out_cell{1,1} =
2 3 5
out_cell{2,1} =
2 3
out_cell{3,1} =
3
out_cell{4,1} =
2 5
out_cell{1,2} =
2 3
out_cell{2,2} =
1 2 3
out_cell{3,2} =
3
out_cell{4,2} =
1 2
out_cell{1,3} =
3
out_cell{2,3} =
3
out_cell{3,3} =
3 4
out_cell{4,3} =
4
out_cell{1,4} =
2 5
out_cell{2,4} =
1 2
out_cell{3,4} =
4
out_cell{4,4} =
1 2 4 5

Related

MATLAB - finding max/min in selected rows/columns of a matrix

if i have a matrix, say:
A = [ 0 2 4 0
2 0 5 0
4 5 0 3
0 0 3 0 ]
and i want to find the maximum value in the matrix i can type:
max(max(A))
or
max(A(:))
if i only want to find the maximum of rows 1 and 2 and columns 3 and 4 i can do this:
a = [1 2]
b = [3 4]
max(max(A(a,b))
but what if i want to find the indices of the rows and columns that correspond to that value?
according to the matlab documentation, if i am using the whole matrix i can use the ind2sub function:
[val,idx] = max(A(:))
[row,col] = ind2sub(size(A),idx)
but how can i get that working for my example where i am using vectors a and b to determine the rows and columns it finds the values over?
here is the only way i have been able to work it out so far:
max_val = 0;
max_idx = [1 1];
for ii = a
[val,idx] = max(A(ii,b))
if val > max_val
max_val = val
max_idx = [ii idx]
but that seems rather clunky to me.. any ideas?
Assuming that the submatrix A(a,b) is contiguous (like in your example):
A = [ 0 2 4 0
2 0 5 0
4 5 0 3
0 0 3 0 ]
a = [1 2]; b = [3 4];
B = A(a,b)
[val,idx] = max(B(:));
[row,col] = ind2sub(size(B),idx);
maxrow = row + a(1) - 1;
maxcol = col + b(1) - 1;
You are finding the relative index in the submatrix B. Which is equivalent to the additional rows and columns from the upper left corner of the submatrix.
Now assuming that a and b result in a set of rows and columns that are NOT a contiguous submatrix, e.g. a = [1 3], b = [3 4], the result is very similar. "row" and "col" are the index in the a and b vectors:
A = [ 0 2 4 0
2 0 5 0
4 5 0 3
0 0 3 0 ]
a = [1 3]; b = [3 4];
B = A(a,b)
[val,idx] = max(B(:));
[row,col] = ind2sub(size(B),idx);
maxrow = a(row);
maxcol = b(col);
Now you're working in the index of indexes.

Finding elements who meet a specific condition

I've a matrix A and I'd like to find elements of first row which have 1 in the second row. i.e. for following matrix
A=
2 5 6 1
1 0 0 1
I'd like to have output as hits = [2 1] without using loops. and then finds the maximum items in the answer. i.e. (2>1) so my final answer is 2. The response is probably using arrayfun but I've problems and get errors using it. What is the correct syntax?
Thanks
Try this:
out = max(A(1,A(2,:) == 1))
Example:
>> A
A =
2 5 6 1
1 0 0 1
>> out
out =
2
Explanation: (if you need)
%// create a mask of which column you want
mask = A(2,:) == 1 %// by checking all values of 2nd row with 1
%// get only the values of row one, meeting 'the' condition
hits = A(1,mask)
%// Find the maximum from that
maxHits = max(hits)
For Cell Array using cellfun
A = {[2 5 6 1; 1 0 0 1], [2 3 2 5 4; 1 1 3 1 2]} %// eg input
A =
[2x4 double] [2x5 double]
out = cellfun(#(x) max(x(1,x(2,:) == 1)),A)
out =
2 5

How to make elements of multiple vectors repeated by MATLAB? [duplicate]

This question already has answers here:
Repeat elements of vector [duplicate]
(3 answers)
Closed 8 years ago.
I have 2 data vectors d1 = [1 2 1 3 4] and d2 = [3 1 2 1], and 2 references howMany1= [3 2 2 1 2] and howMany2 = [2 1 2 2]. The elements of d1 and d2 are to be repeated according to the elements of howMany1 and howMany2: so d1(1) i.e. the first element of d1 should repeat 3 times,d1(2) should repeat 2 times and so on.. and the final result should be d1_repeated = [1 1 1 2 2 1 1 3 4 4] and d2_repeated = [3 3 1 2 2 1 1]
How could I do that in MATLAB? I reviewed the a similar post in which one vector is being repeated, so I tried to do the same and made a for loop. Here is my code:
clear all
close all
clc
% data
d1 = [1 2 1 3 4];
d2 = [3 1 2 1];
howMany1 = [3 2 2 1 2]; % Determines how many times each index in IDX1 should be repeated.
howMany2 = [2 1 2 2];
d = {d1 d2}
howMany = {howMany1 howMany2}
Anew = cell(1,2)
for k = 1:2 % 2 data vectors
Anew{1,k} = arrayfun(#(x) repmat(d{k}(x), howMany{k}(x), 1), 1:numel(d{k}), 'uni', 0);
Anew = vertcat(Anew{:});
end
but I receive the following error:
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
Then I tried to change the vert cat to horzcat, but I receive the repetitions as group of doubles:
Anew =
Columns 1 through 7
[3x1 double] [2x1 double] [1] [2x1 double] [2x1 double] [2x1 double] [3]
Column 8
[2x1 double]
Im wondering what is the problem here? Thank you for taking the time.
This can be done by combining ismember and cumsum:
d1(cumsum(ismember(1:sum(howMany1),cumsum([1 howMany1]))))
Breakdown:
Compare 1:sum(howMany1) with the cumulative sum of howMany1. This is a vector with 1 and 0 that shows the position of where the next value from d1 should start.
ismember(1:sum(howMany1),cumsum([1 howMany1]))
ans =
1 0 0 1 0 1 0 1 1 0
The cumulative sum of this gives a vector that looks like:
cumsum(ismember(1:sum(howMany1),cumsum([1 howMany1])))
ans =
1 1 1 2 2 3 3 4 5 5
Now, this can be used as indices to d1 when creating the d1_repeated.
d1(cumsum(ismember(1:sum(howMany1),cumsum([1 howMany1]))))
ans =
1 1 1 2 2 1 1 3 4 4

How to repeat and split this vector in MATLAB? [duplicate]

This question already has answers here:
Repeat elements of vector [duplicate]
(3 answers)
Closed 8 years ago.
I have 2 data vectors d1 = [1 2 1 3 4] and d2 = [3 1 2 1], and 2 references howMany1= [3 2 2 1 2] and howMany2 = [2 1 2 2]. The elements of d1 and d2 are to be repeated according to the elements of howMany1 and howMany2: so d1(1) i.e. the first element of d1 should repeat 3 times,d1(2) should repeat 2 times and so on.. and the final result should be d1_repeated = [1 1 1 2 2 1 1 3 4 4] and d2_repeated = [3 3 1 2 2 1 1]
How could I do that in MATLAB? I reviewed the a similar post in which one vector is being repeated, so I tried to do the same and made a for loop. Here is my code:
clear all
close all
clc
% data
d1 = [1 2 1 3 4];
d2 = [3 1 2 1];
howMany1 = [3 2 2 1 2]; % Determines how many times each index in IDX1 should be repeated.
howMany2 = [2 1 2 2];
d = {d1 d2}
howMany = {howMany1 howMany2}
Anew = cell(1,2)
for k = 1:2 % 2 data vectors
Anew{1,k} = arrayfun(#(x) repmat(d{k}(x), howMany{k}(x), 1), 1:numel(d{k}), 'uni', 0);
Anew = vertcat(Anew{:});
end
but I receive the following error:
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
Then I tried to change the vert cat to horzcat, but I receive the repetitions as group of doubles:
Anew =
Columns 1 through 7
[3x1 double] [2x1 double] [1] [2x1 double] [2x1 double] [2x1 double] [3]
Column 8
[2x1 double]
Im wondering what is the problem here? Thank you for taking the time.
This can be done by combining ismember and cumsum:
d1(cumsum(ismember(1:sum(howMany1),cumsum([1 howMany1]))))
Breakdown:
Compare 1:sum(howMany1) with the cumulative sum of howMany1. This is a vector with 1 and 0 that shows the position of where the next value from d1 should start.
ismember(1:sum(howMany1),cumsum([1 howMany1]))
ans =
1 0 0 1 0 1 0 1 1 0
The cumulative sum of this gives a vector that looks like:
cumsum(ismember(1:sum(howMany1),cumsum([1 howMany1])))
ans =
1 1 1 2 2 3 3 4 5 5
Now, this can be used as indices to d1 when creating the d1_repeated.
d1(cumsum(ismember(1:sum(howMany1),cumsum([1 howMany1]))))
ans =
1 1 1 2 2 1 1 3 4 4

how to get the intersection of many different length cell vectors in matlab

I have n different length cell vectors, call it c{i}, i=1,2,...,n.
I wanna know if there any c{j} is the subset of c{i}, for example:
c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];
then I hope I can find that c{4} is subset of c{1}, c{5} is subset of c{2}.
I used two for loops with intersect function can process it, but I hope I can use at most one loop to process it, is there any way can achieve it?
Building on the other answers, you can also use ismember:
sets = {[1 2 3 4 5 6], [1 3 5 7], [2 4 6 8], [1 4 6], [3 7]};
N = numel(sets); % number of sets
idx = nchoosek(1:N,2); % indices of combinations
subsets = false(N,N);
for i = 1:size(idx,1)
a = idx(i,1); b = idx(i,2);
% check that set A is a subset of B, and the other way around as well
subsets(a,b) = all(ismember(sets{a},sets{b}));
subsets(b,a) = all(ismember(sets{b},sets{a}));
end
We get a logical matrix:
>> subsets
subsets =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
1 0 0 0 0
0 1 0 0 0
where non-zeros indicate subset relationship:
>> [i,j] = find(subsets)
i =
4
5
j =
1
2
i.e c{4} is a subset of c{1}, and c{5} is a subset of c{2}
Note: it is obvious that any set is a subset of itself, so the diagonals of subsets matrix should also be made 1. You could add that if you want using:
subsets(1:N+1:end) = true;
Here's a option using nchoosek – like cellfun it's also a loop in disguise of course:
c{1} = [1 2 3 4 5 6];
c{2} = [1 3 5 7];
c{3} = [2 4 6 8];
c{4} = [1 4 6];
c{5} = [3 7];
combs = nchoosek(1:numel(c),2);
subC = cell(size(combs,1),1);
for i = 1:size(combs,1)
subC{i} = intersect(c{combs(i,:)});
end
which results in the cell array
subC =
[1x3 double]
[1x3 double]
[1x3 double]
[ 3]
[1x0 double]
[ 1]
[1x2 double]
[1x2 double]
[1x0 double]
[1x0 double]
Each cell in subC corresponds to intersection of the cells indices in combs (a matrix form could easily be built within the loop if that is preferred).
EDIT: If you simply want to know if one vector is a subset of another then you can either use subC and combs from above to determine this or calculate it directly
combs = nchoosek(1:numel(c),2);
isSubC = logical(eye(numel(c)));
for i = 1:size(combs,1)
subC = intersect(c{combs(i,:)});
isSubC(combs(i,1),combs(i,2)) = isequal(subC,c{combs(i,2)});
isSubC(combs(i,2),combs(i,1)) = isequal(subC,c{combs(i,1)});
end
where isSubC(i,j) specifies if c{j} is a subset of c{i}.
You can by using cellfun, but as stated here, it is not a good idea.
For doing so, with one loop:
c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];
cSize = numel( c);
isect=cell(1,cSize)
for k=1:cSize
isect{k}=cellfun(#(in) intersect(in,c{k}),c,'UniformOutput',false);
end
This procedure may be repeated to eliminate the other for:
c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];
isect=cellfun(#(in) cellfun(#(in2) intersect(in,in2),c,'UniformOutput',false),c,'UniformOutput',false);
isect{i}{j} is the intersection from c{i} to {j}
Note: cellfun will do the loop internally over the cell value, so in fact, you are not removing the loops.
Although this was not the initial question, finding the subsets:
c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];c{6}=[];
isSubset=cell2mat(cellfun(#(in) cellfun(#(in2) isequal(intersect(in,in2),in)|isempty(in),c),c,'UniformOutput',false)');
Results:
isSubset =
1 0 0 0 0 0
0 1 0 0 0 0
0 0 1 0 0 0
1 0 0 1 0 0
0 1 0 0 1 0
1 1 1 1 1 1
Which returns a boolean if k is a subset of m by doing isSubset(k,m).