How to programatically construct a large cell array - matlab

How could I construct automatically a dataset like the one below, assuming that the number of columns of matrix summary_whts is approx. 400???
lrwghts = dataset(...
{summary_whts(:,01),'w00'},...
{summary_whts(:,02),'w01'},...
{summary_whts(:,03),'w02'},...
{summary_whts(:,04),'w03'},...
{summary_whts(:,05),'w04'},...
{summary_whts(:,06),'w05'},...
{summary_whts(:,07),'w06'},...
{summary_whts(:,08),'w07'},...
{summary_whts(:,09),'w08'},...
{summary_whts(:,10),'w09'},...
{summary_whts(:,11),'w10'},...
{summary_whts(:,12),'w11'},...
'ObsNames',summary_mthd);

Why not use a simple loop to populate dataset?
nCols = size(summary_whts,1);
dataset = cell(nCols, 2);
for i = 1:nCols
dataset{i,1} = summary_whts(:,i);
dataset{i,2} = sprintf('w%04d', i);
end
dataset{end+1,1} = 'ObsNames';
dataset(end, 2} = summary_mthd;

At last, I found it! This is what I was looking for:
cat = [];
for i = 0:(size(X,2)),
cat = [cat;sprintf('w%03d',i)];
end
cat = cellstr(cat);
lrwghts = dataset({summary_whts,cat{:}},'ObsNames',cellstr(summary_mthd));

Related

Extract fields from Structure Array to put into another Structure Array

I have a structure array with a large number of fields that I don't care about, so I want to extract the limited number of fields I DO care about and put it into a separate structure array.
For a structure array of size one, I've done this by creating the new array from scratch, for example:
structOld.a = 1;
structOld.b = 2;
structOld.usefulA = 'useful information';
structOld.usefulB = 'more useful information';
structOld.c = 3;
structOld.d = 'words';
keepFields = {'usefulA','usefulB'};
structNew = struct;
for fn = keepFields
structNew.(fn{:}) = structOld.(fn{:});
end
which gives
structNew =
usefulA: 'useful information'
usefulB: 'more useful information'
Is there a more efficient way of doing this? How can I scale up to an structure array (vector) of size N?
N = 50;
structOld(1).a = 1;
structOld(1).b = 2;
structOld(1).usefulA = 500;
structOld(1).usefulB = 'us';
structOld(1).c = 3;
structOld(1).d = 'ef';
structOld(2).a = 4;
structOld(2).b = 5;
structOld(2).usefulA = 501;
structOld(2).usefulB = 'ul';
structOld(2).c = 6;
structOld(2).d = 'in';
structOld(3).a = 7;
structOld(3).b = '8';
structOld(3).usefulA = 504;
structOld(3).usefulB = 'fo';
structOld(3).c = 9;
structOld(3).d = 'rm';
structOld(N).a = 10;
structOld(N).b = 11;
structOld(N).usefulA = 506;
structOld(N).usefulB = 'at';
structOld(N).c = 12;
structOld(N).d = 'ion';
In this case, I'd like to end up with:
structNew =
1x50 struct array with fields:
usefulA
usefulB
Keeping elements with empty usefulA/usefulB fields is fine; I can get rid of them later if needed.
Using rmfield isn't great because the number of useless fields far outnumbers the useful fields.
You can create a new struct array using existing data as follows:
structNew = struct('usefulA',{structOld.usefulA},'usefulB',{structOld.usefulB});
If you have an arbitrary set of field names that you want to preserve, you could use a loop as follows. Here, I'm first extracting the data from strcutOld into a cell array data, which contains each of the arguments the the struct call in the previous line of code. data{:} is now a comma-separated list of these arguments, the last line of code below is identical to the line above.
keepFields = {'usefulA','usefulB'};
data = cell(2,numel(keepFields));
for ii=1:numel(keepFields)
data{1,ii} = keepFields{ii};
data{2,ii} = {structOld.(keepFields{ii})};
end
structNew = struct(data{:});

How to generalize Matlab function?

I have a Matlab function. I need to generalize this function. This code’s aim is to check this IndicMPs are in the TableTemp, if it is there, then we extract relevant age limits, such as: Age_Limite_DC, Age_Limite_IT, Age_Limite_Ch and Transfert_Prime_IT_DC. My idea is to generalize, passing parameters to find out the "Type_pret" is.(May be I'm wrong) Since I'm beginner to Matlab can someone help me to encode a more generic function that can be used in a more general context?
function Structure = optimisation_function()
Data = load('Data.mat');
Structure = Data.Structure;
TableTemp = Data.TableTemp;
Age_Limite_DC = zeros(size(Structure,1),1);
Age_Limite_IT = zeros(size(Structure,1),1);
Age_Limite_CH = zeros(size(Structure,1),1);
Transfert_Prime_IT_DC = zeros(size(Structure,1),1);
for IndexMPAL = 1 : length(Structure.AnneeSouscription)
% Determine Type_Pret (Loan Type)
if ~isempty(strfind(Structure.Type_Pret{IndexMPAL},'A'))
Type_Pret = 'A';
elseif ~isempty(strfind(Structure.Type_Pret{IndexMPAL},'B'))
Type_Pret = 'B';
elseif ~isempty(strfind(Structure.Type_Pret{IndexMPAL},'C'))
Type_Pret = 'C';
elseif ~isempty(strfind(Structure.Type_Pret{IndexMPAL},'D'))
Type_Pret = 'D';
elseif ~isempty(strfind(Structure.Type_Pret{IndexMPAL},'E'))
Type_Pret = 'E';
end
MP_CP = Structure.NomCodeProduit(IndexMPAL);
MP_AnSous = Structure.AnneeSouscription(IndexMPAL);
MP_TypePret = Type_Pret;
IndicCP = strcmp(MP_CP, TableTemp.CodeProduit);
IndicAS = MP_AnSous== TableTemp.AnneeSouscription;
IndicTP = strcmp(MP_TypePret, TableTemp.TypePret);
IndicMP = IndicCP & IndicAS & IndicTP;
if ~any(IndicMP)
Msg = strcat('CodeProduct:',MP_CP{1}, ', Année Souscription:', num2str(MP_AnSous), ', Type Prêt:', MP_TypePret);
error('Error', Msg)
else
Age_Limite_DC(IndexMPAL,1) = TableTemp.Age_Limite_DC(IndicMP,1);
Age_Limite_IT(IndexMPAL,1) = TableTemp.Age_Limite_IT(IndicMP,1);
Age_Limite_CH(IndexMPAL,1) = TableTemp.Age_Limite_CH(IndicMP,1);
Transfert_Prime_IT_DC(IndexMPAL,1)=
TableTemp.Transfert_Prime_IT_DC(IndicMP,1);
end
end
Structure.Age_Limite_DC = Age_Limite_DC;
Structure.Age_Limite_IT = Age_Limite_IT;
Structure.Age_Limite_CH = Age_Limite_CH;
Structure.Transfert_Prime_IT_DC = Transfert_Prime_IT_DC;
end
The if/elseif can be simplified with a cell array:
liststr = {'A','BB','C','D','E'}; % builds a cell array, each cell contains a string
Positive_matches = strfind(liststr,Structure.Type_Pret{IndexMPAL}) % returns a list for each cell of the indices where the element was found (empty if none)
Index = find(~cellfun('isempty', Positive_matches )) % converts the previous list into a logical 0/1 array indicating whether an item was found (1) or not (0)
% if isempty(Index); continue; end % If no index is found, to avoid an error in the next instruction, skips the rest of the code.
Type_Pret = liststr(Index(1));
If the Type_Pret are the same in your list and in Structure? , you can usestrcmp`:
liststr = {'A','B','C','D','E'};
if any(strcmp(Structure.Type_Pret,liststr))
Type_Pret = Structure.Type_Pret
else
% handle error
end
You can also work directly on Structure.Age_Limite_DC without using Age_Limite_DC.

Dynamic Output Arguments in for-loop

I am fairly new to Matlab and I created this script to help me gather 4 numbers out of an excel file. This one works so far:
clear all
cd('F:/Wortpaare Analyse/Excel')
VPNumber = input('Which Participant Number?', 's');
filename = (['PAL_',VPNumber,'_Gain_Loss.xlsx']);
sheet = 1;
x1Range = 'N324';
GainBlock1 = xlsread(filename,sheet,x1Range);
x1Range = 'O324';
LossBlock1 = xlsread(filename,sheet,x1Range);
x1Range = 'AD324';
GainBlock2 = xlsread(filename,sheet,x1Range);
x1Range = 'AE324';
LossBlock2 = xlsread(filename,sheet,x1Range);
AnalyseProband = [GainBlock1, LossBlock1, GainBlock2, GainBlock2]
Now I would like to make a script that will analyze the first 20 excel files and tried this:
clear all
cd('F:/Wortpaare Analyse/Excel')
for VPNumber = 1:20 %for the 20 files
a = (['PAL_%d_Gain_Loss.xlsx']);
filename = sprintf(a, VPNumber) % specifies the name of the file
sheet = 1;
x1Range = 'N324';
(['GainBlock1_',VPNummer]) = xlsread(filename,sheet,x1Range);
....
end
The problem seems to be that I can only have one output argument. I would like to change the output argument in each loop, so it doesn't overwrite "GainBlock1" in every cycle.
In the end I would like to have these variables:
GainBlock1_1 (for the first excel sheet)
GainBlock1_2 (for the second excel sheet)
...
GainBlock1_20 (for the 20th excel sheet)
Is there a clever way to do that? I was able to write the first script fairly easily, but was unable to produce any significant progress in the second script. Any help is greatly appreciated.
Best,
Luca
This can be accomplished by either storing the data in an array or a cell. I believe an array will be sufficient for what you're trying to do. I've included a re-organized code sample below, broken out into functions to help make it easier to read and understand.
Basically, the first function handles the loop and controls where the data gets placed in the array, and the second function is your original script which accepts the VPNumber as an input.
What this should return is a 20x4 array, where the first index controls the sheet the data was pulled from and the second index controls whether it's [GainBlock1, LossBlock1, GainBlock2, LossBlock2]. i.e.- GainBlock 1 for sheet 5 would be AnalseProband(5,1), LossBlock2 for sheet 11 would be AnalyseProband(11,4).
function AnalyseProband = getAllProbands()
currentDir = pwd;
returnToOriginalDir = onCleanup(#()cd(currentDir));
cd('F:/Wortpaare Analyse/Excel')
numProbands = 20;
AnalyseProband = zeros(numProbands,4);
for n = 1:numProbands
AnalyseProband(n,:) = getBlockInfoFromXLS(n);
end
end
function AnalyseProband = getBlockInfoFromXLS(VPNumber)
a = 'PAL_%d_Gain_Loss.xlsx';
filename = sprintf(a, VPNumber); % specifies the name of the file
sheet = 1;
x1Range = 'N324';
GainBlock1 = xlsread(filename,sheet,x1Range);
x1Range = 'O324';
LossBlock1 = xlsread(filename,sheet,x1Range);
x1Range = 'AD324';
GainBlock2 = xlsread(filename,sheet,x1Range);
x1Range = 'AE324';
LossBlock2 = xlsread(filename,sheet,x1Range);
AnalyseProband = [GainBlock1, LossBlock1, GainBlock2, LossBlock2];
end

Looking for a more efficient way of writing my MATLAB code

I have written in MATLAB the following
for i = 1:3
alpha11(i) = b+a.*randn(1,1);
alpha22(i) = b+a.*randn(1,1);
alpha12(i) = b+a.*randn(1,1);
alpha21(i) = b+a.*randn(1,1);
AoD11(i) = randi([-180/6 +180/6],1,1);
AoA11(i) = randi([-180/6 +180/6 ],1,1);
AoD22(i) = randi([-180/6 +180/6],1,1);
AoA22(i) = randi([-180/6 +180/6 ],1,1);
AoD21(i) = randi([-180 +180],1,1);
AoA21(i) = randi([-180 +180 ],1,1);
AoD12(i) = randi([-180 +180],1,1);
AoA12(i) = randi([-180 +180 ],1,1);
ctet11(i)= ((2*pi)/lambda)*d*sin(AoD11(i));
ctet22(i)= ((2*pi)/lambda)*d*sin(AoD22(i));
ctet12(i)= ((2*pi)/lambda)*d*sin(AoD12(i));
ctet21(i)= ((2*pi)/lambda)*d*sin(AoD21(i));
f_t11_ula{i}=transpose((1/sqrt(M))*[ 1 exp(j*ctet11(i)) exp(j*2*ctet11(i)) exp(j*3*ctet11(i)) ]);
f_t22_ula{i}=transpose((1/sqrt(M))*[ 1 exp(j*ctet22(i)) exp(j*2*ctet22(i)) exp(j*3*ctet22(i)) ]);
f_t12_ula{i}=transpose((1/sqrt(M))*[ 1 exp(j*ctet12(i)) exp(j*2*ctet12(i)) exp(j*3*ctet12(i)) ]);
f_t21_ula{i}=transpose((1/sqrt(M))*[ 1 exp(j*ctet21(i)) exp(j*2*ctet21(i)) exp(j*3*ctet21(i)) ]);
cter11(i)= ((2*pi)/lambda)*d*sin(AoA11(i));
cter22(i)= ((2*pi)/lambda)*d*sin(AoA22(i));
cter12(i)= ((2*pi)/lambda)*d*sin(AoA12(i));
cter21(i)= ((2*pi)/lambda)*d*sin(AoA21(i));
f_r11_ula{i}=transpose((1/sqrt(O))*[ 1 exp(j*cter11(i)) exp(j*2*cter11(i)) exp(j*3*cter11(i)) ]);
f_r22_ula{i}=transpose((1/sqrt(O))*[ 1 exp(j*cter22(i)) exp(j*2*cter22(i)) exp(j*3*cter22(i))]);
f_r12_ula{i}=transpose((1/sqrt(O))*[ 1 exp(j*cter12(i)) exp(j*2*cter12(i)) exp(j*3*cter12(i)) ]);
f_r21_ula{i}=transpose((1/sqrt(O))*[ 1 exp(j*cter21(i)) exp(j*2*cter21(i)) exp(j*3*cter21(i))]);
channel11{i}= alpha11(i) * f_r11_ula{i}* conj(transpose(f_t11_ula{i})) ;
channel22{i}= alpha22(i) * f_r22_ula{i}* conj(transpose(f_t22_ula{i})) ;
channel12{i}= alpha12(i) * f_r12_ula{i}* conj(transpose(f_t12_ula{i})) ;
channel21{i}= alpha21(i) * f_r21_ula{i}* conj(transpose(f_t21_ula{i})) ;
end
I am writing this question here to ask how I can compress this code, as you can see its not very nicely written and I have basically many repetitions. I don't know how to write them in few commands. Every command is repeated four times and indexed by 11, 12, 21, 22..
P.S If someone wants to run the code the following variables are needed
a = 1;
b = 0;
M=4;
O = 4;
lambda=0.15;
d=lambda/2;
Looking forward for suggestions.
As #David mentioned, it can be done using 3D arrays. This removes much of the code repetition.
Here is an example of how it could be done:
sz=[2,2,3];
alpha=b+a.*randn(sz);
AoD = randi([-180/6 +180/6],sz);
AoA = randi([-180 +180],sz);
mask = logical(repmat(eye(2),1,1,3));
[AoA(mask), AoD(~mask)] = deal(AoD(~mask),AoA(mask));
ctet = 2*pi/lambda * d * sin(AoD);
f_t = reshape(arrayfun(#(x) exp(1j*(0:3)'*ctet(x))/sqrt(M),1:12,'UniformOutput',0),sz);
cter = (2*pi)/lambda*d*sin(AoA);
f_r = reshape(arrayfun(#(x) exp(1j*(0:3)'*cter(x))/sqrt(O),1:12,'UniformOutput',0),sz);
channel = reshape(arrayfun(#(x) alpha(x) * f_r{x} * conj(transpose(f_t{x})), 1:12, 'UniformOutput',0),sz);
Note that for each of the variables mentioned in the question, the same values can be accessed using the corresponding 3D index. For example, using the code above, the value that was previously in AoA11(1) is now in AoA(1,1,1). Similarly, the matrix that was stored in channel11{1} is now in channel{1,1,1}.
Hope this helps.

rearrange/sort matrices using custom function

How can I sort/rearrange a matrix with a custom function?
I.e. if I have data like this:
data = rand(5)
data =
0.9705954 0.9280535 0.1763516 0.0024225 0.8622087
0.4187847 0.3452783 0.7068682 0.9295234 0.0906835
0.3371114 0.4808020 0.8806709 0.9573226 0.5422291
0.6477601 0.5711606 0.1111401 0.3909264 0.3565683
0.0086004 0.8695550 0.7431095 0.8492812 0.6675760
I want to sort/rearrange the matrix columns by the value of the last row divided by the value of the first row. I can't figure out how to do that in a nice way. The only approach I found so far is this ugly hack:
tmp = [data(end,:) ./ data(1,:); data]
tmp =
8.8610e-03 9.3697e-01 4.2138e+00 3.5059e+02 7.7426e-01
9.7060e-01 9.2805e-01 1.7635e-01 2.4225e-03 8.6221e-01
4.1878e-01 3.4528e-01 7.0687e-01 9.2952e-01 9.0683e-02
3.3711e-01 4.8080e-01 8.8067e-01 9.5732e-01 5.4223e-01
6.4776e-01 5.7116e-01 1.1114e-01 3.9093e-01 3.5657e-01
8.6004e-03 8.6955e-01 7.4311e-01 8.4928e-01 6.6758e-01
tmp = sortrows(tmp')'
tmp =
8.8610e-03 7.7426e-01 9.3697e-01 4.2138e+00 3.5059e+02
9.7060e-01 8.6221e-01 9.2805e-01 1.7635e-01 2.4225e-03
4.1878e-01 9.0683e-02 3.4528e-01 7.0687e-01 9.2952e-01
3.3711e-01 5.4223e-01 4.8080e-01 8.8067e-01 9.5732e-01
6.4776e-01 3.5657e-01 5.7116e-01 1.1114e-01 3.9093e-01
8.6004e-03 6.6758e-01 8.6955e-01 7.4311e-01 8.4928e-01
data = tmp(2:end,:)
data =
0.9705954 0.8622087 0.9280535 0.1763516 0.0024225
0.4187847 0.0906835 0.3452783 0.7068682 0.9295234
0.3371114 0.5422291 0.4808020 0.8806709 0.9573226
0.6477601 0.3565683 0.5711606 0.1111401 0.3909264
0.0086004 0.6675760 0.8695550 0.7431095 0.8492812
For large matrices, this is pretty slow :(
Use second output of sort to get the column indices:
[~, ind] = sort(data(end,:)./data(1,:))
result = data(:,ind);