Likeunknown
Likeunknown

Reputation: 217

Making a struct from a string containing matrices and its names

I actually have a text file containing uint8 data like this:

[0, 18, 121] bl
[0, 19, 12] gt
[24, 19, 22] apa
[0, 18, 1] bl
[24, 19, 22] apa bpa
[1, 2, 3] apa

What I ultimately want is a struct, say A, with fields containing this data as:

A.bl= [0, 18, 121;
       0, 18, 1  ];
A.gt = [0, 19, 12];
A.apa = [24,19,22];
A.apa_bpa = [24,19, 22]    or A.apabpa= [24, 19, 22]

So what the above example shows is to find the multiple instances of the matrix and stack them into one. If there is a space in the name, remove the space or replace that with underscore.

Until now, I have:

A = importdata('file.txt');

which creates a cell array (20,000 x 1 for my original data) containing the data. I know the names of the matrices already that the text file will have. So I tried to differentiate the matrices like the following which gives me the cell containing the matrices and its name:

A(~cellfun(@isempty, strfind(A,'bl')))

How do I proceed? Or What would be a simpler solution to this problem with more speed?

Upvotes: 2

Views: 75

Answers (3)

Wolfie
Wolfie

Reputation: 30047

You could use textscan as gnovice suggested to read the data

fid = fopen('file.txt');
data = textscan(fid, '[%d%d%d%s', 'Delimiter', ',]', 'CollectOutput', true);
fclose(fid);
values = data{1};
fields = data{2};

Then use a simple loop to create the struct, since you can access a field of a struct by a string using round brackets

% demo for accessing fields
myStruct.myField 
% is equivalent to
myStruct.('myField')

So:

% Replace spaces with underscores in 'fields'
fields = strrep(fields, ' ', '_');
% Initialise struct
A = struct;
% Loop over fields, assign values
for ii = 1:numel(fields)
    if isfield(A,fields{ii})
        % Append to array if field already exists
        A.(fields{ii}) = [A.(fields{ii}); values(ii,:)];
    else
        % Create field as array if doesn't yet exist
        A.(fields{ii}) = values(ii,:);
    end
end

Upvotes: 1

gnovice
gnovice

Reputation: 125854

I would use textscan instead of importdata in this case, since you're dealing with mixed data types:

fid = fopen('file.txt');
data = textscan(fid, '[%d%d%d%s', 'Delimiter', ',]', 'CollectOutput', true);
fclose(fid);
values = data{1};
fields = data{2};

Which gives you the following results in values and fields:

values =
  6×3 int32 matrix

     0    18   121
     0    19    12
    24    19    22
     0    18     1
    24    19    22
     1     2     3

fields =
  6×1 cell array

    'bl'
    'gt'
    'apa'
    'bl'
    'apa bpa'
    'apa'

Now you can replace spaces in fields with underscores using strrep, find the unique strings with unique, get the number of repeats of each string with accumarray, sort the rows of values to match the list of unique field names, and group rows of values using mat2cell:

[fields, ~, index] = unique(strrep(fields, ' ', '_'));
counts = accumarray(index, 1);
[~, sortIndex] = sort(index);
values = mat2cell(values(sortIndex, :), counts);

Now you can easily put it all together into a structure using cell2struct:

S = cell2struct(values, fields)

S = 
  struct with fields:

        apa: [2×3 int32]
    apa_bpa: [24 19 22]
         bl: [2×3 int32]
         gt: [0 19 12]

Upvotes: 4

Gryphon
Gryphon

Reputation: 549

If your file is rather long, well formatted and you don't want to use awful things with eval, I'd recommend using old style of setting structure field via setfield

% I just copied data from top of the post to the document
fid = fopen('testdoc.txt');
store_struct = struct();
while ~feof(fid)
    tmp_line = fgetl(fid);
    delim_idx = find(tmp_line == ']');
    tmp_mat=uint8(str2num(tmp_line(2:delim_idx-1)));
    tmp_field = deblank(tmp_line(delim_idx+2:length(tmp_line)));
    tmp_field(tmp_field == ' ') = '_';
    if isfield(store_struct,tmp_field)
        store_struct = setfield(store_struct,tmp_field,...
            [getfield(store_struct,tmp_field);tmp_mat]);
    else
        store_struct = setfield(store_struct,tmp_field,...
            tmp_mat);
    end
end
fclose(fid);

Upvotes: 1

Related Questions