comparison of loop and vectorization in matlab - matlab

let us consider following code for impulse function
function y=impulse_function(n);
y=0;
if n==0
y=1;
end
end
this code
>> n=-2:2;
>> i=1:length(n);
>> f(i)=impulse_function(n(i));
>>
returns result
f
f =
0 0 0 0 0
while this code
>> n=-2:2;
>> for i=1:length(n);
f(i)=impulse_function(n(i));
end
>> f
f =
0 0 1 0 0
in both case i is 1 2 3 4 5,what is different?

Your function is not defined to handle vector input.
Modify your impluse function as follows:
function y=impulse_function(n)
[a b]=size(n);
y=zeros(a,b);
y(n==0)=1;
end
In your definition of impulse_function, whole array is compared to zero and return value is only a single number instead of a vector.

In the first case you are comparing an array to the value 0. This will give the result [0 0 1 0 0], which is not a simple true or false. So the statement y = 0; will not get executed and f will be [0 0 0 0 0] as shown.
In the second you are iterating through the array value by value and passing it to the function. Since the array contains the value 0, then you will get 1 back from the function in the print out of f (or [0 0 1 0 0], which is an impulse).
You'll need to modify your function to take array inputs.
Perhaps this example will clarify the issue further:
cond = 0;
if cond == 0
disp(cond) % This will print 0 since 0 == 0
end
cond = 1;
if cond == 0
disp(cond) % This won't print since since 1 ~= 0 (not equal)
end
cond = [-2 -1 0 1 2];
if cond == 0
disp(cond) % This won't print since since [-2 -1 0 1 2] ~= 0 (not equal)
end

You could define your impulse function simply as this one -
impulse_function = #(n) (1:numel(n)).*n==0
Sample run -
>> n = -6:4
n =
-6 -5 -4 -3 -2 -1 0 1 2 3 4
>> out = impulse_function(n)
out =
0 0 0 0 0 0 1 0 0 0 0
Plot code -
plot(n,out,'o') %// data points
hold on
line([0 0],[1 0]) %// impulse point
Plot result -

You can write an even simpler function:
function y=impulse_function(n);
y = n==0;
Note that this will return y as a type logical array but that should not affect later numerical computations.

Related

Perform "outer product" of 2-D matrix and return a 3-D array in MATLAB

I would like to do an operation on a 2-D matrix which somehow looks like the outer product of a vector. I already have written some codes for this task, but it is pretty slow, so I would like to know if there is anything I can do to accelerate it.
I would like to show the code I wrote first, followed by an example to illustrate the task I wanted to do.
My code, version row-by-row
function B = outer2D(A)
B = zeros(size(A,1),size(A,2),size(A,2)); %Pre-allocate the output array
for J = 1 : size(A,1)
B(J,:,:) = transpose(A(J,:))*A(J,:); %Perform outer product on each row of A and assign to the J-th layer of B
end
end
Using the matrix A = randn(30000,20) as the input for testing, it spends 0.317 sec.
My code, version page-by-page
function B = outer2D(A)
B = zeros(size(A,1),size(A,2),size(A,2)); %Pre-allocate the output array
for J = 1 : size(A,2)
B(:,:,J) = repmat(A(:,J),1,size(A,2)).*A; %Evaluate B page-by-page
end
end
Using the matrix A = randn(30000,20) as the input for testing, it spends 0.146 sec.
Example 1
A = [3 0; 1 1; 1 0; -1 1; 0 -2]; %A is the input matrix.
B = outer2D(A);
disp(B)
Then I would expect
(:,:,1) =
9 0
1 1
1 0
1 -1
0 0
(:,:,2) =
0 0
1 1
0 0
-1 1
0 4
The first row of B, [9 0; 0 0], is the outer product of [3 0],
i.e. [3; 0]*[3 0] = [9 0; 0 0].
The second row of B, [1 1; 1 1], is the outer product of [1 1],
i.e. [1; 1]*[1 1] = [1 1; 1 1].
The third row of B, [1 0; 0 0], is the outer product of [1 0],
i.e. [1; 0]*[1 0] = [1 0; 0 0].
And the same for the remaining rows.
Example 2
A =
0 -1 -2
0 1 0
-3 0 2
0 0 0
1 0 0
B = outer2D(A)
disp(B)
Then, similar to the example 1, the expected output is
(:,:,1) =
0 0 0
0 0 0
9 0 -6
0 0 0
1 0 0
(:,:,2) =
0 1 2
0 1 0
0 0 0
0 0 0
0 0 0
(:,:,3) =
0 2 4
0 0 0
-6 0 4
0 0 0
0 0 0
Because the real input in my project is like in the size of 30000 × 2000 and this task is to be performed for many times. So the acceleration of this task is quite essential for me.
I am thinking of eliminating the for-loop in the function. May I have some opinions on this problem?
With auto expansion:
function B = outer2D(A)
B=permute(permute(A,[3 1 2]).*A',[2 3 1]);
end
Without auto expansion:
function B = outer2Dold(A)
B=permute(bsxfun(#times,permute(A,[3 1 2]),A'),[2 3 1]);
end
Outer products are not possible in the matlab language.

How to find a non-zero number between two zeros in a cell array in matlab

I have a cell array (11000x500) with three different type of elements.
1) Non-zero doubles
2) zero
3) Empty cell
I would like to find all occurances of a non-zero number between two zeros.
E.g. A = {123 13232 132 0 56 0 12 0 0 [] [] []};
I need the following output
out = logical([0 0 0 0 1 0 1 0 0 0 0 0]);
I used cellfun and isequal like this
out = cellfun(#(c)(~isequal(c,0)), A);
and got the follwoing output
out = logical([1 1 1 0 1 0 1 0 0 1 1 1]);
I need help to perform the next step where i can ignore the consecutive 1's and only take the '1's' between two 0's
Could someone please help me with this?
Thanks!
Here is a quick way to do it (and other manipulations binary data) using your out:
out = logical([1 1 1 0 1 0 1 0 0 1 1 1]);
d = diff([out(1) out]); % find all switches between 1 to 0 or 0 to 1
len = 1:length(out); % make a list of all indices in 'out'
idx = [len(d~=0)-1 length(out)]; % the index of the end each group
counts = [idx(1) diff(idx)]; % the number of elements in the group
elements = out(idx); % the type of element (0 or 1)
singles = idx(counts==1 & elements==1)
and you will get:
singles =
5 7
from here you can continue and create the output as you need it:
out = false(size(out)); % create an output vector
out(singles) = true % fill with '1' by singles
and you get:
out =
0 0 0 0 1 0 1 0 0 0 0 0
You can use conv to find the elements with 0 neighbors (notice that the ~ has been removed from isequal):
out = cellfun(#(c)(isequal(c,0)), A); % find 0 elements
out = double(out); % cast to double for conv
% elements that have more than one 0 neighbor
between0 = conv(out, [1 -1 1], 'same') > 1;
between0 =
0 0 0 0 1 0 1 0 0 0 0 0
(Convolution kernel corrected to fix bug found by #TasosPapastylianou where 3 consecutive zeros would result in True.)
That's if you want a logical vector. If you want the indices, just add find:
between0 = find(conv(out, [1 -1 1], 'same') > 1);
between0 =
5 7
Another solution, this completely avoids your initial logical matrix though, I don't think you need it.
A = {123 13232 132 0 56 0 12 0 0 [] [] []};
N = length(A);
B = A; % helper array
for I = 1 : N
if isempty (B{I}), B{I} = nan; end; % convert empty cells to nans
end
B = [nan, B{:}, nan]; % pad, and collect into array
C = zeros (1, N); % preallocate your answer array
for I = 1 : N;
if ~any (isnan (B(I:I+2))) && isequal (logical (B(I:I+2)), logical ([0,1,0]))
C(I) = 1;
end
end
C = logical(C)
C =
0 0 0 0 1 0 1 0 0 0 0 0

converting elements in a vector in matlab

For a project, I'm trying to find the first 1 of a series of ones in a vector. For example, I have as input:
x1=[1 0 0 1 1 1 0 1 0 1 0 0 1 1]
and I need as output:
Y1=[1 0 0 1 0 0 0 1 0 1 0 0 1 0]
So every time there is a 1 in the vector, all consequent ones need to be turned into zeroes.
I have the following code, but for some reason it just returns Y1 with exactly the same values as x1.
n=numel(x1);
Y1=zeros(n,1);
for i = 1:n
if x1(i) == 1
Y1(i)= 1;
for j = (i+1): n
if x1(j)== 1
Y1(j)=0;
elseif x1(j) == 0
Y1(j)=0;
i=j+1;
break
end
end
elseif x1(i) == 0
Y1(i)= 0;
end
end
Any help would be greatly appreciated.
Easy with diff. No loops needed.
Y1 = [ x1(1) diff(x1)==1 ];
or equivalently
Y1 = diff([0 x1])==1;
How this works: diff computes the difference of an element with respect to the preceding element. When that difference is 1, a new run of ones has begun. The first element requires special treatment.
A generalization of the answer by #Luis for the case where your vectors don't just contain zeros and ones:
Y1 = diff([0 x1]) & x1 == 1
This checks whether the value is one, and whether it is different from the previous value.

How to count the number of 1's from the total matrix

I have code like below:
N=10;
R=[1 1 1 1 1 0 0 0 0 0;1 1 1 1 1 1 1 1 1 1];
p=[0.1,0.2,0.01];
B = zeros(N , N);
B(1:N,1:N) = eye(N);
C=[B;R];
for q=p(1:length(p))
Rp=C;
for i=1:N
if(rand < p)
Rp(i,:) = 0;
end
end
end
from this code I vary the value of p. So for different value of p, i am getting different Rp. Now I want to get the total number of "1"'s from each Rp matrix. it means may be for p1 I am getting Rp1=5, for p2, Rp=4.
For example
Rp1=[1 0 0 0 0;0 1 0 0 0;0 0 0 0 0],
Rp2=[1 0 0 0 0;0 1 0 0 0;1 0 0 0 0],
Rp3=[0 0 0 0 0;0 1 0 0 0;0 0 0 0 0],
So total result will be 2,3,1.
I want to get this result.
If the matrix contains only 0 and 1 you are trying to count the nonzero values and there is a function for that called nnz
n = nnz(Rp);
As I mentioned in the comments you should replace
if(rand < p)
with
if(rand < q)
Then you can add the number of nonzero values to a vector like
r = [];
for q=p(1:length(p))
Rp=C;
for i=1:N
if(rand < p)
Rp(i,:) = 0;
end
end
r = [r nnz(Rp)];
end
Then r will contain your desired result. There are many ways to improve your code as mentioned in other answers and comments.
Assuming Rp is your matrix, then simply do one of the following:
If your matrix only contains zeros and ones
sum(Rp(:))
Or if your matrix contains multiple values:
sum(Rp(:)==1)
Note that for two dimensional matrices sum(Rp(:)) is the same as sum(sum(Rp))
I think your real question is how to save this result, you can do this by assigning it to an indexed varable, for example:
S(count) = sum(Rp(:));
This will require you to add a count variable that increases with one every step of the loop. It will be good practice (and efficient) to initialize your variable properly before the loop:
S = zeros(length(p),1);
If you need to count the 1's in any matrix M you should be able to do sum(M(:)==1)

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.