Matlab conditional assignment [duplicate] - matlab

This question already has answers here:
Is there a Matlab conditional IF operator that can be placed INLINE like VBA's IIF
(10 answers)
Closed 9 years ago.
I'm looking for Matlab equivalent of c# condition ? true-expression : false-expression conditional assignment. The most I know of is a = 5>2, which is true\false assignment,
but is there any one line conditional assignment for
if condition a=1;else a=2; end?

For numeric arrays, there is another solution --
// C:
A = COND ? X : Y;
becomes
% MATLAB
% A, X and Y are numerics
% COND is a logical condition.
A = COND.*X + (~COND).*Y ;
Advantage:
works wonderfully in parallel for vectors or large arrays - each item in A gets assigned depending on the corresponding condition. The same line works for:
condition is scalar, arrays X and Y are equal in size
condition is an array of any size, X and Y are scalars
condition and X and Y are all arrays of the same size
Warning:
Doesn't work gracefully with NaNs. Beware! If an element of X is nan, or an element of Y is nan, then you'll get a NaN in A, irrespective of the condition.
Really Useful corollary:
you can use bsxfun where COND and X/Y have different sizes.
A = bsxfun( #times, COND', X ) + bsxfun( #times, ~COND', Y );
works for example where COND and X/Y are vectors of different lengths.
neat eh?

One line conditional assignment:
a(a > 5) = 2;
This is an example of logical indexing, a > 5 is a logical (i.e. Boolean or binary) matrix/array the same size as a with a 1 where ever the expression was true. The left side of the above assignment refers to all the positions in a where a>5 has a 1.
b = a > 5; % if a = [9,3,5,6], b = [1,0,0,1]
a(~b) = 3;
c = a > 10;
a(b&c) = ...
Etc...you can do pretty much anything you'd expect with such logical arrays.

Matlab does not have a ternary operator. You though easily write a function that will do such thing for you:
function c = conditional(condition , a , b)
if condition
c = a;
else
c = b;
end
end

Related

Logical tests on Cell Arrays

When dealing with regular arrays in Matlab it's very easy to perform logical tests and to set the cells that fail those tests to a desired value such as NaN without having to use for loops.
TestA=[1 2 10 45];
TestA(TestA<=2)=NaN;
I have a cell array where I can do the logical test OK, but how can I then turn the cell array elements that fail the test to NaN without using a for loop?
TestCell{1}=[1 2 10 45];
TA=TestCell{1} <= 2;
Thanks
Baz
The same concept holds true for cell array elements if they are numeric arrays.
TestCell{1}(TestCell{1} <= 2) = NaN;
Now if you want to loop through all of the elements of the cell without an explicit for loop, this is a little difficult because you can't do assignment within cellfun.
The alternative is to construct some expression which will return an array where there are NaNs when your condition is true and the original values elsewhere.
Such an expression could look like this:
x .* ~(x <= 2) ./ ~(x <= 2);
This will cause each element to be multiplied by 0/0 (NaN) when the condition is true and multiplied by 1/1 (1) when the condition is false. In this way, all values that meet the condition will be set to NaNs.
If we try this on your example data:
x = [1 2 10 45];
x .* ~(x <= 2) ./ ~(x <= 2)
NaN NaN 10 45
We get the NaN's where your condition is true. So now we can plug this into cellfun to perform this operation on all contents.
output = cellfun(#(x)x .* ~(x <= 2)./~(x <= 2), TestCell, 'uni', 0);
The alternative is to write the conditional backwards so all elements which don't meet the criteria will be set to NaN. This will prevent having to negate both of the conditionals in the above equations.
x = [1 2 10 45]
x .* (x > 2) ./ (x > 2)
And we can plug this into cellfun like we did above.
output = cellfun(#(x)x .* (x > 2) ./ (x > 2), TestCell, 'uni', 0);
The primary disadvantage of this approach is that it requires you to evaluate the conditional twice as there is no good way to store the intermediate result within cellfun. This may or may not be an issue depending upon how big each element is. There really is no shame in having a for loop for doing this sort of thing as it is likely more performant than the solution here.

How to get the union of the two 3D matrix?

I have two 3D matrix A and B. The size of A and B are both 40*40*20 double.
The values in matrix A and B are either 0 or 1. The number of "1" in A are 100,
the number of "1" in B are 50. The "1" in matrix A and B may or may not be in
the same coordinates. I want to get the union of matrix A and B, called C. The values in 3D matrix C is either "1" or "0". The number of "1" in C is less than or equal to 150. My question is how to get the 3D matrix C in Matlab?
You can use the operator or, which is a logical or. So or(a,b) is equivalent to the logical operation a | b.
C = or(A,B);
C = a | b;
| and or are the same operator in MatLab, it's just two different way to call it.
I think this is the best solution as long as it's integrated into MatLab. However, you have plenty different ways to do it.
Just as an example, you can do
C = logical(a+b);
logical is an operator that convert every value into logical values. Long story short, it will replace any value different of 0 by 1.
You can approach it in 2 ways. The more efficient one is using vectors but you can also do it in classical nested for loops.
A = rand(40,40,20);
A = A > 0.01; # Get approximate 320 ones and rest zeros
B = rand(40,40,20);
B = B > 0.005; # Get approximate 160 ones and rest zeros
C = zeros(size(A));
for iter1 = 1:size(A,1)
for iter2 = 1:size(A,2)
for iter3 = 1:size(A,3)
C(iter1,iter2,iter3) = A(iter1,iter2,iter3)|B(iter1,iter2,iter3)
end
end
end
This method will be very slow. You can vectorized it to improve performance
C = A|B

MATLAB: Test if anonymous vector is a subset of R^n

I'm trying to use MatLab code as a way to learn math as a programmer.
So reading I'm this post about subspaces and trying to build some simple matlab functions that do it for me.
Here is how far I got:
function performSubspaceTest(subset, numArgs)
% Just a quick and dirty function to perform subspace test on a vector(subset)
%
% INPUT
% subset is the anonymous function that defines the vector
% numArgs is the the number of argument that subset takes
% Author: Lasse Nørfeldt (Norfeldt)
% Date: 2012-05-30
% License: http://creativecommons.org/licenses/by-sa/3.0/
if numArgs == 1
subspaceTest = #(subset) single(rref(subset(rand)+subset(rand))) ...
== single(rref(rand*subset(rand)));
elseif numArgs == 2
subspaceTest = #(subset) single(rref(subset(rand,rand)+subset(rand,rand))) ...
== single(rref(rand*subset(rand,rand)));
end
% rand just gives a random number. Converting to single avoids round off
% errors.
% Know that the code can crash if numArgs isn't given or bigger than 2.
outcome = subspaceTest(subset);
if outcome == true
display(['subset IS a subspace of R^' num2str(size(outcome,2))])
else
display(['subset is NOT a subspace of R^' num2str(size(outcome,2))])
end
And these are the subset that I'm testing
%% Checking for subspaces
V = #(x) [x, 3*x]
performSubspaceTest(V, 1)
A = #(x) [x, 3*x+1]
performSubspaceTest(A, 1)
B = #(x) [x, x^2, x^3]
performSubspaceTest(B, 1)
C = #(x1, x3) [x1, 0, x3, -5*x1]
performSubspaceTest(C, 2)
running the code gives me this
V =
#(x)[x,3*x]
subset IS a subspace of R^2
A =
#(x)[x,3*x+1]
subset is NOT a subspace of R^2
B =
#(x)[x,x^2,x^3]
subset is NOT a subspace of R^3
C =
#(x1,x3)[x1,0,x3,-5*x1]
subset is NOT a subspace of R^4
The C is not working (only works if it only accepts one arg).
I know that my solution for numArgs is not optimal - but it was what I could come up with at the current moment..
Are there any way to optimize this code so C will work properly and perhaps avoid the elseif statments for more than 2 args..?
PS: I couldn't seem to find a build-in matlab function that does the hole thing for me..
Here's one approach. It tests if a given function represents a linear subspace or not. Technically it is only a probabilistic test, but the chance of it failing is vanishingly small.
First, we define a nice abstraction. This higher order function takes a function as its first argument, and applies the function to every row of the matrix x. This allows us to test many arguments to func at the same time.
function y = apply(func,x)
for k = 1:size(x,1)
y(k,:) = func(x(k,:));
end
Now we write the core function. Here func is a function of one argument (presumed to be a vector in R^m) which returns a vector in R^n. We apply func to 100 randomly selected vectors in R^m to get an output matrix. If func represents a linear subspace, then the rank of the output will be less than or equal to m.
function result = isSubspace(func,m)
inputs = rand(100,m);
outputs = apply(func,inputs);
result = rank(outputs) <= m;
Here it is in action. Note that the functions take only a single argument - where you wrote c(x1,x2)=[x1,0,x2] I write c(x) = [x(1),0,x(2)], which is slightly more verbose, but has the advantage that we don't have to mess around with if statements to decide how many arguments our function has - and this works for functions that take input in R^m for any m, not just 1 or 2.
>> v = #(x) [x,3*x]
>> isSubspace(v,1)
ans =
1
>> a = #(x) [x(1),3*x(1)+1]
>> isSubspace(a,1)
ans =
0
>> c = #(x) [x(1),0,x(2),-5*x(1)]
>> isSubspace(c,2)
ans =
1
The solution of not being optimal barely scratches the surface of the problem.
I think you're doing too much at once: rref should not be used and is complicating everything. especially for numArgs greater then 1.
Think it through: [1 0 3 -5] and [3 0 3 -5] are both members of C, but their sum [4 0 6 -10] (which belongs in C) is not linear product of the multiplication of one of the previous vectors (e.g. [2 0 6 -10] ). So all the rref in the world can't fix your problem.
So what can you do instead?
you need to check if
(randn*subset(randn,randn)+randn*subset(randn,randn)))
is a member of C, which, unless I'm mistaken is a difficult problem: Conceptually you need to iterate through every element of the vector and make sure it matches the predetermined condition. Alternatively, you can try to find a set such that C(x1,x2) gives you the right answer. In this case, you can use fminsearch to solve this problem numerically and verify the returned value is within a defined tolerance:
[s,error] = fminsearch(#(x) norm(C(x(1),x(2)) - [2 0 6 -10]),[1 1])
s =
1.999996976386119 6.000035034493023
error =
3.827680714104862e-05
Edit: you need to make sure you can use negative numbers in your multiplication, so don't use rand, but use something else. I changed it to randn.

Set all nonzero matrix elements to 1 (while keeping the others 0)

I have a mesh grid defined as
[X, Y, Z] = meshgrid(-100:100, -100:100, 25); % z will have more values later
and two shapes (ovals, in this case):
x_offset_1 = 40;
x_offset_2 = -x_offset_1;
o1 = ((X-x_offset_1).^2./(2*Z).^2+Y.^2./Z.^2 <= 1);
o2 = ((X-x_offset_2).^2./(2*Z).^2+Y.^2./Z.^2 <= 1);
Now, I want to find all points that are nonzero in either oval. I tried
union = o1+o2;
but since I simply add them, the overlapping region will have a value of 2 instead of the desired 1.
How can I set all nonzero entries in the matrix to 1, regardless of their previous value?
(I tried normalized_union = union./union;, but then I end up with NaN in all 0 elements because I'm dividing by zero...)
Simplest solution: A=A~=0;, where A is your matrix.
This just performs a logical operation that checks if each element is zero. So it returns 1 if the element is non-zero and 0 if it is zero.
First suggestion: don't use union as a variable name, since that will shadow the built-in function union. I'd suggest using the variable name inEitherOval instead since it's more descriptive...
Now, one option you have is to do something like what abcd suggests in which you add your matrices o1 and o2 and use the relational not equal to operator:
inEitherOval = (o1+o2) ~= 0;
A couple of other possibilities in the same vein use the logical not operator or the function logical:
inEitherOval = ~~(o1+o2); % Double negation
inEitherOval = logical(o1+o2); % Convert to logical type
However, the most succinct solution is to apply the logical or operator directly to o1 and o2:
inEitherOval = o1|o2;
Which will result in a value of 1 where either matrix is non-zero and zero otherwise.
There is another simple solution, A=logical(A)

Map function in MATLAB?

I'm a little surprised that MATLAB doesn't have a Map function, so I hacked one together myself since it's something I can't live without. Is there a better version out there? Is there a somewhat-standard functional programming library for MATLAB out there that I'm missing?
function results = map(f,list)
% why doesn't MATLAB have a Map function?
results = zeros(1,length(list));
for k = 1:length(list)
results(1,k) = f(list(k));
end
end
usage would be e.g.
map( #(x)x^2,1:10)
The short answer: the built-in function arrayfun does exactly what your map function does for numeric arrays:
>> y = arrayfun(#(x) x^2, 1:10)
y =
1 4 9 16 25 36 49 64 81 100
There are two other built-in functions that behave similarly: cellfun (which operates on elements of cell arrays) and structfun (which operates on each field of a structure).
However, these functions are often not necessary if you take advantage of vectorization, specifically using element-wise arithmetic operators. For the example you gave, a vectorized solution would be:
>> x = 1:10;
>> y = x.^2
y =
1 4 9 16 25 36 49 64 81 100
Some operations will automatically operate across elements (like adding a scalar value to a vector) while others operators have a special syntax for element-wise operation (denoted by a . before the operator). Many built-in functions in MATLAB are designed to operate on vector and matrix arguments using element-wise operations (often applied to a given dimension, such as sum and mean for example), and thus don't require map functions.
To summarize, here are some different ways to square each element in an array:
x = 1:10; % Sample array
f = #(x) x.^2; % Anonymous function that squares each element of its input
% Option #1:
y = x.^2; % Use the element-wise power operator
% Option #2:
y = f(x); % Pass a vector to f
% Option #3:
y = arrayfun(f, x); % Pass each element to f separately
Of course, for such a simple operation, option #1 is the most sensible (and efficient) choice.
In addition to vector and element-wise operations, there's also cellfun for mapping functions over cell arrays. For example:
cellfun(#upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans =
'A' 'B' 'C'
If 'UniformOutput' is true (or not provided), it will attempt to concatenate the results according to the dimensions of the cell array, so
cellfun(#upper, {'a', 'b', 'c'})
ans =
ABC
A rather simple solution, using Matlab's vectorization would be:
a = [ 10 20 30 40 50 ]; % the array with the original values
b = [ 10 8 6 4 2 ]; % the mapping array
c = zeros( 1, 10 ); % your target array
Now, typing
c( b ) = a
returns
c = 0 50 0 40 0 30 0 20 0 10
c( b ) is a reference to a vector of size 5 with the elements of c at the indices given by b. Now if you assing values to this reference vector, the original values in c are overwritten, since c( b ) contains references to the values in c and no copies.
It seems that the built-in arrayfun doesn't work if the result needed is an array of function:
eg:
map(#(x)[x x^2 x^3],1:10)
slight mods below make this work better:
function results = map(f,list)
% why doesn't MATLAB have a Map function?
for k = 1:length(list)
if (k==1)
r1=f(list(k));
results = zeros(length(r1),length(list));
results(:,k)=r1;
else
results(:,k) = f(list(k));
end;
end;
end
If matlab does not have a built in map function, it could be because of efficiency considerations. In your implementation you are using a loop to iterate over the elements of the list, which is generally frowned upon in the matlab world. Most built-in matlab functions are "vectorized", i. e. it is more efficient to call a function on an entire array, than to iterate over it yourself and call the function for each element.
In other words, this
a = 1:10;
a.^2
is much faster than this
a = 1:10;
map(#(x)x^2, a)
assuming your definition of map.
You don't need map since a scalar-function that is applied to a list of values is applied to each of the values and hence works similar to map. Just try
l = 1:10
f = #(x) x + 1
f(l)
In your particular case, you could even write
l.^2
Vectorizing the solution as described in the previous answers is the probably the best solution for speed. Vectorizing is also very Matlaby and feels good.
With that said Matlab does now have a Map container class.
See http://www.mathworks.com/help/matlab/map-containers.html