Replacing multiple values in a structure - matlab

I have a structure with sample being a numeric vector. I would like to replace the numbers in sample by sample/2. However, I do not know how to overcome the following error:
Scalar structure required for this assignment.
Any suggestions are more than welcome.
Example:
field1 = 'event';
value1 = {'A', 'B', 'C', 'D'};
field2 = 'sample';
value2 = 22;
A = struct(field1, value1, field2, value2);
What I want to do:
A.sample = round([A.sample]/2,0);

You should use the deal function to distribute your calculated matrix to the elements of your structure:
sampleCell = num2cell(round([A.sample]/2,0)); % first convert result to cell, to comply with the `deal` syntax
[A.sample] = deal(sampleCell{:});

Related

How would you perform inter-row operations based on multiple columns? MATLAB

I am a novice programmer that is primarily self-taught. I am new to MATLAB and relational mathematics. Currently, I am attempting to perform math operations between rows. I would like to normalize the exp by the corresponding con and then multiply by the constant.
This constant is a laboratory measurement that could be subject to change in future experments. Thus, I have given it a column.
Below is some sample code that I have generated to exemplify my problem and solution. I am trying to get from myTable to rTable.
I recognize my solution is very sloppy and there must be a way to perform these operations that is human-readable and uses less temporary variables. To put it shortly, there must be a simpler way.
rTable = table();
myTable = table(transpose(1:8), ...
transpose({'Con1', 'Con2', 'Exp1', 'Exp2',...
'Con1', 'Con2', 'Exp1', 'Exp2'}),...
transpose({'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'}),...
ones(8, 1) * 2,...
'VariableNames', {'Values' , 'Condition', 'Group', 'Constant'});
[r, c] = size(myTable)
a = myTable(strcmp(myTable.Group, 'A'), :);
b = myTable(strcmp(myTable.Group, 'B'), :);
aexp1 = a.Values(strcmp(a.Condition, 'Exp1'), :) / a.Values(strcmp(a.Condition, 'Con1'), :) * mean(a.Constant);
aexp2 = a.Values(strcmp(a.Condition, 'Exp2'), :) / a.Values(strcmp(a.Condition, 'Con2'), :) * mean(a.Constant);
bexp1 = b.Values(strcmp(b.Condition, 'Exp1'), :) / b.Values(strcmp(b.Condition, 'Con1'), :) * mean(b.Constant);
bexp2 = b.Values(strcmp(b.Condition, 'Exp2'), :) / b.Values(strcmp(b.Condition, 'Con2'), :) * mean(b.Constant);
aT = table(transpose({aexp1, aexp2}),...
transpose({'Exp1', 'Exp2'}),...
transpose({'A', 'A'}),...
transpose({2, 2,}),...
'VariableNames', {'Values', 'Condition', 'Group', 'Constant'});
bT = table(transpose({bexp1, bexp2}),...
transpose({'Exp1', 'Exp2'}),...
transpose({'B', 'B'}),...
transpose({2, 2,}),...
'VariableNames', {'Values', 'Condition', 'Group', 'Constant'});
rTable = [aT; bT]
Thank you for any input or suggestions. Perhaps, the data structure i am handling is poorly organized.
Here's one solution:
rTable = table();
myTable = table((1:8)',{'Con1', 'Con2', 'Exp1', 'Exp2','Con1', 'Con2', 'Exp1', 'Exp2'}',...
{'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'}','VariableNames', {'Values' , 'Condition', 'Group', 'Constant'})
conditionrows = contains(myTable.Condition,'Con')
exprows = contains(myTable.Condition,'Exp')
conditionTable = myTable(conditionrows,:)
expTable = myTable(exprows,:)
constant = 2
rValues = expTable.Values./conditionTable.Values * constant
rTable = expTable
rTable.Values = rValues
Since you are trying to get a table of only exprows, you separate your original table into a conditionTable and an expTable. I'm assuming you have one condition row for each exp row, and also that you have a good correspondence in the tables (if not it will require more processing), then you can calculate the rValue simply with a one line expression. The ./ is element-wise division. Also note that you can use ' to perform transpose in matlab (further note that if you want a column vector of 1:10 for example you have to do (1:10)', 1:10' gives you a row vector from 1 to 10 since 1:10' is interpreted as vector from 1 to the transpose of 10.

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

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

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 to add an array (double) as a field to a struct variable?

I have a structure variable and want to add a field and fill the row with the values of an array (double).
The following code works but isn't very nice. Is there a more elegant way to add a field including values without the use of the mat2cell function or a for loop?
field1 = 1:10
field2 = 4:13
%create struct with field 'start' with 10 values
A = struct('start',mat2cell(field1,1,ones(1,numel(field1))))
%transform field2 to cell
temp = mat2cell(field2,1,ones([numel(field2),1]));
%add field 'end' with 10 values
[A(1:numel(field2)).end] = temp{:};
You can use num2cell rather than mat2cell which will (by default) place each element in it's own cell. Unfortunately, you will still need a temporary variable.
A = struct('start', num2cell(field1));
tmp = num2cell(field2);
[A.end] = tmp{:};
Download "catstruct":
https://www.mathworks.com/matlabcentral/fileexchange/7842-catstruct
clear
field1 = 1:10;
field2 = 4:13;
A = struct('start',num2cell(field1));
A = catstruct(A, struct('end',num2cell(field2)));
For performance you better use single field that contain array:
clear
field1 = 1:10;
field2 = 4:13;
A=[];
A.start = field1;
A.end = field2;

Removing a field from a Linked List (MatLab)

I have a one way linked list:
s=struct('field1', value1, 'field2', value2, 'field3', value3, 'next',[])
s=struct('field1', value3, 'field2', value5, 'field3', value6, 'next', s)
How do I remove the 'next' field so that my linked list becomes a standard structure array, like so?
s(1)=struct('field1', value1, 'field2', value2, 'field3', value3)
s(2)=struct('field1', value3, 'field2', value5, 'field3', value6)
I have tried the rmfield command but I get a 1x1 structure array, but I want, in this example, a 1x2 structure array.
You will want to first aggregate all of your structs together. You could use a recursive function to do this. Then you can call rmfield on the array of structs. You could also even combine the two at the same time.
function S = flattenList(S)
if isempty(S.next)
S = rmfield(S, 'next');
else
S = cat(2, rmfield(S, 'next'), flattenList(S.next));
end
end
As pointed out in the comments, since we are constantly appending data to the output, it can be slow for larger lists. We could determine the expected output size and then fill it within the loop.
This approach would allow you to pre-allocate the output.
function output = flattenList(S)
% Determine how big to make the output
tmp = S;
count = 1;
while ~isempty(tmp.next)
count = count + 1;
tmp = tmp.next;
end
% Pre-allocate the output
output = repmat(rmfield(S(1), 'next'), [1 count]);
tmp = S;
count = 1;
while true
output(count) = rmfield(tmp, 'next');
if isempty(tmp.next)
break;
else
tmp = S.next;
end
end
end