matlab indexing with multiple condition - matlab

I can't figure out how to create a vector based on condition on more than one other vectors. I have three vectors and I need values of one vector if values on other vectors comply to condition.
As an example below I would like to choose values from vector a if values on vector b==2 and values on vector c==0 obviously I expect [2 4]
a = [1 2 3 4 5 6 7 8 9 10];
b = [1 2 1 2 1 2 1 2 1 2];
c = [0 0 0 0 0 1 1 1 1 1]
I thought something like:
d = a(b==2) & a(c==0)
but I have d = 1 1 1 1 1 not sure why.
It seems to be basic problem but I can find solution for it.

In your case you can consider using a(b==2 & c==0)

Use ismember to find the matching indices along the rows after concatenating b and c and then index to a.
Code
a(ismember([b;c]',[2 0],'rows'))
Output
ans =
2
4
You may use bsxfun too for the same result -
a(all(bsxfun(#eq,[b;c],[2 0]'),1))
Or you may just tweak your method to get the correct result -
a(b==2 & c==0)

Related

Remove single elements from a vector

I have a vector M containing single elements and repeats. I want to delete all the single elements. Turning something like [1 1 2 3 4 5 4 4 5] to [1 1 4 5 4 4 5].
I thought I'd try to get the count of each element then use the index to delete what I don't need, something like this:
uniq = unique(M);
list = [uniq histc(M,uniq)];
Though I'm stuck here and not sure how to go forward. Can anyone help?
Here is a solution using unique, histcounts and ismember:
tmp=unique(M) ; %finding unique elements of M
%Now keeping only those elements in tmp which appear only once in M
tmp = tmp(histcounts(M,[tmp tmp(end)])==1); %Thanks to rahnema for his insight on this
[~,ind] = ismember(tmp,M); %finding the indexes of these elements in M
M(ind)=[];
histcounts was introduced in R2014b. For earlier versions, hist can be used by replacing that line with this:
tmp=tmp(hist(M,tmp)==1);
You can get the result with the following code:
A = [a.', ones(length(a),1)];
[C,~,ic] = unique(A(:,1));
result = [C, accumarray(ic,A(:,2))];
a = A(~ismember(A(:,1),result(result(:,2) == 1))).';
The idea is, add ones to the second column of a', then accumarray base on the first column (elements of a). After that, found the elements in first column which have accum sum in the second column. Therefore, these elements repeated once in a. Finally, removing them from the first column of A.
Here is a cheaper alternative:
[s ii] = sort(a);
x = [false s(2:end)==s(1:end-1)]
y = [x(2:end)|x(1:end-1) x(end)]
z(ii) = y;
result = a(z);
Assuming the input is
a =
1 1 8 8 3 1 4 5 4 6 4 5
we sort the list s and get index of the sorted list ii
s=
1 1 1 3 4 4 4 5 5 6 8 8
we can find index of repeated elements and for it we check if an element is equal to the previous element
x =
0 1 1 0 0 1 1 0 1 0 0 1
however in x the first elements of each block is omitted to find it we can apply [or] between each element with the previous element
y =
1 1 1 0 1 1 1 1 1 0 1 1
we now have sorted logical index of repeated elements. It should be reordered to its original order. For it we use index of sorted elements ii :
z =
1 1 1 1 0 1 1 1 1 0 1 1
finally use z to extract only the repeated elements.
result =
1 1 8 8 1 4 5 4 4 5
Here is a result of a test in Octave* for the following input:
a = randi([1 100000],1,10000000);
-------HIST--------
Elapsed time is 5.38654 seconds.
----ACCUMARRAY------
Elapsed time is 2.62602 seconds.
-------SORT--------
Elapsed time is 1.83391 seconds.
-------LOOP--------
Doesn't complete in 15 seconds.
*Since in Octave histcounts hasn't been implemented so instead of histcounts I used hist.
You can test it Online
X = [1 1 2 3 4 5 4 4 5];
Y = X;
A = unique(X);
for i = 1:length(A)
idx = find(X==A(i));
if length(idx) == 1
Y(idx) = NaN;
end
end
Y(isnan(Y)) = [];
Then, Y would be [1 1 4 5 4 4 5]. It detects all single elements, and makes them as NaN, and then remove all NaN elements from the vector.

MATLAB: After reshaping matrix to array, how can we know back where the value originally belongs to?

Let z = [1 3 5 6] and by getting all the difference between each elements:
we get:
bsxfun(#minus, z', z)
ans =
0 -2 -4 -5
2 0 -2 -3
4 2 0 -1
5 3 1 0
I now want to order these values in ascending order and remove the duplicates. So:
sort(reshape(bsxfun(#minus, z', z),1,16))
ans =
Columns 1 through 13
-5 -4 -3 -2 -2 -1 0 0 0 0 1 2 2
Columns 14 through 16
3 4 5
C = unique(sort(reshape(bsxfun(#minus, z', z),1,16)))
C =
-5 -4 -3 -2 -1 0 1 2 3 4 5
But by looking at -5 in [-5 -4 -3 -2 -1 0 1 2 3 4 5],
how can I tell where -5 comes from. By reading myself the matrix,
0 -2 -4 -5
2 0 -2 -3
4 2 0 -1
5 3 1 0
I know it comes from z(1) - z(4), i.e. row 1 column 4.
Also 2 comes from both z(3) - z(2) and z(2) - z(1), which comes from two cases. Without reading the originally matrix itself, how can we know that the 2 in [-5 -4 -3 -2 -1 0 1 2 3 4 5] is originally in row 3 column 2 and row 2 column 1 of the original matrix?
So by looking at each element in [-5 -4 -3 -2 -1 0 1 2 3 4 5], how do we know, for example, where -5 comes from in the original matrix index efficiently. I want to know as I need to do operation on ,e.g.,-5 and two indices that produce this: for example, for each difference, say -5, i do (-5)*1*6, as z(1)- z(6) = -5. But for 2, I need to do 2*(3*2+2*1) as z(3) - z(2) = 2, z(2) - z(1) = 2 which is not distinct.
Thinking hard, I think i should not reshape bsxfun(#minus, z', z) to array. I will also create two index array such that I can do operations like (-5)*1*6 stated above effectively. However, this is easier said than done and I also have to take care of nondistinct sources. Or should I do the desired operations first?
Use the third output from unique. And don't sort, unique will do that for you.
[sortedOutput,~,linearIndices] = unique(reshape(bsxfun(#minus, z', z),[1 16]))
You can reconstruct the result from bsxfun like so:
distances = reshape(sortedOutput(linearIndices),[4 4]);
If you want to know where a certain value appears, you write
targetValue = -5;
targetValueIdx = find(sortedOutput==targetValue);
linearIndexIntoDistances = find(targetValueIdx==linearIndices);
[row,col] = ind2sub([4 4],linearIndexIntoDistances);
Because linearIndices is 1 wherever the first value in sortedOutput appears in the original vector.
If you save the result of bsxfun in an intermediate variable:
distances=bsxfun(#minus, z', z)
Then you can look for the values of C in distances using find iteratively.
[rows,cols]=find(C(i)==distances)
This will give all rows and cols if the values are repeated. You just need to then use them for your equation.
You can use accumarray to collect all row and column indices that correspond to the same value in the matrix of differences:
z = [1 3 5 6]; % data vector
zd = bsxfun(#minus, z.', z); % matrix of differences
[C, ~, ind] = unique(zd); % unique values and indices
[rr, cc] = ndgrid(1:numel(z)); % template for row and col indices
f = #(x){x}; % anonymous function to collect row and col indices
row = accumarray(ind, rr(:), [], f); % group row indices according to ind
col = accumarray(ind, cc(:), [], f); % same for col indices
For example, C(6) is value 0, which appears four times in zd, at positions given by row{6} and col{6}:
>> row{6}.'
ans =
3 2 1 4
>> col{6}.'
ans =
3 2 1 4
As you see, the results are not guaranteed to be sorted. If you need to sort them in linear order:
rowcol = cellfun(#(r,c)sortrows([r c]), row, col, 'UniformOutput', false);
so now
>> rowcol{6}
ans =
1 1
2 2
3 3
4 4
I'm not sure I've followed exactly but some points to consider:
unique will sort the data for you by default so you don't need to call sort first
unique actually has three outputs and you can recover your original vector (i.e. with duplicates) using the third output so
[C,~,ic] = unique(reshape(bsxfun(#minus, z', z),1,16))
now you can get back to bsxfun(#minus, z', z),1,16) by calling
reshape(C(ic), numel(z), numel(z))
You might be more interested in the second output of unique which tells you what index each unique value was at in your 1-by-16 vector. It really depends on what you're trying to do though. But with this you could get a list of row column pairs to match your unique values:
[rows, cols] = ndgrid(1:4);
coords = [rows(:), cols(:)];
[C, ia] = unique(reshape(bsxfun(#minus, z', z),1,16));
coords_pairs = coords(ia,:)
which results in
coords_pairs =
1 4
1 3
2 4
2 3
3 4
4 4
4 3
3 2
4 2
3 1
4 1

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

generate Combinations from a value in matlab

How to generate the different combinations possible for a certain number
Example:
m=2 gives:
[1 1;1 2;2 1;2 2]
m=3 gives:
[1 1;1 2;1 3;2 1;2 2;2 3;3 1;3 2;3 3]
and so on...
using perms([1 2]) generates [1 2;2 1] only
You can use ndgrid:
m = 3;
[A,B] = ndgrid(1:m);
Here A and B look like this:
A =
1 1 1
2 2 2
3 3 3
B =
1 2 3
1 2 3
1 2 3
So you can concatenate them vertically to get the combinations. Using the colon operator transforms the matrices into column-vectors, i.e. listing all the elements column-wise. Therefore, you could use either
P = sortrows([A(:), B(:)])
or
P = [B(:) A(:)] %// Thanks #knedlsepp :)
to get sorted combinations.
P now looks like this:
P =
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
Note that your question is highly related to the following, where the goal is to find combinations from 2 vectors.: How to generate all pairs from two vectors in MATLAB using vectorised code?. I suggest you look at it as well to get more ideas.
That being said the question might be a duplicate...anyhow hope that helps.
It's a little tricky, as nchoosek can not be used straight out of the box:
n = 3;
X = nchoosek([1:n, n:-1:1],2);
Y = unique(X,'rows','legacy');
respectively in one line:
Y = unique(nchoosek([1:n, n:-1:1],2),'rows','legacy');

How to split vector by zeros in MATLAB

I have got a problem with splitting a vector by zeros.
I have a vector for example
v=[1 3 2 6 4 0 0 2 4 6 0 0 0 3 1]
I need to get vectors like
v1=[1 3 2 6 4]
v2=[2 4 6]
v3=[3 1]
Is there any way to do this by using MATLAB functions?
Of course I don't know of how many subvectors are included in main vector v and how many zeros delimits vectors.
I'm not a programmer and also I'm not a pro in MATLAB.
I know a procedural way to do this but want do it by MATLAB somehow.
I found a function A = strsplit(str,delimiter) but I don't have string I have a vector.
So I searched for conversion function. I found S = char(V) but when I executed it it crashed.
It's better to have the output as a cell array, not as separate variables. That way the output will be easier to handle.
Try this:
v = [1 3 2 6 4 0 0 2 4 6 0 0 0 3 1]; %// data
w = [false v~=0 false]; %// "close" v with zeros, and transform to logical
starts = find(w(2:end) & ~w(1:end-1)); %// find starts of runs of non-zeros
ends = find(~w(2:end) & w(1:end-1))-1; %// find ends of runs of non-zeros
result = arrayfun(#(s,e) v(s:e), starts, ends, 'uniformout', false); %// build result
Result (for your example):
>> result{:}
ans =
1 3 2 6 4
ans =
2 4 6
ans =
3 1
The strsplit() solution for a vector of whole numbers smaller than 9 (so a very specific solution, for a general solution see Luis Mendo's). Split and convert back to number:
res = strsplit(char(v), char(0));
res = cellfun(#(x) x - 0,res,'un',0);
celldisp(res)
res{1} =
1 3 2 6 4
res{2} =
2 4 6
res{3} =
3 1