Write field name and value form structures to a file - matlab

I am wondering how can I write a full structure in an output file *.txt with all fieldnames and the respectives values. For example
allvariables=
names='raw.txt'
date= = '**/**/2013'
User = Mr/Mrs *
timeProcessing = 20
numberIterations = 1000;
Should be written in output.txt with all the information displayed before.
This is just an example but my structure has a length of 50 fieldnames, so I would appreciate any suggestion!

Here's something you can use:
%// Extract field data
fields = fieldnames(allvariables);
values = struct2cell(allvariables);
%// Optional: add enclosing apostrophes around string values
idx = cellfun(#ischar, values);
values(idx) = cellfun(#(x){['''', x, '''']}, values(idx));
%// Convert numerical values to strings
idx = cellfun(#isnumeric, values);
values(idx) = cellfun(#num2str, values(idx), 'UniformOutput', false);
%// Convert cell arrays of strings to comma-delimited strings
idx = cellfun(#iscellstr, values);
stringify_cellstr = #(x){['{' sprintf('''%s'', ', x{1:end - 1}) ...
sprintf('''%s''', x{end}) '}']};
values(idx) = cellfun(stringify_cellstr, values(idx));
%// Convert cell array of numbers to strings
idx = cellfun(#iscell, values);
isnumber = #(x)isnumeric(x) && isscalar(x);
idx_num = cellfun(#(x)all(arrayfun(#(k)isnumber(x{k}),1:numel(x))), values(idx));
idx(idx) = idx_num;
stringify_cellnum = #(x){['{' sprintf('%d, ' x{1:end - 1}) num2str(x{end}) '}']};
values(idx) = cellfun(stringify_cellnum, values(idx));
%// Combine field names and values in the same array
C = {fields{:}; values{:}};
%// Write fields to text file
fid = fopen('output.txt', 'wt');
fprintf(fid, repmat('%s = %s\n', 1, size(C, 2)), C{:});
fclose(fid);
This solution is essentially a variant of this one. Note that it assumes that each field contains a scalar value or a string, but it can be extended to handle other types, of course.
EDIT: added handling for fields storing cell arrays of strings and cell arrays of numbers.

Related

Matlab script can't count categorical variable correctly based on value from another column

I have this script to count the number of true segments (column3 "Segment Good") for each category ("go", "nogo" under column 1), but it never returns the actual number :(
I would appreciate to have another pairs of eyes to look at it! Thank you!
(This .txt file is converted from .log file from EGI netstation if it matters)
sample .txt file:
category Segment number Segment Good Eye movements
go 1 true true
go 2 false false
go 3 true true
go 4 false false
nogo 1 true true
nogo 2 false false
Files2 = dir(strcat('/Users/EGI/GoNogo2/log22/','*.txt'));
lengthFiles2 = length(Files2);
for ff = 1:lengthFiles2
ff
try
filename = Files2(ff).name;
idx=strfind(filename,'_');
name{ff} = filename(1:idx(1)-1);
path = strcat('/Users/EGI/GoNogo2/log22/',filename);
data = readtable(path);
category = data(:,1);
segmentgood = data(:,3);
record = zeros(size(category,1),2);
for i=1:size(category,1)
tt = category(i,1).Category{1};
if strcmp(tt,'go')
record(i,1)=1;
elseif strcmp(tt,'nogo')
record(i,2)=1;
end
end
segment = zeros(size(segmentgood,1),2);
for i=1:size(category,1)
tt = segmentgood(i,1).SegmentGood{1};
if strcmp(tt,'true')
segment(i,1)=1;
elseif strcmp(tt,'false')
segment(i,2)=1;
end
end
goTrue = 0;
nogoTrue=0;
for i=1:size(category,1)
if record(i,1)==1&segment(i,1)==1
goTrue=goTrue+1;
else record(i,2)==1&segment(i,1)==1
nogoTrue=nogoTrue+1;
end
end
result(ff,1:2) = [goTrue nogoTrue];
result2(ff,1:2) = [sum(record)];
end
end
results = {};
results2={};
for i=1:lengthFiles2
results {i,1} = name{i};
results {i,2} = result(i,1);
results {i,3} = result(i,2);
%results {i,4} = result(i,3);
results2 {i,1} = name{i};
results2 {i,2} = result2(i,1);
results2 {i,3} = result2(i,2);
% results2 {i,4} = result2(i,3);
end
xlswrite('/Users/EGI/Desktop/log2/.xls',results,'results');
Brief Explanation and Preface:
Not too exactly sure of all the implementation requirements of this task but here is a method of reading .txt/.log files. It uses the function textscan() to scan the file into MATLAB as a cell array with each data entry formatted as %s %d %s %s (string, integer, string, string).
Category: string → %s
Segment Number: integer → %d
Segment Good: string → %s
Eye Movements: string → %s
After reading this data as a cell array denoted as Data in the script below we can split this array into columns. Now we can check which indices/rows have the "Category" go and nogo by using the contains() function. The contains function will take in two arguments. The first argument is the string/array of strings that are being searched and the second argument is the string to search for. The contains() function will return true "1" for all the indices where it can find the string to search for:
Example:
Result = contains(["Apple", "Pear", "Grape", "Apple"],"Apple");
will return
Result = [1 0 1 0];
After evaluating the indices corresponding to go and nogo we can matrix index the third column using these values. Matrix indexing allows us to grab all the indices corresponding to a condition/using a logical array. In this case, our logical array/condition was the indices pertaining to nogo and go. Applying contains(,"true") to these new subsets will allow us to find where true occurs given each category go and nogo. Finally using the function nnz() (number of non-zeroes) will allow you to find how many ones/times contains() returned true.
Script:
clear;
clc;
%Reading in the data as a cell array%
fileID = fopen('sample.log', 'r');
Header = string(fgetl(fileID));
Data = textscan(fileID,'%s %d %s %s');
fclose(fileID);
%Splitting the data into specific columns%
Category = Data(:,1);
Category = string(Category{1,1});
Segment_Number = Data(:,2);
Segment_Number = string(Segment_Number{1,1});
Segment_Good = Data(:,3);
Segment_Good = string(Segment_Good{1,1});
Eye_Movements = Data(:,4);
Eye_Movements = string(Eye_Movements{1,1});
%Finding the indices corresponding to "go" and "nogo"%
No_Go_Indices = contains(Category,"nogo");
Go_Indices = ~No_Go_Indices;
%Finding how many true cases in column 3 (Segment_Good) corresponding to "go" and "nogo"
Go_True_Cases = nnz(contains(Segment_Good(Go_Indices),"true"));
No_Go_True_Cases = nnz(contains(Segment_Good(No_Go_Indices),"true"));
%Counting the number of times true occurs in each column%
Column_3_True_Count = nnz(contains(Segment_Good,"true"));
Column_4_True_Count = nnz(contains(Eye_Movements,"true"));
%Printing the results to the command window%
fprintf("Category: go -> %d true\n",Go_True_Cases);
fprintf("Category: nogo -> %d true\n\n",No_Go_True_Cases);
fprintf("Total true cases: %d\n", Column_3_True_Count);
Extension: Looping Through Files in a Directory
clear;
clc;
%Full or relative directory path%
%Adding directory with the files to the be accessible%
Directory_Path = "Files";
addpath(Directory_Path);
%Reading the filenames within the directory and the number of files%
Text_Files = dir(Directory_Path+'/*.txt');
Number_Of_Files = length(Text_Files);
%Creating arrays that will hold the results%
All_No_Go_True_Cases = zeros(Number_Of_Files,1);
All_Go_True_Cases = zeros(Number_Of_Files,1);
%Looping through the files and running the function that will grab the
%results%
for File_Index = 1: Number_Of_Files
File_Name = string(Text_Files(File_Index).name);
[No_Go_True_Cases,Go_True_Cases] = Get_Data(File_Name);
All_No_Go_True_Cases(File_Index) = No_Go_True_Cases;
All_Go_True_Cases(File_Index) = Go_True_Cases;
end
All_No_Go_True_Cases
All_Go_True_Cases
%Local function definition%
function [No_Go_True_Cases,Go_True_Cases] = Get_Data(File_Name)
%Reading in the data as a cell array%
fileID = fopen(File_Name, 'r');
Header = string(fgetl(fileID));
Data = textscan(fileID,'%s %d %s %s');
fclose(fileID);
%Splitting the data into specific columns%
Category = Data(:,1);
Category = string(Category{1,1});
Segment_Number = Data(:,2);
Segment_Number = string(Segment_Number{1,1});
Segment_Good = Data(:,3);
Segment_Good = string(Segment_Good{1,1});
Eye_Movements = Data(:,4);
Eye_Movements = string(Eye_Movements{1,1});
%Finding the indices corresponding to "go" and "nogo"%
No_Go_Indices = contains(Category,"nogo");
Go_Indices = ~No_Go_Indices;
%Finding how many true cases in column 3 (Segment_Good) corresponding to "go" and "nogo"
Go_True_Cases = nnz(contains(Segment_Good(Go_Indices),"true"));
No_Go_True_Cases = nnz(contains(Segment_Good(No_Go_Indices),"true"));
%Counting the number of times true occurs in each column%
Column_3_True_Count = nnz(contains(Segment_Good,"true"));
Column_4_True_Count = nnz(contains(Eye_Movements,"true"));
%Printing the results to the command window%
fprintf("Category: go -> %d true\n",Go_True_Cases);
fprintf("Category: nogo -> %d true\n",No_Go_True_Cases);
fprintf("Total true cases: %d\n\n", Column_3_True_Count);
end
Ran using MATLAB R2019b

cell string array manipulation

I have a cell with strings in the following format:
data = {'para1_left = numeric value';'para1_right = numeric value';
'para2_left = numeric value';'para2_right = numeric value';
........
'para100_up = numeric value';'para100_down = numeric value';
and so on...I have a few hundreds of these};
I want two cells out of this cell: one with just the parameter names, p_name, and another with just the values, p_val.
Once I have the two cells, I want to compare the p_name cell with another cell of shorter length. This new cell will have strings in the following format:
new_cell = {'para1';'para5';'para10';...'para25'};
Basically these strings miss the trailing parts: _left, _right, etc.
Then, I want to have a list of indices of p_name that contain any of the strings in new_cell, indx_match = [1;2;10;20....and so on] so that I can get the values of the matching parameter names by doing p_val{indx_match}.
I want to do the above with the minimum number of lines, probably using cellfun. I figured out how to find the indices by using strfind command, but then it creates a cell array and p_val{indx_match} doesn't work (I tried various ways using cellfun, but no success yet).
I'm not sure exactly what you want, but this should get you on the right track.
Org = {'a_l = 5'; 'a_r = 7'; 'b_l = 6'; 'b_r = 7'};
Shr = {'a'};
splt = cellfun(#(s) strsplit(s, {'=', ' '}), Org, 'uni', 0);
p_name = cellfun(#(c) c{1}, splt, 'uni', 0);
p_val = cellfun(#(c) str2num(c{2}), splt);
param = cellfun(#(c) strsplit(c, '_'), p_name, 'uni', 0);
param = cellfun(#(c) c{1}, param, 'uni', 0);
index = cellfun(#(s) strfind(param, s), Shr, 'uni', 0);

Save a sparse array in csv

I have a huge sparse matrix a and I want to save it in a .csv. I can not call full(a) because I do not have enough ram memory. So, calling dlmwrite with full(a) argument is not possible. We must note that dlmwrite is not working with sparse formatted matrices.
The .csv format is depicted below. Note that the first row and column with the characters should be included in the .csv file. The semicolon in the (0,0) position of the .csv file is necessary too.
;A;B;C;D;E
A;0;1.5;0;1;0
B;2;0;0;0;0
C;0;0;1;0;0
D;0;2.1;0;1;0
E;0;0;0;0;0
Could you please help me to tackle this problem and finally save the sparse matrix in the desired form?
You can use csvwrite function:
csvwrite('matrix.csv',a)
You could do this iteratively, as follows:
A = sprand(20,30000,.1);
delimiter = ';';
filename = 'filecontaininghugematrix.csv';
dims = size(A);
N = max(dims);
% create names first
idx = 1:26;
alphabet = dec2base(9+idx,36);
n = ceil(log(N)/log(26));
q = 26.^(1:n);
names = cell(sum(q),1);
p = 0;
for ii = 1:n
temp = repmat({idx},ii,1);
names(p+(1:q(ii))) = num2cell(alphabet(fliplr(combvec(temp{:})')),2);
p = p + q(ii);
end
names(N+1:end) = [];
% formats for writing
headStr = repmat(['%s' delimiter],1,dims(2));
headStr = [delimiter headStr(1:end-1) '\n'];
lineStr = repmat(['%f' delimiter],1,dims(2));
lineStr = ['%s' delimiter lineStr(1:end-1) '\n'];
fid = fopen(filename,'w');
% write header
header = names(1:dims(2));
fprintf(fid,headStr,header{:});
% write matrix rows
for ii = 1:dims(1)
row = full(A(ii,:));
fprintf(fid, lineStr, names{ii}, row);
end
fclose(fid);
The names cell array is quite memory demanding for this example. I have no time to fix that now, so think about this part yourself if it is really a problem ;) Hint: just write the header element wise, first A;, then B; and so on. For the rows, you can create a function that maps the index ii to the desired character, in which case the complete first part is not necessary.

MATLAB convert from struct to table and ouput as csv

As part of an image processing pipeline using 'regionprops' in Matlab I generate the struct:
vWFfeatures =
1631x1 struct array with fields:
Area
Centroid
MajorAxisLength
MinorAxisLength
Eccentricity
EquivDiameter
Where 'Centroid' is a Vector containing [x, y] for example [12.4, 26.2]. I would like to convert this struct to a table and save as a CSV file. The objective is to separate the 'Centroid' vector into two columns in the table labelled Centroid_X and Centroid_Y for example. I am not sure how to achieve this.
So far I have investigated using the 'struct2table' function. This ouputs the 'Centroid' as one column. In addition when I try to assign the output to a variable I get an error:
table = struct2table(vWFfeatures)
Error using struct2table
Too many output arguments.
I cannot understand this, any help please?
Since the original struct2table isn't available to you, you might want to implement specifically the behavior you're trying to achieve yourself.
In this case, this means extracting the values you want to save, (split the array,) then save the data:
data_Centroid = vertcat(vWFfeatures.Centroid); %// contains the centroid data
Centroid_X = data_Centroid(:,1); %// The first column is X
Centroid_Y = data_Centroid(:,2); %// the second column is Y
csvwrite('centroid.csv',data_Centroid); %// writes values into csv
If you want column headers in your csv, it gets complicated because csvwrite can only handle numeric arrays:
celldata = num2cell(num2str(data_Centroid)); %// create cell array
celldata(:,3) = celldata(:,4); %// copy col 4 (y data) into col 3 (spaces)
for i=1:length(celldata)
celldata{i,2} = ','; %// col 2 has commas
celldata{i,4} = '\n'; %// col 4 has newlines
end
celldata = celldata'; %'// transpose to make the entries come columnwise
strdata = ['Centroid_X,Centroid_Y\n',celldata{:}]; %// contains all as string
fid = fopen('centroid.csv','w'); % writing the string into the csv
fprintf(fid,strdata);
fclose(fid);
This is how I solved it in the end: extracted each field from struct used horzcat to join into a new array then defined headers and used csvwrite_with_headers, to ouput to csv.
wpbFeatures = regionprops(vWFlabelled, 'Area','Centroid', 'MajorAxisLength', 'MinorAxisLength', 'Eccentricity', 'EquivDiameter');
wpbArea = vertcat(wpbFeatures.Area);
wpbCentroid = vertcat(wpbFeatures.Centroid);
wpbCentroidX = wpbCentroid(:,1);
wpbCentroidY = wpbCentroid(:,2);
wpbFeret = max(imFeretDiameter(vWFlabelled, linspace(0,180,201)), [], 2);
wpbMajorAxisLength = vertcat(wpbFeatures.MajorAxisLength);
wpbMinorAxisLength = vertcat(wpbFeatures.MinorAxisLength);
wpbEccentricity = vertcat(wpbFeatures.Eccentricity);
wpbEquivDiameter = vertcat(wpbFeatures.EquivDiameter);
wpbFeatures = horzcat(wpbArea, wpbCentroidX, wpbCentroidY, wpbFeret, wpbMajorAxisLength, wpbMinorAxisLength, wpbEccentricity, wpbEquivDiameter);
headers = {'Area','CentroidX','CentroidY', 'Feret', 'MajorAxisLength', 'MinorAxisLength', 'Eccentricity', 'EquivDiameter'};
csvwrite_with_headers(strcat(PlateName, '_ResultsFeatures.csv'),wpbFeatures,headers);

write cell array of combined string and numerical input into text file

Consider the following:
DateTime = {'2007-01-01 00:00';'2007-02-01 00:00';'2007-03-01 00:00'};
Headers = {'Datetime','Data'};
Dat = [100,200,300];
Data = [DateTime,num2cell(Dat')];
Final = [Headers;Data];
How would I write the data in 'Final' into a tab delimited text file. I know how to use fopen, fprintf and so on when the variable is composed of solely numerical inputs but am struggling to solve this problem. I have tried:
fid = fopen('C:\Documents\test.txt','wt');
fprintf(fid,'%s\t%s\n',Final{:});
fclose(fid);
However, this does not generate a text file that is in the same format as that generated in matlab. How can this problem be solved?
This solution gives what I think you need; some remarks which I hope to be useful are on side
DateTime = {'2007-01-01 00:00';'2007-02-01 00:00';'2007-03-01 00:00'};
Headers = {'Datetime','Data'};
Dat = [100,200,300];
% // In the way you used fprintf it expects just strings ('%s\t%s\n'),
% // therefore Data should be composed exclusively by them.
% // Numbers are converted to strings by using num2str
% // by using cellfun we iteratively convert every element of num2cell(Dat')
% // in strings, obtaining a cell
Data = [DateTime,cellfun(#num2str, num2cell(Dat'), 'UniformOutput' , false )];
Final = [Headers;Data];
fid = fopen('test.txt','wt');
% // this iterates fprintf on the cell rows, giving you the output
cellfun(#(x,y) fprintf(fid,'%s\t%s\n',x,y),Final(:,1),Final(:,2));
fclose(fid);
result
Datetime Data
2007-01-01 00:00 100
2007-02-01 00:00 200
2007-03-01 00:00 300
EDIT:(from comments) in the general case of a N-columns cell, you can simply go for a for loop, e.g.
for i = 1 : size(Final,1)
fprintf(fid,'%s ', Final{i,:} );
fprintf(fid,'\n');
end
(same result, but not depending on the number of columns).