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);
Related
I have a set of structs that all have the same field names. For example structs A, B, and C all have field names name and section. Is there a way to rearrange this organization of data so that the data goes from:
A.name = 'bb';
A.section = 199;
B.name = 'joe';
B.section = 101;
C.name = 'rob';
C.section = 33;
to this:
name =
A: 'bb'
B: 'joe'
C: 'rob'
section =
A: 199
B: 101
C: 33
The current code I have, for example, operates like this:
% ORIGINAL STRUCTS
A.name = 'bb';
A.section = 199;
B.name = 'joe';
B.section = 101;
C.name = 'rob';
C.section = 33;
% CREATE VARIABLE NAMES FOR NEW STRUCTS
oldFNames = fieldnames(A); % old field names
oldVNames{1} = varname(A); % old variable names
oldVNames{2} = varname(B);
oldVNames{3} = varname(C);
% RESTRUCTURE STRUCTS (SWITCH FIELDNAMES AND VARIABLE NAMES)
for j = 1:length(oldFNames)
for k = 1:length(oldVNames)
eval([oldFNames{j} '.' oldVNames{k} ' = ' oldVNames{k} '.' oldFNames{j}]);
end
end
function out = varname(var) % Function to get variable name
out = inputname(1);
end
I find the hack to use the varname function to be not great, and I don't know whether there is a way to make it easily adaptable to the number of variables I have. Any input on how to simplify this procedure would be great. Thanks.
It would be easier to do it all in a function, instead of just using a function to get the input variable name. Here's one solution:
function invert_struct(varargin)
varNames = arrayfun(#inputname, 1:nargin, 'UniformOutput', false); % Get input names
s = [varargin{:}]; % Combine inputs into a structure array
for f = fieldnames(s).' % Loop over fields
assignin('caller', f{:}, cell2struct({s.(f{:})}, varNames, 2));
end
end
And you would call it like so, with however many inputs you like:
invert_struct(A, B, C, ...);
The function uses assignin to create your new variables in the calling function and cell2struct to create a new structure from your field data and old variable names. Note that it also makes use of dynamic field names.
I am entering a dynamic data into excel sheet from a textbox. The problem is that it enters letter by letter in every cell.
This is the code I have tried:
>>function bntEnterData_callback(hObject,eventdata,handles)
>>val1 = get(handles.txt1,'string'));
>>val2 = get(handles.txt2,'string'));
>>val3 = get(handles.txt3,'string'));
>>values = [ val1 val2 val3]
>>filename= 'try.xls';
>>sheet= 1;
>>xkRange= 'A2';
>>xlswrite(filename,values,sheet,xlRange);
Assuming your handles.txt* are edit uicontrols, the 'String' property is going to be a character vector. Concatenating multiple character vectors just creates a longer vector:
a = 'foo';
b = 'bar';
values = [a b]
Which creates:
values =
'foobar'
When not explicitly provided a range of cells to write to, xlswrite will interpret the input single cell (or the default A1) as the starting point, and write all elements of the input array using the single cell as the origin. Because 'foobar' is an array of characters, this gives the expected behavior of:
To fix this, concatenate into a cell array:
a = 'foo';
b = 'bar';
values = {a b};
filename= 'try.xls';
sheet= 1;
xlRange= 'A2';
xlswrite(filename,values,sheet,xlRange);
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
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 !
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