Finding all occurences of a string within a cell array (itself part of a struc) - matlab

I have the following structure
dataDens =
dens: [1x172 double]
level: {1x172 cell}
raga: {1x172 cell}
within which dataDens.raga consists of (reducing the number of columns below for simplicity)
Columns 1 through 3
'Multani' 'Tori' 'Tori'
I'd like to find the indices at which 'Tori' appears (that is, [2 3] for the example above). However, all of the commands I tried (below) either give an error, or return blank outputs. I think it's probably just a matter of adding/removing a curly bracket somewhere, or using some conversion; but I am at my wit's end, and hope someone can help clarify
indices = find(strcmp([dataDens.raga{:}], {'Tori'}))
indices = ismember('Tori', dataDens.raga)
[if,where] = ismember('Tori', dataDens.raga)

The issue had indeed to do with brackets. [dataDens.raga{:}] will result in concatenation of the character arrays, like so:
>> [dataDens.raga{:}]
ans =
'MultaniToriTori'
Using strcmp to compare this to 'Tori' will result in false, since there is no exact match. You can however compare the entire cell using strcmp, which will then return a boolean array, in which find can be used to obtain the indices of true entries:
indices = find(strcmp(data, {'Tori'}))
Alternatively, ismember would also work, but using ismember('Tori', dataDens.raga) you are checking whether 'Tori' is in dataDens.raga, not the other way around. Changing this to
ismember(dataDens.raga, 'Tori')
would again give you a boolean array, and find will obtain the indices you are looking for.

Related

Matlab: How do you seperate text in existing cell

I'm a bit new to the matlab world, and I'm running into an issue that I'm sure has an easy solution.
I've imported some data from a text file and parsed out the headers, which resulted in a 1x35 cell called Data. In each cell (for example Data{1,1,1}) is data that looks like:
'600000 -947.772827 -107.045776 -70.818062'
'600001 -920.431396 -86.098122 -56.485119'
'600002 -878.332886 -88.673630 -85.249130'
'600003 -851.637695 -68.546539 -96.691711'
'600004 -834.707642 -28.951260 -73.218872'
'600005 -783.431580 40.657402 24.242268'
The problem is, each line is contained in a single column. I'd like to parse it out so that I have 4 columns instead of one.
I tried parsing out the Data cell even further using:
textscan(Data{1,1,1}, '%u%f10%f10%f10', 1)
But it resulted in the following error:
Error using textscan
First input must be of type double or string.
Can I use textscan this way, or do I need to use some other method to break out the text?
With textscan, you can only specify a single string or a single number. With your input, I suspect it is a 6 x 1 cell array of strings. As such, you have no choice but to iterate over each cell and convert each cell array contents with textscan Also, get rid of the %10 spacing as it's actually screwing up where you're parsing out the string. Also, set the identifier to identify the first number you see to double (%f) as opposed to unsigned integer (%u) to allow for easier conversion.
Therefore, do something like this:
>> Data{1,1,1} = {'600000 -947.772827 -107.045776 -70.818062'
'600001 -920.431396 -86.098122 -56.485119'
'600002 -878.332886 -88.673630 -85.249130'
'600003 -851.637695 -68.546539 -96.691711'
'600004 -834.707642 -28.951260 -73.218872'
'600005 -783.431580 40.657402 24.242268'};
>> format long g;
>> vals = cell2mat(cellfun(#(x) cell2mat(textscan(x, '%f%f%f%f', 1)), Data{1,1,1}, 'uni', 0))
vals =
Columns 1 through 3
600000 -947.772827 -107.045776
600001 -920.431396 -86.098122
600002 -878.332886 -88.67363
600003 -851.637695 -68.546539
600004 -834.707642 -28.95126
600005 -783.43158 40.657402
Column 4
-70.818062
-56.485119
-85.24913
-96.691711
-73.218872
24.242268
That statement vals = ... is quite a mouthful, but easy to explain. Start with this statement:
cell2mat(textscan(x, '%f%f%f%f', 1))
For a given cell x in Data{1,1,1}, we want to parse out four numbers for each string that is stored in x. textscan will place these numbers as individual cell elements into a cell array. We want to convert each element into a numeric array, and so cell2mat is required for us to do so.
In order to operate over all of the elements in Data{1,1,1}, we need to use cellfun to allow us to do so:
cellfun(#(x) cell2mat(textscan(x, '%f%f%f%f', 1)), Data{1,1,1}, 'uni', 0)
The first input is a function that operates on each cell stored in Data{1,1,1} (the second input). We are basically telling cellfun that we want to operate on each cell in the cell array stored in Data{1,1,1} in the way I talked about before. This function has input parameter x, which is one cell from Data{1,1,1}. Now, the uni flag is set to 0 because the output of cellfun will not be a single number, but an array of numbers - one array per line that you have in your cell array. The output of this stage would be a 6 element cell array where each location is a 4 element numeric array. To finish it off, we call cell2mat on this output to finally convert our text into a 2D matrix and therefore:
vals = cell2mat(cellfun(#(x) cell2mat(textscan(x, '%f%f%f%f', 1)), Data{1,1,1}, 'uni', 0))
format long g allows for better display formatting so we can see both the dominant number as well as the floating point numbers neatly.

MATLAB Nesting Expression

Just a simple nesting question:
I've got a <100x100 double> matrix mat_B, with I cumsum. From the resulting matrix mat_A, I just need the last row vec_C, which I need to cumsum again. My code looks like this:
mat_A = cumsum(mat_B);
vec_C = cumsum(mat_A(end,:));
My question is, if it's possible to put all of this inside one line of code. I know that cumsum(mat_B) returns a matrix, but if I put (end, :) behind the expression, it won't work.
I know it sounds quite silly, but I'd like to know how nesting works in those kind of situations.
You could skip the first cumsum and just use sum, since the last line of cumsum is equivalent to the result of sum:
>> mat_B=rand(5);
>> cumsum(mat_B)
ans =
0.2517 0.4522 0.8838 0.3751 0.2527
0.6847 0.7778 1.3412 0.7487 0.8376
1.5270 1.1579 2.1404 1.2327 1.3613
1.7115 2.0444 2.2745 2.2021 1.5247
2.2197 2.8056 2.3398 2.5442 2.0111
>> sum(mat_B)
ans =
2.2197 2.8056 2.3398 2.5442 2.0111
Therefore
vec_C = cumsum(sum(mat_B));
should do what you want.

Setting property in array of Matlab objects

I am working with arrays of structs and objects in Matlab. I want to set properties for all the members of a certain array as fast as possible.
For the problem of setting a certain struct field, I reached a solution that involves using arrayfun and setfield. The following works like a charm:
myStru.id = 0;
myStru.name = 'blah';
arrayStru = repmat(myStru,10,1); % Array of 10 elements. All of them have id=0
arrayStru = cell2mat( arrayfun( #(x,y)setfield(x,'id',y), arrayStru, (1:10)', 'UniformOutput', false ) ); % ids ranging from 1 to 10 :D
The problem is that, for objects, this does not work. I understand that setfield is for structures, so I have tried some other alternatives. The most excruciating error pops out when I try the following:
arrayfun( #(x,y) eval(['x.id=y;']), arrayOfObjects, arrayOfValues, 'UniformOutput', false );
(The class is a very simple one, which accepts empty constructor and has a real public property called 'id'). It results in:
Error using setFieldOfStructArray>#(x,y)eval(['x.id=y;']) (line 17)
Error: The expression to the left of the equals sign is not a valid target for an
assignment.
ALTHOUGH if I put a breakpoint in that line, it seems that the expression can be executed with the expected effects.
My two (three) questions:
Why does the above solution fail? How can I get that to work?
My final goal is to set properties fast and simple in arrays of objects. Which is the best technique for this?
(Note: I can write loops, but I always feel itchy when I have to do that :P)
I think the problem may be that your propety may be readonly because setfield works also for classes.
Anyway there is some alternative, if your class inherit from hgsetget you can use set instead of setfield.
You can also use
subsasgn(x,struct('type','.','subs','id'),y)
instead of
setfield(x,'id',y)
If can use cell of values, which will be automatically interpreted as struct array
>> s = struct('a', num2cell(1:10)', 'b', 's')
s =
10x1 struct array with fields:
a
b
>> [s.a]
ans =
1 2 3 4 5 6 7 8 9 10
>> [s.b]
ans =
ssssssssss

assigning values to a field of an structure array in MATLAB

I want to replace the value of the fields in a structure array. For example, I want to replace all 1's with 3's in the following construction.
a(1).b = 1;
a(2).b = 2;
a(3).b = 1;
a([a.b] == 1).b = 3; % This doesn't work and spits out:
% "Insufficient outputs from right hand side to satisfy comma separated
% list expansion on left hand side. Missing [] are the most likely cause."
Is there an easy syntax for this? I want to avoid ugly for loops for such simple operation.
Credits go to #Slayton, but you actually can do the same thing for assigning values too, using deal:
[a([a.b]==1).b]=deal(3)
So breakdown:
[a.b]
retrieves all b fields of the array a and puts this comma-separated-list in an array.
a([a.b]==1)
uses logical indexing to index only the elements of a that satisfy the constraint. Subsequently the full command above assigns the value 3 to all elements of the resulting comma-separated-list according to this.
You can retrieve that the value of a field for each struct in an array using cell notation.
bVals = {a.b};
bVals = cell2mat( bVals );
AFAIK, you can't do the same thing for inserting values into an array of structs. You'll have to use a loop.

MATLAB concatenate combination variable length string and vector

Many near-solutions are online, but nothing exact...
I am building a data matrix vector-by-vector:
OutputMatrix(NextSubject,:)=[OutputVector]
I need to lead each row with the name of the data being processed in that loop. The name has the form:
12345.dat
So if OutputVector=[1 2 3 4] the output should look like:
12345.dat 1 2 3 4
I have tried dozens of solutions, but a few examples:
{char(Filename(i).name) OutputVector}
{strcat((Filename(i).name) OutputVector)}
[Filname(i).name OutputVector]
Any help? Please :)
You can't store a string and a vector in a matrix. However, you can do that in a cell.
So you might consider doing:
OutputCell(NextSubject,:) = { Filename(i).name OutputVector };
The curly braces denote that you are storing the object as a cell.
Often though it is better to store strings and number separately. Something like:
OutputMatrix = [];
OutputFile = {};
...
OutputMatrix(NextSubject,:) = OutputVector;
OutputFile{NextSubject} = Filename(i).name;
Then if you access or select rows from output matrix, use the same index for the cell array:
foo(OutputMatrix(index,:), OutputFile(index))