assigning values to a field of an structure array in MATLAB - 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.

Related

length of a just one array dimension in minizinc

If I define my array like this:
array[_, 1..2] of J: A;
where J is a enum, what function can I use to get the length of the first dimension of A? The length function returns 14 (and states that it doesn't work for multiple dimensions). I had it hard-coded like so in my testing, but it won't always be 13:
constraint forall(row in 1..13)(S[A[row, 2]] >= C[A[row, 1]]);
The length function is really only meant for getting the number of elements. When you want the retrieve indexes of an array, you should instead use the index_set function. This returns the actual index set, which you can iterate over: i in index_set(A)
For multidimensional arrays there are specific functions for each index set of the array. For example, to get the first index set from a two-dimensional array (as in your code fragment) you can use the function index_set_1of2

MATLAB: removing non-unique numbers from a field within a structure

I have a structure in Matlab, each field contains elements with varying numbers of variables. I would like to remove duplicates of numbers that appear within the same field: I know the unique() function and know how to use it to scan through fields one at a time but not an entire field.
I think I want something like:
structure(1:length(structure)).field=unique(structure(1:length(structure)).field
and get
original
field=[1,2,3] [1,4,5] [2,5,8]
to turn into
field=[1,2,3] [4,5] [8]
Maybe a complicated for loop similar to below (isn't working) which would grab the values from the first element in the field, search through each additional element, and if that value is present set it equal to =[];, and iterate through that way?
for n=1:length(RESULTS)
for m=1:length(RESULTS(n).Volumes)
for l=1:length(RESULTS)
for o=1:length(RESULTS(l).Volumes)
if RESULTS(n).Volumes(m)==RESULTS(l).Volumes(o)
RESULTS(l).Volumes(o)=[];
end
end
end
end
end
Thanks!
This is a quick-and-dirty attempt, but you might be able to improve on it. Assume your struct array and field are sa(:).v. I'm also assuming that the field contains a 1xn array of numbers, as in your example. First, make a "joint" array with the concatenation of all field values and filter the non-unique values:
joint = cell2mat({sa.v});
[uniqJoint,~,backIdx] = unique(joint);
The "uniqJoint" array has also been sorted, but the "backIdx" array contains the indices that, if applied to uniqJoint, will rebuild the original "joint" array. We need to somehow connect those to the original indices (i,j) into the struct array and within the field value sa(i).v(j). To do that, I tried creating an array of the same size as "joint" that contains the indices of the struct that originally had the corresponding element in the "joint" array:
saIdx = cell2mat(arrayfun(#(i) i * ones(1,length(sa(i).v)), ...
1:length(sa), 'UniformOutput', false));
Then you can edit (or, in my case, copy and modify the copy of) the struct array to set the field to the values that have not appeared before. To do that, I keep a logical array to mark the indices of "backIdx" as "already used", in which case I skip those values when rebuilding the fields of each struct:
sb = sa;
used = false(length(backIdx));
for i = 1:length(sa)
origInd = find(saIdx == i); % Which indices into backIdx correspond to this struct?
newInd = []; % Which indices will be used?
for curI = backIdx(origInd)
if ~used(curI)
% Mark as used and add to the "to copy" list
used(curI) = true;
newInd(end+1) = curI;
end
end
% Rewrite the field with only the indices that were not used before
sb(i).v = uniqJoint(newInd);
end
In the end, the data in sb(i).v contains the same numbers as sa(i).v without repeats, and removing those that appeared in any previous elements of the struct.

Correct way of adding to a list from cell array in matlab?

I'm writing some code to split up a 180x2 matlab cell array based on a string in the second column. This string is one of EP,GA,PS,SS or SA. In python I could define empty lists then use conditionals to iterate over the elements of the list and append them to the relevant lists.
The Code
EP=[];
GA=[];
PS=[];
SA=[];
SS=[];
for i=1:size(d),
if strcmp(d(i,2),'EP'),
append(EP,d(i,1))
elseif strcmp(d(i,2),'GA'),
append(GA,i)
elseif strcmp(d(i,2),'PS'),
append(PS,i)
elseif strcmp(d(i,2),'SA'),
append(SA,i)
elseif strcmp(d(i,2),'SS'),
append(SS,i)
end
end
Note that 'd' is a 180x2 cell array that I copied and pasted into matlab rather than import. The general structure of the data however is:
12.9089000000000 'EP'
13.3697000000000 'SA'
13.4335000000000 'EP'
13.5302000000000 'PS'
13.8434000000000 'EP'
14.2583000000000 'EP'
14.8221000000000 'GA'
However when attempting this tactic in matlab I get an error:
Error using append (line 38)
Wrong number of input arguments for obsolete
matrix-based syntax.
Error in Boxplot_All_results (line 12)
append(GA,i)
Could somebody tell me the correct way of doing this in matlab
First of all, please not that d is a cell array. To index the elements of a cell array, use {}. If you index using () like you did, you end up with a small cell array containing only the indexed element(s).
To append in Matlab you basically have two choices:
%concatenate the list with a scalar. Also suitable for two lists.
EP=[EP,d{i,1}] %could also be done using cat
%append to the end
EP(end+1)=d{i,1}
While this fixes the problem, I recommend to implement it on a more generic way:
names={'EP','GA','PS','SA','SS'}
s=struct()
for idx=1:numel(names)
s.(names{idx})=[d{strcmpi(d(:,2),names{idx}),1}]
end
You end up with a struct containing the data you want.
Can you show MatLab code?
How do you append those values to cell array?
You can just create one a = {}
and then append element at the size+1 index. That operation extends your cell array.
Or you can also do a trick like that: a = [a; {value}]
To append elements to a cell array, a correct syntax would be:
for i=1:size(d),
if strcmp(d(i,2),'EP'),
EP = [EP ; d(i,1) ] ; %// append(EP,d(i,1))
elseif strcmp(d(i,2),'GA'),
GA = [GA ; d(i,1) ] ; %// append(GA,i)
but there are more ways, as you can read in the documentation: Add Cells to a Cell Array
There are also more ways to build your final extracted arrays in one assignment instead of having them growing dynamically (mLint will complain about that by the way).
Get the indices of the elements which satisfy your condition then create an array with only the matching elements. For example:
iEP = cellfun( #(c) strcmp(c,'EP') , d(:,2) ) ; %// logical array of indexes where the condition is true
EP = d(iEP,1) ; %// Create "EP" in one assignment - EP is a [cell] array
If you only have numeric values to retrieve in these new variables, may be having a double array instead of a cell array will be convenient :
iGA = cellfun( #(c) strcmp(c,'GA') , d(:,2) ) ; %// logical array of indexes where the condition is true
GA = cell2mat( d(iGA,1) ) ; %// Create "GA" in one assignment - GA is a [double] array
Of course you can bypass the intermediate variable holding the indices:
PS = d( cellfun(#(c)strcmp(c,'PS'),d(:,2)),1) ; %// Create "PS" in one assignment [cell] array
SA = cell2mat(d(cellfun(#(c)strcmp(c,'SA'),d(:,2)),1)) ; %// Create "SA" in one assignment [double] array

Give value, return field name in matlab structure

I have a Matlab structure like this:
Columns.T21=6;
Columns.ws21=9;
Columns.wd21=10;
Columns.u21=11;
Is there some elegant way I can give the value and return the field name? For instance, if I give 6 and it would return 'T21.' I know that fieldnames() will return all the field names, but I want the fieldname for a specific value. Many thanks!
Assuming that the structure contains fields with scalar numeric values, you can use this struct2array based approach -
search_num = 6; %// Edit this for a different search number
fns=fieldnames(Columns) %// Get field names
out = fns(struct2array(Columns)==search_num) %// Logically index into names to find
%// the one that matches our search
Goal:
Construct two vectors from your struct, one for the names of fields and the other for their respective values. This has analogy to the dict in Python or map in C++, where you have unique keys being mapped to possibly non-unique values.
Simple Solution:
You can do this very simply using the various functions defined for struct in Matlab, namely: struc2cell() and cell2mat()
For the particular element of interest, say 1 of your struct Columns, get the names of all fields in the form of a cell array, using fieldnames() function:
fields = fieldnames( Columns(1) )
Similarly, get the values of all the fields of that element of Columns, in the form of a matrix
vals = cell2mat( struct2cell( Columns(1) ) )
Next, find the field with the corresponding value, say 6 here, using the find function and convert the resulting 1x1 cell into a char using cell2mat() function :
cell2mat( fields( find( vals == 6 ) ) )
which will yield:
T21
Now, you can define a function that does this for you, e.g.:
function fieldname = getFieldForValue( myStruct, value)
Advanced Solution using Map Container Data Abstraction:
You can also choose to define an object of the containers.map class using the field-names of your struct as the keySet and values as valueSet.
myMap = containers.Map( fieldnames( Columns(1) ), struct2cell( Columns(1) ) );
This allows you to get keys and values using corresponding built-in functions:
myMapKeys = keys(myMap);
myMapValues = values(myMap);
Now, you can find all the keys corresponding to a particular value, say 6 in this case:
cell2mat( myMapKeys( find( myMapValues == 6) )' )
which again yields:
T21
Caution: This method, or for that matter all methods for doing so, will only work if all the fields have the values of the same type, because the matrix to which we are converting vals to, need to have a uniform type for all its elements. But I assume from your example that this would always be the case.
Customized function/ logic:
struct consists of elements that contain fields which have values, all in that order. An element is thus a key for which field is a value. The essence of "lookup" is to find values (which are non-unique) for specific keys (which are unique). Thus, Matlab has a built-in way of doing so. But what you want is the other way around, i.e. to find keys for specific values. Since its not a typical use case, you need to write up your own logic or function for it.
Suppose your structure is called S. First extract all the field names into an array:
fNames=fieldnames(S);
Now define a following anonymous function in your code:
myfun=#(yourArray,desiredValue) yourArray==desiredValue;
Then you can get the desired field name as:
desiredFieldIndex=myfun(structfun(#(x) x,S),3) %desired value is 3 (say)
desiredFieldName=fNames(desiredFieldIndex)
Alternative using containers.Map
Assuming each field in the structure contains one scalar value as in the question (not an array).
Aim is to create a Map object with the field values as keys and the field names as values
myMap = containers.Map(struct2cell(Columns),fieldnames(Columns))
Now to get the fieldname for a value index into myMap with the value
myMap(6)
ans =
T21
This has the advantage that if the structure doesn't change you can repeatedly use myMap to find other value-field name pairs

Simultaneously assign values to multiple structure fields

I have a matlab structure that follows the following pattern:
S.field1.data1
...
.field1.dataN
...
.fieldM.data1
...
.fieldM.dataN
I would like to assign values to one data field (say, data3) from all fields simultaneously. That would be semantically similar to:
S.*.data3 = value
Where the wildcard "*" represents all fields (field1,...,fieldM) in the structure. Is this something that can be done without a loop in matlab?
Since field1 .. fieldM are structure arrays with identical fields, why not make a struct array for "field"? Then you can easily set all "data" members to a specific value using deal.
field(1).data1 = 1;
field(1).data2 = 2;
field(2).data1 = 3;
field(2).data2 = 4;
[field.data1] = deal(5);
disp([field.data1]);
A loop-based solution can be flexible and easily readable:
names = strtrim(cellstr( num2str((1:5)','field%d') )); %'# field1,field2,...
values = num2cell(1:5); %# any values you want
S = struct();
for i=1:numel(names)
S.(names{i}).data3 = values{i};
end
In simple cases, you could do that by converting your struct into a cell array using struct2cell(). As you have a nested structure, I don't think that will work here.
On the other side, is there any reason why your data is structured like this. Your description gives the impression that a simple MxN array or cell array would be more suitable.