Matlab creating a design of experiment list - matlab

I have a range of conditions I want to run through a Matlab code,
Var1 = {'A1', 'B1'};
Var2 = {'A2', 'B2', 'C2', 'D2'};
Var3 = {2.5, 3, 3.5, 4, 4.5};
Var4 = {2E-6, 5E-6, 10E-6, 0.25E-3, 0.5E-3, 1E-3, 2E-3};
Var5 = {5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 200};
Is there an easy way I can create a matrix (or cell array) that has all the 2*4*5*7*16 combinations?

The result needs to be a cell array, because a matrix can't contain numbers and strings simultaneously.
This question and its answers achieve what you want but with vector inputs (as opposed to cell array inputs). So you can
Transform each of your input variables into a numeric vector, to be used as index;
Generate all combinations;
Convert back by indexing.
Code:
%// Step 1:
vectors = {1:numel(Var1), 1:numel(Var2), 1:numel(Var3), 1:numel(Var4), 1:numel(Var5)};
%// Step 2:
n = numel(vectors);
combs = cell(1,n);
[combs{end:-1:1}] = ndgrid(vectors{end:-1:1});
combs = cat(n+1, combs{:});
combs = reshape(combs,[],n);
%// Step 3:
result = [Var1(combs(:,1)).' Var2(combs(:,2)).' Var3(combs(:,3)).' ...
Var4(combs(:,4)).' Var5(combs(:,5)).'];
In your example, this produces the following 4480x5 cell array:
result =
'A1' 'A2' [2.5000] [2.0000e-006] [ 5]
'A1' 'A2' [2.5000] [2.0000e-006] [ 10]
'A1' 'A2' [2.5000] [2.0000e-006] [ 15]
...
'B1' 'D2' [4.5000] [ 0.0020] [150]
'B1' 'D2' [4.5000] [ 0.0020] [200]

Related

A method to vectorise a call to prod() for lots of arrays of varying length?

So my problem is, I'd like to do this without the for loop. Geting the prod() of multiple vectors but of different lengths.
I am dealing with rays intersecting voxels. I typically have 1e6 rays and 1e5 voxels, but this can vary.
intxRays is a list of rays that have intersected voxels.
gainList is a one dimensional vector, each element has a value that corresponds to a specific ray voxel intersection calculated previously (actually with the help of you lovely lot here).
rayIntxStart and rayIntxEnd are vectors of indices for, where in the gainlist array, each ray's corresponding values start and end (they're all in order).
Here is the code and some examples and expected outputs.
gainSum = zeros(1, 5);
% only interested in the intx uniques
intxSegCtr = 1;
% loop through all of the unique segments
for rayCtr = 1:max(intxRays)
if rayCtr == intxRays(intxSegCtr)
startInd = rayIntxStart(intxSegCtr);
endInd = rayIntxEnd(intxSegCtr);
% find which rows correspoond to those segements
gainVals = gainList(startInd:endInd);
gainProd = prod(gainVals);
% get the product of the gains for those voxels
gainSumIdx = intxRays(intxSegCtr);
gainSum(gainSumIdx) = gainProd;
% increment counter
intxSegCtr = intxSegCtr + 1;
end
end
Example data for five rays and nine voxels. Assume the voxel gain array looked like this (for simplicity) for nine voxels (used in previous step).
voxelGains = 10:10:90;
Now say rays 1 and 3 don't hit anything, ray 2 hits voxels 1 and 2, ray 4 hits voxels 2:7
and ray 5 hits voxels 6:9
intxRays = [2, 4, 5];
gainList = [10, 20, 20, 30, 40, 50, 60, 70, 60 70, 80, 90];
rayIntxStart = [1, 3, 9];
rayIntxEnd = [2, 8, 12];
For these numbers the above code would give as a result:
gainSum = [0, 200, 0, 5.0400e+09, 3.024e+07];
I hope this all makes sense.
When I developed it I was using far smaller ray and voxel numbers and it worked fine. As I'm moving up though, the major bottleneck in my code is this loop. Actually just the gainVals and gainProd assignment is like 80% and 15% of my runtime on their own.
This is the only method I can find that works, padding and the like won't work due to the sizes involved.
Is there a way to get the value I want, without this loop?
Many thanks!
ok this is a very small performance boost, but it might help. for testing the matrix way without the loop a bigger data sample is needed.
These are 3 soultions, your original, an optimized and the optimized way as a oneliner. could you please try if this is already doing something for you?
clear all
% orignial loop through all Rays
intxRays = [2, 4, 5];
gainList = [10, 20, 20, 30, 40, 50, 60, 70, 60 70, 80, 90];
rayIntxStart = [1, 3, 9];
rayIntxEnd = [2, 8, 12];
gainSum = zeros(1, 5);
tic
% only interested in the intx uniques
intxSegCtr = 1;
% loop through all of the unique segments
for rayCtr = 1:max(intxRays)
if rayCtr == intxRays(intxSegCtr)
startInd = rayIntxStart(intxSegCtr);
endInd = rayIntxEnd(intxSegCtr);
% find which rows correspoond to those segements
gainVals = gainList(startInd:endInd);
gainProd = prod(gainVals);
% get the product of the gains for those voxels
gainSumIdx = intxRays(intxSegCtr);
gainSum(gainSumIdx) = gainProd;
% increment counter
intxSegCtr = intxSegCtr + 1;
end
end
toc
clear all
%loop insted of every single one to max just through the intxRays
intxRays = [2, 4, 5];
gainList = [10, 20, 20, 30, 40, 50, 60, 70, 60 70, 80, 90];
rayIntxStart = [1, 3, 9];
rayIntxEnd = [2, 8, 12];
gainSum = zeros(1, 5);
tic
for rayCtr=1:length(intxRays)
%no if as you just go through them
%intxRays(rayCtr) is the corresponding element
startInd = rayIntxStart(rayCtr);
endInd = rayIntxEnd(rayCtr);
% find which rows correspoond to those segements
gainVals = gainList(startInd:endInd);
gainProd = prod(gainVals);
% get the product of the gains for those voxels and set them to the ray
gainSum(intxRays(rayCtr)) = gainProd;
end
%disp(gainSum);
toc
clear all
%same as above, but down to 1 line so no additional values are generated
intxRays = [2, 4, 5];
gainList = [10, 20, 20, 30, 40, 50, 60, 70, 60 70, 80, 90];
rayIntxStart = [1, 3, 9];
rayIntxEnd = [2, 8, 12];
gainSum = zeros(1, 5);
tic
for rayCtr=1:length(intxRays)
gainSum(intxRays(rayCtr))=prod(gainList(rayIntxStart(rayCtr):rayIntxEnd(rayCtr)));
end
toc

Derive Matlab value matrix from Matlab key matrix and lookup vector

I have a Matlab object of integer keys in the range 1:1:7 e.g.
[3, 1, 4, 5, 6]
I also have a size 7 vector containing an associated value for each integer key, e.g.
vals = (10, 20, 30, 4000, 50, 60, 70)
what is the most efficient way to create a matrix of the values using the keys as indices, e.g. a matrix
[30, 10, 4000, 50, 60]
(in reality the key object is 6D). Must I loop?
For the case of a 1D matrix a general approach could be:
keys=[3, 1, 4, 5, 6];
vals = [10, 20, 30, 4000, 50, 60, 70]
m=vals(keys)
With this approach you use the values stored in the keys array as indices of the vals array. You can find more information about array insdexing here.
In a more general case in which keys has n rows (3 in the following example):
keys=[3, 1, 4, 5, 6;
1 3 2 4 6 ;
7 6 5 4 3];
vals = [10, 20, 30, 4000, 50, 60, 70]
m=reshape(vals(keys(:)),size(keys))
Hope this helps.
Qapla'
I think this should work. If I got the question.
inds = [3, 1, 4, 5, 6];
vals = inds;
vals(vals==1) = 10;
vals(vals==2) = 20;
vals(vals==3) = 30;
vals(vals==4) = 4000;
vals(vals==5) = 50;
vals(vals==6) = 60;
Is it like that?

Constructing structure for wind data

I have been trying to construct a structure for wind data in matlab using the struct function:
struct(fieldname1,value1,fieldname2,value2,......).
I have wind speed and direction at different heights such as 40,50,80,90 meters. The problem is I don't understand how to represent the 'value', for my big data.
wind_data=struct(ws40,[],ws50,[],ws80,[],ws90,[],wd40,[],wd50,[],wd80,[],wd90,[])
ws=wind speed. wd=wind direction and each of them are vectors.
You can allocate structs by hand:
wind_data.ws40 = [1, 2, 3];
wind_data.wd40 = [4, 5, 6];
wind_data.ws50 = [11, 22, 33];
wind_data.wd50 = [44, 55, 66];
or dynamically:
heights = [40, 50, 80, 90];
ws = round(10*rand(4,3));
wd = round(10*rand(4,3));
for hh = 1:numel(heights)
wind_data.( [ 'ws' num2str(heights(hh)) ] ) = ws(hh,:)
wind_data.( [ 'wd' num2str(heights(hh)) ] ) = wd(hh,:)
end
or directly allocate them, you have to put the fieldname in '' as Ed Smith already said:
heights = [40, 50, 80, 90];
ws = round(10*rand(4,3));
wd = round(10*rand(4,3));
wind_data = struct('ws40', ws(1,:), ...
'wd40', wd(1,:), ...
'ws50', ws(2,:), ...
'wd50', wd(2,:) );

cut vector according to NaN values

data_test is a vector that is populated by numbers with some NaN.
data_test = [NaN, 2, 3, 4, NaN,NaN,NaN, 12 ,44, 34, NaN,5,NaN];
I would like to cut data_test according to the NaNs and create a cell array containing the pieces of data_set in between NaNs.
data_cell{1}=[2 3 4];
data_cell{2}=[12 44 34];
data_cell{3}=[5];
at this point I need to filter these values (this is OK, just as an example the filtered values will be the same of data_test +1)
data_cell{1} -> data_cell_filt{1}
data_cell{2} -> data_cell_filt{2}
data_cell{3} -> data_cell_filt{3}
and put back the filtered values in data_test.
data_cell_filt{1}
data_cell_filt{2} -> data_test
data_cell_filt{3}
in order that data_test is
data_test = [NaN, 3, 4, 5, NaN,NaN,NaN, 13 ,45, 35, NaN, 6, NaN];
ps (data_test in my case is ~20000 elements)
You can do it easily with a loop or use arrayfun like this:
A = [NaN, 2, 3, 4, NaN, NaN, NaN, 13, 45, 35, NaN, 6, NaN]
i1 = find(diff(isnan(A))==-1)+1 %// Index where clusters of numbers begin
i2 = find(diff(isnan(A))==1) %// Index where clusters of numbers end
data_cell_filt = arrayfun(#(x,y)({A(x:y)}),i1,i2 ,'uni', false)
One approch with accumarray and cumsum and diff
%// find the index of regular numbers
idx = find(~isnan(data_test))
%// group the numbers which are adjacent, to some index number
idx1 = cumsum([1,diff(idx)~=1])
%// put all those numbers of same index number into a cell
out = accumarray(idx1.',data_test(idx).',[],#(x) {x.'})
Sample run:
data_test = [NaN, 2, 3, 4, NaN,NaN,NaN, 12 ,44, 34, NaN,5,NaN];
>> celldisp(out)
out{1} =
2 3 4
out{2} =
12 44 34
out{3} =
5
Convolution-based approach:
ind = isnan(data_test);
t = conv(2*x-1, [-1 1], 'same'); %// convolution is like correlation but flips 2nd input
starts = find(t==2); %// indices of where a run of non-NaN's starts, minus 1
ends = find(t==-2); %// indices of where it ends
result = mat2cell(data_test(~ind), 1, ends-starts); %// pick non-NaN's and split

Instruction inside a While Loop Matlab

What does this instruction vector=[vector,sum(othervector)] does in matlab inside a while loop like:
vector=[];
while a - b ~= 0
othervector = sum(something') %returns a vector
vector=[vector,sum(othervector)]; %it keeps a new vector?
...
end
vector=vector./100
Executing a = [a,b] means append b to a, thus vector will eventually be a matrix where each column is the row-wise sum of something'.
More concretely: suppose something' is this matrix:
something' = [ 1, 2; 3, 4 ];
Then sum(something') is:
othervector = [ 3 ; 7 ]
And initially vector is empty, so this sets vector to
vector = [ 3 ; 7 ]
Suppose we repeat with a new something' consisting of
[ 5, 5; 5, 6 ]
Then sum(something') is:
othervector = [ 10; 11 ]
And now we augment this to vector using vector = [vector, sum(othervector)]:
vector = [ vector, [10; 11] ] = [ 3, 10 ; 7, 11 ]