Matlab add string variable as column in table - matlab

I'm trying to use YOLOv4 in MATLAB R2022b to carry out detections on all images in a directory, and append results to a text file.
I can append just the detection results to each line, but when I try to add the filename I get this error:
You might have intended to create a one-row table with the character vector '000001.jpg' as one of its variables. To store text data in a table, use a string array or a cell array of character vectors rather than character arrays. Alternatively, create a cell array with one row, and convert that to a table using CELL2TABLE.
I understand that the filename is a string, and the values returned by YOLO are a categorical array, but I don't understand the most efficient way to deal with this.
filesDir = dir("/home/ADL-Rundle-1/img1/");
for k=1:length(filesDir)
baseFileName=filesDir(k).name
fullFileName = fullfile(filesDir(k).folder, baseFileName);
if isfile(fullFileName)
img = imread(fullFileName);
[bboxes,scores,labels] = detect(detector,img);
T = table(baseFileName, labels, bboxes, scores);
writetable(T,'/home/tableDataPreTrained.txt','WriteMode','Append','WriteVariableNames',0);
end
end
The format of results from YOLO is
And I'd like a file with
000001.jpg, 1547.3, 347.35, 355.64, 716.94, 0.99729
000001.jpg, 717.81, 370.64, 76.444, 108.92, 0.61191
000002.jpg, 1, 569.5, 246.49, 147.25,0.56831

baseFileName is a char vector.
The error message is telling you to use a cell array of char vectors:
T = table({baseFileName}, labels, bboxes, scores);
or a string array:
T = table(string(baseFileName), labels, bboxes, scores);
I would use the string array, it's the more modern MATLAB, and the table looks prettier when displayed. But both accomplish the same thing.
Given that labels and the other two variables have multiple rows, you need to replicate the file name that number of times:
frame = repmat(string(baseFileName), size(labels,1), 1);
T = table(frame, labels, bboxes, scores);

Related

Put several values into 1 cell (e.g. array{1} = [1,2,3]) for multiple condition SPM analysis; All I get is array{1} = [1] [2] [3]

I'm working to get a 1st level analysis completed on some fMRI data, and as it's my first time using SPM in this way, it seems as though there is no end to my frustrations. SPM includes these specific instructions:
"This *.mat file must include the following cell arrays (each 1 x n): names, onsets, and durations. eg. names=cell(1,5), onsets=cell(1,5), durations=cell(1,5), then names{2}="Second condition", onsets{2}=[3,5,19,22],durations{2}=[0,0,0,0], contain the required details of the second condition."
The code I'm using grabs the data I need from the various excel files the behavioral data is stored in, and adds them to these cell arrays.
sessionFiles = dir('*.xlsx');
allNames = {sessionFiles.name}';
conditions = 36;
% go through excel files to grab relevant column information for SPM
for i=1:length(sessionFiles)
[~,fileName,~] = fileparts(allNames{i});
% initialize cells SPM needs
names = cell(1,conditions);
onsets = cell(1,conditions);
durations = {1.75};
durations = repmat(durations,1,conditions);
% read in excel file
[num,~,~] = xlsread(sessionFiles(i).name);
trialType = num(:,6);
% grab condition information from columns: seconds=9, name=6
for j=1:conditions
index = find(trialType==j);
trialOnsets = cell(1,length(index));
names{1,j} = j;
for k=1:length(index)
trialOnsets{1,k}=double(num(index(k),9));
end
onsets{1,j} = trialOnsets;
end
% save new data for SPM
save(fileName,'names','onsets','durations');
clear names onsets durations fileName num raw text
end
I found an example which shows each cell should look like this:
I just can't figure out how to grab the numbers automatically and put them in cells like that.
I know this isn't the SPM forums, but I've seen a few questions posted and I thought I would try my luck.
With the line trialOnsets = cell(1,length(index));, trialOnsets is specified as a cell array of size 1xlength(index). Then, trialOnsets is assigned to onsets{1,j}. With this workflow, each cell of onsets will be of size 1xlength(index).
Instead, each cell of onsets should be of size 1x1 and each 1x1 cell in onsets should have a matrix of size 1xlength(index). To do this, do the following.
Specify trialOnsets as a matrix, instead of as a cell array. To do this, replace trialOnsets = cell(1,length(index)); with trialOnsets = zeros(1,length(index));.
Assign the values from num to trialOnsets, which is now a matrix (previously was a cell array). To do this, replace trialOnsets{1,k}=double(num(index(k),9)); with trialOnsets(1,k)=double(num(index(k),9));.
The edited code should be as follows:
sessionFiles = dir('*.xlsx');
allNames = {sessionFiles.name}';
conditions = 36;
% go through excel files to grab relevant column information for SPM
for i=1:length(sessionFiles)
[~,fileName,~] = fileparts(allNames{i});
% initialize cells SPM needs
names = cell(1,conditions);
onsets = cell(1,conditions);
durations = {1.75};
durations = repmat(durations,1,conditions);
% read in excel file
[num,~,~] = xlsread(sessionFiles(i).name);
trialType = num(:,6);
% grab condition information from columns: seconds=9, name=6
for j=1:conditions
index = find(trialType==j);
trialOnsets = zeros(1,length(index));
names{1,j} = j;
for k=1:length(index)
trialOnsets(1,k)=double(num(index(k),9));
end
onsets{1,j} = trialOnsets;
end
% save new data for SPM
save(fileName,'names','onsets','durations');
clear names onsets durations fileName num raw text
end
I could not test this code, since there was no sample data. Let me know if this works for you.

Matlab, Convert cell to matrix

Hope some of you can help me. I have converted a pdf with a lot of txt and tables to .txt file. I did this because three values of the pdf has to be writen into exel. This has to be done more than a thusind times a mounth, therefore i thought there has to be a better eay than doing it manually. The only things that has to be extracted is the Date, Repport number and a single volume. I found out that the date and repport number always is at the same line, so thats pretty easy to extract, even though its readen into a 145x1 cell. But this brings me to my first question.
Each of the cells looks like this:
Date 23/4-2015
Repportnumber 8
How do i remove the whitespace?
I also have to extract the volume. this was more difficult, cause the linemunber of the volume differentiates from one pdf to another, therefore i created a searchfunction, which works and founds the volume, which is created to a cellarry looking like this:
[233.4 452.2 94.6]
I only needs the middlenumber, so how do i create this into a matrix?
Keep in mind it is a 1x1 cell, with whitespace!
Hope some of you guys can help me.
For your first question, you can remove the spaces by searching the line of characters and identifying the spaces with strcmp, then setting those elements of the character string to be empty ([]). Here is an example of the code for that:
% number of character
N = length(my_string);
% character to remove (initialize all 0)
icut = zeros(1,N);
% check each character
for i = 1:N;
% if character is a space, tag for removal
if strcmp(my_string(i),' ');
icut(i) = 1;
end
end
% remove space characters
my_string(icut == 1) = [];
For your second question, you can convert the contents of the cell to a numeric array then simply take the 2nd element.
% convert the cell contents to an array of numbers
cell_array = str2num(my_cell{1});
% get the middle value
middle_value = cell_array(2);
This assumes the cell contains the array of values as a string, as in my_cell = {'[233.4 452.2 94.6]'};.
You can remove the whitespace from a string using strrep. This works on cells containing strings or on char arrays and returns the same object type that it was applied to. If you pass in a cell to strrep it will return a cell, if you pass in a char array it will return a char array.
>> C = {'Date 23/4-2015 Repportnumber 8'};
>> strrep(C, ' ', '') % Cell containing string (char array)
ans =
'Date23/4-2015Repportnumber8'
>> strrep(C{1}, ' ', '') % String (char array)
ans =
Date23/4-2015Repportnumber8
To convert the version cell array to a matrix you can use str2num. Then you can use linear indexing to extract the correct version.
>> C = {'[233.4 452.2 94.6]'};
>> C = str2num(C{1});
>> C(2)
ans =
452.2000

Performing find and replace functions on elements of a table in Matlab

I am working with a 400x1200 imported table (readtable generated from an .xls) which contains strings, doubles, dates, and NaNs. Each column is typed consistently. I am looking for a way to locate all instances in the table of any given string ('Help me please') and replace them all with a double (1). Doing this in Matlab will save me loads of work making changes to the approach used on the rest of this project.
Unfortunately, all of the options I've looked at (regexp, strrep, etc) can only take a string as a replacement. Strfind was similarly unhelpful, because of the typing across the table. The lack of cellfun has also made this harder than it should be. I know the solution should have something to do with finding the indices of the strings I want and then just looping DataFile{subscript} = [1], but I can't find a way to do it.
First you should transform your table at a cell array.
Then, you can use the strrep along with str2num, e.g.
% For a given cell index
strrep(yourCellIndexVariable, "Help me please", "1");
str2num(yourCellIndexVariable);
This will replace the string "Help me please" with the string "1" (the strrep function) and the str2num will change the cell index to the double value according to the string.
By yourCellIndexVariable I mean an element from the cell array. There are several ways to get all cells from a cell array, but I think that you have solved that part already.
What you can do is as follows:
[rows, cols] = size(table); % Get the size of your table
YourString = 'Help me please'; % Create your string
Strmat = repmat(YourString,rows,cols); % Stretch to fill a matrix of table size
TrueString = double(strcmp(table,Strmat)); % Compares all entries with one another
TrueString now contains logicals, 1 where the string 'Help me please' is located, and 0 where it is not.
If you have a table containing multiple classes it might be handy to switch to cells though.
Thank you very much everyone for helping think through to a solution. Here's what I ended up with:
% Reads data
[~, ~, raw] = xlsread ( 'MyTable.xlsx');
MyTable = raw;
% Makes a backup of the data in table form
MyTableBackup = readtable( 'MyTable.xlsx' );
% Begin by ditching 1st row with variable names
MyTable(1,:) = [];
% wizard magic - find all cells with strings
StringIndex = cellfun('isclass', MyTable, 'char');
% strrep goes here to recode bad strings. For example:
MyTable(StringIndex) = strrep(MyTable(StringIndex), 'PlzHelpMe', '1');
% Eventually, we are done, so convert back to table
MyTable = cell2table(MyTable);
% Uses backup Table to add variable names
% (the readtable above means the bad characters in variable names are already escaped!)
MyTable.Properties.VariableNames = MyTableBackup.Properties.VariableNames;
This means the new values exist as strings ('1', not 1 as a double), so now I just str2double when I access them for analysis. My takeaway - Matlab is for numbers. Thanks again all!

Variable labels in MATLAB

I have a huge table data= {1000 x 1000} of binary data.
They table's variable names are encoded for eg D1,D2,...,DA2,DA3,... with their real labels given in a .txt file.
The .txt file also consists of some text for eg:
D1: Age
Mean age: 33
Median :
.
.
.
D2: weight
I would just like to pick out these names from the text file and create a table with the real variable names.
Any suggestions?
If there is a specific number of lines between each of those labels, then you can extract them by reading in the file, and looping over the relevant lines. For each label, it simple to extract the label with strsplit()
e.g. Let's say there's 5 lines between each label
uselessLines = 5;
% imports as a vertical matrix with each line from the file.
dataLabelsFile = importdata(filename);
% get the total number of lines
numLines = size(dataLabelsFile);
% pre-allocate array for labels, a cell is used for a string
dataLabels = cell(ceil(numLines/(uselessLines+1)));
% use a seperate counting variable
m = 1;
% now, for each label, we add it to the dataLabels matrix
for i=1:(uselessLines+1):numLines
line = strsplit(dataLabelsFile{i}); % by default splits on whitespace
dataLabels(m) = line(2);
m = m + 1;
end
By the end of that loop you should have a variable called dataLabels that holds all of the labels. Now, you can actually very easily work out which label goes with which set of data
provided they are still in the same order. The indexes will be the same for the label to the data.
This is a method you could try if the labels are evenly spaced.
However, if the labels are a random number of lines, then you probably want to do a check with a regular expression like the person below me has suggested. Then you just replace the last two lines of the loop with something like this.
...
if (regular expression matched)
dataLabels(m) = line(2);
m = m + 1;
end
...
That being said, while regular expressions are flexible, if you can get away with replacing it with literally one function call, it's usually better to do that. Regex efficiencies are determined by the skill of the programmer, while in-built functions have generally been tested by some of the better programmers in the world. Additionally, Regex's are harder to understand if you ever want to go back and change it.
Of course there are times when Regex's are amazing, I'm just not convinced this is one of those times.
An implemention of the approach in my earlier comment:
fid = fopen(filename);
varNames = cell(0);
proceed = true;
while proceed
line = fgetl(fid);
if ischar(line)
startIdx = regexp(line,'(?<=^[A-Z]*\d*:)\s');
if ~isempty(startIdx)
varNames{end+1} = strtrim(line(startIdx:end)); %#ok<SAGROW>
end
else
proceed = false;
end
end
fclose(fid);
I cant put the resulting varNames in a table for you, since I have a version of Matlab that does not support tables.

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);