Matlab - Subtract 1 vector with another in struct array - arrays

I have to different struct arrays(In the same Matlab file), what I want is to take 1 parameter/vector from a variable in a struct array and subtract it with different parameters from another variable in another struct array, is this possible?
Here is a small part of my code:
Dist(1).name = 'Pristina'
Dist(1).KM_To_Fushe_ks = 13.7 % 199-13.7 =
Dist(1).KM_to_Lipjan = 8.7 % 199-8.7 =
Dist(1).KM_to_Sllatina = 4.2 % 199-4.2 =
Dist(1).KM_to_Hajvali = 3.5 % 199-3.5 =
Dist(1).KM_to_Mitrovica = 46.9 % 199-46.9 =
Dist(1).KM_to_Anija = 1.9 % 199-1.9 =
EV(1).name = 'Nissan Leaf 24 kWh pack'
EV(1).RangeInKM_By_Manufacturer = 199 %SUBTRACT this with parameters above:
EV(1).Battery_Capacity = 21.6
EV(1).Battery_Warranty_KM = 100000
EV(1).Battery_Warrany_Year = 5
EV(1).EnginePower_Kw = 80
EV(1).EnginePower_hK = 109
EV(1).Torque_in_NewtonMeter = 254
EV(1).QuickCharging_type = 'CHAdeMO'
EV(1).QuickChargingEffect_kW_DC = 50
EV(1).NormalCharging_OnBoard_kW_AC = 3.3
EV(1).Seats = 5
EV(1).Luggage_in_Liters = 370
EV(1).Consumption_Mixed_kWh_per_10km_NEDC = 1.5
EV(1).Weight_Without_Driver = 1475
EV(1).TopSpeed_KM_per_hour = 144
EV(1).Acceleration_0to100KM_per_hour = 11.5
EV(1).RangeInKM_By_Manufacturer_RANK = 10
What I want is to have the number off 199 as a vector, and substract it by all these numbers = [13.7, 8.7, 4.2, 3.5, 46.9, 1.9]
How to do this?

Maybe I misinterpret your question, but this seem to work:
EV(1).RangeInKM_By_Manufacturer = 199 - Dist(1).KM_To_Fushe_ks
In the line you quote in your question, you left the initialization of KM_To_Fushe_ks after the difference; in short, you cannot have to vaiable assignements in the same command.
Also, if you end your lines with semi-colons you will suppress the output to the command window. Like this:
Dist(1).name = 'Pristina';
Dist(1).KM_To_Fushe_ks = 13.7;
Dist(1).KM_to_Lipjan = 8.7;
% Etc...

Here is one solution to my problem:
distances = [KM_to_Fushe_KS, KM_to_Lipjan];
remainingrange = arrayfun(#(s) s.RangeInKM - distances, EV, 'UniformOutput', false)
Or I could do this:
remainingrange = cell(size(EV));
for evidx = 1:numel(EV)
remaingrange{evidx} = EV(evidx).RangeInKM - distances;
end

Another solution is doing is putting multiple distances in once matrix:
Example:
Towns = {'Town1', 'Town2', 'Town3', 'Town4'};
distances = [0 200 13.7 8.7;
200 0 13.3 9.3;
13.7 13.3 0 255;
8.7 9.3 255 0];
EVs = {'Nissan Leaf 24 kWh pack', 'Nissan Leaf 30 kWh pack'};
ranges = [199 250];
And then I can calculate distances as a 3D matrix:
remainingrange = permute(ranges, [1 3 2]) - distances;
remainingrange = bsxfun(#minus, permute(ranges, [1 3 2]), distances);
If I want to check if a EV has not enough range in KM, I could write:
tooFarForMyEV = find(remainingrange < 0)
[from, to, ev] = ind2sub(size(remainingrange), tooFarForMyEV);
lackingrange = table(Towns(from)', Towns(to)', EVs(ev)', remainingrange(tooFarForMyEV), 'VariableNames', {'From', 'To', 'EV', 'Deficit'})

Related

MATLAB - Avoid repeated values in a vector inside cell arrays and take next

This is the problem:
I have a cell array on the form indx{ii} where each ii is an array of size 1xNii (this means the arrays have different size).
And another cell array on the form indy{jj} where each jj is an array of the same size as ii.
The question is that I would like to create a function evaluates the values in the arrays of indx{:} and take the first one that is not repeated, and if is a repeated value then take the next.
I will try to explain with an example. Suppose we have indx and indy that are the cell arrays:
indx{1} = [1 3 2 7];
indx{2} = [3 8 5];
indx{3} = [3 6 2 9];
indx{4} = [1 3 4];
indx{5} = [3 1 4];
indy{1} = [0.12 0.21 0.31 0.44];
indy{2} = [0.22 0.34 0.54];
indy{3} = [0.13 0.23 0.36 0.41];
indy{4} = [0.12 0.16 0.22];
indy{5} = [0.14 0.19 0.26];
What I want the code to do is take the first value and is not repeated in indx and the equivalent in indy. So the answer for the example should be:
ans=
indx{1} = 1;
indx{2} = 3;
indx{3} = 6;
indx{4} = 4;
indx{5} = [];
indy{1} = 0.12;
indy{2} = 0.22;
indy{3} = 0.23;
indy{4} = 0.22;
indy{5} = [];
In ans, for indx{1} the code takes 1 because is the first and it's not repeated and takes the equivalent value in indy. Then for indx{2} it takes 3 because is the first value and is not repeated as first value in any array before. But for ind{3} it takes 6, because the first value that is 3 is repeated, and takes the equivalent value to 6 in indy which is 0.23. For ind{4} the first and second value they are already repeated as first values so the code takes 4 and its equivalent in indy. And last, for indx{5} since all values are already repeated the code should take no value.
indx{1} = [1 3 2 7];
indx{2} = [3 8 5];
indx{3} = [3 6 2 9];
indx{4} = [1 3 4];
indx{5} = [3 1 4];
indy{1} = [0.12 0.21 0.31 0.44];
indy{2} = [0.22 0.34 0.54];
indy{3} = [0.13 0.23 0.36 0.41];
indy{4} = [0.12 0.16 0.22];
indy{5} = [0.14 0.19 0.26];
indx2 = NaN(numel(indx),1);
indx2(1) = indx{1}(1);
indy2 = NaN(numel(indy),1);
indy2(1) = indy{1}(1);
for ii = 2:numel(indx)
tmp1 = indx{ii}'; % get the original as array
tmp2 = indy{ii}';
if numel(tmp1)>numel(indx2)
tmp3 = [indx2;NaN(numel(tmp1)-numel(indx2),1)];
tmp4 = [indx2;NaN(numel(tmp1)-numel(indx2),1)];
else
tmp1 = [tmp1;NaN(numel(indx2)-numel(tmp1),1)];
tmp2 = [tmp2;NaN(numel(indx2)-numel(tmp2),1)];
tmp3 = indx2;
tmp4 = indy2;
end
tmp5 = ~ismember(tmp1,tmp3); % find first non equal one
tmp6 = find(tmp5,1,'first');
indx2(ii) = tmp1(tmp6); % save values
indy2(ii) = tmp2(tmp6);
end
N = numel(indx2);
indx2 = mat2cell(indx2, repmat(1,N,1));
N = numel(indy2);
indy2 = mat2cell(indy2, repmat(1,N,1));
indx2 =
[ 1]
[ 3]
[ 6]
[ 4]
[NaN]
What I have done here is to first initialise your output cells to have the same number of cells as your original data. Then I assign value 1, since that one will always be unique, it is the first entry. After that I use a for loop to first convert all four cell arrays (2 input, two output) to regular arrays for processing with ismember, where I check for the all non-equal number between the next input cell and the existing numbers in your output. Then find is employed to get the first non-matching number. Lastly, the numbers are assigned to the arrays if present.
As a comment on the usage of booleans with NaN, try NaN ~=NaN and NaN ==NaN. The first will give you 1, whilst the second will give you zero. This quality makes NaNs the ideal choice of filler here, because 0 == 0 will result in 1:
A = [1,2,5,4,NaN];
B = [1,3,7,NaN,NaN];
ismember(A,B)
=
1 0 0 0 0
Thus the NaNs do not equal one another and will therefore not pollute your solution.

Conversion from 3D cell array to a set of 2D matrices

I have a 3D-cell array designated as A{s,i,h}, serving as a store for large amounts of numerical data during a nested-loop portion of my script. Some of the cell entries will be blank [ ], whilst the rest consist of numbers - either singular or in arrays (1 x 10 double etc.):
I want to convert this cell array to a set of 2D matrices.
Specifically, one separate matrix for each value of h (h is always equal 1:3) and one column in each matrix for every value of s. Each column will contain all the numerical data combined - it does not need to be separated by i.
How can I go about this? I ordinarily deal with 3D-cell arrays in this form to produce separate matrices (one for each value of h) using something like this:
lens = sum(cellfun('length',reshape(A,[],size(A,3))),1);
max_length = max(lens);
mat = zeros(max_length,numel(lens));
mask = bsxfun(#le,[1:max_length]',lens);
mat(mask) = [A{:}];
mat(mat==0) = NaN;
mat = sort(mat*100);
Matrix1 = mat(~isnan(mat(:,1)),1);
Matrix2 = mat(~isnan(mat(:,2)),2);
Matrix3 = mat(~isnan(mat(:,3)),3);
However in this instance, each matrix had only a single column. I'm have trouble adding multiple columns to each output matrix.
1. Result in the form of a cell array of matrices (as requested)
Here's one possible approach. I had to use one for loop. However, the loop can be easily avoided if you accept a 3D-array result instead of a cell array of 2D-arrays. See second part of the answer.
If you follow the comments in the code and inspect the result of each step, it's straightforward to see how it works.
%// Example data
A(:,:,1) = { 1:2, 3:5, 6:9; 10 11:12 13:15 };
A(:,:,2) = { 16:18, 19:22, 23; 24:28, [], 29:30 };
%// Let's go
[S, I, H] = size(A);
B = permute(A, [2 1 3]); %// permute rows and columns
B = squeeze(mat2cell(B, I, ones(1, S), ones(1, H))); %// group each col of B into a cell...
B = cellfun(#(x) [x{:}], B, 'uniformoutput', false); %// ...containing a single vector
t = cellfun(#numel, B); %// lengths of all columns of result
result = cell(1,H); %// preallocate
for h = 1:H
mask = bsxfun(#le, (1:max(t(:,h))), t(:,h)).'; %'// values of result{h} to be used
result{h} = NaN(size(mask)); %// unused values will be NaN
result{h}(mask) = [B{:,h}]; %// fill values for matrix result{h}
end
Result in this example:
A{1,1,1} =
1 2
A{2,1,1} =
10
A{1,2,1} =
3 4 5
A{2,2,1} =
11 12
A{1,3,1} =
6 7 8 9
A{2,3,1} =
13 14 15
A{1,1,2} =
16 17 18
A{2,1,2} =
24 25 26 27 28
A{1,2,2} =
19 20 21 22
A{2,2,2} =
[]
A{1,3,2} =
23
A{2,3,2} =
29 30
result{1} =
1 10
2 11
3 12
4 13
5 14
6 15
7 NaN
8 NaN
9 NaN
result{2} =
16 24
17 25
18 26
19 27
20 28
21 29
22 30
23 NaN
2. Result in the form of 3D array
As indicated above, using a 3D array to store the result permits avoiding loops. In the code below, the last three lines replace the loop used in the first part of the answer. The rest of the code is the same.
%// Example data
A(:,:,1) = { 1:2, 3:5, 6:9; 10 11:12 13:15 };
A(:,:,2) = { 16:18, 19:22, 23; 24:28, [], 29:30 };
%// Let's go
[S, I, H] = size(A);
B = permute(A, [2 1 3]); %// permute rows and columns
B = squeeze(mat2cell(B, I, ones(1, S), ones(1, H))); %// group each col of B into a cell...
B = cellfun(#(x) [x{:}], B, 'uniformoutput', false); %// ...containing a single vector
t = cellfun(#numel, B); %// lengths of all columns of result
mask = bsxfun(#le, (1:max(t(:))).', permute(t, [3 1 2])); %'// values of result to be used
result = NaN(size(mask)); %// unused values will be NaN
result(mask) = [B{:}]; %// fill values
This gives (compare with result of the first part):
>> result
result(:,:,1) =
1 10
2 11
3 12
4 13
5 14
6 15
7 NaN
8 NaN
9 NaN
result(:,:,2) =
16 24
17 25
18 26
19 27
20 28
21 29
22 30
23 NaN
NaN NaN
Brute force approach:
[num_s, num_i, num_h] = size(A);
cellofmat = cell(num_h,1);
for matrix = 1:num_h
sizemat = max(cellfun(#numel, A(:,1,matrix)));
cellofmat{matrix} = nan(sizemat, num_s);
for column = 1:num_s
lengthcol = length(A{column, 1, matrix});
cellofmat{matrix}(1:lengthcol, column) = A{column, 1,matrix};
end
end
Matrix1 = cellofmat{1};
Matrix2 = cellofmat{2};
Matrix3 = cellofmat{3};
I don't know what your actual structure looks like but this works for A that is setup using the following steps.
A = cell(20,1,3);
for x = 1:3
for y = 1:20
len = ceil(rand(1,1) * 10);
A{y,1,x} = rand(len, 1);
end
end

Inserting One Row Each Time in a Sequence from Matrix into Another Matrix After Every nth Row in Matlab

I have matrix A and matrix B. Matrix A is 100*3. Matrix B is 10*3. I need to insert one row from matrix B each time in a sequence into matrix A after every 10th row. The result would be Matrix A with 110*3. How can I do this in Matlab?
Here's another indexing-based approach:
n = 10;
C = [A; B];
[~, ind] = sort([1:size(A,1) n*(1:size(B,1))+.5]);
C = C(ind,:);
For canonical purposes, here's how you'd do it via loops. This is a bit inefficient since you're mutating the array at each iteration, but it's really simple to read. Given that your two matrices are stored in A (100 x 3) and B (10 x 3), you would do:
out = [];
for idx = 1 : 10
out = [out; A((idx-1)*10 + 1 : 10*idx,:); B(idx,:)];
end
At each iteration, we pick out 10 rows of A and 1 row of B and we concatenate these 11 rows onto out. This happens 10 times, resulting in 330 rows with 3 columns.
Here's an index-based approach:
%//pre-allocate output matrix
matrixC = zeros(110, 3);
%//create index array for the locations in matrixC that would be populated by matrixB
idxArr = (1:10) * 11;
%//place matrixB into matrixC
matrixC(idxArr,:) = matrixB;
%//place matrixA into matrixC
%//setdiff is used to exclude indexes already populated by values from matrixB
matrixC(setdiff(1:110, idxArr),:) = matrixA;
And just for fun here's the same approach sans magic numbers:
%//define how many rows to take from matrixA at once
numRows = 10;
%//get dimensions of input matrices
lengthA = size(matrixA, 1);
lengthB = size(matrixB, 1);
matrixC = zeros(lengthA + lengthB, 3);
idxArr = (1:lengthB) * (numRows + 1);
matrixC(idxArr,:) = matrixB;
matrixC(setdiff(1:size(matrixC, 1), idxArr),:) = matrixA;
Just for fun... Now with more robust test matrices!
A = ones(3, 100);
A(:) = 1:300;
A = A.'
B = ones(3, 10);
B(:) = 1:30;
B = B.' + 1000
C = reshape(A.', 3, 10, []);
C(:,end+1,:) = permute(B, [2 3 1]);
D = permute(C, [2 3 1]);
E = reshape(D, 110, 3)
Input:
A =
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
19 20 21
22 23 24
25 26 27
28 29 30
31 32 33
34 35 36
...
B =
1001 1002 1003
1004 1005 1006
...
Output:
E =
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
19 20 21
22 23 24
25 26 27
28 29 30
1001 1002 1003
31 32 33
34 35 36
...
Thanks to #Divakar for pointing out my previous error.
Solution Code
Here's an implementation based on logical indexing also known as masking and must be pretty efficient when working with large arrays -
%// Get sizes of A and B
[M,d] = size(A);
N = size(B,1);
%// Mask of row indices where rows from A would be placed
mask_idx = reshape([true(A_cutrow,M/A_cutrow) ; false(1,N)],[],1);
%// Pre-allocate with zeros:
%// http://undocumentedmatlab.com/blog/preallocation-performance
out(M+N,d) = 0;
%// Insert A and B using mask and ~mask
out(mask_idx,:) = A;
out(~mask_idx,:) = B;
Benchmarking
%// Setup inputs
A = rand(100000,3);
B = rand(10000,3);
A_cutrow = 10;
num_iter = 200; %// Number of iterations to be run for each approach
%// Warm up tic/toc.
for k = 1:50000
tic(); elapsed = toc();
end
disp(' ------------------------------- With MASKING')
tic
for iter = 1:num_iter
[M,d] = size(A);
N = size(B,1);
mask_idx = reshape([true(A_cutrow,M/A_cutrow) ; false(1,N)],[],1);
out(M+N,d) = 0;
out(mask_idx,:) = A;
out(~mask_idx,:) = B;
clear out
end
toc, clear mask_idx N M d iter
disp(' ------------------------------- With SORT')
tic
for iter = 1:num_iter
C = [A; B];
[~, ind] = sort([1:size(A,1) A_cutrow*(1:size(B,1))+.5]);
C = C(ind,:);
end
toc, clear C ind iter
disp(' ------------------------------- With RESHAPE+PERMUTE')
tic
for iter = 1:num_iter
[M,d] = size(A);
N = size(B,1);
C = reshape(A.', d, A_cutrow , []);
C(:,end+1,:) = permute(B, [2 3 1]);
D = permute(C, [2 1 3]);
out = reshape(permute(D,[1 3 2]),M+N,[]);
end
toc, clear out D C N M d iter
disp(' ------------------------------- With SETDIFF')
tic
for iter = 1:num_iter
lengthA = size(A, 1);
lengthB = size(B, 1);
matrixC = zeros(lengthA + lengthB, 3);
idxArr = (1:lengthB) * (A_cutrow + 1);
matrixC(idxArr,:) = B;
matrixC(setdiff(1:size(matrixC, 1), idxArr),:) = A;
end
toc, clear matrixC idxArr lengthA lengthB
disp(' ------------------------------- With FOR-LOOP')
tic
for iter = 1:num_iter
[M,d] = size(A);
N = size(B,1);
Mc = M/A_cutrow;
out(M+N,d) = 0;
for idx = 1 : Mc
out( 1+(idx-1)*(A_cutrow +1): idx*(A_cutrow+1), :) = ...
[A( 1+(idx-1)*A_cutrow : idx*A_cutrow , : ) ; B(idx,:)];
end
clear out
end
toc
Runtimes
Case #1: A as 100 x 3 and B as 10 x 3
------------------------------- With MASKING
Elapsed time is 4.987088 seconds.
------------------------------- With SORT
Elapsed time is 5.056301 seconds.
------------------------------- With RESHAPE+PERMUTE
Elapsed time is 5.170416 seconds.
------------------------------- With SETDIFF
Elapsed time is 35.063020 seconds.
------------------------------- With FOR-LOOP
Elapsed time is 12.118992 seconds.
Case #2: A as 100000 x 3 and B as 10000 x 3
------------------------------- With MASKING
Elapsed time is 1.167707 seconds.
------------------------------- With SORT
Elapsed time is 2.667149 seconds.
------------------------------- With RESHAPE+PERMUTE
Elapsed time is 2.603110 seconds.
------------------------------- With SETDIFF
Elapsed time is 3.153900 seconds.
------------------------------- With FOR-LOOP
Elapsed time is 19.822912 seconds.
Please note that num_iter was different for these two cases, as the idea was to keep the runtimes > 1 sec mark to compensate for tic-toc overheads.

Matlab create a matrix with values

I've written a function in matlab which generates a matrix using a loop. I was wondering if it is possible to generate the same results without a loop. X can either be a 1 x 50, 2 x 50, 3 x 50, etc... the values range from 1 to 50 incrementally for each column per row.
For example
1 x 1 = 1,
2 x 1 = 1,
3 x 1 = 1,
1 x 2 = 2,
2 x 2 = 2,
3 x 2 = 2,
.....................
1 x 50 = 50,
2 x 50 = 50,
3 x 50 = 50,
My function:
function [i] = m(x)
[a, b] = size(x);
i = zeros(a, b);
for c = 1 : a
i(c, :) = (1:size(x,2));
end
end
Thanks.
Try this:
N = 3;
M = 50;
x = repmat((1:N)',M,1);
y = reshape(repmat((1:M)',1,N)',N*M,1);
%z = x.*y
z = strcat(num2str(x),'x',num2str(y),'=',num2str(x.*y))
This will give the same format in your question.
Use repmat:
output = repmat(1:size(x,2), size(x,1), 1);
Some alternatives are
output = ones(size(x,1),1)*(1:size(x,2));
and
output = cumsum(ones(size(x)),2);
One alternate to repmat(Luis's answer) is bsxfun
out = bsxfun(#times,ones(size(x,1),1),1:size(x,2))

mean of parts of an array in octave

I have two arrays. One is a list of lengths within the other. For example
zarray = [1 2 3 4 5 6 7 8 9 10]
and
lengths = [1 3 2 1 3]
I want to average (mean) over parts the first array with lengths given by the second. For this example, resulting in:
[mean([1]),mean([2,3,4]),mean([5,6]),mean([7]),mean([8,9,10])]
I am trying to avoid looping, for the sake of speed. I tried using mat2cell and cellfun as follows
zcell = mat2cell(zarray,[1],lengths);
zcellsum = cellfun('mean',zcell);
But the cellfun part is very slow. Is there a way to do this without looping or cellfun?
Here is a fully vectorized solution (no explicit for-loops, or hidden loops with ARRAYFUN, CELLFUN, ..). The idea is to use the extremely fast ACCUMARRAY function:
%# data
zarray = [1 2 3 4 5 6 7 8 9 10];
lengths = [1 3 2 1 3];
%# generate subscripts: 1 2 2 2 3 3 4 5 5 5
endLocs = cumsum(lengths(:));
subs = zeros(endLocs(end),1);
subs([1;endLocs(1:end-1)+1]) = 1;
subs = cumsum(subs);
%# mean of each part
means = accumarray(subs, zarray) ./ lengths(:)
The result in this case:
means =
1
3
5.5
7
9
Speed test:
Consider the following comparison of the different methods. I am using the TIMEIT function by Steve Eddins:
function [t,v] = testMeans()
%# generate test data
[arr,len] = genData();
%# define functions
f1 = #() func1(arr,len);
f2 = #() func2(arr,len);
f3 = #() func3(arr,len);
f4 = #() func4(arr,len);
%# timeit
t(1) = timeit( f1 );
t(2) = timeit( f2 );
t(3) = timeit( f3 );
t(4) = timeit( f4 );
%# return results to check their validity
v{1} = f1();
v{2} = f2();
v{3} = f3();
v{4} = f4();
end
function [arr,len] = genData()
%#arr = [1 2 3 4 5 6 7 8 9 10];
%#len = [1 3 2 1 3];
numArr = 10000; %# number of elements in array
numParts = 500; %# number of parts/regions
arr = rand(1,numArr);
len = zeros(1,numParts);
len(1:end-1) = diff(sort( randperm(numArr,numParts) ));
len(end) = numArr - sum(len);
end
function m = func1(arr, len)
%# #Drodbar: for-loop
idx = 1;
N = length(len);
m = zeros(1,N);
for i=1:N
m(i) = mean( arr(idx+(0:len(i)-1)) );
idx = idx + len(i);
end
end
function m = func2(arr, len)
%# #user1073959: MAT2CELL+CELLFUN
m = cellfun(#mean, mat2cell(arr, 1, len));
end
function m = func3(arr, len)
%# #Drodbar: ARRAYFUN+CELLFUN
idx = arrayfun(#(a,b) a-(0:b-1), cumsum(len), len, 'UniformOutput',false);
m = cellfun(#(a) mean(arr(a)), idx);
end
function m = func4(arr, len)
%# #Amro: ACCUMARRAY
endLocs = cumsum(len(:));
subs = zeros(endLocs(end),1);
subs([1;endLocs(1:end-1)+1]) = 1;
subs = cumsum(subs);
m = accumarray(subs, arr) ./ len(:);
if isrow(len)
m = m';
end
end
Below are the timings. Tests were performed on a WinXP 32-bit machine with MATLAB R2012a. My method is an order of magnitude faster than all other methods. For-loop is second best.
>> [t,v] = testMeans();
>> t
t =
0.013098 0.013074 0.022407 0.00031807
| | | \_________ #Amro: ACCUMARRAY (!)
| | \___________________ #Drodbar: ARRAYFUN+CELLFUN
| \______________________________ #user1073959: MAT2CELL+CELLFUN
\__________________________________________ #Drodbar: FOR-loop
Furthermore all results are correct and equal -- differences are in the order of eps the machine precision (caused by different ways of accumulating round-off errors), therefore considered rubbish and simply ignored:
%#assert( isequal(v{:}) )
>> maxErr = max(max( diff(vertcat(v{:})) ))
maxErr =
3.3307e-16
Here is a solution using arrayfun and cellfun
zarray = [1 2 3 4 5 6 7 8 9 10];
lengths = [1 3 2 1 3];
% Generate the indexes for the elements contained within each length specified
% subset. idx would be {[1], [4, 3, 2], [6, 5], [7], [10, 9, 8]} in this case
idx = arrayfun(#(a,b) a-(0:b-1), cumsum(lengths), lengths,'UniformOutput',false);
means = cellfun( #(a) mean(zarray(a)), idx);
Your desired output result:
means =
1.0000 3.0000 5.5000 7.0000 9.0000
Following #tmpearce comment I did a quick time performance comparison between above's solution, from which I create a function called subsetMeans1
function means = subsetMeans1( zarray, lengths)
% Generate the indexes for the elements contained within each length specified
% subset. idx would be {[1], [4, 3, 2], [6, 5], [7], [10, 9, 8]} in this case
idx = arrayfun(#(a,b) a-(0:b-1), cumsum(lengths), lengths,'UniformOutput',false);
means = cellfun( #(a) mean(zarray(a)), idx);
and a simple for loop alternative, function subsetMeans2.
function means = subsetMeans2( zarray, lengths)
% Method based on single loop
idx = 1;
N = length(lengths);
means = zeros( 1, N);
for i = 1:N
means(i) = mean( zarray(idx+(0:lengths(i)-1)) );
idx = idx+lengths(i);
end
Using the next test scrip, based on TIMEIT, that allows checking performance varying the number of elements on the input vector and sizes of elements per subset:
% Generate some data for the performance test
% Total of elements on the vector to test
nVec = 100000;
% Max of elements per subset
nSubset = 5;
% Data generation aux variables
lenghtsGen = randi( nSubset, 1, nVec);
accumLen = cumsum(lenghtsGen);
maxIdx = find( accumLen < nVec, 1, 'last' );
% % Original test data
% zarray = [1 2 3 4 5 6 7 8 9 10];
% lengths = [1 3 2 1 3];
% Vector to test
zarray = 1:nVec;
lengths = [ lenghtsGen(1:maxIdx) nVec-accumLen(maxIdx)] ;
% Double check that nVec is will be the max index
assert ( sum(lengths) == nVec)
t1(1) = timeit(#() subsetMeans1( zarray, lengths));
t1(2) = timeit(#() subsetMeans2( zarray, lengths));
fprintf('Time spent subsetMeans1: %f\n',t1(1));
fprintf('Time spent subsetMeans2: %f\n',t1(2));
It turns out that the non-vectorised version without arrayfun and cellfun is faster, presumably due to the extra overhead of those functions
Time spent subsetMeans1: 2.082457
Time spent subsetMeans2: 1.278473

Resources