Matlab loop to assign rows to cell array - arrays

I have a big cell array A=cell(a,b,c,d) and a row vector B with dimensions 1-by-b.
I want to build a loop in MATLAB that does the following:
for i=1:n
B = Calculate_row(input1,input2) %this is a function that creates my B row
A{a,:,c,i} = B(:)
end
anyway if I try to do A{a,:,c} = B(:) I receive the following error:
Expected one output from a curly brace or dot indexing expression, but there were b results.
And if I try to do A(a,:,c) = B(:) I receive the following error:
Conversion to cell from double is not possible.
Is there a way to do this? (I know a less elegant way that probably works would be to assign each value to the cell separately, but I would prefer not to do it).

One way to do this is to make B a cell array and then take advantage of comma-separated-lists:
B_cell = num2cell(B);
[A{a,:,c}] = B_cell{:} %// or [A{a,:,c,i}] = B_cell{:} if tim's comment is correct
Have a look at Loren Shure's article Deal or No Deal and also this answer for more.
The problem with your syntax, A{a,:,c} = B(:), is that the RHS (i.e. B(:)) is just one single matrix whereas the LHS is a comma-separated-list of b results. So you are basically requesting that 1 output be assigned to b variables and MATLAB doesn't like that, also hence the error message.
The problem with A(a,:,c) = B(:) is that indexing a cell array with () returns a cell array and you can't just assign a matrix (i.e. B(:)) to a cell array hence you second error.

Related

How do I split results into separate variables in matlab?

I'm pretty new to matlab, so I'm guessing there is some shortcut way to do this but I cant seem to find it
results = eqs\soltns;
A = results(1);
B = results(2);
C = results(3);
D = results(4);
E = results(5);
F = results(6);
soltns is a 6x1 vector and eqs is a 6x6 matrix, and I want the results of the operation in their own separate variables. It didn't let me save it like
[A, B, C, D, E, F] = eqs\soltns;
Which I feel like would make sense, but it doesn't work.
Up to now, I have never come across a MATLAB function doing this directly (but maybe I'm missing something?). So, my solution would be to write a function distribute on my own.
E.g. as follows:
result = [ 1 2 3 4 5 6 ];
[A,B,C,D,E,F] = distribute( result );
function varargout = distribute( vals )
assert( nargout <= numel( vals ), 'To many output arguments' )
varargout = arrayfun( #(X) {X}, vals(:) );
end
Explanation:
nargout is special variable in MATLAB function calls. Its value is equal to the number of output parameters that distribute is called with. So, the check nargout <= numel( vals ) evaluates if enough elements are given in vals to distribute them to the output variables and raises an assertion otherwise.
arrayfun( #(X) {X}, vals(:) ) converts vals to a cell array. The conversion is necessary as varargout is also a special variable in MATLAB's function calls, which must be a cell array.
The special thing about varargout is that MATLAB assigns the individual cells of varargout to the individual output parameters, i.e. in the above call to [A,B,C,D,E,F] as desired.
Note:
In general, I think such expanding of variables is seldom useful. MATLAB is optimized for processing of arrays, separating them to individual variables often only complicates things.
Note 2:
If result is a cell array, i.e. result = {1,2,3,4,5,6}, MATLAB actually allows to split its cells by [A,B,C,D,E,F] = result{:};
One way as long as you know the size of results in advance:
results = num2cell(eqs\soltns);
[A,B,C,D,E,F] = results{:};
This has to be done in two steps because MATLAB does not allow for indexing directly the results of a function call.
But note that this method is hard to generalize for arbitrary sizes. If the size of results is unknown in advance, it would probably be best to leave results as a vector in your downstream code.

Subscripted assignment dimension mismatch when combining corresponding array and cell array values in matlab

I have an array and a cell array with the same dimensions: A is a 1x2492 double array, and B is an 1x2492 cell cell array. I want to make a new cell array that assigns the values in A to the corresponding column values of B. Here was my code:
for n = 1:numel(B)
newArray(n) = [A(n),B{n}(2)];
newCellArray{n} = newArray;
end
When I ran the code, I got the error 'Subscripted assignment dimension mismatch.'
I think it's because some cells in B have multiple columns, and the code loop doesn't recognize that I want to assign the same value of A to all values in the cell.
For example, if cell 1 of B contains:
2 2355
23 1293
37 1222
I would like my code loop to assign the corresponding first value of A to 2355, 1293, and 1222. So basically, I'd like to have a new cell like this:
1 2355
1 1293
1 1222
I realize that this is a very confusing explanation, but I hope that it makes sense. Any and all help would be greatly appreciated - thank you very much!
I am not really sure what you are trying to do, but the code below will assign the value of A(ii) to every element in the first column of B{ii}. I am just saying this based on your example, but your explanation is really unclear..
C = B;
for ii=1:numel(C)
C{ii}(:,1)=A(ii);
end
And you are getting an error on newArray(n) = [A(n),B{n}(2)]; because you are trying to assign a vector to a singleton dimension. Try a(1) = [1 2] and you will still get an error, and this is independent on what your cell dimensions are, etc.. a(1,:)=[1 2], however, might work if the 2nd dimension of a is 2.
Try:
newCellArray = cell(numel(B),2);
for n = 1:numel(B)
lenB = length(B{n}(2));
newA = repmat(A(n),lenB);
newArray = [newA,B{n}(2)];
newCellArray = [newCellArray; newArray];
end

Set specific rows of matrices in cell array to zero without using a for-loop

I'd like to replace a specific number of elements of my cell to zero without using for. For example to replace elements of row 2 in example cell a below: How should I proceed possibly using cellfun?
a=cell(2,3);
cellfun(#(x)(zeros(a{x}(2,:))),a);
It gives the error "Bad cell reference operation".
what if I'd like to make row 2 empty again?
Thanks in advance for any help
The action you want to perform requires an assignment within a function. The only way to achieve this is using eval, which is considered bad practice.
A loop is therefore the best remaining option, if you want to keep everything in one script:
A = {randn(2,3),randn(2,3)};
for ii = 1:numel(A)
A{ii}(2,:) = 0;
end
If you don't bother using multiple files, you can put the assignment in a function:
function [ out ] = setZero( cellarray, rowidx )
out = cellarray;
out(rowidx,:) = 0;
end
and use it as follows:
A = cellfun(#(x) setZero(x,2),A ,'uni',0)
You need to find a transformation that turns a given matrix A to a matrix where the second row is all-zero. Here are three alternatives
A=cellfun(#(x) [x(1,:); zeros(size(x(2,:))); x(3:end,:)], A, 'uni', 0)
and
A=cellfun(#(x) diag(1:size(x,1)~=2)*x, A, 'uni', 0)
and
A=cellfun(#(x) bsxfun(#times, (1:size(x,1))' ~= 2, x), A, 'uni', 0)
The first one is the most robust one because it will handle the cases that your matrix has NaN elements. The second and third alternatives simply multiply the second row by zero. The second achieves this by multiplying it with a diagonal matrix where all diagonal elements are 1 except element (2,2) which is zero. The third alternative achieves this using bsxfun.
This is to demonstrate that you can achieve this without for loops however a simple for loop is much more readable.

Dynamic slicing of Matlab array

I have an n-dimensional array A and want to slice it dynamically, i.e., given a list of array dimensions, like [2 4], and a list of values, like [6 8], I want
B = A(:,6,:,8,:,:,:,:,...)
List lengths are unknown. Using eval would work but is not an option. This question is a generalization of a previous post to multiple indices and dimensions without a for-loop.
You can still use the previous post I linked to (which I originally flagged as a duplicate) to answer your question. This original post only slices in one dimension. I originally flagged it as a duplicate and closed it because all you need to do is replace one line of code in the original post's accepted answer to achieve what you want. However, because it isn't that obvious, I have decided to reopen the question and answer the question for you.
Referring to the previous post, this is what Andrew Janke (the person with the accepted answer on the linked post) did (very clever I might add):
function out = slice(A, ix, dim)
subses = repmat({':'}, [1 ndims(A)]);
subses{dim} = ix;
out = A(subses{:});
Given a matrix A, an index number ix and the dimension you want to access dim, the above function would equivalently perform:
out = A(:, :, ..., ix, :, :,...:);
^ ^ ^ ^
dimensions --> 1 2 dim dim+1
You would access your desired dimension in dim, and place what value you want to use to slice into that dimension. As such, you'd call it like this:
out = slice(A, ix, dim);
How the function works is that subses would generate a cell array of ':' strings (that will eventually be converted into ':' operators) that is as long as the total number of dimensions of A. Next, you would access the element at dim, which corresponds to the dimension you want and you would replace this with ix. You would then unroll this cell array so that we would access A in the manner that you see in the above equivalent statement.
Who would have thought that you can use strings to index into an array!?
Now, to generalize this, all you have to do is make one small but very crucial change. ix would now be a vector of indices, and dim would be a vector of dimensions you want to access. As such, it would look something like this:
function out = slice(A, ix, dim)
subses = repmat({':'}, [1 ndims(A)]);
subses(dim) = num2cell(ix);
out = A(subses{:});
The only difference we see here is the second line of the code. We have to use num2cell so that you can convert each element into a cell array, and we slice into this cell array to replace the : operators with your desired dimensions. Note that we are using () braces and not {} braces. () braces are used to slice through cell arrays while {} are used to access cell array contents. Because we are going to assign multiple cells to subses, () is needed. We then perform our slicing in A accordingly.
As such, given your problem and with the above modifications, you would do:
out = slice(A, [6 8], [2 4]);
Be advised that ix and dim must contain the same number of elements and they must be 1D. Also, ix and dim should be sensible inputs (i.e. not floating point and negative). I don't do this error checking because I'm assuming you know what you're doing and you're smart enough to know how to use this properly.
Good luck!

How to use cell arrays in Matlab?

I am a beginner at using Matlab and came across cell arrays but I am not sure how to use indexing for it.
I have created a cell array of 5 rows and 3 cols by doing the following:
A = cell(5,3);
Now is it possible to go through the cell array by row first and then col like how a nested for loop for a normal array?
for i=1:5
for j=1:3
A{i,j} = {"random"} //random numbers/ string etc
end
end
With cell arrays you have two methods of indexing namely parenthesis (i.e. (...)) and braces (i.e. {...}).
Lets create a cell array to use for examples:
A = {3, 9, 'a';
'B', [2,4], 0};
Indexing using paranthesis returns a portion of the cell array AS A CELL ARRAY. For example
A(:,3)
returns a 2-by-1 cell array
ans =
'a'
0
Indexing using braces return the CONTENTS of that cell, for example
A{1,3}
returns a single character
ans =
a
You can use parenthesis to return a single cell as well but it will still be a cell. You can also use braces to return multiple cells but these return as comma separated lists, which is a bit more advanced.
When assigning to a cell, very similar concepts apply. If you're assigning using parenthesis, then you must assign a cell matrix of the appropriate size:
A(:,1) = {1,1}
if you assign a single value using parenthesis, then you must put it in a cell (i.e. A(1) = 2 will give you an error, so you must do A(1) = {2}). So it's better to use braces as then you are directly affecting the contents of the cell. So it is correct to go
A{1} = 2
this is equivalent to A(1) = {2}. Note that A{1} = {2}, which is what you've done, will not give a error but what is does is nests a cell within your cell which is unlikely what you were after.
Lastly, if you have an matrix inside one of your cells, then Matlab allows you to index directly into that matrix like so:
A{2,2}(1)
ans =
3
for example:
for i=1:5
for j=1:3
A{i,j} = rand(3)
end
end
should work perfectly fine
just skip the { } on the right side of the =

Resources