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

Trying to set all of the values in an array to 0. It seems like it would be easy. If you have an array called have, then just have=0;. But then I get an error 
ERROR: Array subscript out of range at line 37 column 13.

Not sure how to solve this error. I know I can just do DO OVER have; have=0; END; but I thought that this way should work. 

1 ACCEPTED SOLUTION

Accepted Solutions
FreelanceReinh
Jade | Level 19

Hello @hubertsng and welcome to the SAS Support Communities!

 

There are some CALL routines and a few functions which can change the values of several variables (e.g. the elements of an array) at the same time. In your case CALL STDIZE can do the trick:

 

If the original values are all non-missing:

data _null_;
array x[4] (2 3 5 7);
call stdize('mult=',0,of x[*]);
put x[*];
run;

If some (but not all) may be missing:

data _null_;
array x[4] (2 3 . 7);
call stdize('replace','mult=',0,of x[*]);
put x[*];
run;

If you cannot rule out that all are missing:

data _null_;
array x[4];
call stdize('replace','mult=',0,of x[*],_iorc_);
put x[*];
run;

(_iorc_ can be replaced by any other variable with a non-missing value, e.g. _n_, _error_, etc.).

 

Honestly, I have never used PROC CALL STDIZE for this purpose and I don't know if it's slower than the traditional DO-loop approach. Readability of the code is another potential issue.

View solution in original post

10 REPLIES 10
Tom
Super User Tom
Super User

You cannot refer to multiple variables in an array like that. And you cannot assign values to multiple variables at once.

 

Referencing an array name without an explicit index, as in your example, will just use an implicit index.  That is how the DO OVER statement works.  If you didn't define a different variable to use for the implicit index then the default is to use _I_.  So your error message is saying that the variable _I_ has a value that is not an integer between 1 and the number of elements in the array. Most likely because it has a missing value instead.

 

You can READ from multiple variables in the array at once by using the syntax of an explicit index with a value of *.  For example to find the average value you could do:

mean_have = mean(of have(*));

 

PaigeMiller
Diamond | Level 26

@Tom wrote:

And you cannot assign values to multiple variables at once.

 


The documentation says you can. And it works for me.

--
Paige Miller
Tom
Super User Tom
Super User

@PaigeMiller wrote:

@Tom wrote:

And you cannot assign values to multiple variables at once.

 


The documentation says you can. And it works for me.


That would be interesting development.  Can you show an example?

 

The link you posted before is the syntax for how to set the INITIAL values of an array when the array is defined. Not how to assign values during the execution phase of a data step.

hashman
Ammonite | Level 13

@PaigeMiller :

 

The documentation says you can do it at compile time, not at run time.

Concur on that with @Tom .

 

Kind regards

Paul D.

hashman
Ammonite | Level 13

@Tom :

Methinks by using the [*] directive, you tell SAS to go over all the variables in the list. But it doesn't mean that when SAS executes this directive, it reads all the variables "at once". In fact, I bet that the underlying software reads them one at a time, except it does it much quicker compared to an explicit DO loop in the overlying software.

 

"And you cannot assign values to multiple variables at once."

That's true if you are talking about the assignment statement. However, there exists a SAS call routine that can set the values of multiple array variables to specific values all at once without ever using the assignment statement. Say, you have an 5-item array with the values 0-4 and you want to change them to 5-9. Then:

 

data _null_ ;                                                                                       
  array have[*] a b c d e (0 1 2 3 4) ;                                                             
  put "have = " have[*] ;                                                                           
  call pokelong (cats(put(5,rb8.),put(6,rb8.),put(7,rb8.),put(8,rb8.),put(9,rb8.)),addrlong(a),40) ;
  put "have = " have[*] ;                                                                           
run ;                                                                                               

Of course, the concatenation of the replacement values into the string of their respective RB8 memory images can be done much sleeker than shown above - I've dumbed it down just to illustrate the concept. Normally, you'd parameterize the replacement values in some manner and concat them in a loop at _N_=1.

 

If all the array items have to be set to one and the same value, it's still simpler since you can use the REPEAT function to create the replacement RB8 concatenation string. For example, if, as in the OP's question, you want to replace them all with zeroes:

data _null_ ;                                           
  array have[*] a b c d e (0 1 2 3 4) ;                 
  put "have = " have[*] ;                               
  call pokelong (repeat(put(0,rb8.),4),addrlong(a),40) ;
  put "have = " have[*] ;                               
run ;                                                   

That said, I'd prefer the @FreelanceReinh method since it doesn't require to understand the intricacies of the physical addressing needed to use the APP functions conscientiously, even though it's a bit (~20%) slower at a high number of iterations:

data _null_ ;                                              
  array have[*] a b c d e (0 1 2 3 4) ;                    
  put "have = " have[*] ;                                  
  r = put (repeat (put (0, rb8.), 4), $40.) ;              
  addra = addrlong (a) ;                                   
  do i = 1 to 1e8 ;                                        
    call pokelong (r, addra, 40) ;                         
  end ;                                                    
  put "have = " have[*] ;                                  
run ;                                                      
                                                           
data _null_ ;                                              
  array have[*] a b c d e (0 1 2 3 4) ;                    
  put "have = " have[*] ;                                  
  do i = 1 to 1e8 ;                                        
    call stdize('replace','mult=',0,of have[*],_iorc_);   ;
  end ;                                                    
  put "have = " have[*] ;                                  
run ;                                                      

Kind regards

Paul D.     

 

 

 

FreelanceReinh
Jade | Level 19

Hello @hubertsng and welcome to the SAS Support Communities!

 

There are some CALL routines and a few functions which can change the values of several variables (e.g. the elements of an array) at the same time. In your case CALL STDIZE can do the trick:

 

If the original values are all non-missing:

data _null_;
array x[4] (2 3 5 7);
call stdize('mult=',0,of x[*]);
put x[*];
run;

If some (but not all) may be missing:

data _null_;
array x[4] (2 3 . 7);
call stdize('replace','mult=',0,of x[*]);
put x[*];
run;

If you cannot rule out that all are missing:

data _null_;
array x[4];
call stdize('replace','mult=',0,of x[*],_iorc_);
put x[*];
run;

(_iorc_ can be replaced by any other variable with a non-missing value, e.g. _n_, _error_, etc.).

 

Honestly, I have never used PROC CALL STDIZE for this purpose and I don't know if it's slower than the traditional DO-loop approach. Readability of the code is another potential issue.

hashman
Ammonite | Level 13

@FreelanceReinh :

Thanks 1e6 for posting it - always good to learn something new and utile. 

 

Perhaps I coould return the favor by pointing out that, in addition to CALL POKE(LONG) I've mentioned earlier, there's another call routine designed exactly for the purpose - namely, CALL FILLMATRIX. It has a couple of shortcomings, to wit:

  • it works only for temporary and 1-based arrays
  • it has to be compiled into another call routine via FCMP (though it isn't a big problem - see below)

However, where it's applicable, i.e. temporary, 1-based arrays, it vastly outperforms STDIZE:

%let d = %eval (2**10) ;                                  
%let n = 1e7 ;                                            
                                                          
data _null_ ;                                             
  array h[&d] _temporary_ ;                               
  do n = 1 to &n ;                                        
    call stdize ('replace', 'mult=', 0, of h[*], _iorc_) ;
  end ;                                                   
run ;                                                     
                                                          
proc fcmp outlib=work.f.f ;                               
  subroutine fillarray (a[*], value) ;                    
    outargs a ;                                           
    call fillmatrix (a, value) ;                          
  endsub ;                                                
quit ;                                                    
                                                          
option cmplib=work.f ;                                    
                                                          
data _null_ ;                                             
  array h[&d] _temporary_ ;                               
  do n = 1 to &n ;                                        
    call fillarray (h, 0) ;                               
  end ;                                                   
run ;                                                     

Running it shows that FILLARRAY does the job about 15 times faster than STDIZE (4 seconds vs 1 minute in my 9.4 Windows test). 

 

Kind regads

Paul D.

   

FreelanceReinh
Jade | Level 19

@hashman: Thanks for reminding me of the matrix operation routines available in PROC FCMP. These can be particularly helpful for users (like me) who don't have a SAS/IML license. They've mostly been under my radar because they are not listed in the table "SAS Functions and CALL Routines by Category," which I use very often. Indeed, that's how I came up with the CALL STDIZE idea: I went through the list of CALL routines ...

 

I'm not surprised that the CALL FILLMATRIX routine, which is "designed exactly for the purpose," performs better than CALL STDIZE, even after dropping "'replace'" and "_iorc_" (which triples the speed of CALL STDIZE in your example).

 

Thank you, @hubertsng, for sparking such a fruitful discussion.

hashman
Ammonite | Level 13

@hubertsng :

You can use your syntax to do what you need if you've define the array as implicitly subscripted in the first place. E.g.:

data _null_ ;                    
  array have a b c d e ;         
  do over have ;                 
    have = ceil (ranuni(1) * 9) ;
  end ;                          
  put "have = " have[*] ;        
  do over have  ;                
    have = 0 ;                   
  end ;                          
  put "have = " have[*] ;        
run ;                            

The log will show:

have = 2 9 4 3 9
have = 0 0 0 0 0

If you've defined the array as explicitly subscripted, you'll have to use an explicit index, e.g.:

data _null_ ;                            
  array have[*] a b c d e ;              
  do i = lbound (have) to hbound (have) ;
    have[i] = ceil (ranuni(1) * 9) ;     
  end ;                                  
  put "have = " have[*] ;                
  do i = lbound (have) to hbound (have) ;
    have[i] = 0 ;                        
  end ;                                  
  put "have = " have[*] ;                
run ;                                    

with the same result.

 

Kind regards

Paul D. 

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!

Mastering the WHERE Clause in PROC SQL

SAS' Charu Shankar shares her PROC SQL expertise by showing you how to master the WHERE clause using real winter weather data.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 10 replies
  • 7989 views
  • 6 likes
  • 5 in conversation