BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
shellp55
Quartz | Level 8

Hello

 

I have data formatted as wide and I want to loop through to find a code from the same range twice.  So I want the second do loop to start 1 after finding the other.

 

For instance, if the first code is found at occurrence 3 of 25  I want the next do loop to start at 4 and go to 25.  But they won't all be in the same occurrence so I want to be able to use the results of the first occurrence dynamically.  Thanks.

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

One of the nice features of DO loops is you can combine iterative loops with WHILE or UNTIL condition testing.

data test;
  set test_grp;
  array dx dcde1-dcde4;
  crit1=0;
  crit2=0;
  dx1_occur=0;

  do i = 1 to dim(dx) until (crit1);
    crit1 = "Y60" <=: dx[i] <=: "Y84";
  end;
  if crit1 then dx1_occur=i;

  do i = (dx1_occur+1) to dim(dx) until (crit2);
    crit2 = "Y60" <=: dx[i] <=: "Y84";
  end;
  drop i;
  if crit1 and crit2 then output;
run;

View solution in original post

15 REPLIES 15
Tom
Super User Tom
Super User

Just use a variable for the lower end of the DO loop.

do second_loop= first_location +1 to dim(array_name);
Reeza
Super User
How many diags are you looking at? You could use WHICHC/WHICHN to find the first index easily and then use that number as your loop counter as Tom suggests.

Alternatively, doing this in a long format data set is very trivial with BY group processing.
shellp55
Quartz | Level 8

Thanks Tom but I tried what you suggested and it didn't work.  Below is some test data and it doesn't work but you can see where I want to start where the first occurrence was found +1. 

 

Thanks Reeza, I'd like to see if there is a solution I can use with the data wide but certainly if I have to make it long then I will. 

 

In the below example, charts 111111, 33333, 55555, and 66666 all have two codes from the same range and is what I would want picked up in the criteria of crit1 gt 0 and crit2 gt 0.

 

data test_grp;
input @1  chart $5.
	  @6  regn  $3.
	  @9  dcde1  $3. 
	  @12 dcde2  $3.
	  @15 dcde3  $3.
	  @18 dcde4  $3.	 
;
cards;
11111001Y60J90B55Y84
22222002J90B89C40A40
33333003Y60Y68B94J90
44444004C93C82J44Y60
55555005Y60Y89L98M42
66666006Y60Y54A42G90
77777007B92F04C44C83
run; 

data test;
set test_grp;

array dx(4) dcde1-dcde4;

crit1=0;
crit2=0;

do i = 1 to 4;
if "Y60" <= substr(dx[i],1,3) <= "Y84" then crit1=1;
if "Y60" <= substr(dx[i],1,3) <= "Y84" then dx1_occur=i;
end;

do j = (dx1_occur+1) to 4;
if "Y60" <= substr(dx[j],1,3) <= "Y84" then crit1=1;
end;


if crit1 gt 0 and crit2 gt 0 then output;

run;

shellp55
Quartz | Level 8
Noticed an error in the code for j do loop, should be crit2=1. Note also that the code for the do loop starting point doesn't work, just leaving it to show what I want to be able to do. Thanks.
Reeza
Super User

 

 

data test;
set test_grp;

array dx(4) dcde1-dcde4;

crit1=0;
crit2=0;

do i = 1 to 4 while(crit1=0);
if "Y60" <= substr(dx[i],1,3) <= "Y84" then crit1=1;
end;

do j = i to 4;
if "Y60" <= substr(dx[j],1,3) <= "Y84" then crit2=1;
end;


*if crit1 gt 0 and crit2 gt 0 then output;

run;

I switched it to a WHILE loop, so you can use I directly in the next loop. It automatically increments by 1, so you don't need to do that step either. I commented out the last line to check results. 

Astounding
PROC Star

Given that you have explained a little more, here is a shorter version of what you could do:

 

data test;
set test_grp;

array dx(4) dcde1-dcde4;

crit1=0;
crit2=0;

do i = 1 to 4;
   if crit1=1 and "Y60" <=: dx[i] <=: "Y84" then crit2=1;
   if "Y60" <=: dx[i] <=: "Y84" then crit1=1;
end;

if crit1 gt 0 and crit2 gt 0 then output;

run;

Notice that you don't need SUBSTR as long as you add the colon to the end of the comparison ... a significant time-saver.

 

I'm also trying to understand why you need two flags instead of one:

 

data test;
set test_grp;

array dx(4) dcde1-dcde4;

crit=0;

do i = 1 to 4;
   if "Y60" <=: dx[i] <=: "Y84" then crit + 1;
end;

if crit gt 1 then output;

run;
Reeza
Super User
Just a quick FYI - character comparisons are tricky but I think it will work in this situation. It may not work for all though, so ensure that you do thorough testing with the character comparisons.
shellp55
Quartz | Level 8

Thanks Reeza but that also picks up the cases that only have 1 of the codes in the range, not two codes. 

 

Thanks Astounding but crit1 isn't greater than zero unless I run the code to first find out that is the case i.e. nowhere else is crit1 mentioned to then be used in the do loop..or am I missing something?

 

Shelley

Reeza
Super User
@shellp55, only because I commented out the last criteria. Otherwise pretty sure it works. You only want record 1 and 3, correct?
Astounding
PROC Star

Yes, you're missing the logic of the DO loop.

 

CRIT1 can get set within the loop, and CRIT2 only gets set after CRIT1 has already been set.

 

Give it a try.  And especially notice both possible programs.

Tom
Super User Tom
Super User

If you just want to find cases with 2 or more codes in the same group then keep count of how many you find.

data test;
  set test_grp;
  array dx dcde1-dcde4;
  found=0;
  do i = 1 to dim(dx) until (found>=2);
    found +  ("Y60" <=: dx[i] <=: "Y84");
  end;
  if found >= 2 then output;
  drop i;
run;
Tom
Super User Tom
Super User

One of the nice features of DO loops is you can combine iterative loops with WHILE or UNTIL condition testing.

data test;
  set test_grp;
  array dx dcde1-dcde4;
  crit1=0;
  crit2=0;
  dx1_occur=0;

  do i = 1 to dim(dx) until (crit1);
    crit1 = "Y60" <=: dx[i] <=: "Y84";
  end;
  if crit1 then dx1_occur=i;

  do i = (dx1_occur+1) to dim(dx) until (crit2);
    crit2 = "Y60" <=: dx[i] <=: "Y84";
  end;
  drop i;
  if crit1 and crit2 then output;
run;
shellp55
Quartz | Level 8

This works Tom, thanks so much!  I firstly indicated that charts 55555 and 66666 should also be flagged but I noticed when they didn't show up with your code it is because two of the codes are out of range within those charts so we are good. 

 

Thanks again to everyone who offered assistance, greatly appreciated!

novinosrin
Tourmaline | Level 20

data test_grp;
input @1  chart $5.
	  @6  regn  $3.
	  @9  dcde1  $3. 
	  @12 dcde2  $3.
	  @15 dcde3  $3.
	  @18 dcde4  $3.	 
;
cards;
11111001Y60J90B55Y84
22222002J90B89C40A40
33333003Y60Y68B94J90
44444004C93C82J44Y60
55555005Y60Y89L98M42
66666006Y60Y54A42G90
77777007B92F04C44C83
run; 
data test1;
set test_grp;

array dx(4) dcde1-dcde4;

crit1=0;
crit2=0;

do i = 1 to 4;
if "Y60" <= substr(dx[i],1,3) <= "Y84" then crit1=1;
if "Y60" <= substr(dx[i],1,3) <= "Y84" then do;
dx1_occur=i;
leave;
end;
end;
if dx1_occur>. then  do j = (dx1_occur+1) to 4;
if "Y60" <= substr(dx[j],1,3) <= "Y84" then crit2=1;
end;
if crit1 and crit2 then output;
run;
proc print noobs;run;
chart regn dcde1 dcde2 dcde3 dcde4 crit1 crit2 i dx1_occur j
11111 001 Y60 J90 B55 Y84 1 1 1 1 5
33333 003 Y60 Y68 B94 J90 1 1 1 1 5

SAS Innovate 2025: Call for Content

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!

Submit your idea!

How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 15 replies
  • 1702 views
  • 8 likes
  • 5 in conversation