matlab. vectorization within if/else if/else statements - matlab

I need some help with the following code:
if x(:,3)>x(:,4)
output=[x(:,1)-x(:,2)];
elseif x(:,3)<x(:,4)
output=[x(:,2)-x(:,1)];
else
output=NaN
end
Here is a sample data:
matrix x output
10 5 1 2 -5
10 5 2 1 5
NaN 1 1 3 NaN
I'm not sure how to make the code work. It just takes the first argument and ignores the else if and else arguments. Please help. Thank you.

if x(:,3)>x(:,4) doesn't really work, if expects either true or false not a vector. So it only evaluates the first element of the vector x(:,3)>x(:,4) which is why it appears to ignore your elseif.
So you must either use a loop or even better you can use logical indexing like this:
x= [10 5 1 2
10 5 2 1
NaN 1 1 3]
output = NaN(size(x,1),1)
I = x(:,3)>x(:,4);
output(I) = x(I,1)-x(I,2);
I = x(:,3)<x(:,4);
output(I) = x(I,2)-x(I,1)

Using sign to avoid indexing for different conditions.
B=diff(x,1,2);
B(B(:,3)==0,3) = NaN;
output = B(:,1) .* sign(B(:,3));
Or in a shorter and less readable form:
B=diff(x,1,2);
output = B(:,1) .* (sign(B(:,3))+0./sign(B(:,3)));

Here is how you can do it:
output = NaN(size(x,1),1);
idx1 = x(:,3)>x(:,4);
idx2 = x(:,3)<x(:,4);
output(idx1) = x(idx1,1)-x(idx1,2);
output(idx2) = x(idx2,2)-x(idx2,1);

Related

Simplify a Matlab code involving finding the position of maximum elements of array

I would like your help to make more efficient (maybe, by vectorising) the Matlab code below. The code below does essentially the following: take a row vector A; consider the maximum elements of such a row vector and let, for example, be i and j their positions; construct two columns vectors, the first with all zeros but a 1 positioned at i, the second with all zeros but a 1 positioned at j.
This is my attempt with loops, but it looks more complicated than needed.
clear
rng default
A=[3 2 3];
max_idx=ismember(A,max(A));
vertex=cell(size(A,2),1);
for j=1:size(max_idx,2)
if max_idx(j)>0
position=find(max_idx(j));
vertex_temp=zeros(size(A,2),1);
vertex_temp(position)=1;
vertex{j}=vertex_temp;
else
vertex{j}=[];
end
end
vertex=vertex(~cellfun('isempty',vertex));
Still using a for loop, but more readable:
A = [3 2 3];
% find max indices
max_idx = find(A == max(A));
vertex = cell(numel(max_idx),1);
for k = 1:numel(max_idx)
vertex{k} = zeros(size(A,2),1); % init zeros
vertex{k}(max_idx(k)) = 1; % set value in vector to 1
end
If you really wanted to avoid a for loop, you could probably also use something like this:
A=[3 2 3];
max_idx = find(A==max(A));
outMat = zeros(numel(A), numel(max_idx));
outMat((0:(numel(max_idx)-1)) * numel(A) + max_idx) = 1;
then optionally, if you want them in separate cells rather than columns of a matrix:
outCell = mat2cell(outMat, numel(A), ones(1,numel(max_idx)))';
However, I think this might be less simple and readable than the existing answers.
Is there a specific reason you want a cell array rather than a matrix?
If you can have it all in one vector:
A = [3 2 3]
B_rowvec = A == max(A)
B_colvec = B_rowvec'
If you need them separated into separate vectors:
A = [3 2 3]
Nmaxval = sum(A==max(A))
outmat = zeros(length(A),Nmaxval)
for i = 1:Nmaxval
outmat(find(A==max(A),i),i)=1;
end
outvec1 = outmat(:,1)
outvec2 = outmat(:,2)
Basically, the second input for find will specify which satisfactory instance of the first input you want.
so therefore
example = [ 1 2 3 1 2 3 1 2 3 ]
first = find(example == 1, 1) % returns 1
second = find(example == 1, 2) % returns 4
third = find(example == 1, 3) % returns 7

For-loop exits too early, need help as to why

I've written a simple function that takes a vector vec, iterates through it, performing an operation whose result is stored in another vector vecRes of same size at same index, and returns vecRes upon completing the loop. Below is function code:
function [ vecRes ] = squareTerms( vec )
vecSize = size(vec);
vecRes = zeros(vecSize);
for i = 1:vecSize
vecRes(i) = vec(i)^2;
end
end
Problem is that it seems to exit too early, after only one iteration in fact as the output appears as:
vecRes = 1 0 0 0 0 0 0 0 0 0
For input:
vec = 1 2 3 4 5 6 7 8 9 10
I can't figure out why it does so. Any help is greatly appreciated.
Size returns 2 values, rows and columns. Probably you are a having a 1xN vector. So size returns [1 N] and your loop runs 1 time.
>>> size ([1 2 3])
>
> ans =
>
> 1 3
>
>>> 1:size ([1 2 3])
>
> ans =
>
> 1
Others have pointed out the problem. My preferred solution in this sort of case is to use numel, i.e.
vecRes = zeros(size(vec));
for i = 1:numel(vec)
vecRes(i) = vec(i) ^ 2;
end
Of course, in this case, vectorisation is better still:
vecRes = vec .^ 2;
Replace
for i = 1:vecSize
with
for i = 1:vecSize(2)
vecSize is an array of numbers, not just a single value. For example, if vec is a 1 by 8 vector, then size(vec) will return [1, 8].
Therefore, your for-loop-statement,
for i = 1:vecSize
, is actually equivalent to something like:
for i = 1:[1, 8]
This doesn't make much sense. There are a number of ways to fix the problem. You could write:
for i = 1:length(vec)
or
for i = 1:numel(vec) % "numel" stands for "number of elements"
If the vector is 1xn instead of nx1, you can write:
for i = 1:size(vec, 2)
Yet another alternative is:
for i = 1:max(vecSize)
However, the most sensible option is not to write the squareTerms function at all and simply write
vecRes = vec.^2;
Note the dot before the caret. vec^2 and vec . ^2 are not the same thing.
If you put a dot before an operator sign, the operation will be performed element-wise. For example,
C = A * B
performs matrix multiplication, but
C = A .* B
will cause the first element of A to by multiplied by the first element of B, and the result will be assigned to the first element of C. Then, the product of the second elements of A and B will be taken, and the result will be stuck in the second element of C, and so on.
vecRes = vec.^2;

Recurring Function with Matrix Input

I believe most functions in MATLAB should be able to receive matrix input and return the output in the form of matrix.
For example sqrt([1 4 9]) would return [1 2 3].
However, when I tried this recurring factorial function:
function k = fact(z)
if z ~= 0
k = z * fact(z-1);
else
k = 1;
end
end
It works perfectly when a number is input into fact. However, when a matrix is input into fact, it returns the matrix itself, without performing the factorial function.
E.g.
fact(3) returns 6
fact([1 2 3]) returns [1 2 3] instead of [1 2 6].
Any help is appreciated. Thank you very much!
Since MATLAB is not known to be good with recursive functions, how about a vectorized approach? Try this for a vector input -
mat1 = repmat([1:max(z)],[numel(z) 1])
mat1(bsxfun(#gt,1:max(z),z'))=1
output1 = prod(mat1,2)
Sample run -
z =
1 2 7
output1 =
1
2
5040
For the sake of answering your original question, here's the annoying loopy code for a vector or 2D matrix as input -
function k1 = fact1(z1)
k1 = zeros(size(z1));
for ii = 1:size(z1,1)
for jj = 1:size(z1,2)
z = z1(ii,jj);
if z ~= 0
k1(ii,jj) = z .* fact1(z-1);
else
k1(ii,jj) = 1;
end
end
end
return
Sample run -
>> fact1([1 2 7;3 2 1])
ans =
1 2 5040
6 2 1
You can use the gamma function to compute the factorial without recursion:
function k = fact(z)
k = gamma(z+1);
Example:
>> fact([1 2 3 4])
ans =
1 2 6 24
Not sure if all of you know, but there is an actual factorial function defined in MATLAB that can take in arrays / matrices of any size, and computes the factorial element-wise. For example:
k = factorial([1 2 3 4; 5 6 7 8])
k =
1 2 6 24
120 720 5040 40320
Even though this post is looking for a recursive implementation, and Divakar has provided a solution, I'd still like to put my two cents in and suggest an alternative. Also, let's say that we don't have access to factorial, and we want to compute this from first principles. What I would personally do is create a cell array that's the same size as the input matrix, but each element in this cell array would be a linear index array from 1 up to the number defined for each location in the original matrix. You would then apply prod to each cell element to compute the factorial. A precondition is that no number is less than 1, and that all elements are integers. As such:
z1 = ... ; %// Define input matrix here
z1_matr = arrayfun(#(x) 1:x, z1, 'uni', 0);
out = cellfun(#prod, z1_matr);
If z1 = [1 2 3 4; 5 6 7 8];, from my previous example, we get the same output with the above code:
out =
1 2 6 24
120 720 5040 40320
This will obviously be slower as there is an arrayfun then cellfun call immediately after, but I figured I'd add another method for the sake of just adding in another method :) Not sure how constructive this is, but I figured I'd add my own method and join Divakar and Luis Mendo :)

Does anyone know a simpler way to solve this increment a vector of numbers?

Problem statement:
Take as input an array of digits (e.g. x = [1 2 3]) and output an array of digits that is that number "incremented" properly, (in this case, y = [1 2 4]).
Examples of proper input/output:
x = [1 9 1 9] ----> y = [1 9 2 0]
and
x = [9 9 9] ----> y = [1 0 0 0]
I thought the easiest solution would be to convert the vector to a matrix, increment it, and then convert back. It sounds convoluted but it worked better than trying to do the addition normally.
My attempt:
function ans = incrementor(x)
x=sprintf('%1d',x)
x=str2num(x)
num2str(x+1) - '0'
Can anyone come up with a more efficient solution?
(I'm thinking there may be a command to perform the addition directly without converting to a scalar, but I don't know it.)
I believe that matlab has no native function for what you need, at some point you will have to convert your values ​​in scalar mode to increase
Combine the digits, add 1,
yn=x*(10.^(length(x)-1:-1:0))' + 1
Then convert back:
numDigits = floor(log10(yn)+1)
tens = 10.^(1:numDigits);
y = fliplr(floor(mod(yn,tens)./(tens/10)))
Or, as the OP (user3020151) points out, you can convert back with num2str:
y = num2str(yn) - '0'
Addressing the search for a single command, I do not know of a built-in command, but you can construct an anonymous function as follows:
>> incrementor = #(x) num2str(x*(10.^(length(x)-1:-1:0)).' + 1) - '0'; %' anonymous
>> x = [9 9 9];
>> y = incrementor(x)
y =
1 0 0 0
No need to use strings or convert to a number: Just find the rightmost non-nine digit (if any), add 1 to it, and set any digit to its right to zero. The case when all digits are nine needs to be dealt with separately, because then the numer of digits has to be increased.
k = find(x~=9,1,'last');
if isempty(k)
y = [1 zeros(1,numel(x))];
else
y = [x(1:k-1) x(k)+1 zeros(1,numel(x)-k)];
end
x = [1 2 3];
r1=sprintf('%1d',x);
r1= str2num(r1) + 1;
r2=sscanf( sprintf( num2str(r1) ), '%1d' )'

Two FOR statements coupled into one

Is is possible to put two for statements into one statement. Something like
A = [ 0 0 0 5
0 2 0 0
1 3 0 0
0 0 4 0];
a=size(A);
b=size(A);
ind=0;
c=0;
for ({i=1:a},{j=1:b})
end
Your question is very broad, but one thing to consider is that in MATLAB you can often take advantage of linear indexing (instead of subscripting), without actually having to reshape the array. For example,
>> A = [ 0 0 0 5
0 2 0 0
1 3 0 0
0 0 4 0];
>> A(3,2)
ans =
3
>> A(7) % A(3+(2-1)*size(A,1))
ans =
3
You can often use this to your advantage in a for loop over all the elements:
for ii=1:numel(A),
A(ii) = A(ii) + 1; % or something more useful
end
Is the same as:
for ii=1:size(A,2),
for jj=1:size(A,1),
A(jj,ii) = A(jj,ii) + 1;
end
end
But to address your specific goal in this problem, as you stated in the comments ("I am storing the non zero elements in another matrix; with elements like the index number, value, row number and column number."), of making sparse matrix representation, it comes to this:
>> [i,j,s] = find(A);
>> [m,n] = size(A);
>> S = sparse(i,j,s,m,n)
S =
(3,1) 1
(2,2) 2
(3,2) 3
(4,3) 4
(1,4) 5
But that's not really relevant to the broader question.
Actually you can combine multiple loops into one for, however it would require you to loop over a vector containing all elements rather than the individual elements.
Here is a way to do it:
iRange = 1:2;
jRange = 1:3;
[iL jL] = ndgrid(iRange,jRange);
ijRange = [iL(:) jL(:)]';
for ij = ijRange
i = ij(1); j = ij(2);
end
Note that looping over the variables may be simpler, but perhaps this method has some advantages as well.
No
read this http://www.mathworks.com/help/matlab/matlab_prog/loop-control-statements.html
i also don't see any added value even if it was possible
No I don't think you can put two for loops in one line.
Depends on your operation, you may be able to reshape it and use one for loop. If you are doing something as simple as just printing out all elements,
B = reshape(A,a*b,1);
for i=1:a*b
c = B(i);
...
end