Storing values from a loop in a function in Matlab - arrays

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);

Related

How to call a customized proposal function in AdaptiveMCMC.jl

I've been trying to use a customized proposal distribution that generates the proposed arrays so we can use them and test or sample them in a Metropolis-Hastings algorithm, with a log_target function, i wrote the metropolis-hastings code manually and it works fine, tho it doesn't give the same satisfactory results as using Klara.jl which is a closed package now. The proposal distribution is written like this
using Distributions
nonneg(v) = all(v.>=0) ? true : false
struct OrthoNNDist <: DiscreteMultivariateDistribution
x0::Vector{Float64}
oc::Array{Float64,2}
x1s::Array
prob::Float64
#return a new uniform distribution with all vectors in x1s orthogonal to oc
function OrthoNNDist(x0::Vector{Float64}, oc::Array{Float64,2})
x1s = []
for i = 1:size(oc)[2]
x1 = x0 + oc[:, i]
if nonneg(x1)
push!(x1s, x1)
end
x1 = x0 - oc[:, i]
if nonneg(x1)
push!(x1s, x1)
end
end
new(x0, oc, x1s, 1.0/length(x1s))
end
end
Base.length(d::OrthoNNDist) = length(d.x0)
Distributions.rand(d::OrthoNNDist) = rand(d.x1s)
Distributions.pdf(d::OrthoNNDist, x::Vector) = x in d.x1s ? d.prob : 0.0
Distributions.pdf(d::OrthoNNDist) = fill(d.prob, size(d.x1s))
Distributions.logpdf(d::OrthoNNDist, x::Vector) = log(pdf(d, x))
you can test it using for example x0=[1.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0, 1.0] and mat = [0 1 0 0 1 1 0 0 1 0 0 0; 1 0 0 0 1 0 0 0 0 0 0 1; 1 0 1 0 0 0 0 1 0 0 1 0; 0 1 0 0 0 0 1 0 0 0 1 0; 0 0 0 1 0 0 0 1 1 0 0 0; 0 0 0 1 0 0 0 0 0 0 0 1; 0 0 1 0 0 1 0 0 0 1 0 0; 0 0 0 0 0 0 1 0 0 1 0 0]. we can fix the mat and let x0 to be the variable by writing for example proposal(x::Vector)= OrthoNNDist(x,mat).
If we have a log_target function in general called logtarget(x::Vector) and using this proposal distribution above and i want to use it in AdaptiveMCMC package or any other package that can be used in this case with a minimum example, i tried AdvancedMH but a part of my target function uses JuMP hence i can't get the gradient of the target, i've read the Mamba documentation but i couldn't understand it exactly i would with a minimum example in this case of a customized proposal function and a target function, AdaptiveMCMC looks more simple but i keep getting some MethodError regarding the distribution it's supposed to be a function but mine it's not as you can see in the code above. i can provide here an example of the code using Klara with comments.
function qrelay(alpha, delta, name)
n = 2
chi = fill(sqrt(0.06), n)
phi = im * tanh(chi)
omega = 1.0 / prod(cosh(chi))^2
syms, op = qrelay_op(n, phi, alpha, delta) #it gives an array
op_a, op_ab, mat, coef = op_mat(op) #array, array, matrice, array of coefficients
op_q2 = [syms.apH[1], syms.apV[1], syms.bpH[end], syms.bpV[end]] #array
op_q1 = [syms.apH[2:end]..., syms.apV[2:end]..., syms.bpH[1:end-1]..., syms.bpV[1:end-1]...] #array
mask_q1 = [op in op_q1 for op in op_a]; #array
mask_q2 = [op in op_q2 for op in op_a]; #array
qq = [x in syms.apH || x in syms.bpV ? 1 : 0 for x in op_a] #array
pdet0 = pdet_maker(0.04, 1e-5) #it gives a probability
qrs = QSampler(mat, coef, omega, pdet0) #calling a module QSampler
targetcache = Dict{Vector{Int}, Float64}()
function plogtarget(na::Vector{Int})
get!(targetcache, na) do
log(qrs.prob(qq, na, mask_q1) * qrs.prob(na))
end
end
# plogtarget(na::Vector{Int}) = log(qrs.prob(qq, na, mask_q1) * qrs.prob(na))
p = BasicDiscMuvParameter(:p, logtarget=plogtarget)
model = likelihood_model([p], isindexed=false)
sampler = MH(qrs.psetproposal, symmetric=false) #this is where the proposal function is called qrs.psetproposal(x::Vector)= qrs.OthoNNDist(x, mat)
mcrange = BasicMCRange(nsteps=2^20 + 2^10, burnin=2^10, thinning=2^5)
v0 = Dict(:p=>zeros(qq))
outopts = Dict{Symbol, Any}(
:monitor=>[:value, :logtarget],
:diagnostics=>[:accept],
:destination=>:iostream,
:filepath=>"$dataname/"*name
)
job = BasicMCJob(model, sampler, mcrange, v0, outopts=outopts)
funcQ(v) = qrs.prob(qq, v, mask_q2) #it returns a probability
return qrs, job, funcQ
end
This piece of code won't work of course because Klara is closed but i just put it here to give you a clearer view of how the code was working before.

extracting 2D array from 4D array in matlab [duplicate]

This question already has answers here:
How do I get the two last dimensions of an N-D array as a 2D array?
(3 answers)
Closed 7 years ago.
I have a 4D array in matlab something like this,
test(:,:,1,1) =
0
test(:,:,2,1) =
0
test(:,:,3,1) =
0
test(:,:,4,1) =
0
test(:,:,5,1) =
0
test(:,:,1,2) =
0
test(:,:,2,2) =
0
test(:,:,3,2) =
0
test(:,:,4,2) =
0
test(:,:,5,2) =
0
test(:,:,1,3) =
0
test(:,:,2,3) =
0
test(:,:,3,3) =
0
test(:,:,4,3) =
0
test(:,:,5,3) =
0
test(:,:,1,4) =
0
test(:,:,2,4) =
0
test(:,:,3,4) =
0
test(:,:,4,4) =
0
test(:,:,5,4) =
0
test(:,:,1,5) =
0
test(:,:,2,5) =
0
test(:,:,3,5) =
0
test(:,:,4,5) =
0
test(:,:,5,5) =
0
K>> test
test(:,:,1,1) =
0
test(:,:,2,1) =
0
test(:,:,3,1) =
0
test(:,:,4,1) =
0
test(:,:,5,1) =
0
test(:,:,1,2) =
0
test(:,:,2,2) =
0
test(:,:,3,2) =
0
test(:,:,4,2) =
0
test(:,:,5,2) =
0
test(:,:,1,3) =
0
test(:,:,2,3) =
0
test(:,:,3,3) =
0
test(:,:,4,3) =
0
test(:,:,5,3) =
0
test(:,:,1,4) =
0
test(:,:,2,4) =
0
test(:,:,3,4) =
0
test(:,:,4,4) =
0
test(:,:,5,4) =
0
test(:,:,1,5) =
0
test(:,:,2,5) =
0
test(:,:,3,5) =
0
test(:,:,4,5) =
0
test(:,:,5,5) =
0
Now I want to get an array, something like this
ans =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
Is there a simple way to convert the dimentions.
I am very much new to multidimensional arrays in Matlab.
Please let me know if anyone knows of any solution.
Use the squeeze function, it will return a matrix of size [5 4] when you input a matrix of size [1 1 5 4]

How to pre-allocate arrays in multi-loop iteration

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;

How can I generate this matrix (containing only 0s and ±1s)?

I would like to generate matrix of size (n(n-1)/2, n) that looks like this (n=5 in this case):
-1 1 0 0 0
-1 0 1 0 0
-1 0 0 1 0
-1 0 0 0 1
0 -1 1 0 0
0 -1 0 1 0
0 -1 0 0 1
0 0 -1 1 0
0 0 -1 0 1
0 0 0 -1 1
This is what I, quickly, came up with:
G = [];
for i = 1:n-1;
for j = i+1:n
v = sparse(1,i,-1,1,n);
w = sparse(1,j,1,1,n);
vw = v+w;
G = [G; vw];
end
end
G = full(G);
It works, but is there a faster/cleaner way of doing it?
Use nchoosek to generate the indices of the columns that will be nonzero:
n = 5; %// number of columns
ind = nchoosek(1:n,2); %// ind(:,1): columns with "-1". ind(:,2): with "1".
m = size(ind,1);
rows = (1:m).'; %'// row indices
G = zeros(m,n);
G(rows + m*(ind(:,1)-1)) = -1;
G(rows + m*(ind(:,2)-1)) = 1;
You have two nested loops, which leads to O(N^2) complexity of non-vectorized operations, which is too much for this task. Take a look that your matrix actually has a rectursive pattern:
G(n+1) = [ -1 I(n)]
[ 0 G(n)];
where I(n) is identity matrix of size n. That's how you can express this pattern in matlab:
function G = mat(n)
% Treat original call as G(n+1)
n = n - 1;
% Non-recursive branch for trivial case
if n == 1
G = [-1 1];
return;
end
RT = eye(n); % Right-top: I(n)
LT = repmat(-1, n, 1); % Left-top: -1
RB = mat(n); % Right-bottom: G(n), recursive
LB = zeros(size(RB, 1), 1); % Left-bottom: 0
G = [LT RT; LB RB];
end
And it gives us O(N) complexity of non-vectorized operations. It probably will waste some memory during recursion and matrix composition if Matlab is not smart enought to factor these out. If it is critical, you may unroll recursion into loop and iteratively fill up corresponding places in your original pre-allocated matrix.

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