Creating an array with characters and incrementing numbers - arrays

Pretty simple problem, I want to create an array with char in a for loop.
code:
a = [1:5];
arr = [];
for i = 1:length(a)
arr(i) = ['f_',num2str(i)]
end
I am getting error:
In an assignment A(I) = B, the number of elements in B and I must be the same.
all i want is an array:
[f_1 f_2 f_3....]

This is because arr(i) is a single element, while ['f:', num2str(i)] contain three characters. Also, for i = 1:length(1) doesn't really make sense, since length(1) is guaranteed to be 1. I guess you wanted for i = 1:length(a). If that's the case I suggest you substitute length with numel and i with ii.
The better way to create the array you want is using sprintf like this:
sprintf('f_%i\n',1:5)
ans =
f_1
f_2
f_3
f_4
f_5
Or possiblby:
sprintf('f_%i ',1:5)
ans =
f_1 f_2 f_3 f_4 f_5
I guess this is what you really wanted:
for ii = 1:5
arr{ii} = ['f_', num2str(ii)];
end
arr =
'f_1' 'f_2' 'f_3' 'f_4' 'f_5'
Or simpler:
arr = arrayfun(#(n) sprintf('f_%i', n), 1:5, 'UniformOutput', false)
The last two can be indexed as follows:
arr{1}
ans =
f_1
You can also do (same result):
str = sprintf('f_%i\n', 1:5);
arr = strsplit(str(1:end-1), '\n')
If you're doing this to create variable names, then please don't. Use cells or structs instead.

Related

How do I delete all-zero pages from a 3D matrix in a loop?

How can I delete all-zero pages from a 3D matrix in a loop?
I have come up with the following code, though it is not 'entirely' correct, if at all. I am using MATLAB 2019b.
%pseudo data
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;
xy = cat(3,x,y); %this is a 3x2x4 array; (:,:,1) and (:,:,2) are all zeros,
% (:,:,3) is ones and zeros, and (:,:,4) is all ones
%my aim is to delete the arrays that are entirely zeros i.e. xy(:,:,1) and xy(:,:,2),
%and this is what I have come up with; it doesn't delete the arrays but instead,
%all the ones.
for ii = 1:size(xy,3)
for idx = find(xy(:,:,ii) == 0)
xy(:,:,ii) = strcmp(xy, []);
end
end
Use any to find indices of the slices with at least one non-zero value. Use these indices to extract the required result.
idx = any(any(xy)); % idx = any(xy,[1 2]); for >=R2018b
xy = xy(:,:,idx);
I am unsure what you'd expect your code to do, especially given you're comparing strings in all-numerical arrays. Here's a piece of code which does what you desire:
x = zeros(3,2,2);
y = ones(3,2,2);
positions = 2:4;
y(positions) = 0;
xy = cat(3,x,y);
idx = ones(size(xy,3),1,'logical'); % initialise catching array
for ii = 1:size(xy,3)
if sum(nnz(xy(:,:,ii)),'all')==0 % If the third dimension is all zeros
idx(ii)= false; % exclude it
end
end
xy = xy(:,:,idx); % reindex to get rid of all-zero pages
The trick here is that sum(xy(:,:,ii),'all')==0 is zero iff all elements on the given page (third dimension) are zero. In that case, exclude it from idx. Then, in the last row, simply re-index using logical indexing to retain only pages whit at least one non-zero element.
You can do it even faster, without a loop, using sum(a,[1 2]), i.e. the vectorial-dimension sum:
idx = sum(nnz(xy),[1 2])~=0;
xy = xy(:,:,idx);

Matlab: initialize empty array of matrices

I need to create an empty array of matrices, and after fill it with matrices of the same size.
I have made a little script to explain:
result = [];
for i = 0: 4;
M = i * ones(5,5); % create matrice
result = [result,M]; % this would have to append M to results
end
Here result is a matrix of size 5*25 and I need an array of matrices 5*5*4.
I have been researched but I only found this line: result = [result(1),M];
The issue is that [] implicitly concatenates values horizontally (the second dimension). In your case, you want to concatenate them along the third dimension so you could use cat.
result = cat(3, result, M);
But a better way to do it would be to actually pre-allocate your result array using zeros
result = zeros(5, 5, 4);
And then within your loop fill each "slice" of the 3D array with the values.
for k = 0:4
M = k * ones(5,5);
result(:,:,k+1) = M;
end

Grow 3D array in Matlab

Is there a way to grow a 3D array in the third dimension using the end index in a loop in Matlab?
In 2D it can be done like
a = [];
for x = y
a(end + 1, :) = f(x);
end
But in 3D the same thing will not work as a(1,1,end) will try to index a(1,1,1) the first iteration (not a(1,1,0) as one might expect). So I can't do
im = [];
for x = y
im(:, :, end + 1) = g(x);
end
It seems the end of a in third dimension is handled a bit differently than in the first two:
>> a = [];
>> a(end,end,end) = 1
Attempted to access a(0,0,1); index must be a positive integer or logical.
Am I missing something about how end indexing works here?
What you're asking...
If you know the size of g(x), initialize im to an empty 3d-array:
im = zeros(n, m, 0); %instead of im = [];
I think your code should work now.
A better way...
Another note, resizing arrays each iteration is expensive! This doesn't really matter if the array is small, but for huge matrices, there can be a big performance hit.
I'd initialize to:
im = zeros(n, m, length(y));
And then index appropriately. For example:
i = 1;
for x = y
im(:, :, i) = g(x);
i = i + 1;
end
This way you're not assigning new memory and copying over the whole matrix im each time it gets resized!

Access structure array whose index is stored in a string

I want to get a value from a structure array by code, and I'll have the index stored in a string.
I've tried to run this code:
function M = getdata(matrix,field,varargin)
exp = [];
for i = 1:nargin-3
exp = [exp num2str(varargin{i}) ','];
end
exp = [exp num2str(varargin{nargin-2})];
M = eval('matrix(exp).(Field)');
end
However, it fails.
For example, suppose I have a structure array with 2 fields, A and B. So, I could write
MyStruct(1,1).A
A possible use would be:
M = getdata(MyStruct,A,1,1)
and I want the program to do:
M = MyStruct(1,1).A
How could I do that?
Thanks!
You can use the getfield function:
M = getfield(MyStruct, {1,1} ,'A');
Or if you wanted, say, MyStruct(1,1).A(3).B:
M = getfield(MyStruct, {1,1}, 'A', {3},'B');
For the example you give, this will suffice:
function M = getdata(matrix,field,varargin)
M = matrix(varargin{:}).(field);
which you call like
getdata(myStruct, 'A', 1,1)
which makes that function pretty useless.
But, in general, when you have indices given as strings, you can follow roughly the same approach:
%// Your indices
str = {'1', '2'};
%// convert to numbers
str = cellfun(#str2double, str, 'UniformOutput', false);
%// use them as indices into structure
M = myStruct(str{:}).(field)
And if you really insist, your call to eval is just wrong:
M = eval(['matrix(' exp ').(' field ')']);
And, as a general remark, please refrain from using exp as the name of a variable; it is also the name of a built-in function (the natural exponential function).

Variable dimensions in file don't match with dimension of indexing subscript if one dimension is singleton

I want to test a function func(par1,par2,par3) with all combinations of the parameters par1, par2 and par3 and store the output in a .mat file. My code looks like this at the moment:
n1 = 3;
n2 = 1;
n3 = 2;
parList1 = rand(1,n1); % n1,n2,n3 is just some integer
parList2 = rand(1,n2); % the lists are edited by hand in the actual script
parList3 = rand(1,n3);
saveFile = matfile('file.mat','Writable',true);
% allocate memory
saveFile.output = NaN(numel(parList1),numel(parList2),numel(parList3));
counter1 = 0;
for par1 = parList1
counter1 = counter1 + 1;
counter2 = 0; % reset inner counter
for par2 = parList2
counter2 = counter2 + 1;
counter3 = 0; % reset inner counter
for par3 = parList3
counter3 = counter3 + 1;
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
end
end
This works except if parList3 has only one item, i.e. if n3 = 1. Then the saveFile.output has singleton dimensions and I get the error
Variable 'output' has 2 dimensions in the file, this does not match the 3 dimensions in the indexing subscripts.
Is there a elegant way to fix this?
The expression in the for statement needs to be a row array, not a column array as in your example. The loops will exit after the first value with your code. Set a breakpoint on the saveFile.output command to see what I mean. With a column array, par1 will not be a scalar as desired, but the whole parList1 column. With a row array, par1 will iterate through each value of parList1 as intended
Another thing is that you need to reset your inner counters (counter2 and counter2) or your second and third dimensions will blow up larger than you expected.
The n3=1 problem is expected behavior because matfile defines the variables with fixed number of dimensions and it will treat saveFile.output as 2D. Once you have fixed those issues, you can solve the n3=1 problem by changing the line,
saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
to
if n3==1, saveFile.output(counter1,counter2) = sum([par1,par2,par3]);
else saveFile.output(counter1,counter2,counter3) = sum([par1,par2,par3]);
end
By now I realized that actually in matfiles all the singleton dimensions, except for the first two are removed.
In my actual programm I decided to save the data in the file linearly and circumvent matfile's lack of linear indexing capability by using the functions sub2ind and ind2sub.

Resources