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

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

Related

Create Non Zero elements of Matrix in vector form in Matlab

I have a Matrix of size M by N in which each row has some zero entries. I want to create M row vectors such that each of the vector contains the non zero elements of each row. For example if I have the following Matrix
A=[0 0 0 5;0 0 4 6;0 1 2 3;9 10 2 3]
I want four different row vectors of the following form
[5]
[4 6]
[1 2 3]
[9 10 2 3]
This can be done with accumarray using an anonymous function as fourth input argument. To make sure that the results are in the same order as in A, the grouping values used as first input should be sorted. This requires using (a linearized version of) A transposed as second input.
ind = repmat((1:size(A,2)).',1,size(A,2)).';
B = A.';
result = accumarray(ind(:), B(:), [], #(x){nonzeros(x).'});
With A = [0 0 0 5; 0 0 4 6; 0 1 2 3; 9 10 2 3]; this gives
result{1} =
5
result{2} =
4 6
result{3} =
1 2 3
result{4} =
9 10 2 3
Since Matlab doesn't support non-rectangular double arrays, you'll want to settle on a cell array. One quick way to get the desired output is to combine arrayfun with logical indexing:
nonZeroVectors = arrayfun(#(k) A(k,A(k,:)~=0),1:size(A,1),'UniformOutput',false);
I used the ('UniformOutput',false) name-value pair for the reasons indicated in the documentation (I'll note that the pair ('uni',0) also works, but I prefer verbosity). This input produces a cell array with the entries
>> nonZerosVectors{:}
ans =
5
ans =
4 6
ans =
1 2 3
ans =
9 10 2 3

Replace one element of a column in another

Let A and B be column vectors:
A = (1:6).'; %'// [1;2;3;4;5;6]
B = (7:12).'; %'// [7;8;9;10;11;12]
How do I construct a matrix such that one element of A is replaced each time by an element from B to get C as:
C = [...
7 1 1 1 1 1
2 8 2 2 2 2
3 3 9 3 3 3
4 4 4 10 4 4
5 5 5 5 11 5
6 6 6 6 6 12];
If C is to be indexed as index = [1 1 1 2 2 3 3]', how is it possible to produce
c1 = [...
1 7 1 1
1 2 8 2
1 3 3 9];
c2 = [...
2 10 4
2 5 11];
c3 = [3 6]; %// either this
c3 = [3 12]; %// or this
Problem #1:
C = bsxfun(#plus,A.',-diag(A)+diag(B));
gives:
7 2 3 4 5 6
1 8 3 4 5 6
1 2 9 4 5 6
1 2 3 10 5 6
1 2 3 4 11 6
1 2 3 4 5 12
Problem #2:
As far as I understand, you want to extract blocks out of the previously found C matrix, in the following fashion:
Where c1, c2, c3 are the green, red and blue blocks, repectively; and "missing" elements are replaced with NaN (in the initial version of your question).
index = [1 1 1 2 2 3 3]; %// Block definition
[blkSz,~] = histcounts(index,numel(unique(index))); %// Conversion...
paddedC = padarray(C,numel(index)-numel(diag(C))*[1 1],NaN,'post');
blocks = mat2cell(paddedC,blkSz,blkSz);
This results in a cell-array blocks with the following contents:
blocks =
[3x3 double] [3x2 double] [3x2 double]
[2x3 double] [2x2 double] [2x2 double]
[2x3 double] [2x2 double] [2x2 double]
Where for example blocks{1,1} is:
ans =
7 2 3
1 8 3
1 2 9
Then you can pad the array using the index of the loop, to get the c cell, like so:
c = cell(numel(blkSz),1); %// Preallocation
for ind1 = 1:numel(blkSz)
c{ind1} = padarray(blocks{ind1,ind1},1,ind1,'pre');
end
Note that c1 is found in c{1} etc.
Addition:
In the case when you don't want c1...c3 to be (possibly) padded with NaNs, the best way in my opinion would be trimming the index vector to the length of A or B ("length(C)"). This ensures you won't access blocks outside your square matrix. For example:
newIdx = index(1:numel(A));
Then just use newIdx instead of index in the rest of the code.

comparing rows of a logical matrix in 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

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).