Parse text file into structure - matlab

For some purposes, I wanna give an external text file as input of one of my MATLAB functions.
Generally this text file shows the following layout:
-----------------------------------------------------
HubHt = 90;
GridWidth = 220;
GridHeight = 220;
Ny = 35;
Nz = 37;
Nfft = 8192;
time = 620;
Uhub = 15;
Coherence = Bladed;
-----------------------------------------------------
To read it in, I'm currently calling this piece of code:
fid = fopen('test.inp','r+');
A = textscan(fid,'%s','Delimiter',';','commentStyle', '-','CollectOutput',1);
fclose(fid);
A = A{1};
inputs = regexp(A,' = ','split');
The last variable, inputs results in a <9x1> cell; each element will be a <1x2> cell.
The first element of the <1x2> cell is supposed to be the field of a overall INPUT structure, whereas the second element is the associated parameter.
At the moment, I'm using a quite static and awful way to achieve my goal:
inp = struct(char(inputs{1}(1)),str2double(inputs{1}(2)),char(inputs{2}(1)),str2double(inputs{2}(2)),char(inputs{3}(1)),str2double(inputs{3}(2)),char(inputs{4}(1)),str2double(inputs{4}(2)),char(inputs{5}(1)),str2double(inputs{5}(2)),char(inputs{6}(1)),str2double(inputs{6}(2)),char(inputs{7}(1)),str2double(inputs{7}(2)),char(inputs{8}(1)),str2double(inputs{8}(2)),char(inputs{9}(1)),char(inputs{9}(2)));
I believe that exist quite better ways to do the same: I'd like if you could share one with me.

You can use cell2struct:
% create cell vector where fieldnames and values alternate
tmp = [inputs{:}];
inp = cell2struct(tmp(2:2:end), tmp (1:2:end), 2);

Since what you have written is (nearly) valid Matlab source code why not give it the file extension .m and just run it ? Or call it from inside your function.
This is is an approach which we've used a lot; it's straightforward and simple. Obviously you have to make sure that it is (entirely) valid Matlab source but that's not difficult.

Related

How can I avoid this for-loop in spite of every element having to be checked individually?

Using Matlab R2019a, is there any way to avoid the for-loop in the following code in spite of the dimensions containing different element so that each element has to be checked? M is a vector with indices, and Inpts.payout is a 5D array with numerical data.
for m = 1:length(M)-1
for power = 1:noScenarios
for production = 1:noScenarios
for inflation = 1:noScenarios
for interest = 1:noScenarios
if Inpts.payout(M(m),power,production,inflation,interest)<0
Inpts.payout(M(m+1),power,production,inflation,interest)=...
Inpts.payout(M(m+1),power,production,inflation,interest)...
+Inpts.payout(M(m),power,production,inflation,interest);
Inpts.payout(M(m),power,production,inflation,interest)=0;
end
end
end
end
end
end
It is quite simple to remove the inner 4 loops. This will be more efficient unless you have a huge matrix Inpts.payout, as a new indexing matrix must be generated.
The following code extracts the two relevant 'planes' from the input data, does the logic on them, then writes them back:
for m = 1:length(M)-1
payout_m = Inpts.payout(M(m),:,:,:,:);
payout_m1 = Inpts.payout(M(m+1),:,:,:,:);
indx = payout_m < 0;
payout_m1(indx) = payout_m1(indx) + payout_m(indx);
payout_m(indx) = 0;
Inpts.payout(M(m),:,:,:,:) = payout_m;
Inpts.payout(M(m+1),:,:,:,:) = payout_m1;
end
It is possible to avoid extracting the 'planes' and writing them back by working directly with the input data matrix. However, this yields more complex code.
However, we can easily avoid some indexing operations this way:
payout_m = Inpts.payout(M(1),:,:,:,:);
for m = 1:length(M)-1
payout_m1 = Inpts.payout(M(m+1),:,:,:,:);
indx = payout_m < 0;
payout_m1(indx) = payout_m1(indx) + payout_m(indx);
payout_m(indx) = 0;
Inpts.payout(M(m),:,:,:,:) = payout_m;
payout_m = payout_m1;
end
Inpts.payout(M(m+1),:,:,:,:) = payout_m1;
It seems like there is not a way to avoid this. I am assuming that each for lop independently changes a variable parameter used in the main calculation. Thus, it is required to have this many for loops. My only suggestion is to turn your nested loops into a function if you're concerned about appearance. Not sure if this will help run-time.

Matlab Data Import from a text file

How do i import data of the following format.
1.0,X
.,-1.0
The size of the table may differ. As in the next file can also be a valid input file
.,.,.,1.0
.,X,.,-1.0
.,.,.,.
I have tried doing this using readtable to read the data and then use strcmp to assign the values to a different matrix but it doesnt work. What am i doing wrong.
Code:
foo = readtable(environment_file)
bar = zeros(size(foo,1),size(foo,2));
for i = 1:size(foo,1)
for j = 1:size(foo,2)
if strcmp(foo(i,j),'.')
bar(i,j) = 3;
elseif strcmp(foo(i,j),'X')
bar(i,j) = -10;
elseif strcmp(foo(i,j),'1.0')
bar(i,j) = 1;
elseif strcmp(foo(i,j),'-1.0')
bar(i,j) = -1;
end
end
end
bar
There are two problems in your code. One is that readtable reads the file as a table and takes the first row in the file as the names of the columns. So therefore, you should have the names at the top of the file.
Secondly, to access the contents of the table you need to use {} and not (). Also, there is no need to use for loops as you can use logical indexing to assign values to your matrix bar_b. Therefore, the changed code is as follows:
dataArray = readtable('environment_file.txt');
[n, m] = size(dataArray);
bar_b = zeros(n, m);
bar_b(strcmp(dataArray{:, :}, '.')) = 3;
bar_b(strcmp(dataArray{:, :}, 'X')) = -10;
bar_b(strcmp(dataArray{:, :}, '1.0')) = 1;
bar_b(strcmp(dataArray{:, :}, '-1.0')) = -1;

How to get out a value outside of objective function of multistart (MS) of Matlab

I have a multi-start fmincon code. One variable needs to be determined: u0. Inside ObjectiveFunc there is a variable, Parameter I need to output when running multi-start, so I am trying to output a parameter that changes inside an objective function. I wrote a simple example below.
How can I output the value of the Parameter inside Func(u0) below when running run(ms,Prob,big start)?
ObjectiveFunc = #(u0) Func(u0);
gs = GlobalSearch; ms = MultiStart(gs); opts = optimoptions(#fmincon);
Prob = createOptimProblem('fmincon','x0',1,'objective',ObjectiveFunc,'options',opts);
u0_ini_range = 0.1:1:20;
[u0_iniGrid] = ndgrid(u0_ini_range);
W = u0_iniGrid(:);
bigstart = CustomStartPointSet(W);
[u0_OptVal Delta_u0] = run(ms,Prob,bigstart);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Delta_u0 = Func(u0)
Parameter = randn(1);
Delta_u0 = u0+Parameter;
There are a variety of ways to solve this issue. None of them are pretty. The problem is confounded due to the use of MultiStart, which runs multiple instances simultaneously, only one of which returns the global optimum. This means that the last evaluation of your objective function (and it's call to randn) isn't necessarily the relevant one.
Here's one way to accomplish this with persistent variables. First, you objective function need to look something like this (here Delta is your Parameter):
function [z,Delta_u0_Opt]=HBM1_SlipSolution02hbm3Fcn(u0)
persistent Delta idx;
if isempty(idx)
idx = 0; % Initialize persistent index
end
if nargout > 1
% Output Delta history
Delta_u0_Opt = Delta;
z = (u0-Delta).^2;
else
% Normal operation
idx = idx+1;
Delta(idx) = randn; % Save Delta history
z = (u0-Delta(idx)).^2;
end
Then run your setup code as before:
ObjectiveFunc = #(u0)HBM1_SlipSolution02hbm3Fcn(u0);
u0_ini0 = 0;
gs = GlobalSearch;
ms = MultiStart(gs);
opts = optimoptions(#fmincon);
Lowbound = 0.1;
Xst_rig = 1;
Upbound = 20*Xst_rig;
Prob = createOptimProblem('fmincon','x0',u0_ini0,'objective',ObjectiveFunc,...
'lb',Lowbound,'options',opts);
u0_ini_range = 0.1:1:Upbound;
[u0_iniGrid] = ndgrid(u0_ini_range);
W = u0_iniGrid(:);
bigstart = CustomStartPointSet(W);
[u0_OptVal,fval1] = run(ms,Prob,big start);
Then extract your Delta value:
[fval2,Delta_u0_Opt] = HBM1_SlipSolution02hbm3Fcn(u0_OptVal)
Delta_u0_Opt = Delta_u0_Opt(fval1==fval2)
The main problem with this solution is that the entire history of Delta must be retained via constantly appending to a vector. And it requires that the function value of the solution, fval1, uniquely match only one of fval2. The solution could probably be optimized a bit and the last issue resolved by saving more state history and clever use of an output function. I have no idea how or if this would work if you decide to turn on UseParallel. As you can see, this optimization scheme is not at all designed for what you're trying to get it to do.
Finally, are you sure that it's a good idea to use random values in the way that you are for this kind of optimization scheme? At minimum, be sure to specify a seed so results can be replicated. You might consider creating your own global optimization method based on fmincon if you want something more straightforward and efficient.

Looping a process, outputting numerically labelled variables each time

I have about 50 different arrays and I want to perform the following operation on all of them:
data1(isnan(data1)) = 0;
coldata1 = nonzeros(data1);
avgdata1 = mean(coldata1);
and so on for data2, data3 etc... the goal being to turn data1 into a vector without NaNs and then take a mean, saving the vector and the mean into coldata1 and avgdata1.
I'm looking for a way to automate this for all 50, rather than copy it 50 times and change the numbers... any ideas? I've been playing with eval but no luck so far. Also tried:
for y = 1:50
data(y)(isnan(data(y))) = 0;
coldata(y) = nonzeros(data(y));
avgdata(y) = mean(coldata(y));
end
You can do it with eval but really should not. Rather use a cell array as suggested here: Create variables with names from strings
i.e.
for y = 1:50
data{y}(isnan(data{y})) = 0;
coldata{y} = nonzeros(data{y});
avgdata{y} = mean(coldata{y});
end
Also read How can I create variables A1, A2,...,A10 in a loop? for alternative options.

How can you split import text to different variables

I have text which is (could be 100K lines like this)
time,10 a b,20 c d
(time = HH:mm:ss.ffff with milliseconds)
I want to import it into 2 arrays
time,a,b
time,c,d
whats the shortest way? I need to store the script/code for future use too...
MATLAB has several text input options. While regexp-based options (like textscan) are often effective, it sounds like you have a fixed format that might be better handled by manually reading the lines sequentially. I've found that performance with this method is more consistent than with textscan or import. If a, b, c, d are not fixed width, you'll need to do something else. In that case, I'd just use the import wizard to set up the input, and then save the import code and modify as needed to automate it.
array1 = NaN(<numberoflines>,6);
array2 = NaN(<numberoflines>,6);
fname = 'path_to_some_file';
fid = fopen(fname);
stop = 0;
jj = 1;
while ~stop
cline = fgetl(fid);
if ischar(cline)
HH = str2double(cline(1:2));
MM = str2double(cline(...));
...
array1(jj,:) = [HH MM SS MS a b];
array2(jj,:) = ...;
else
disp('End of file')
stop = 1;
end
end
fclose(fid)
Try using regexp. A very powerful tool for parsing strings in matlab.