Matlab Find-Replace in Matrix Single Liner? - matlab

Say I have the following matrix:
1 0 1 1 0 0
0 0 1 0 1 0
1 1 1 0 0 1
0 1 1 0 0 1
1 1 1 1 1 0
I want to convert it to a different format, where I replace each 1 in each row by its column index, so it would become the following:
1 0 3 4 0 0
0 0 3 0 5 0
1 2 3 0 0 6
0 2 3 0 0 6
1 2 3 4 5 0
I can do it the 'dumb' way:
[H, W] = size(a);
for i = 1:H
for j = 1:W
if(a(i, j) == 1)
a(i, j) = j;
end
end
end
But there surely must be a way to do it with one line (perhaps using the 'find' function), anyone know how?

This isn't super general but does what you want. find returns indices into the one-dimensional version of the data, so we need to do a little arithmetic to get the two-d versions:
a(a == 1) = floor((find(a == 1) - 1) / size(a, 1)) + 1
If you wanted to do the row indices instead, you could use
a(a == 1) = mod(find(a == 1) - 1, size(a, 1)) + 1
If you were doing this with a big matrix, you might want to assign find(a == 1) to a temporary variable first:
inds = find(a == 1)
a(inds) = floor((inds - 1) / size(a, 1)) + 1
(Note that indexing into a with either a list of indices or a matrix of booleans works the same.)
You could also just use find(a) if you know the original matrix is only 0s and 1s.
Note that this is just doing manually basically what #tmpearce's answer does.

you have a matrix a
[r,c]=ind2sub(size(a),find(a));
a(find(a))=c;
Edit: this is doable in one line, since that's important to you:
[r,a(find(a))]=ind2sub(size(a),find(a));

You can use meshgrid to do this:
[H, W] = size(a);
a = a.*meshgrid(1:H,1:W);

It's been a long time since I have use matlab, so I wont be able to give you the code out of my head. But here is the way I would do it:
Create a vector 1:colums, repeat it once for each row using repmat and then multiply this elementwise with the original matrix.
Also since loops are slow in matlab whereas matrix operations are fast, such a one liner will probably be much faster than the code you have right now.

Related

How does Y = eye(K)(y, :); replace a "for" loop? Coursera

Working on an assignment from Coursera Machine Learning. I'm curious how this works... From an example, this much simpler code:
% K is the number of classes.
K = num_labels;
Y = eye(K)(y, :);
seems to be a substitute for the following:
I = eye(num_labels);
Y = zeros(m, num_labels);
for i=1:m
Y(i, :)= I(y(i), :);
end
and I have no idea how. I'm having some difficulty Googling this info as well.
Thanks!
Your variable y in this case must be an m-element vector containing integers in the range of 1 to num_labels. The goal of the code is to create a matrix Y that is m-by-num_labels where each row k will contain all zeros except for a 1 in column y(k).
A way to generate Y is to first create an identity matrix using the function eye. This is a square matrix of all zeroes except for ones along the main diagonal. Row k of the identity matrix will therefore have one non-zero element in column k. We can therefore build matrix Y out of rows indexed from the identity matrix, using y as the row index. We could do this with a for loop (as in your second code sample), but that's not as simple and efficient as using a single indexing operation (as in your first code sample).
Let's look at an example (in MATLAB):
>> num_labels = 5;
>> y = [2 3 3 1 5 4 4 4]; % The columns where the ones will be for each row
>> I = eye(num_labels)
I =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
>> Y = I(y, :)
Y =
% 1 in column ...
0 1 0 0 0 % 2
0 0 1 0 0 % 3
0 0 1 0 0 % 3
1 0 0 0 0 % 1
0 0 0 0 1 % 5
0 0 0 1 0 % 4
0 0 0 1 0 % 4
0 0 0 1 0 % 4
NOTE: Octave allows you to index function return arguments without first placing them in a variable, but MATLAB does not (at least, not very easily). Therefore, the syntax:
Y = eye(num_labels)(y, :);
only works in Octave. In MATLAB, you have to do it as in my example above, or use one of the other options here.
The first set of code is Octave, which has some additional indexing functionality that MATLAB does not have. The second set of code is how the operation would be performed in MATLAB.
In both cases Y is a matrix generated by re-arranging the rows of an identity matrix. In both cases it may also be posible to calculate Y = T*y for a suitable linear transformation matrix T.
(The above assumes that y is a vector of integers that are being used as an indexing variables for the rows. If that's not the case then the code most likely throws an error.)

Count the number of the first zero elements

I would line to find the number of the first consecutive zero elements. For example in [0 0 1 -5 3 0] we have two zero consecutive elements that appear first in the vector.
could you please suggest a way without using for loops?
V=[0 0 1 -5 3 0] ;
k=find(V);
Number_of_first_zeros=k(1)-1;
Or,
Number_of_first_zeros=find(V,1,'first')-1;
To solve #The minion comment (if that was the purpose):
Number_of_first_zeros=find(V(find(~V,1,'first'):end),1,'first')-find(~V,1,'first');
Use a logical array to find the zeros and then look at where the zeros and ones are alternating.
V=[1 2 0 0 0 3 5123];
diff(V==0)
ans =
0 1 0 0 -1 0
Create sample data
V=[1 2 0 0 0 3 5123];
Find the zeros. The result will be a logical array where 1 represents the location of the zeros
D=V==0
D =
0 0 1 1 1 0 0
Take the difference of that array. 1 would then represent the start and -1 would represent the end.
T= diff(D)
ans =
0 1 0 0 -1 0
find(T==1) would give you the start and find(T==-1) would give you the end. The first index+1 of T==1 would be the start of the first set of zeros and the first index of T==-1 would be the end of the first set of zeros.
You could find position the first nonzero element using find.
I=find(A, 1);
The number of leading zeros is then I-1.
My solution is quite complex yet it doesn't use the loops and it does the trick. I am pretty sure, that there is a more direct approach.
Just in case no one else posts a working solution here my idea.
x=[1 2 4 0 20 0 10 1 23 45];
x1=find(x==0);
if numel(x1)>1
x2=[x1(2:end), 0];
x3=x2-x1;
y=find(x3~=1);
y(1)
elseif numel(x1)==1
display(1)
else
display('No zero found')
end
x is the dataset. x1 contains the index of all zero elements. x2 contains all those indices except the first one (because matrix dimensions must agree, one zero is added. x3 is the difference between the index and the previous index of zeros in your dataset. Now I find all those differences which are not 1 (do not correspond to sequences of zeros) and the first index (of this data is the required result. The if case is needed in case you have only one or no zero at all.
I'm assuming your question is the following: for the following vector [0 0 1 -5 3 0], I would like to find the index of the first element of a pair of 0 values. Is this correct? Therefore, the desired output for your vector would be '1'?
To extend the other answers to find any such pairs, not just 0 0 (eg. 0 1, 0 2, 3 4 etc), then this might help.
% define the pattern
ptrn = [ 0 0 ];
difference = ptrn(2) - ptrn(1)
V = [0 0 1 -5 3 0 0 2 3 4 0 0 1 0 0 0]
x = diff(V) == difference
indices = find(x)
indices =
1 6 11 14 15

Select first n of 1 values from a binary vector (0,1) in MATLAB

I have a binary vector, e.g:
x = [1 1 1 0 0 1 0 1 0 0 0 1]
I want to keep the first 4 elements that are '1' (substituting the rest with '0's). In my example the resulting vector should be:
z = [ 1 1 1 0 0 1 0 0 0 0 0 0]
Any help would be much appreciated.
First construct a vector of zeroes, then use find:
z = false(size(x));
z(find(x, 4)) = true;
No need for find for a binary vector. Use cumsum instead!
>> z = x;
>> z(cumsum( z, 2 ) > 4) = 0;
This solution (unlike find-based answers) can process a stack of such binary vectors at once (all you need is to verify that cumsum works on the proper dimension).
Try following:
z=x;
A=find(z);
z(A(5:end))=0;
Idea here is to make all, but first n, 1's to 0's

Effiicient ways to count a streak of consecutive integers in MATLAB

Say I have a vector containing only logical values, such as
V = [1 0 1 0 1 1 1 1 0 0]
I would like to write a function in MATLAB which returns a 'streak' vector S for V, where S(i) represents the number of consecutive 1s in V up to but not including V(i). For the example above, the streak vector would be
S = [0 1 0 1 0 1 2 3 4 0]
Given that I have to do this for a very large matrix, I would very much appreciate any solution that is vectorized / efficient.
This should do the trick:
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
The complexity is only O(n), which I guess should be good enough.
For your sample input:
V = [1 0 1 0 1 1 1 1 0 0];
S = zeros(size(V));
for i=2:length(V)
if(V(i-1)==1)
S(i) = 1 + S(i-1);
end
end
display(V);
display(S);
The result would be:
V =
1 0 1 0 1 1 1 1 0 0
S =
0 1 0 1 0 1 2 3 4 0
You could also do it completely vectorized with a couple intermediate steps:
V = [1 0 1 0 1 1 1 1 0 0];
Sall = cumsum(V);
stopidx = find(diff(V)==-1)+1;
V2=V;
V2(stopidx) = -Sall(stopidx)+[0 Sall(stopidx(1:end-1))];
S2 = cumsum(V2);
S = [0 S2(1:end-1)];
Afaik the only thing that can take a while is the find call; you can't use logical indexing everywhere and bypass the find call, because you need the absolute indices.
It's outside the box - but have you considered using text functions? Since strings are just vectors for Matlab it should be easy to use them.
Regexp contains some nice functions for finding repeated values.

find non-overlapping sequences of zeros in matlab arrays

This is related to:
Finding islands of zeros in a sequence.
However, the problem is not exactly the same:
Let's take the same vector with the above postfor the purpose of comparison:
sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
What I am trying to find are the starting indices of islands of n consecutive zeros; however, overlapping is not allowed. For example for n=2, I want the result:
v=[3, 5, 14, 25];
I found the solution of Amro brilliant as a starting point (especially with regards to strfind), but the second part of his answer does not give me the result that I expect. This is a non-vectorized solution that I have so far:
function v=findIslands(sig, n)
% Finds indices of unique islands
% sig --> target vector
% n --> This is the length of the island
% This will find the starting indices for all "islands" of ones
% but it marks long strings multiple times
startIndex = strfind(sig, zeros(1,n));
L=length(startIndex);
% ongoing gap counter
spc=0;
if L>0 % Check if empty
v=startIndex(1);
for i=2:L
% Count the distance
spc=spc+(startIndex(i)-startIndex(i-1));
if spc>=n
v=[v,startIndex(i)];
% Reset odometer
spc=0;
end
end
else
v=[];
display('No Islands Found!')
end
I was wondering if someone has a faster vectorized solution to the above problem.
You can convert everything into strings and use regular expressions:
regexp(sprintf('%d', sig(:)), sprintf('%d', zeros(n, 1)))
Example
>> sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
>> n = 2;
>> regexp(sprintf('%d', sig(:)), sprintf('%d', zeros(n, 1)))
ans =
3 5 14 25
Do this:
As an example let's look at the case where the run length you want is 2.
Convert vector to binary number
Set index = size-1, set starting = []
Loop until n < 4:
Is n divisible by 4?
Yes? Append index to starting. Set n = n / 4
No? Set n = n / 2
Goto 3
For any other run length replace 4 with 2**run.
Use gnovice's answer from the same linked question. It's vectorized, and the runs where duration == n are the ones you want.
https://stackoverflow.com/a/3274416/105904
Take the runs with duration >= n, and then divide duration by n, and that'll tell you how many consecutive runs you have at each position and how to expand the index list. This could end up faster than the regexp version, if your island density isn't too high.