I'm trying to write a Matlab code that, given a matrix, outputs 3 matrices (according to some rules). I'm having difficulty getting this to work though - I can't output a vector with matrices as entries. I get the error message:
??? In an assignment A(I) = B, the number of elements in B and I
must be the same.
How can I go about doing this?
You could write
function [A B C] = myFunction(X)
A = X;
B = 2 * X;
C = 3 * X;
end
and call it with
[a b c] = myFunction(ones(2))
If you won't want all of the outputs, just call it with
a = myFunction(ones(2))
or
[a b] = myFunction(ones(2))
to get just the first argument, or just the first two arguments.
You can also use cells:
A=cell(1,3); %% or A=cell(1,2); if you want to output only 2 matrices
A{1}=B;
A{2}=C;
A{3}=D;
If your matrices all have the same size you can also concatenate them:
A=zeros(m,n,3);
A(:,:,1)=B;
A(:,:,2)=C;
A(:,:,3)=D;
Function declaration:
function [A, B, C] = something (Input_mat)
%Do whatever needs to be done here, for example:
A= Input_mat;
B= Input_mat';
C= ones(18);
And then when you call it using:
[A,B,C] = something (Some_mat)
A, B and C are filled.
Related
Suppose I have a matrix a = [1,2,3;4,1,2;3,4,2].
I need to create a logical matrix which is 1 wherever there is an element of b in a. The equivalent of a==4 | a==1 if my array b is a small one like [1,4].
I know one way to do this is:
b = [1,4];
c = logical(zeros(size(a)));
for i=b
c = c | a==i;
end
This solution may not scale well if a and b are large. Is there a cleaner way to do it for larger arrays?
I was hoping a == b would give me what i wanted, but it doesn't.
You can use ismember to output a logical array which is true whenever an element of the first input is a member of the second input. The output is the same size as the first input.
c = ismember( a, b );
In your example:
a = [1,2,3;
4,1,2;
3,4,2];
b = [1,4];
c = ismember( a, b );
% >> c =
% [1,0,0;
% 1,1,0;
% 0,1,0]
I have a 7x21 matrix called A. Within this matrix there are three equally sized 7x7 submatrices. I call them B,C and D, where B = A(:,1:7), C = A(:,8:14) and D = A(:,15:21).
How can I produce a matrix E which is also 7x7 matrix where simply B, C and D are added up, i.e. E = B+C+D.
Thanks a lot for your help!
Generic code to get such an output -
N = 3; %// Number of submatrices
[m,n] = size(A) %// Get size [no. of cols must be multiple of N
E = reshape(sum(reshape(A,m*n/N,[]),2),m,n/N)
I don't see what's going to be more straightforward and concise than
E = A(:,1:7) + A(:,8:14) + A(:,15:21)
Unless you need an expression that generalizes in some way you're not describing...
I have four 52-by-140 matrices in Matlab. Lets call them a, b, c, and d:
I want to apply the eigs function to a Hessian matrix [a,b;c,d] for every point in the original matrix like:
for i = 1:52
for j = 1:140
eigs([a(i,j),b(i,j);c(i,j),d(i,j)])
end
end
How can I do this in a simpler way, i.e., without for loops?
This can probably be done using arrayfun and defining the action you do in a single iteration using an anonymous function (untested)
result = arrayfun(#(a_ij, b_ij, c_ij, d_ij) eigs([a_ij, b_ij; c_ij, d_ij]), ...
a, b, c, d, 'uniformoutput', false);
Note that since eig returns a vector, the result can only be stored in a cell array.
You could name the parameters of the anonymous functions a, b, ... instead of a_ij, b_ij, ..., since they are only used inside the function, but I prefer to it this way to make it clear that inside the function you are using scalars, while the parameters to arrayfun are matrices. Personally, I often use upper/lower case to indicate the difference:
result = arrayfun(#(a, b, c, d) eigs([a, b; c, d]), A, B, C, D, 'uni', 0);
but then you would have to rename your variables.
Try this solution to get all of the matrixes teed up:
>> abcd = cat(3,a,b,c,d);
>> H = permute(reshape(permute(abcd,[3 1 2]),2,2,[]),[2 1 3]);
>> size(H)
ans =
2 2 7280
>> i=3;j=2;
>> [a(i,j),b(i,j);c(i,j),d(i,j)]
ans =
0.4984 0.7935
0.3524 0.2273
>> H(:,:,i+(j-1)*size(abcd,1))
ans =
0.4984 0.7935
0.3524 0.2273
>>
Then to run eigs on all 2D matrixes in H:
E=arrayfun(#(i)eigs(H(:,:,i)),1:size(H,3),'uni',false);
I have the following for-loop part of a function:
for i=1:5
for j=1:2
m=x(i)-c(j);
end
end
As a call to the function which includes the code above, I pass two values for c. Say the values passed are (3,5) for c1 and c2 respectively.
As you see in the for-loop above, I will have a two values for c, nanely, c(1) and c(2).
For the 3 and 5 values I have above, how can I assign them to c(1) and c(2) respectively?
When I did the following for instance:
c(1)=center1;
c(2)=center2;
where center1 and center2 represent the passed value to the function, I got the following error:
In an assignment A(I) = B, the number of elements in B and I must be
the same.
Error in functionName (line 32)
c(1)=center1;
It looks like center1 is not a scalar. Print out the value or use isscalar to check it. This works:
c(1) = 1;
but this will not:
c(1) = [1 2];
Also, your double for loop makes no sense because you're overwriting the value of m on each iteration. Presumably you have more stuff inside it. However, you could just create a matrix m without any for loop at all using bsxfun:
x = rand(1,5);
c = rand(1,2);
m = bsxfun(#minus,x(:),c(:).')
This results in m being a 5-by-2 matrix. You can use bsxfun(#minus,x(:).',c(:)) if you prefer a 2-by-5 matrix.
If I have a short list (let's say two or three elements) I would like to have function that split it in several variables. Something like that:
li=[42 43];
[a b]=split(li)
--> a=42
--> b=43
I am looking for some way to make my code shorter in matlab. This one would be nice in some situations For instance:
positions=zeros(10,3);
positions= [....];
[x y z]=split(max(positions,1))
instead of doing:
pos=max(positions,1)
x=pos(1);
y=pos(2);
z=pos(3);
The only way I know of to do this is to use deal. However this works with cell arrays only, or explicit arguments in to deal. So if you want to deal matrices/vectors, you have to convert to a cell array first with num2cell/mat2cell. E.g.:
% multiple inputs
[a b] = deal(42,43) % a=2, b=3
[x y z] = deal( zeros(10,1), zeros(10,1), zeros(10,1) )
% vector input
li = [42 43];
lic = num2cell(li);
[a b]=deal(lic{:}) % unforunately can't do num2cell(li){:}
% a=42, b=43
% matrix input
positions = zeros(10,3);
% create cell array, one column each
positionsc = mat2cell(positions,10,[1 1 1]);
[x y z] = deal(positionsc{:})
The first form is nice (deal(x,y,...)) since it doesn't require you to explicitly make the cell array.
Otherwise I reckon it's not worth using deal when you have to convert your matrices to cell arrays only to convert them back again: just save the overhead. In any case, it still takes 3 lines: first to define the matrix (e.g. li), then to convert to cell (lic), then to do the deal (deal(lic{:})).
If you really wanted to reduce the number of lines there's a solution in this question where you just make your own function to do it, repeated here, and modified such that you can define an axis to split over:
function varargout = split(x,axis)
% return matrix elements as separate output arguments
% optionally can specify an axis to split along (1-based).
% example: [a1,a2,a3,a4] = split(1:4)
% example: [x,y,z] = split(zeros(10,3),2)
if nargin < 2
axis = 2; % split along cols by default
end
dims=num2cell(size(x));
dims{axis}=ones([1 dims{axis}]);
varargout = mat2cell(x,dims{:});
end
Then use like this:
[a b] = split([41 42])
[x y z]= split(zeros(10,3), 2) % each is a 10x1 vector
[d e] = split(zeros(2,5), 1) % each is a 1x5 vector
It still does the matrix -> cell -> matrix thing though. If your vectors are small and you don't do it within a loop a million times you should be fine though.
You can extract the values in the list into different variables with
li = [42 43 44];
tmp = num2cell(li);
[a b c] = deal(tmp{:})
a =
42
b =
43
c =
44
You can manage this in a single expression if you really wanted to:
a = [1, 2, 3, 4]
[b, c, d, e]=feval(#(x)x{:}, num2cell(a))
Result:
b =
1
c =
2
d =
3
e =
4
If you wanted to use this a lot you could quickly make an anonymous function:
expand = #(A) feval(#(x)x{:}, num2cell(A))
[b, c, d, e]=expand(a)
I believe this would work on anything that num2cell will accept, which doesn't even have to be numeric, just needs to be an array, which almost everything in Matlab is.