Reset a temporary array in SAS

2019-08-21 23:54发布

问题:

After I declares an array, I'd like to reset its values for the rest of the code.

array cutoffs[4] _temporary_ (1 2 3 4); /*works well*/
... use of the array
array cutoffs[3] _temporary_ (3.5 5 7.5); /*Error*/
... use of the updated array

The error is as following :

ERROR 124-185: The variable cutoffs has already been defined.

This error is very clear but I wonder how could I reattribute the array without changing its name (which would be most tedious).

I tried some syntaxes but couldn't find by myself, and I saw no ressources on google, nor on stackoverflow.

How can I do it ?

EDIT : the main purpose is that I created a function (with proc fcmp) that take arrays as parameter and cut the value (like R's cut function). The function is to be used on a lot of columns but with different cutoffs, and I don't want to tediously create an array for each and every column.

回答1:

You can change the values of the cutoffs array one by one.

array cutoffs{4} _temporary_ (1 2 3 4); /*works well*/
... use of the array
cutoffs[1]=3.5;
cutoffs{2}=5;
cutoffs{3}=7.5;
cutoffs{4}=.;

or you could just use another name for the array the second time.

With that said, the way you are using this seems a bit strange.

EDIT: you could consider rewriting your proc fcmp function to expect the list of values as a character string (e.g. '3.5,5,7.5') instead of an array and do away with arrays entirely.

Your proc fcmp would change from something like

do i=1 to dim(array);
  val=array{i};
  ...
end;

to something like;

do i=1 to countw(array,',');
  val=input(scan(array,i,','),best32.);
  ...
end;


回答2:

Here is a macro version of your FCMP function:

%macro cut2string(var,cutoffs,values);
%if &var. lt %scan(&cutoffs.,1,%str( )) %then "%scan(&values.,1,%str( ))";
%else %if &var. ge %scan(&cutoffs.,-1,%str( )) %then "%scan(&values.,-1,%str( ))";
%else %do i=1 %to %sysfunc(countw(&cutoffs.,%str( )));
    %if &var. ge %scan(&cutoffs.,&i.,%str( )) and &var. lt %scan(&cutoffs.,%eval(&i.+1),%str( )) %then "%scan(&values.,%eval(&i.+1),%str( ))";
%end;
%mend;

And here is how you would call it, using the same example as you used in your linked page:

data Work.nonsales2;
    /*set Work.nonsales;*/
    salary_string  = %cut2string(30000, 20000 100000 500000, <20k 20k-100k 100k-500k >500k);
run;

You could use keyword parameter instead of positional to make your calls clearer:

%macro cut2string(var=,cutoffs=,values=);
...
salary_string  = %cut2string(var=30000,cutoffs=20000 100000 500000,values=<20k 20k-100k 100k-500k >500k);

HOWEVER now that I see the code, this should really be a format in SAS:

proc format;
  values cutoffs
    low-<20000='<20k'
    20000-<100000='20k-100k'
    100000-<500000='100k-500k'
    500000-high='>500k'
    ;
run;
data work.nonsales2
  salarystrings=put(30000,cutoffs.);
run;


回答3:

Why not use a macro instead of function?

%macro cut(invar,outvar,cutoffs,categories,dlm=%str( ));
/*
"CUT" continuous variable into categories
by generating SELECT code that can be used in
a data step.

The list of CATEGORIES must have one more entry that the list of CUTOFFS
*/
%local i ;
select ;
%do i=1 %to %sysfunc(countw(&cutoffs,&dlm));
  when (&invar <= %scan(&cutoffs,&i,&dlm)) &outvar=%scan(&categories,&i,&dlm) ;
%end;
  otherwise &outvar= %scan(&categories,-1,&dlm);
end;
%mend ;

Here is an example that creates both a numeric and a character output variable. For character variables either define the variable before using the macro or make sure the values for the first category is long enough for all values.

Let's test it.

data test ;
  input x @@;
  %cut(invar=x,outvar=y,cutoffs=3.5 5 7,categories=1 2 3 4)
  %cut(invar=x,outvar=z,cutoffs=3.5|5|7,categories="One  "|"Two"|"Three"|"Four",dlm=|)
cards;
2 3.5 4 5 6 7.4 8
;

If you turn on the MPRINT option you can see the generated code in the SAS log.

2275    %cut(invar=x,outvar=y,cutoffs=3.5 5 7,categories=1 2 3 4)
MPRINT(CUT):   select ;
MPRINT(CUT):   when (x <= 3.5) y=1 ;
MPRINT(CUT):   when (x <= 5) y=2 ;
MPRINT(CUT):   when (x <= 7) y=3 ;
MPRINT(CUT):   otherwise y= 4;
MPRINT(CUT):   end;
2276    %cut(invar=x,outvar=z,cutoffs=3.5|5|7,categories="One  "|"Two  "|"Three"|"Four ",dlm=|)
MPRINT(CUT):   select ;
MPRINT(CUT):   when (x <= 3.5) z="One  " ;
MPRINT(CUT):   when (x <= 5) z="Two" ;
MPRINT(CUT):   when (x <= 7) z="Three" ;
MPRINT(CUT):   otherwise z= "Four";
MPRINT(CUT):   end;

Results

Obs     x     y      z

 1     2.0    1    One
 2     3.5    1    One
 3     4.0    2    Two
 4     5.0    2    Two
 5     6.0    3    Three
 6     7.4    4    Four
 7     8.0    4    Four


标签: arrays sas