How to extract file names in a loop into a character array? - matlab

I have a code like below. I want to generate an array with all the file names and combine with the data that I collected from each file.
DataCC = dir('*-CC.xls'); %select the file type
MeanAreaCC=[];
PlateNameCC=[];
for w = 1: numel(DataCC)
basefilenamedata=DataCC(w).name; %extract the file name
T=readtable(basefilenamedata); %read table in
PlateNameCC=[PlateNameCC basefilenamedata]; %generate the file name array
MeanAreaCC = [MeanAreaCC mean(T.Area)]; %generate the data array
end
x=array2table([PlateNameCC, transpose(MeanAreaCC)],'VariableNames',{'Iso-Condi-Rep','MeanAreaCC'}); %combine two arrays just generated
writetable(x,fullfile(DataFolder,'DataSummary.xls'),'Sheet',1,'Range','A1');
But my code didn't work, as the PlateNameCC is generated as one character but not an array. This came to an error complaining different array size, when I combine PlateNameCC with MeanAreaCC. Could somebody check it for me? Thank you!

There are a couple things wrong here.
First, PlateNameCC = [PlateNameCC basefilenamedata]; is going to create one long string of garbage.
For example:
fnames = dir('*.m')
namelist = [];
for ii = 1:numel(fnames)
namelist = [namelist fnames(ii).name]
end
Gives me:
namelist =
'SOcode.mcallbacksclass.mtestcode.m'
You want to use a string array or a cell array:
fnames = dir('*.m');
namelist1 = string({fnames.name});
namelist2 = {fnames.name};
Which returns:
namelist1 =
1×3 string array
"SOcode.m" "callbacksclass.m" "testcode.m"
namelist2 =
1×3 cell array
{'SOcode.m'} {'callbacksclass.m'} {'testcode.m'}
Second, there's no point trying to concatenate two (very) unlike arrays to create a table when you can just use the table constructor itself:
fnames = dir('*.m');
namelist = string({fnames.name});
data = [1, 2, 3];
T = table(namelist.', data.');
Which gives:
T =
3×2 table
Var1 Var2
__________________ ____
"SOcode.m" 1
"callbacksclass.m" 2
"testcode.m" 3

Related

What is wrong with my loop code?

I wrote a loop code to extract my table files names into a string or an array and meanwhile collect the data into arrays. But I found my code went wrong as only one file was read and repeated in the loop over and over again. I had no idea where my code is wrong and it already took me several hours to look for the problem. Could somebody help me?
DataCircle = dir('*-circle.xls');
MeanAreaCircle = [];
ColonyNumCircle = [];
PlateNameCircle = [];
for zz = 1:numel(DataCircle)
basefilenamedata1 = DataCircle(w).name; % generate the base name
DataName1 = regexprep(basefilenamedata1,'-circle.xls',''); %replace part of the name and the extension
PlateNameCircle = [PlateNameCircle DataName1]; % collect the file name into a string
T1 = readtable(basefilenamedata1); % read data in
MeanAreaCircle = [MeanAreaCircle mean(T1.area)]; % collect the mean for area
end
What I got is like this, which is wrong:
>> PlateNameCircle
PlateNameCircle ='IMG_0813IMG_0813IMG_0813IMG_0813IMG_0813IMG_0813IMG_0813IMG_0813IMG_0813IMG_0813'
>> MeanAreaCircle
MeanAreaCircle =
1.0e+03 *
6.4152 6.4152 6.4152 6.4152 6.4152 6.4152 6.4152 6.4152 6.4152 6.4152
My input file list:
IMG_0809-CC.xls
IMG_0809-circle.xls
IMG_0810-CC.xls
IMG_0810-circle.xls
IMG_0812-CC.xls
IMG_0812-circle.xls
IMG_0813-CC.xls
IMG_0813-circle.xls
What I want is a column or a character array or a string like this:
PlateNameCircle = 'IMG_0809' 'IMG_0810' 'IMG_0811' 'IMG_0812' 'IMG_0813'
Issues
1: you're using w to index the name, instead of zz, the loop variable. Apparently, there is a stray variable called w in your workspace, equal to 8. That's why you're always reading the same file, regardless of iteration number.
2: you're not adding a space in the name:
PlateNameCircle = [PlateNameCircle ' ' DataName1];
3: you're adding the same name on each two consecutive iterations:
PlateNameCircle = 'IMG_0809-CC IMG_0809 IMG_0810-CC IMG_0810 ...'
Improvements
vectorize and use cell strings
preallocate instead of grow-on-the-fly
give your variables even-better names (although you already did a pretty good job there already, tbh)
something like:
filenames = {D.name};
PlateNameCircle = regexprep(filenames,'-circle.xls',''); %...doubt this is actually what you want, but it *is* what you've written...
MeanAreaCircle = zeros(numel(filenames),1);
ColonyNumCircle = []; % <- not used?
for zz = 1:numel(filenames)
T1 = readtable(filenames{zz}); % read data in
MeanAreaCircle(zz) = mean(T1.area); % collect the mean for area
end
I think the w in
basefilenamedata1 = DataCircle(w).name;
should be zz instead. Otherwise you are always looking at the first file in the list.

How to concatenate / assign string of different length to existing Matlab table?

Consider table in Matlab.
a = table();
a.c = 'a';
How can I add one row containing a string of different length to that table? i.e I want to get:
c
______
'a'
'aa'
For example this simple attempt gives an error:
b = table();
b.c = 'aa';
result = [a; b]
Error:
Could not concatenate the table variable 'c' using VERTCAT.
Caused by:
Error using vertcat
Dimensions of matrices being concatenated are not consistent.
Due to how MATLAB's table objects treats the contained data, it tries to be smart with the data types. Occasionally when things try to be smart behind the scenes they get tripped up in ways that aren't necessarily readily apparent to the user.
What's happening here is that since your c column is created with a character array, MATLAB attempts to keep this column homogeneous and concatenate 'a' with 'aa'. This will error out due to MATLAB's handling of character arrays as matrices of characters, which comes with a size enforcement: all rows must have the same number of columns.
You have a couple options: use a string array (introduced in R2016b), or use a cell array. While string arrays are essentially cell arrays under the hood, they come with the advantage of dedicated string methods, allowing you to natively perform various string operations without needing to explicitly index into a cell array.
To change your code, simply use double quotes ("") instead of single quotes (''):
a = table();
a.c = "a";
b = table();
b.c = "aa";
T = [a;b]
Which returns:
T =
2×1 table
c
____
"a"
"aa"
Alternatively, you can explicitly force the type of c as a cell array:
a = table();
a.c = {'a'};
b = table();
b.c = 'aa';
T = [a; b]
Which returns the same.
If you have an entire column of data, you can create a column from a cell array
tbl = table();
tbl.mycol = {'some text';
'something else';
'third item'};
If you want to append a single items (like in a loop) you could do
tbl = table();
mycell = {'some text';
'something else';
'third item'};
tbl.mycol = {};
for ii = 1:numel(mycell)
tbl.mycol(ii) = mycell(ii);
end
Similarly, you can append to the end as you would an array
tbl.mycol(end+1) = {'fourth item'};
You can merge two tables by concatenating them using vertcat
myothercell = {'append this';
'...and this'};
tbl1 = table();
tbl1.mycol = mycell;
tbl2 = table();
tbl2.mycol = myothercell;
tbl3 = vertcat(tbl1, tbl2);

How do I iterate through an imported excel doc?

what I need to do for this code is import a giant (302x11) excel doc, and then ask the user for an input. Then, I need to iterate through each element in the 5th column of the excel array, and if that element matches the user input, save the entire row to a new array. After going through all 302 rows, I need to display the new array.
So far, I have this:
Vin = input('Vin: ');
filename='MagneticCore.xlsx';
sheet=2;
xlRange='B2:L305';
[ndata, text, alldata] = xlsread(filename,sheet,xlRange,'basic');
After this, I'm not sure how to iterate through the alldata array.
alldata is a cell, to select the fifth column you can use alldata{:,5}. Searching in Cells is done this way without iterating
Try it on your own, if you get stuck update your question with code and error message
Daniel R is right, you can do this without iterating through the cell array. Here's how you could iterate through the array if you needed to:
[ndata, text, alldata] = xlsread('Book1.xlsx');
target = 12;
newArray = {};
for r = 1:size(alldata, 1)
% get the element in the fifth column of the current row
e = raw{r,5};
if e == target
% add to newArray
newArray{end + 1} = alldata(r,:);
end
end
% display newArray
for r = 1:size(newArray, 1)
disp(newArray{r})
end

Removing items from a structure array in matlab

I have a very large structure array in matlab. Suppose, for argument's sake, to simplify the situation, I have something like:
structure(1).name = 'a';
structure(2).name = 'b';
structure(3).name = 'c';
structure(1).returns = 1;
structure(2).returns = 2;
structure(3).returns = 3;
Now suppose I have some condition that comes along and makes me want to delete everything from structure(2) (any and all entries in my structure array). What is a good way to do that?
My solution has been to just set the corresponding fields to [] (e.g. structure(1).name = [];), but that doesn't remove them, that only makes them empty. How do I actually remove them completely from the structure array? Is there a way?
simple if you want to delete element at index i do the following:
i = 3
structure(i) = [];
And that will remove element at index 3.
Example:
st.name = 'text';
st.id = 1524;
arrayOfSt = [st st st st st];
Now:
arrayOfSt =
1x5 struct array with fields:
name
id
If we execute:
arrayOfSt(2) = [];
then the new value of the array of structers will be:
arrayOfSt =
1x4 struct array with fields:
name
id
Try it !

MATLAB: Using the If statement with a string to return values from other arrays

I have a matlab program where I am importing some arrays from excel. I am trying to write an if statement that looks in the first array, say:
Thing-1 Thing-1 Thing-3 Thing-5
If a column is a "Thing-1", then it goes to a different array, and calculates 3 values that are to be given different variable names...any guidance would be much appreciated! Thanks!
You need a function like vlookup as in Excel.
I have written one. Here is the source code:
function [content, index] = vlookup(m, e, column, lookcolumn)
if isempty(m) || isempty(e), return; end
if nargin <= 3, lookcolumn = 1; end
isechar = ischar(e);
assert(isechar || isnumeric(e), 'the second parameter must be a string or numeric');
if iscell(m)
content = {}; index = [];
if isechar
index = find(strcmp(e, m(:, lookcolumn)));
content = m(index, column);
else
for i = 1:size(m, 1)
if isnumeric(m{i, lookcolumn}) && m{i, lookcolumn} == e
content = [content; m(i, column)]; %#ok<*AGROW>
index = [index; i];
end
end
end
else
assert(~isechar, 'When the first para is a matrix, the second para must be numeric');
index = find(m(:, lookcolumn) == e);
content = m(index, column);
end
The question is... not very clear, but let me try and give you some suggestions.
Say you read some data from an Excel workbook in which the first row is headers, followed by lots of rows with numbers.
[num,txt] = xlsread(excelFileName);
so that num contains the numeric data and txt the string column headers.
Then you can check for the string Thing-1 in the column headers. thingOneIdx is an array with indices into the columns of the header. In your example, it would be [1 2], since the first two columns are Thing-1.
thingOneIdx = find(strcmp('Thing-1',txt));
You can create three cell arrays, firstValue, secondValue, and thirdValue that will store the results of the three calculations. If you need to keep the Thing-1 data around in an additional array, you can do that analogously.
%# define cell arrays (do it in one go using deal)
[firstValue,secondValue,thirdValue] = deal(cell(length(thingOneIdx),1));
%# for simplicity and readability, loop through isThingOneIdx to assign data
for ct = 1:length(thingOneIdx)
myIdx = thingOneIdx(ct);
firstValue{ct} = someCalculation(num(myIdx,:));
secondValue{ct} = someOtherCalculation(num(myIdx,:));
%# etc
end