Save fields of branched structure with dynamic names in matlab - matlab

I need to know how to save just one branch of a structure in MATLAB.
The structure contains more levels with more fields per level. For example:
data.level1.level21
data.level1.level22
I want now to save the branches data.level1.level21 and data.level1.level21 individually. I have tried the following but it doesn't work:
firstLevelName = fieldnames(data);
secondLevelNames = fieldnames(data.(firstLevelName{1}));
for pL = 1:length(secondLevelNames)
save([filename '.mat'], '-struct', 'data', firstLevelName{1}, secondLevelNames{pL});
end

The structure-saving method that you're trying to use doesn't work quite the way you are expecting. All the arguments after your struct variable name are fields of that struct to save.
The way MATLAB is interpreting your code is that you're trying to save the level1, and level21 fields of data which obviously doesn't work since level21 is a subfield of level1 not data.
To save the nested fields, the easiest thing is probably to create a new variable pointing to the structure data.level and then call save on that and specify the specific fields to save.
level1 = data.level1;
for pL = 1:numel(secondLevelNames)
save(filename, '-struct', 'level1', secondLevelNames{pL});
end
If you actually want the double nesting in the saved data, you would need to create a new structure containing only the data that you wanted and then save that.
for pL = 1:numel(secondLevelNames)
newstruct = struct(firstLevelName{1}, struct());
newstruct.(secondLevelNames{pL}) = data.(firstLevelName{1}).(secondLevelNames{pL});
save(filename, '-struct', 'newstruct')
end

Related

Synchronize timetables stored in a structure

I am dynamically storing data from different data recorders in timetables, nested in a structure DATA, such as DATA.Motor (timetable with motor data), DATA.Actuators (timetable with actuators data) and so on.
My objective is to have a function that synchronizes and merges these timetables so I can work with one big timetable.
I am trying to use synchronize to merge and synchronize those timetables:
fields = fieldnames(DATA);
TT = synchronize(DATA.(fields{1:end}));
but get the following error:
Expected one output from a curly brace or dot indexing expression, but there were 3 results.
This confuses me because DATA.(fields{1}) return the timetable of the first field name of the DATA structure.
Any thought on how I can solve this is greatly appreciated.
The problem here is that fields{1:end} is returning a "comma-separated list", and you're not allowed to use one of those as a struct dot-index expression. I.e. it's as if you tried the following, which is not legal:
DATA.('Motor','Actuators')
One way to fix this is to pull out the values from DATA into a cell array, and then you can use {:} indexing to generate the comma-separated list as input to synchronize, like this:
DATA = struct('Motor', timetable(datetime, rand), ...
'Actuators', timetable(datetime, rand));
DATA_c = struct2cell(DATA);
TT = synchronize(DATA_c{:});

Sequential import of datafiles according to rule in Matlab

I have a list of .txt datafiles to import. Suppose they are called like that
file100data.txt file101data.txt ... file109data.txt I want to import them all using readtable.
I tried using the for to specify a vector a = [0:9] through which matlab could loop the readtable command but I cannot make it work.
for a = [0:9]
T_a_ = readtable('file10_a_data.txt')
end
I know I cannot just put _a_ where I want the vector to loop through, so my question is how can I actually do it?
Thank you in advance!
Here is a solution that should work even if you have missing files in your folder (e.g. you have file100data.txt to file107data.txt, but you are missing file file108data.txt and file109data.txt):
files=dir('file10*data.txt'); %list all data files in your folder
nof=size(files,1); %number of files
for i=1:nof %loop over the number of files
table_index=files(i).name(7) %recover table index from data filename
eval(sprintf('T%s = readtable(files(i).name)', table_index)); %read table
end
Now, please note that is it generally regarded as poor practice to dynamically name variables in Matlab (see this post for example). You may want to resort to structures or cells to store your data.
You need to convert the value of a into a string and combine strings together, like this:
Tables = struct();
for a = 0:9
% note: using dynamic structure field names to store the imported tables
fname = ['file10_' num2str(a) '_data'];
Tables.(fname) = readtable([fname '.txt']);
end

How to import many csv files to tables in MATLAB with names that correspond to their filenames [duplicate]

This question already has answers here:
Dynamically name a struct variable in MATLAB
(2 answers)
Closed 6 years ago.
I have a folder full of csv data files that I want to import to MATLAB. The import function will be the same for each of them. Their names follow a pattern, e.g. 'a0-b0.csv', 'a0-b5.csv', 'a0-b10.csv', 'a0-b15.csv' ... 'a2-b0' etc.
I want to import all these files at once. I want to import them as tables with sensible names that correspond to the data they contain.
Doing something like
filepath = 'file/path/'
for (a = [0, 2])
for (b = [0:5:50])
filename = strcat(filepath, 'a', num2str(a), '-b', num2str(b), '.csv')
varname = strcat('a', num2str(a), '_b', num2str(b))
varname = importfile(filename, startrow, endrow)
end
end
made sense in concept to me.
As 'varname' itself is the variable, not the string it contains, this does not do what I want. I've seen a lot of answers to similar situations suggesting eval(), while simultaneously vehemently advocating against it.
eval() has side effects that make it annoying to implement. Everyone's intense aversion to it make me wonder if it's worth putting in the effort to try to implement it.
I can think of no other way to do this, however. I don't see how using arrays (the recommended alternative) would result in appropriate/relevant names.
Does anyone have a suggestion as to how to automatically import many csv files to tables in MATLAB with names that correspond to their filenames?
(Regarding duplicates: I haven't yet found a question that addresses the automatic creation of corresponding variable names, which is the main issue here)
As #transversality condition wrote in their comment, you should rethink you approach.
I'd suggest two steps:
If you are about to change files in and import htem again, create .mat file with already imported data and check hwther to rescan the folder or load the .mat file.
Decide whether you want to use struct class or cell class to contain your data.
The code for struct can be:
filepath = 'file/path/'; % define the data folder
FileNames=dir('filepath\*.csv'); % list all csv. files within filepath
N=numel(FileNames); % count the csv files
DATA(N)=struct('name','','data',[]); % preallocate DATA
for (ii=1:N)
Temp=regexp(FileNames(ii).name,'\.','split'); % separate file name and abbreviation
DATA(ii).name = Temp{1}; % save the file name
DATA(ii).data = importfile([filepath,'\',FileNames(ii).name], startrow, endrow); % save the data
end
The code for cell can be:
filepath = 'file/path/'; % define the data folder
FileNames=dir('filepath\*.csv'); % list all csv. files within filepath
N=numel(FileNames); % count the csv files
DATA=cell(2,N); % preallocate DATA
for (ii=1:N)
Temp=regexp(FileNames(ii).name,'\.','split'); % separate file name and abbreviation
DATA{1,ii} = Temp{1}; % save the file name
DATA{2,ii} = importfile([filepath,'\',FileNames(ii).name], startrow, endrow); % save the data
end
In both cases you have to find the appropriate line by, for example, DATA(find(strcmpi(DATA.name,'<name>))).data.
You can also use the cell approach to create struct. Suppose we've run the code for cell.
Then command
DataStruct=struct(DATA{:});
will allow you to access your data via Data.Struct.<filename> command directly.

Matlab: how to rename a sub-field

Let me describe the question in this way. I have a .mat file, and if I open it, it contains a 1x10 struct data. In each data, it has a 1x5 struct (or field) called res. In res, it has a 1x1 struct (or field) called, let's say, foo. Thus, I have ixj copies of data(i).res(j).foo .
Is there anyway I can change the name of this foo? say I want all data(i).res(j).foo to become data(i).res(j).bar
I did search on the internet, and tried a few ways (add field and delete, create a temp field, use cell2field or fieldtofile, etc.) and all of them didn't work. The most frequent returned error is "Subscripted Assignment between dissimilar structures."
Please help, thanks in advance!
The safest way is probably by looping over data twice, the first pass creating a new field bar for each subfield like data(i).res(j).bar=data(i).res(j).foo, then the second pass deletes the old fields like data(i).res(j) = rmfield(data(i).res(j),'foo').
Thanks caoy and NotLikeThat. I finally came to an conclusion.
data2 = data
for i=1:10
for j = 1:5
data(i).res(j).bar = data2(i).res(j).foo;
end
data(i).res = rmfield(data(i).res, 'foo');
end
I probably need to removed i, j, and data2 after running this script.

Matlab: dynamic name for structure

I want to create a structure with a variable name in a matlab script. The idea is to extract a part of an input string filled by the user and to create a structure with this name. For example:
CompleteCaseName = input('s');
USER WRITES '2013-06-12_test001_blabla';
CompleteCaseName = '2013-06-12_test001_blabla'
casename(12:18) = struct('x','y','z');
In this example, casename(12:18) gives me the result test001.
I would like to do this to allow me to compare easily two cases by importing the results of each case successively. So I could write, for instance :
plot(test001.x,test001.y,test002.x,test002.y);
The problem is that the line casename(12:18) = struct('x','y','z'); is invalid for Matlab because it makes me change a string to a struct. All the examples I find with struct are based on a definition like
S = struct('x','y','z');
And I can't find a way to make a dynamical name for S based on a string.
I hope someone understood what I write :) I checked on the FAQ and with Google but I wasn't able to find the same problem.
Use a structure with a dynamic field name.
For example,
mydata.(casename(12:18)) = struct;
will give you a struct mydata with a field test001.
You can then later add your x, y, z fields to this.
You can use the fields later either by mydata.test001.x, or by mydata.(casename(12:18)).x.
If at all possible, try to stay away from using eval, as another answer suggests. It makes things very difficult to debug, and the example given there, which directly evals user input:
eval('%s = struct(''x'',''y'',''z'');',casename(12:18));
is even a security risk - what happens if the user types in a string where the selected characters are system(''rm -r /''); a? Something bad, that's what.
As I already commented, the best case scenario is when all your x and y vectors have same length. In this case you can store all data from the different files into 2 matrices and call plot(x,y) to plot each column as a series.
Alternatively, you can use a cell array such that:
c = cell(2,nufiles);
for ii = 1:numfiles
c{1,ii} = import x data from file ii
c{2,ii} = import y data from file ii
end
plot(c{:})
A structure, on the other hand
s.('test001').x = ...
s.('test001').y = ...
Use eval:
eval(sprintf('%s = struct(''x'',''y'',''z'');',casename(12:18)));
Edit: apologies, forgot the sprintf.