How to pre-allocate arrays in multi-loop iteration - arrays

I need to pre-allocate arrays in my code below.
I don't quite understand how to pre-allocate arrays in multi-loop iteration.
a=0:1:2;
b=0:1:2;
c=0:1:2;
xx1=[];yy1=[];zz1=[];xx2=[];yy2=[];zz2=[];
for k=1:length(c)-1;
z1=c(k); z2=c(k+1);
for w=1:length(b)-1;
y1=b(w); y2=b(w+1);
for q=1:length(a)-1;
x1=a(q); x2=a(q+1);
xx1=[xx1;x1]; xx2=[xx2;x2];
yy1=[yy1;y1]; yy2=[yy2;y2];
zz1=[zz1;z1]; zz2=[zz2;z2];
end
end
end
The expected results are:
[xx1 xx2 yy1 yy2 zz1 zz2]
ans =
0 1 0 1 0 1
1 2 0 1 0 1
0 1 1 2 0 1
1 2 1 2 0 1
0 1 0 1 1 2
1 2 0 1 1 2
0 1 1 2 1 2
1 2 1 2 1 2

Increase a counter in the innermost loop to keep track of which entry of xx1 etc you should fill.
a = 0:1:2;
b = 0:1:2;
c = 0:1:2;
xx1 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1); %// preallocate
xx2 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
yy1 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
yy2 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
zz1 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
zz2 = NaN((length(a)-1)*(length(b)-1)*(length(c)-1),1);
n = 0; %// initiallize counter
for k=1:length(c)-1;
z1=c(k); z2=c(k+1);
for w=1:length(b)-1;
y1=b(w); y2=b(w+1);
for q=1:length(a)-1;
n = n + 1; %// increase counter;
x1 = a(q);
x2 = a(q+1);
xx1(n) = x1; %// fill values
xx2(n) = x2;
yy1(n) = y1;
yy2(n) = y2;
zz1(n) = z1;
zz2(n) = z2;
end
end
end
Anyway, it can be done without loops, adapting the procedure given in this answer. This has two advantages:
It may be faster if a, b, c are large.
The same code works for any number of vectors, not just 3. Simply define vectors1 and vectors2 accordingly in the code below.
Code without loops:
a = 0:1:2;
b = 0:1:2;
c = 0:1:2;
vectors1 = { a(1:end-1), b(1:end-1), c(1:end-1) };
vectors2 = { a(2:end), b(2:end), c(2:end) };
n = numel(vectors1);
combs1 = cell(1,n);
[combs1{:}] = ndgrid(vectors1{end:-1:1});
combs1 = reshape(cat(n+1, combs1{:}),[],n);
combs2 = cell(1,n);
[combs2{:}] = ndgrid(vectors2{end:-1:1});
combs2 = reshape(cat(n+1, combs2{:}),[],n);
result(:,2:2:2*n) = combs2;
result(:,1:2:2*n) = combs1;

Related

How to find the number of times a group of a specific value is present in an array?

I have a 1 by 1000 (1 row by 1000 columns) matrix that contain only 0 and 1 as their elements. How can I find how many times 1 is repeated 3 times consecutively.
If there are more than 3 ones then it is necessary to reset the counting. So 4 would be 3+1 and it counts as only one instance of 3 consecutive 1s but 6 would be 3+3 so it counts as two instances of having 3 consecutive 1s.
This approach finds the differences between when A goes from 0 to 1 (rising edge) and from 1 to 0 (falling edge). This gives the lengths of consecutive 1s in each block. Then divide these numbers by 3 and round down to get the number of runs of 3.
Padding A with a 0 at the start and end just ensures we have a rising edge at the start if A starts with a 1, and we have a falling edge at the end if A ends with a 1.
A = round(rand(1,1000));
% padding with a 0 at the start and end will make this simpler
B = [0,A,0];
rising_edges = ~B(1:end-1) & B(2:end);
falling_edges = B(1:end-1) & ~B(2:end);
lengths_of_ones = find(falling_edges) - find(rising_edges);
N = sum(floor(lengths_of_ones / 3));
Or in a much less readable 2 lines:
A = round(rand(1,1000));
B = [0,A,0];
N = sum(floor((find(B(1:end-1) & ~B(2:end)) - find(~B(1:end-1) & B(2:end))) / 3));
You can define your custom functions like below
v = randi([0,1],1,1000);
% get runs in cell array
function C = runs(v)
C{1} = v(1);
for k = 2:length(v)
if v(k) == C{end}(end)
C{end} = [C{end},v(k)];
else
C{end+1} = v(k);
end
end
end
% count times of 3 consecutive 1s
function y = count(x)
if all(x)
y = floor(length(x)/3);
else
y = 0;
end
end
sum(cellfun(#count,runs(v)))
Here is another vectorized way:
% input
n = 3;
a = [1 1 1 1 0 0 1 1 1 0 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1]
% x x x x x = 5
% output
a0 = [a 0];
b = cumsum( a0 ) % cumsum
c = diff( [0 b( ~( diff(a0) + 1 ) ) ] ) % number of ones within group
countsOf3 = sum( floor( c/n ) ) % groups of 3
You like it messy? Here is a one-liner:
countsOf3 = sum(floor(diff([0 getfield(cumsum([a 0]),{~(diff([a 0])+1)})])/n))

Finding number(s) that is(are) repeated consecutively most often

Given this array for example:
a = [1 2 2 2 1 3 2 1 4 4 4 5 1]
I want to find a way to check which numbers are repeated consecutively most often. In this example, the output should be [2 4] since both 2 and 4 are repeated three times consecutively.
Another example:
a = [1 1 2 3 1 1 5]
This should return [1 1] because there are separate instances of 1 being repeated twice.
This is my simple code. I know there is a better way to do this:
function val=longrun(a)
b = a(:)';
b = [b, max(b)+1];
val = [];
sum = 1;
max_occ = 0;
for i = 1:max(size(b))
q = b(i);
for j = i:size(b,2)
if (q == b(j))
sum = sum + 1;
else
if (sum > max_occ)
max_occ = sum;
val = [];
val = [val, q];
elseif (max_occ == sum)
val = [val, q];
end
sum = 1;
break;
end
end
end
if (size(a,2) == 1)
val = val'
end
end
Here's a vectorized way:
a = [1 2 2 2 1 3 2 1 4 4 4 5 1]; % input data
t = cumsum([true logical(diff(a))]); % assign a label to each run of equal values
[~, n, z] = mode(t); % maximum run length and corresponding labels
result = a(ismember(t,z{1})); % build result with repeated values
result = result(1:n:end); % remove repetitions
One solution could be:
%Dummy data
a = [1 2 2 2 1 3 2 1 4 4 4 5 5]
%Preallocation
x = ones(1,numel(a));
%Loop
for ii = 2:numel(a)
if a(ii-1) == a(ii)
x(ii) = x(ii-1)+1;
end
end
%Get the result
a(find(x==max(x)))
With a simple for loop.
The goal here is to increase the value of x if the previous value in the vector a is identical.
Or you could also vectorized the process:
x = a(find(a-circshift(a,1,2)==0)); %compare a with a + a shift of 1 and get only the repeated element.
u = unique(x); %get the unique value of x
h = histc(x,u);
res = u(h==max(h)) %get the result

Storing values from a loop in a function in Matlab

I am writing a function in Matlab to model the length of stay in hospital of stroke patients. I am having difficulty in storing my output values.
Here is my function:
function [] = losdf(age, strokeType, dest)
% function to mdetermine length of stay in hospitaal of stroke patients
% t = time since admission (days);
% age = age of patient;
% strokeType = 1. Haemorhagic, 2. Cerebral Infarction, 3. TIA;
% dest = 5.Death 6.Nursing Home 7. Usual Residence;
alpha1 = 6.63570;
beta1 = -0.03652;
alpha2 = -3.06931;
beta2 = 0.07153;
theta0 = -8.66118;
theta1 = 0.08801;
mu1 = 22.10156;
mu2 = 2.48820;
mu3 = 1.56162;
mu4 = 0;
nu1 = 0;
nu2 = 0;
nu3 = 1.27849;
nu4 = 0;
rho1 = 0;
rho2 = 11.76860;
rho3 = 3.41989;
rho4 = 63.92514;
for t = 1:1:365
p = (exp(-exp(theta0 + (theta1.*age))));
if strokeType == 1
initialstatevec = [1 0 0 0 0 0 0];
elseif strokeType == 2
initialstatevec = [0 1 0 0 0 0 0];
else
initialstatevec = [0 0 (1-p) p 0 0 0];
end
lambda1 = exp(alpha1 + (beta1.*age));
lambda2 = exp(alpha2 + (beta2.*age));
Q = [ -(lambda1+mu1+nu1+rho1) lambda1 0 0 mu1 nu1 rho1;
0 -(lambda2+mu2+nu2+rho2) lambda2 0 mu2 nu2 rho2;
0 0 -(mu3+nu3+rho3) 0 mu3 nu3 rho3;
0 0 0 -(mu4+nu4+rho4) mu4 nu4 rho4;
0 0 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 0 0 0 0 0];
Pt = expm(t./365.*Q);
Pt = Pt(strokeType, dest);
Ft = sum(initialstatevec.*Pt);
Ft
end
end
Then to run my function I use:
losdf(75,3,7)
I want to plot my values of Ft in a graph from from 0 to 365 days. What is the best way to do this?
Do I need to store the values in an array first and if so what is the best way to do this?
Many ways to do this, one straightforward way is to save each data point to a vector while in the loop and plot that vector after you exit your loop.
...
Ft = zeros(365,1); % Preallocate Ft as a vector of 365 zeros
for t = 1:365
...
Ft(t) = sum(initialstatevec.*Pt); % At index "t", store your output
...
end
plot(1:365,Ft);

Efficient technique to interleave data sets by classes in MATLAB

The data set is in the following format: Input sample matrix X and output class vector Y such that each row in X is a sample and each of its column corresponds to a feature. Each index in Y corresponds to the respective output class for the corresponding sample in X. X can contain real numbers while Y contains positive integers.
My aim is to order the data set in terms of its class. For example
X = Y =
1 8 3 2
4 2 6 1
7 8 9 2
2 3 4 3
1 4 6 1
should be ordered and interleaved as
X = Y =
4 2 6 1
1 8 3 2
2 3 4 3
1 4 6 1
7 8 9 2
The code I've attempted seems to take a long time to run as it is based on serial execution. It is the following.
X = csvread('X.csv');
Y = csvread('Y.csv');
n = size(unique(Y),1);
m = size(X,1);
for i = 1:n
Dataset(i).X = X(Y==i,:);
Dataset(i).Y = Y(Y==i);
end
[num, ~] = hist(Y,n);
maxfreq = max(num);
NewX = [];
NewY = [];
for j = 1:maxfreq
for i = 1:n
if(j <= size(Dataset(i).X,1))
NewX = [NewX; Dataset(i).X(j,:)];
NewY = [NewY; i];
end
end
end
X = NewX;
Y = NewY;
clear NewX;
clear NewY;
csvwrite('OrderedX.csv', X);
csvwrite('OrderedY.csv', Y);
Is is possible to parallelize the above code?
You're resizing matrices all the time which is expensive. A quick speedup for your algorithm would be to set NewX and NewY to the proper size and just copy data in:
NewX = zeros(size(X));
NewY = zeros(size(Y));
k = 1;
for j = 1:maxfreq
for i = 1:n
if(j <= size(Dataset(i).X,1))
NewX(k,:) = Dataset(i).X(j,:);
NewY(k) = i;
k=k+1;
end
end
end
Approach #1 Using cumsum and diff following the same philosophy as the one listed in this solution -
function [outX,outY] = interleave_cumsum_diff(X,Y)
Y = Y(:);
[R,C] = find(bsxfun(#eq,Y,unique(Y).'));
lens = accumarray(C,1);
out = ones(1,numel(R));
shifts = cumsum(lens(1:end-1));
out(shifts+1) = 1- diff([0 ; shifts]);
[~,idx] = sort(cumsum(out));
sort_idx = R(idx)';
outX = X(sort_idx,:);
outY = Y(sort_idx,:);
Approach #1 Using bsxfun -
function [outX,outY] = interleave_bsxfuns(X,Y)
Y = Y(:);
[R,C] = find(bsxfun(#eq,Y,unique(Y).'));
lens = accumarray(C,1);
mask = bsxfun(#le,[1:max(lens)]',lens.');
V = zeros(size(mask));
V(mask) = R;
Vt = V.';
sort_idx = Vt(mask.');
outX = X(sort_idx,:);
outY = Y(sort_idx,:);
Sample run -
1) Inputs :
>> X
X =
1 8 3
4 2 6
7 8 9
2 3 4
1 4 6
>> Y
Y =
2
1
2
3
1
2) Outputs from the two approaches :
>> [NewX,NewY] = interleave_cumsum_diff(X,Y)
NewX =
4 2 6
1 8 3
2 3 4
1 4 6
7 8 9
NewY =
1
2
3
1
2
>> [NewX,NewY] = interleave_bsxfuns(X,Y)
NewX =
4 2 6
1 8 3
2 3 4
1 4 6
7 8 9
NewY =
1
2
3
1
2

matlab: inserting element after element?

Is there a way to insert an element into an array after verifying a certain element value? For example, take
A = [0 0 1 1 0 1 0]
After each 1 in the array, I want to insert another 1 to get
Anew = [0 0 1 1 1 1 0 1 1 0]
However I want a way to code this for a general case (any length 1 row array and the ones might be in any order).
A = [0 0 1 1 0 1 1];
i = (A == 1); % Test for number you want insert after
t = cumsum(i);
idx = [1 (2:numel(A)) + t(1:end-1)];
newSize = numel(A) + sum(i);
N = ones(newSize,1)*5; % Make this number you want to insert
N(idx) = A
Output:
N =
0 0 1 5 1 5 0 1 5 0
I made the inserted number 5 and split things onto multiple lines so it's easy to see what's going on.
If you wanted to do it in a loop (and this is how I would do it in real life where no-one can see me showing off)
A = [0 0 1 1 0 1 0];
idx = (A == 1); % Test for number you want insert after
N = zeros(1, numel(A) + sum(idx));
j = 1;
for i = 1:numel(A)
N(j) = A(i);
if idx(i)
j = j+1;
N(j) = 5; % Test for number you want to insert after
end
j = j+1;
end
N
Output:
N =
0 0 1 5 1 5 0 1 5 0
This code is not the most elegant, but it'll answer your question...
A=[0 0 1 1 0 1 0];
AA=[];
for ii=1:length(A);
AA=[AA A(ii)];
if A(ii)
AA=[AA 1];
end
end
I'm sure there will be also a vectorized way...
This should do the trick:
>> A = [0 0 1 1 0 1 0]
>>
>> sumA = sum(A);
>> Anew = zeros(1, 2*sumA+sum(~A));
>> I = find(A) + (0:sumA-1);
>> Anew(I) = 1;
>> Anew(I+1) = 8.2;
Anew =
0 0 1 8.2 1 8.2 0 1 8.2 0

Resources