Hi Friends,
I have a query, I need the code for the below output, Could you please me on this. Its the Zig Zag concept I need to implement in SAS.
Order | Day1 | Day2 | Day3 | Day4 | Day5 | Day6 | Day7 | Day8 | Day9 | Day10 |
1 | 1 | 3 | 6 | 10 | 14 | 18 | ? | ? | ? | ? |
2 | 2 | 5 | 9 | 13 | 17 | 21 | ? | ? | ? | ? |
3 | 4 | 8 | 12 | 16 | 20 | 23 | ? | ? | ? | ? |
4 | 7 | 11 | 15 | 19 | 22 | 24 | ? | ? | ? | ? |
The above table for 4*6 table -- I need to create a table automatically n rows and n columns with the same logic.
Here is method using a temporary 2-D matrix.
%let rows=4;
%let cols=6 ;
data want;
array x[&rows,&cols] _temporary_ ;
array out day1-day&cols ;
do diag=1 to &rows+&cols-1;
row=min(&rows,diag);
col=1 + max(0,diag-&rows);
do while(1<=row and col<=&cols);
counter+1;
x[row,col]=counter;
col+1;
row+-1;
end;
end;
do row=1 to &rows;
do col=1 to &cols;
out[col] = x[row,col];
end;
output;
end;
drop diag row col counter ;
run;
proc print;
run;
Obs day1 day2 day3 day4 day5 day6 1 1 3 6 10 14 18 2 2 5 9 13 17 21 3 4 8 12 16 20 23 4 7 11 15 19 22 24
The above table for 4*6 table -- I need to create a table automatically n rows and n columns with the same logic.
Hi @ganeshsas764,
There are at least two possible approaches:
Maybe approach #1 would be more elegant, but here is an implementation of approach #2:
%macro zigzag(m,n,out=want);
data &out(rename=(i=Order) drop=j k);
do i=1 to &m;
array Day[&n];
do j=1 to &n;
k=i+j-2;
Day[j]=(k*k+k
-(k>&m)*(k-&m)*(k-&m+1)
-(k>&n)*(k-&n)*(k-&n+1))/2 + min(j,&m-i+1);
end;
output;
end;
run;
%mend zigzag;
%zigzag(4,6)
proc print data=want noobs;
run;
You may want to run a whole series of test cases, which is the purpose of macro testzz below:
%macro testzz(maxrows,maxcols,out=want);
%local m n;
%do m=1 %to &maxrows;
%do n=1 %to &maxcols;
%zigzag(&m,&n,out=&out)
title "m=&m n=&n";
proc print data=&out noobs;
run;
%end;
%end;
title;
%mend testzz;
%testzz(6,6)
(Edit: minor simplifications)
Here is method using a temporary 2-D matrix.
%let rows=4;
%let cols=6 ;
data want;
array x[&rows,&cols] _temporary_ ;
array out day1-day&cols ;
do diag=1 to &rows+&cols-1;
row=min(&rows,diag);
col=1 + max(0,diag-&rows);
do while(1<=row and col<=&cols);
counter+1;
x[row,col]=counter;
col+1;
row+-1;
end;
end;
do row=1 to &rows;
do col=1 to &cols;
out[col] = x[row,col];
end;
output;
end;
drop diag row col counter ;
run;
proc print;
run;
Obs day1 day2 day3 day4 day5 day6 1 1 3 6 10 14 18 2 2 5 9 13 17 21 3 4 8 12 16 20 23 4 7 11 15 19 22 24
Thank you very much for your help with this, it's working fine. I really impressed with your coding skills.
It is easy for IML.
%let nrow=4;
%let ncol=6;
proc iml;
count=0;
max=&nrow <> &ncol;
x=j(max,max,0);
do n=2 to 2#max;
temp=loc(row(x)+col(x)=n);
idx=idx//temp[ncol(temp):1];
end;
dim=nrow(x)||ncol(x);
s=ndx2sub(dim,idx);
do i=1 to nrow(s);
if s[i,1]<=&nrow & s[i,2]<=&ncol then do;
count=count+1;
full=full//(count||s[i,]);
end;
end;
want=full(full);
create want from want[c=("day1":"day&ncol")];
append from want;
close;
quit;
proc print;run;
You've seen a row-by-row solution with direct calculation of each element of a given row. And a serpentine solution that winds through the matrix.
This is a row by row solution in which only row 1 is "directly" calculated. Every subsequent row is calculated from the prior row (i.e. X{i,j}=X{i-1,j+1}-1).
Row 1 has day1=1, day2=day1+2, day3=day2+3, etc. up to a maximum increment of the number of rows. And the last &norders increments (for the "extra" array elements below) will be 4,3,2,1 (for &norders=4).
My first program failed to properly set up the values for order 1 (thanks @Ksharp ). Here's the corrected version:
%let norders=4;
%let ncols=10;
%let array_length=%eval(&ncols+&norders);
data want (drop=_:);
order=1;
array days {&array_length} day1-day&ncols _dummy1-_dummy&norders (1);
do order=1 to &norders;
do _d=ifn(order=1,2,1) to &array_length-order+1;
_d2=&array_length - _d + 1 ;
if order=1 then days{_d}= days{_d-1}+min(_d,_d2,&norders);
else days{_d}=days{_d+1}-1;
end;
output;
end;
run;
The array is given the length of NORDERS+NCOLS, (not just NCOLS), which is fully populated for order1. This allows the diagonal copying to provide valid values for the lower right segment of the matrix.
Also the first element of array DAYS is initialized to a 1, which make programming the do loop iteration over days easier (do _d=2 to &array_length for order=1, but _d=1 to &array_length-order+1 for other orders).
The _d2 variable is for counting down from the upper bound of the array.
I like that idea.
Basically the increment needed for the first row's value in a 4 row by 6 col solution is:
COL: 1 2 3 4 5 6 7 8 9 10 Value: 1 3 6 10 14 18 22 25 27 28 Diff: 1 2 3 4 4 4 4 3 2 1
So the amount to increment increases by one up to the number of ROWS and stays constant until reaching the number of columns, and then the increment decreases by one each time.
%let rows=4 ;
%let cols=6 ;
data want ;
do row=1 to &rows;
array x day1-day&cols d1-d&rows ;
if row=1 then do increment=1 to &cols,&rows to 1 by -1 ;
col+1;
next+min(&rows,increment);
x[col]=next;
end;
else do col=1 to dim(x)-1;
x[col]=x[col+1]-1;
end;
output;
end;
drop next col increment d1-d&rows;
run;
Note the advantage of my first solution using the 2-D temporary array is that it is easy to adapt to fill the "matrix" with values from a dataset instead of the just the sequence 1,2,3,...
I found another version of Zig Zag Matrix in internet.
How do you get it ?
I post IML code firstly here .
%let nrow=4 ;
%let ncol=6;
proc iml;
max=&nrow <> &ncol;
j=j(max,max,0);
do n=2 to 2#max;
temp=loc(row(j)+col(j)=n);
if mod(n-1,2)=1 then idx=idx||t(temp[ncol(temp):1]);
else idx=idx||temp;
end;
dim=max||max;
sub=ndx2sub(dim,idx) ;
count=0;
do i=1 to nrow(sub);
if sub[i,1]<=&nrow & sub[i,2]<=&ncol then do;
count=count+1;
full=full//(count||sub[i,]);
end;
end;
want=full(full);
create want from want[c=("date1":"date&ncol")];
append from want;
close;
quit;
proc print;run;
So that one is alternating the order that the diagonals are traversed instead of always going in the same direction.
%let rows=4;
%let cols=6;
data want;
array x[&rows,&cols] _temporary_ ;
array out day1-day&cols ;
do diag=1 to &rows+&cols-1;
direction=-1+2*mod(diag,2);
if direction=1 then do;
row=min(diag,&rows);
col=1 + max(0,diag-&rows);
end;
else do;
col=min(diag,&cols) ;
row=1 + max(0,diag-&cols);
end;
do while(1<=row<=&rows and 1<=col<=&cols);
counter+1;
x[row,col]=counter;
col+direction;
row+-direction;
end;
end;
do row=1 to &rows;
do col=1 to &cols;
out[col] = x[row,col];
end;
output;
end;
drop diag row col direction counter ;
run;
To modify the above code to replicate the original ZigZag order just force DIRECTION to always be one instead of alternating between 1 and -1.
mkeintz,
When I set the following:
%let norders=40;
%let ncols=2;
Your code get wrong result:
@mkeintz ,
I still get wrong result when I set this :
%let norders=40;
%let ncols=2;
%let array_length=%eval(&ncols+&norders);
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.