Convert struct fields from string to number - matlab

I have a struct with several fields, some that should be numeric, and some that should be char. However, after my use of regexp I have chars in the fields that I want to use as numbers.
For example:
foo.str = 'one';
foo.data = '1';
foo(2).str = 'two';
foo(2).data = '2';
In my dreams I could do: foo.data = str2double(foo.data), but this does not work.
I could iterate through the struct, but that's only an ok option.
It's a long struct (100,000) with about 20 files.
for i = 1:length(foo)
foo(i).data = str2double(foo(i).data);
end
Any ideas?

Collect all elements from the subfield and call str2double once:
str2double({foo.data})

Here is a fairly compact solution that deals numerical values back into each foo.data:
fdnc = num2cell(str2double({foo.data}));
[foo.data] = deal(fdnc{:});
Remember to clear and redefine foo when testing.
EDIT: Fixed vertcat problem with multiple digits. Thanks, nispio.

A little cumbersome, but this works; no loops:
N = length(foo);
[aux_str{1:N}] = deal(foo.str);
[aux_data{1:N}] = deal(foo.data);
aux_data = mat2cell(str2double(aux_data),1,ones(1,N));
foo = cell2struct([aux_str; aux_data],{'str','data'},1);

Related

How to use an entry in an array to call a different array entry

I've looked at a few other questions but I couldn't come up with an answer, I'm relatively new to MatLab (but not programming) so I apologize if it's a duplicate.
I'm sure the title isn't very clear so here's an example:
I have an array, say name = ['Jack';'Jill'];. The elements in this array reference other arrays, such as:
Jack.income = 31000;
Jack.car = 1;
Jill.income = 55000;
Jill.car = 0;
Now, I would like to use name to pull data from the other arrays, such as:
data = name(1).income, which should return 31000, or data = name(2).car, which should return 0.
Any help on this would be greatly appreciated.
Thank you very much,
Eric
You'd be better off using an array of structs (or making your own person object perhaps):
people(1).name = 'Jack';
people(1).income = 31000;
people(1).car = 1;
people(2).name = 'Jill';
people(2).income = 55000;
people(2).car = 0;
Now you can generate a list of names like this (see cell arrays and comma separated lists):
names = {people.name};
which you can convert into an index like this (see logical indexing and ismember):
ind = ismember(names, 'Jack');
and then finally extract someone's income:
people(ind).income

Vectorize filtering on matlab cell structures

I have a huge cell vector cc (size: 1xN) of the form:
cc{1} = {'indexString1', 'str_row1col1', 'str_row1col2' }
cc{2} = {'indexString2', 'str_row2col1', 'bighello', 'str_row1col3' }
cc{3} = {'indexString3','str_row3col1'}
cc{4} = {'indexString4','str_row3col1', 'helloWorld'}
I want to traverse each cell and remove specific cells that contain the word "hello", e.g c{4}{2}. Can we do that without for loops keeping the final structure of cc?
Best,
Thoth.
EDIT: From the answers and comments I have seen that the structure of the cell impose some limitations. So any other suggestion to store my data are welcome. I just want to keep together all the cells (e.g. 'str_row1col1', 'str_row1col2') that correspond to the same indexString*n* (e.g. indexString1). I made this edit in case it helps some final reshape.
Using regular expressions, you can obtain a logical array in which zeros represent occurences of the word 'hello' somewhere in the nested cell. As #LuisMendo pointed out, this would be much easier to delete the unwanted cells if they were not nested:
clc
clear
cc{1} = {'str_row1col1', 'str_row1col2' };
cc{2} = {'str_row2col1', 'bighello', 'str_row1col3' };
cc{3} = {'str_row3col1'};
cc{4} = {'str_row3col1', 'helloWorld'};
A = (cellfun(#isempty,regexp([cc{:}],'(\w*hello|hello\w*)','match')))
Gives the following array:
A =
1 1 1 0 1 1 1 0
For the rest I think you would need a loop since the nested cells are not all of the same size. Anyhow I hope it helps you a bit.
EDIT Here is what you can do using a for loop. In order to identify words of interest (earth and water as in your comment below), simply add them to the argument in the call to regexp. This character: | is used to make some sort of list so that Matlab checks all the expressions in the brackets.
Please refer to this page for more infos on regular expressions. There is also a possibility to look for regular expressions with case-sensitivity.
Sample code, in which I added strings containing earth and water:
cc{1} = {'str_row1col1', 'earth!superman' 'str_row1col2' 'DummyString'};
cc{2} = {'str_row2col1', 'bighello', 'str_row1col3' };
cc{3} = {'str_row3col1' 'str_row3col3' 'water_batman'};
cc{4} = {'str_row3col1' 'str_row4col2' 'helloWorld'};
cc{5} = {'str_row5_LegoMan' 'str_row5col2' 'AnotherDummyString' 'Useless String' 'BonjourWorld'};
% With a for loop, for example:
FinalCell = cell(size(cc,2),1);
for k = 1:size(cc,2)
DummyCell = cc{k}; % Use dummy cell for easier indexing
% This is where you tell Matlab what words/expressions you are looking for
A = cellfun(#isempty,regexp(cc{k},'(\w*hello|hello\w*|earth|water)','match'));
DummyCell(~A) = []; % Remove the cells containing the strings/words of interest
FinalCell{k} = DummyCell;
end
Then you're good to go. Hope that helps!
The closest thing possible I found is:
clear all
cc{1} = {'str_row1col1', 'str_row1col2' };
cc{2} = {'str_row2col1', 'bighello', 'str_row1col3' };
cc{3} = {'str_row3col1'};
cc{4} = {'str_row3col1', 'helloWorld'};
cc1 = [cc{:}];
cc1 = cc1(~strcmp('bighello',cc1));
This reorganize your array into a one dimensional array and it cannot match regular expression, but only whole words.
For a better job I am afraid you have to use for loops.

Logical elements substitution [3D Matrix]

Let's say that I have 2 3D-Matrix:
A = rand(10,4,100);
B = rand(10,4,100);
L = gt(A,B);
Now I want substitute all elements of B with elements of A only where L==1but this doesn't work:
B(L==1,:,:) = A(L==1,:,:);
Any suggestion?
Our even shorter whithout find
B(L) = A(L);
Sounds like a job for the find() function.
p = find(L);
B(p) = A(p);
EDIT: Just realized you don't need the find() function. Just use logical indexing like this:
B(L==1) = A(L==1);

Concatenate strings of digits in matlab

Suppose I have a series of strings such as:
a = '101010101010'
b = '010101'
c = '000101010'
is there a way in Matlab to concatenate them and produce the binary number 101010101010010101000101010?
Use the concatenation operator [ ], with horizontal concatenation , (vertical concatenation ; will fail here unless you reshape() into column vectors):
[a,b,c]
However, I suggest storing your variables in a cell array:
s = {'101010101010','010101', '000101010'};
[s{:}]
or
cat(2,s{:})
To concatenate strings, you could say:
out = [a b c];
Alternatively:
out = strcat(a,b,c);
Yet another way:
out = sprintf('%s', a,b,c);
I think that this should work:
res = [a,b,c]
or alternatively call
res = strcat(a,b,c)
or, yet
res = cat(2,a,b,c)

How do I rename a field in a structure array in MATLAB?

Given a structure array, how do I rename a field? For example, given the following, how do I change "bar" to "baz".
clear
a(1).foo = 1;
a(1).bar = 'one';
a(2).foo = 2;
a(2).bar = 'two';
a(3).foo = 3;
a(3).bar = 'three';
disp(a)
What is the best method, where "best" is a balance of performance, clarity, and generality?
Expanding on this solution from Matthew, you can also use dynamic field names if the new and old field names are stored as strings:
newName = 'baz';
oldName = 'bar';
[a.(newName)] = a.(oldName);
a = rmfield(a,oldName);
Here's a way to do it with list expansion/rmfield:
[a.baz] = a.bar;
a = rmfield(a,'bar');
disp(a)
The first line was originally written [a(:).baz] = deal(a(:).bar);, but SCFrench pointed out that the deal was unnecessary.
Here's a a way to do it with struct2cell/cell2struct:
f = fieldnames(a);
f{strmatch('bar',f,'exact')} = 'baz';
c = struct2cell(a);
a = cell2struct(c,f);
disp(a)