Allan Bowe
Allan Bowe

Reputation: 12691

put values to a file using functions without creating new variables

I am processing a dataset, the contents of which I do not know in advance. My target SAS instance is 9.3, and I cannot use SQL as that has certain 'reserved' names (such as "user") that cannot be used as column names.

The puzzle looks like this:

data _null_;

  set some.dataset;  file somefile;

  /* no problem can even apply formats */
  put name age;

  /* how to do this without making new vars? */
  put somefunc(name) max(age);

run;

I can't put var1=somefunc(name); put var1; as that may clash with a source variable named var1.

I'm guessing the answer is to make some macro function that will read the dataset header and return me a "safe" (non-clashing) variable, or an fcmp function in a format, but I thought I'd check with the community to see - is there some "old school" way to outPUT directly from a function, in a data step?

Upvotes: 1

Views: 195

Answers (3)

Robert Penridge
Robert Penridge

Reputation: 8513

I think you would be pretty safe choosing some unlikely to collide names. An easy way to generate these and still make the code somewhat readable would be to just hash a string to create a valid SAS varname and use a macro reference to make the code readable. Something like this:

%macro get_low_collision_varname(iSeed=);
  %local try cnt result;  
  %let cnt = 0;
  %let result = ;
  %do %while ("&result" eq "");
    %let try = %sysfunc(md5(&iSeed&cnt),hex32.);
    %if %sysfunc(anyalpha(%substr(&try,1,1))) gt 0 %then %do;
      %let result = &try;
    %end;
    %let cnt = %eval(&cnt + 1);
  %end;  
  &result
%mend;

The above code takes a seed string and just adds a number to the end of it. It iterates the number until it gets a valid SAS varname as output from the md5() function. You could even then test the target dataset name to make sure the variable doesn't already exist. If it does build that logic into the above function.

Test it:

%let my_var = %get_low_collision_varname(iSeed=this shouldnt collide);    
%put &my_var;

data _null_;
  set sashelp.class;
  &my_var = 1;
  put _all_;
run;

Results:

Name=Alfred Sex=M Age=14 Height=69 Weight=112.5 C34FD80ED9E856160E59FCEBF37F00D2=1 _ERROR_=0 _N_=1
Name=Alice Sex=F Age=13 Height=56.5 Weight=84 C34FD80ED9E856160E59FCEBF37F00D2=1 _ERROR_=0 _N_=2

This doesn't specifically answer the question of how to achieve it without creating new varnames, but it does give a practical workaround.

Upvotes: 0

Richard
Richard

Reputation: 27508

The PUT statement does not accept a function invocation as a valid item for output.

A DATA step does not do columnar functions as you indicated with max(age) (so it would be even less likely to use such a function in PUT ;-)

Avoid name collisions

My recommendation is to use a variable name that is highly unlikely to collide.

_temp_001 = somefunc(<var>);
_temp_002 = somefunc2(<var2>);
put _temp_001 _temp_002;

drop _temp_:;

or

%let tempvar = _%sysfunc(rand(uniform, 1e15),z15.);
&tempvar = somefunc(<var>);
put &tempvar;

drop &tempvar;
%symdel tempvar;

Repurpose

You can re-purpose any automatic variable that is not important to the running step. Some omni-present candidates include:

  • numeric variables:
    • _n_
    • _iorc_
    • _threadid_
    • _nthreads_
    • first.<any-name> (only tweak after first. logic associated with BY statement)
    • last.<any-name>
  • character variables:
    • _infile_ (requires an empty datalines;)
    • _hostname_
  • avoid
    • _file_
    • _error_

Upvotes: 1

data _null_
data _null_

Reputation: 9109

Temporary array?

34         data _null_;
35            set sashelp.class;
36            array _n[*] _numeric_;
37            array _f[3] _temporary_;
38            put _n_ @;
39            do _n_ = 1 to dim(_f);
40               _f[_n_] = log(_n[_n_]);
41               put _f[_n_]= @;
42               end;
43            put ;
44            run;

1 _f[1]=2.6390573296 _f[2]=4.2341065046 _f[3]=4.7229532216
2 _f[1]=2.5649493575 _f[2]=4.0342406382 _f[3]=4.4308167988
3 _f[1]=2.5649493575 _f[2]=4.1789920363 _f[3]=4.5849674787
4 _f[1]=2.6390573296 _f[2]=4.1399550735 _f[3]=4.6298627986
5 _f[1]=2.6390573296 _f[2]=4.1510399059 _f[3]=4.6298627986
6 _f[1]=2.4849066498 _f[2]=4.0483006237 _f[3]=4.4188406078
7 _f[1]=2.4849066498 _f[2]=4.091005661 _f[3]=4.4367515344
8 _f[1]=2.7080502011 _f[2]=4.1351665567 _f[3]=4.7229532216
9 _f[1]=2.5649493575 _f[2]=4.1351665567 _f[3]=4.4308167988

Upvotes: 2

Related Questions