How to extract field based on field value - matlab

%Input structure
s.a=[1; 2; 1; 3; 4; 1; 2]
s.b=[4; 9; 7; 1; 0; 3; 8]
% output required
s.a=[1; 1; 1]
s.b=[4; 7; 3]
The actual structure contains many fields of long size. How to extract corresponding field values, when condition is put for field 'a' (when a==1).

Try this and adapt to the other fields:
s.b(s.a==1)
To do it for all fields in s except a and collect the results in a struct t you can use a loop:
t = struct()
fn = fieldnames(s);
for k=1:numel(fn)
t.(fn{k}) = s.(fn{k})(s.a==1);
end

Related

Extract column data of table in a struct array

Summary / TLDR
How do I extract all rows of one table-column if the table is in a struct array and I want to combine alle struct-array-elements to one big matrix?
First Approch
I have a table (Values) with multiple columns and rows stored in a struct. Multiple of these structs are stored in an array (Data):
Data(1).Values = table([1; 2; 3; 4; 8], 'VariableNames', {'Rk'});
Data(2).Values = table([3; 6; 10; 8], 'VariableNames', {'Rk'});
Data(3).Values = table([2; 10; 11; 7], 'VariableNames', {'Rk'});
There are many more variables in the struct and also in the table, so it is just a simplified example. As you can see the height of the table can vary. I want to plot the columns Rk in a boxplot. So i need to create a matrix like this:
matrix = [1 1; 2 1; 3 1; 4 1; 8 1; 3 2; 6 2; 10 2; 8 2; 2 3; 10 3; 11 3; 7 3];
I use the following code to create the matrix:
matrix = zeros(0, 2);
for i=1:length(Data)
l = height(Data(1, i).Values(:, 'Rk'));
e = length(matrix) + 1;
% Reshape the data into an array
matrix((end+1):(end+l), 1) = table2array(Data(1, i).Values(:, 'Rk'));
% Creating the index of each data-row
matrix(e:end, 2) = i;
end
% Plot the boxplot
boxplot(matrix(:, 1), matrix(:, 2))
I really don't like this for-loop-version, especially because it becomes slow for big Data-Arrays and also because I don't know the size of matrix, so I can't reserve the space. Theoretically I could run through the whole data-array, counting the elements, initalizing the matrix-variable and then fill it.
Is there a more elegant version without a for-loop?
Second approach
I already tried another solution by changing the structure of the struct. Semantically, this really makes no sense, but this way I found a more elegant solution, creating the matrix-Variable without the problems of the first solution:
% Creating Data
Data(1).Values.Rk = [1; 2; 3; 4; 8];
Data(2).Values.Rk = [3; 6; 10; 8];
Data(3).Values.Rk = [2; 10; 11; 7];
% Reshape the data into an array
a = {cell2mat({Data.Values}).Rk};
b = vertcat(a{:});
% Creating the index of each data (b)-row
c = cumsum(cellfun('length', a(1, :)));
d = meshgrid(1:c(end), 1:length(c));
e = d>c';
f = sum(e);
% Plot the boxplot
boxplot(b, f);
Questions
I would apreciate a solution combining both approaches (having a table, no for-loop, no need of matrix-size-calculation) but:
I don't know how to extract the data of the table in a struct in an array.
I am asking myself if there is a more elegant solution creating the boxplot-indexes.
Whole code
%% Boxplot of Table
clear
% Creating Data
Data(1).Values = table([1; 2; 3; 4; 8], 'VariableNames', {'Rk'});
Data(2).Values = table([3; 6; 10; 8], 'VariableNames', {'Rk'});
Data(3).Values = table([2; 10; 11; 7], 'VariableNames', {'Rk'});
matrix = zeros(0, 2);
for i=1:length(Data)
l = height(Data(1, i).Values(:, 'Rk'));
e = length(matrix) + 1;
% Reshape the data into an array
matrix((end+1):(end+l), 1) = table2array(Data(1, i).Values(:, 'Rk'));
% Creating the index of each data
matrix(e:end, 2) = i;
end
boxplot(matrix(:, 1), matrix(:, 2));
%% Boxplot of Arrays
clear
% Creating Data
Data(1).Values.Rk = [1; 2; 3; 4; 8];
Data(2).Values.Rk = [3; 6; 10; 8];
Data(3).Values.Rk = [2; 10; 11; 7];
% Reshape the data into an array
a = {cell2mat({Data.Values}).Rk};
b = vertcat(a{:});
% Creating the index of each data (b)-row
c = cumsum(cellfun('length', a(1, :)));
d = meshgrid(1:c(end), 1:length(c));
e = d>c';
f = sum(e);
% Plot the boxplot
boxplot(b, f);
I have a partial answer if you are allowed to change the dimensions on your input table. If you transpose your input values, such that you have a row vector
Data(1).Values.Rk = [1; 2; 3; 4; 8]';
...
You can use the following commands to concatenate all the elements:
tmp = [Data.Values];
allvals = [tmp.Rk]'; %output is a column vector of your aggregated values
If you eliminate the Rk field (if it is not informative), you don't need the first step and can do the operation in one step.
If you aggregate the values into a column vector in this manner you now know the dimensions of the second column and can initialize that column before executing a for loop to populate the second column with a monotonically increasing index for each element of data.
I cannot think of a way to get the total number of elements and corresponding value for your box plot in the second column (without a for loop) unless you have the flexibility to add another field to your data structure (e.g. Data(1).nVals = 5, etc.), in which case you can get the total number of elements via sum([Data.nVals])

Combine 2 matrices

There is:
a = [1;2;3;4;5;6;7;8;9;10]; %(10x1 double)
b = [1;3;4;5;6;9]; %(6x1 double)
I hope to combine a and b. So my expected result is:
I think may be use conditional or first import zeros(10 2)? Could you help me?
Method 1: Conditional Checking
Checks if the values in the arrays match before filling the combined array. If they match both columns are filled. If they do not match the "NaN" undefined term is placed in the array. The variable Index that controls the scanning for array b is only incremented upon finding a match between both arrays.
%Method 1: Conditional%
a = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10];
b = [1; 3; 4; 5; 6; 9];
%Creating a result array of the same length as array "a"%
Combined_Array = zeros(length(a),2);
%Copying the first column into the result array "Combined_Array"%
for Row = 1: +1: length(a)
Combined_Array(Row,1) = a(Row,1);
end
%Padding the array "b" with zeroes to match the length of array "a"%
b = [b; zeros(length(a) - length(b),1)];
Index = 1;
for Row = 1: +1: length(a)
%If the values in arrays "a" and "b" do not match%
if a(Row,1) ~= b(Index,1)
Combined_Array(Row,2) = "NaN";
Index = Index - 1;
end
%If the values in arrays "a" and "b" match%
if a(Row,1) == b(Index,1)
Combined_Array(Row,2) = b(Index,1);
end
Index = Index + 1;
end
%Printing the result%
Combined_Array
Method 2: Concatenation and Filling Arrays
Fill in the arrays where the undefined term "NaN" is expected and concatenate the rest of the content accordingly. Using horzcat will concatenate columns together side by side. (vertcat concantates rows on top of one another)
%Method 2: Hard-Coded and Concatenation%
a = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10];
b = [1; 3; 4; 5; 6; 9];
b = [b(1); "NaN"; b(2:5);"NaN"; "NaN"; b(6); "NaN"];
Combined_Array = horzcat(a,b);

Fill structure more efficient

How can a structure i.e. 'settings' be filled more easily than with this code:
settings(1).exposure = 1;
settings(1).rebalancing = 0;
settings(2).exposure = 0;
settings(2).rebalancing = 0;
settings(3).exposure = 1;
settings(3).rebalancing = 1;
settings(4).exposure = 0;
settings(4).rebalancing = 1;
settings(5).exposure = 'benchmark';
settings(5).rebalancing = 0;
settings(6).exposure = 'benchmark';
settings(6).rebalancing = 1;
You can compress it using the struct function:
>> s = struct('exposure',{1,0,1,0,'benchmark','benchmark'},'rebalancing',{0,0,1,1,0,1});
>> s(6)
ans =
exposure: 'benchmark'
rebalancing: 1
The array literals can be replaced by any variable that contains your data, as long as all arrays are conforming in size.
you can create an array / matrix with [ 1 2 3 4 5 6]
then in a for loop, for each number ask an input
i=1:6;
for i:6;
settings(i).exposure=input(...);
settings(i).rebalancing=input(...);
end
I think you should be able with this. (its been sometime since I last used a computer with MatLab so I can't confirm)

Create group identifier from two vectors in matlab

I would like to create a unique group identifier vector (G) based on the values in two column vectors (A and B).
A = [1; 1; 1; 2; 2; 1; 1; 2; 2]
B = [1; 1; 2; 1; 2; 1; 1; 1; 2]
I would like G to look like this:
G = [1; 1; 2; 3; 4; 1; 1; 3; 4]
This is probably something simple, but I just can't seem to find the command(s) to do this.
Simple indeed. You need to use unique(...'rows') on vertically stacked input vectors and the third output from it would be your desired output, like so -
[~,~,G] = unique([A(:) B(:)],'rows')

removing missing elements from a vector

I have a vector B=[1; 2; 1; 2; 3; 5; 6; 8; 9; 10]
where the elements a=[4 7] are missing.
I would like to map B to a "continuous" vector like
B_map=[1; 2; 1; 2; 3; 4; 5; 6; 7; 8]
removing the "missing" elements (4 7) and "scaling" the rest accordingly..
my problem is that depending on the number of missing elemenst (in this case 2) I have to scale the vector B of different amounts...
I think I figured it out...
a = sort(a);
B_map = B;
for i = 1:numel(a)
clear id_sub
id_sub = find(B >= a(i));
B_map(id_sub) = B_map(id_sub)-1;
end