Retain a variable value till condition is met - date

I have dataset which has 3 variables which includes ID, date and enrol, and it is sorted by ID and date. My goal is to create a new dataset which has another variable say enrol_flag which should be 1 once the enrol is 1 but it should become 0 once enrol is 0 and retains that 0 value.
I am using SAS to program.
My dataset looks like:
ID Date enrol
1 01JAN21 0
1 O1FEB21 1
1 01MAR21 1
1 01APR21 1
1 01MAY21 0
1 01JUN21 0
1 01JUL21 1
1 01AUG21 1
2 01JAN21 1
2 O1FEB21 1
2 01MAR21 1
2 01APR21 1
2 01MAY21 1
2 01JUN21 0
2 01JUL21 0
2 01AUG21 0
3 O1FEB21 0
3 01MAR21 0
3 01APR21 0
3 01MAY21 0
3 01JUN21 0
3 01JUL21 1
Output I want
ID Date enrol enrol_flag
1 01JAN21 0 0
1 O1FEB21 1 1
1 01MAR21 1 1
1 01APR21 1 1
1 01MAY21 0 0
1 01JUN21 0 0
1 01JUL21 1 0
1 01AUG21 1 0
2 01JAN21 1 1
2 O1FEB21 1 1
2 01MAR21 1 1
2 01APR21 1 1
2 01MAY21 1 1
2 01JUN21 0 0
2 01JUL21 0 0
2 01AUG21 0 0
3 O1FEB21 0 0
3 01MAR21 0 0
3 01APR21 0 0
3 01MAY21 0 0
3 01JUN21 0 0
3 01JUL21 1 1
Thank you in advance for your help.

Try this
data have;
input ID Date :date7. enrol;
format Date date7.;
datalines;
1 01JAN21 0
1 01FEB21 1
1 01MAR21 1
1 01APR21 1
1 01MAY21 0
1 01JUN21 0
1 01JUL21 1
1 01AUG21 1
2 01JAN21 1
2 01FEB21 1
2 01MAR21 1
2 01APR21 1
2 01MAY21 1
2 01JUN21 0
2 01JUL21 0
2 01AUG21 0
3 01FEB21 0
3 01MAR21 0
3 01APR21 0
3 01MAY21 0
3 01JUN21 0
3 01JUL21 1
;
data want;
set have;
by ID Date;
if first.ID then do;
enrol_flag = enrol;
d = 0;
end;
if enrol = 1 & d = 0 then do;
enrol_flag = 1;
d = 1;
end;
if enrol = 0 and d = 1 then enrol_flag = 0;
retain enrol_flag d;
run;

The sequences of enrol with in group id can form bands of 0's and 1's. You want to set flag to zero for every point after enrol transitions from 1 to 0, thus you need a secondary variable to track that case for enforcing the 0 rule.
Example (DOW loop):
data have;
input id date enrol;
attrib date informat=date7. format=date7.;
datalines;
1 01JAN21 0
1 01FEB21 1
1 01MAR21 1
1 01APR21 1
1 01MAY21 0
1 01JUN21 0
1 01JUL21 1
1 01AUG21 1
2 01JAN21 1
2 01FEB21 1
2 01MAR21 1
2 01APR21 1
2 01MAY21 1
2 01JUN21 0
2 01JUL21 0
2 01AUG21 1
3 01FEB21 0
3 01MAR21 0
3 01APR21 0
3 01MAY21 0
3 01JUN21 0
3 01JUL21 1
;
data want (keep=id date enrol flag);
do until (last.id);
set have;
by id;
_d = dif(enrol); /* unconditional place ensures consistent dif */
if first.id then flag=enrol;
else
if _d = -1 then _z = 1; /* track transition 1 -> 0 */
if _z
then flag = 0; /* enforce rule */
else flag = enrol;
output;
end;
run;

Related

MATLAB: Identify maxima and minima and split data accordingly

I have the following arrays:
x = [1:33];
y = [0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 0 1 2 1 0 1 2 3 4 3 2 1 0];
I need to separate y into different sections. I need to obtain the ascending parts, the descending parts, and the combined ascending and descending parts.
For example:
Ascending parts = [1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 0 0 1
1 1 1 0 0 0 0];
Descending parts = [0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1
1 1 1 1 0 0 1 1 0 0 0 0 1 1 1 1];
Combined parts = [1 1 1 1 1 1 1 1 1
1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4];
I can, of course, do this manually for these arrays, but I need to do this for arrays with hundreds of thousands of points and I wish to do it automatically. I have been playing around with the findpeaks functions but this isn't straightforward as it sometimes picks up peaks during the descending/ascending parts, rather than at the end points.
Any tips on how I can do this?
version MATLAB 2017/b
For a problem like this, you should consider using Matlab's diff.
For y = [0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 0 1 2 1 0 1 2 3 4 3 2 1 0];
a=diff(y)
1 1 1 1 1 -1 -1 -1 -1 -1 1 1 1 1 1 -1 -1 -1 -1 -1 1 1 -1 -1 1 1 1 1 -1 -1 -1 -1
b=a, c =a;
b(b<0)=0;
c(c>0)=0;
Will give you:
b = 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 0 0 1 1 1 1 0 0 0 0
c = 0 0 0 0 0 -1 -1 -1 -1 -1 0 0 0 0 0 -1 -1 -1 -1 -1 0 0 -1 -1 0 0 0 0 -1 -1 -1 -1
For the second part, you can do the following:
z=diff(y, 2);
zd=[0 find(z~=0);0 z(find(z~=0))]
0 5 10 15 20 22 24 28 32
0 -2 2 -2 2 -2 2 -2 2
Assuming that this is how your function looks like in general, the above pattern shows convex and concave regions in the sequence. With this assumption, the following should work in your case:
za=[0 zd(1,zd(2,:)>0)];
zad=diff(za);
cell2mat(arrayfun(#(x,y) repelem(x,y), 1:length(zad),zad,'UniformOutput',false))
ans: 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 4 4 4 4 4 4 4 4
which I believe is close to what you want. Hope this helps.

label over specific cells (index) in imagesc

I have created this map of Jamaica using matrix A. I want to insert text labels on this image at specific indexes for cities. For Example kingston on this map is at point 15, 38, where 15 is the row and 38 the column, this point I would label "kingston". My matrix is below and the image of it generated from imagesc is below as well. I was playing around with get(gca, 'position') but that was not successful.
% Cities
kingston = [15 38];
montegoBay = [4 15];
portRoyal = [18 31];
stThomas = [10 55];
mandeville = [13 21];
ochoRios = [2 29];
A = [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 3 3 0 0 0 0 3 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 3 0 0 0 0 3 0 3 3 3 0 0 0 0 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 3 3 3 0 3 0 3 3 3 3 0 3 0 3 0 3 3 3 0 3 3 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 3 3 3 0 0 0 3 3 3 3 3 3 0 3 0 3 0 0 0 0 0 0 3 0 0 0 3 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 0 0 3 3 0 0 0 3 3 3 3 3 0 3 3 3 3 0 0 0 0 0 0 0 0 0 3 0 0 0 3 3 3 0 3 3 3 3 0 0 3 0 0 1 1 1 1 1 1 1 1 1 1;
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 3 3 3 0 0 3 3 0 3 0 0 0 0 3 3 3 3 0 0 0 0 0 0 0 0 1 1 1 1 1 1;
1 0 3 0 3 3 0 0 0 3 3 3 0 3 0 3 3 3 3 3 0 3 3 3 0 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0 0 0 0 0 1;
1 0 0 0 0 0 3 0 0 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0 0 0 3 3 3 3 3 0 3 0 3 3 0 0 3 3 3 0 0 3 0 0 3 0 3 3 0 3 0 3 0 1;
1 1 1 0 0 0 0 0 0 3 3 3 3 0 0 3 3 3 3 3 3 3 3 0 3 3 3 0 0 0 3 3 3 3 0 3 3 0 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1;
1 1 1 1 1 1 1 1 0 0 0 0 0 0 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 3 1 0 0 0 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 3 3 0 3 0 0 3 3 3 3 3 3 0 3 0 0 0 0 0 3 3 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 3 3 3 3 0 0 0 0 0 3 3 3 3 3 3 0 3 0 3 0 3 3 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 3 3 3 0 0 0 0 0 0 0 3 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 0 3 0 1 1 1 1 1 3 0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 0 3 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1];
Cmap = [1 1 1; 0 0 1; 0 1 0];
colormap(Cmap);
imagesc(A);
axis off
You should use the function text like this:
text(38,15,'kingston')
i.e. in opposite order, because your axis is in i-j direction, and not x-y (try typing axis xy to see what I mean).
(I have changed the font size to 15: text(38,15,'kingston','FontSize',15))
If you want to go a step further, define your cities as a cell array:
Cities = {'kingston','montegoBay','portRoyal','stThomas','mandeville','ochoRios'};
and their location in a matrix:
location = [15 38;
4 15;
18 31;
10 55;
13 21;
2 29];
And then all you need is one text command:
text(location(:,2),location(:,1),Cities,'FontSize',12)
to get the final result:
You can use text function to do that as:
text(x,y,'MyText')
See the documentation for more info:
https://www.mathworks.com/help/matlab/ref/text.html
Hope it helps.

Convert adjacency matrix to specific edge list in MATLAB

If I have the matrix
1 0 0
0 0 1
0 0 0
and I want this form in MATLAB
1 2 3 1 2 3 1 2 3
1 1 1 2 2 2 3 3 3
1 0 0 0 0 0 0 1 0
also I want the values of third row in result. i.e. ans= [1 0 0 0 0 0 0 1 0]
Here you go -
[X,Y] = ndgrid(1:size(A,1),1:size(A,2));
out = [X(:).' ; Y(:).' ; A(:).']
For the last part of your question, use the last row of out : out(end,:) or A(:).'.
Sample run -
>> A
A =
1 0 0
0 0 1
0 0 0
>> [X,Y] = ndgrid(1:size(A,1),1:size(A,2));
>> out = [X(:).' ; Y(:).' ; A(:).']
out =
1 2 3 1 2 3 1 2 3
1 1 1 2 2 2 3 3 3
1 0 0 0 0 0 0 1 0

Using 25 characters or less to create a specific matrix in MATLAB

What matlab command, or combination of commands (using 25 characters or less), could be used to create the following matrix?
1 0 0 1 0 0 1 0 0 1 0 0 1 0 0
1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 1 0 0 1 0 0 1 0 0
1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 1 0 0 1 0 0 1 0 0
1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 1 0 0 1 0 0 1 0 0 1 0 0
1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
I got as far as this;
repmat(tril(ones(3,3)),5)
But repmat creates a 5 by 5 matrix. I however, need a 4,5 matrix.
Thank you for taking the time to help!
Add one more argument to repmat and remove one from ones (as Divakar noted):
repmat(tril(ones(3)),4,5)
As you can see, you can specify how many replications you want for both the rows and the columns. A single value argument to either function will use that value for both rows and columns.
I'll throw the kron solution out there. Just because.
kron(ones(4,5),tril(ones(3)))
More than 25 characters, but hey:
bsxfun(#le,mod(0:3*5-1,3),mod(0:3*4-1,3).')

How to apply logical indexing on multi-dimension matrix in matlab

My question is simple. I have an rgb image and a logical matrix. I want to set the pixel which is true in the corresponding element of logical matrix to (150,160,170).
For example:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0
r= 1 1 1 1 1 g= 1 1 1 1 1 b=1 1 1 1 1 logical_mat =1 0 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0
I want it results in
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
r= 150 1 1 1 1 g= 160 1 1 1 1 b=170 1 1 1 1
150 150 1 1 1 160 160 1 1 1 170 170 1 1 1
150 150 150 1 1 160 160 160 1 1 170 170 170 1 1
I have tried logical index, if set pixel into same color is easy
lm = repmat(logical_mat,[1 1 3]);
rgb(lm) = 150;
But I dont know how to set the value channel by channel.
Thanks in advance.
You're already creating the right logical matrix:
lm = repmat(logical_mat,[1 1 3]);
You need to create a 3-channel color matrix the same size.
cm = repmat(cat(3,150,160,170), size(lm,1), size(lm,2))
Then, index into the color matrix with lm:
rgb(lm) = cm(lm);