BookmarkSubscribeRSS Feed
julesvdz
Calcite | Level 5

I have isolated my problem into a small piece of code. In this example I am passing a parameter to a macro. The parameter is a variable name that can be either a numeric or a character variable. Depending on the type I want to copy the value to a new variable inside the macro.

 

 

%macro test(name=);
  
  length numvar    8 
         charvar $ 200;

  if vtype(&name) = "N" then do; numvar = &name; end;
  if vtype(&name) = "C" then do; charvar = &name; end;

%mend test;

data work.test;
  set sashelp.class;
  %test(name=age);
  %test(name=name);
run;

 

The problem is that this throws a note (either "character values have been converted to numeric" or vice versa) which I don't like. I think it happens because SAS will always parse the code, so will even parse "charvar = &name" even is &name is a numeric variable (and vice versa again).

 

I don't want to add additional parameters, nor can I change this macro to be used from outside the datastep.

 

What are your thoughts?

 

edit: slightly modified the code by adding a second macro call with a variable of a different type.

8 REPLIES 8
novinosrin
Tourmaline | Level 20

are you looking to do this?

 


%macro test(name=);
  
  length numvar    $8          charvar $ 200;

  if vtype(&name) = "N" then do; numvar = "&name"; end;
  if vtype(&name) = "C" then do; charvar = "&name"; end;

%mend test;

data work.test;
  set sashelp.class;
  %test(name=age);
run;
julesvdz
Calcite | Level 5

Took me a while to figure out you added the quotation marks. That wouldn't work well either because now you're assigned a character variable to a numeric variable, and you're adding the literal variable name instead of the value.

ballardw
Super User

Please explain exactly what you are attempting and why you are concerned about that note but not concerned that you are creating empty variables? Since your macro would create exactly two variables and if you try to use it with 3 you would overwrite one of the previously created versions I do not see what you are actually gaining over:

Numvar= age;

Charvar= name;

 

The only actual purpose I could see would be to create a length 200 character variable from a shorter one.

By default a new numeric variable is set to length 8 even if assigned the value of an existing shorter variable. So your length 8 for numvar isn't needed. Also the length of character variable will be inherited so unless the actual purpose is make a longer variable you don't need length for charvar. See this example:

data junk;
   length x 4 text $ 100;
   input x text $;
datalines;
16   word
0     a
333333333 b
;
run;

data junk2;
   set junk;
   y=x;
   z=text;
run;

Y is length 8 and z length 100 in Junk2.

 

julesvdz
Calcite | Level 5

This small example comes from a larger macro that processes various date input formats to various date output formats, including numeric and character sources. It can be called multiple times from within the same data step. It doesn't matter that the results from run 2 overwrite run 1 because once processed, results from run 1 are no longer necessary. In fact, both variables are cleared (call missing) each time but I have not included that in my example.

 

I can remove the length statement but that would result in both variables being created as the same type (because of that same inheritance, e.g. both numeric if the first macro call uses a numeric variable) which would make the 2nd macro run go wrong if it uses a character input variable.

ballardw
Super User

You don't do yourself any favors by providing a short version of macro. With more details comes better suggestions.

 

Generally I would say any reliance of type conversion between variables is suboptimal coding and an appropriate INPUT or PUT statement should be used. Relying on a macro to detect variable type means that you are likely going to have to settle for such notes in your logs. Unless you want to turn off all notes. Is there some reason you don't know what type the variables are before you start?

 

Can you provide explicit example of the date "character" values? It may be that a more sophisticated use of INPUTN gets you around some of the issue.

 

Also once a value is a date value then the informat should not matter and the desired format could be used as needed after the data step. So the only utility I see is converting the character version to numeric with an appropriate informat. Which, as I understand this so far, means that the entire bit of if testing the current value to be numeric is not needed, unless by "date" you mean numeric values of literally 20181230 that should likely have been read differently earlier in your process as dates. By any chance is Proc Import being used frequently to read data in your shop?

Tom
Super User Tom
Super User

The problem is that even though the IF statement prevent those statements from running SAS is still sending the note.

 

What you really need to do is test the type of the variable in the source dataset and then only generate the valid assignment statements. You can do that with a macro that opens the source dataset and checks the type of the specified variable. 

Like this one: https://github.com/sasutils/macros/blob/master/varexist.sas

 

Then your program becomes something like this instead.

%macro test(ds,name);
length numvar 8 charvar $200 ; 
call missing(numvar,charvar); 
%if %varexist(&ds,&name,type)=N %then %do;
   numvar = &name; 
%end;
%if %varexist(&ds,&name,type)=C %then %do;
   charvar = &name; 
%end;

%mend test;

Example:

643   options mprint;
644   data work.test2;
645     set sashelp.class(obs=1);
646     %test(name=age,ds=sashelp.class);
MPRINT(TEST):   length numvar 8 charvar $200 ;
MPRINT(TEST):   call missing(numvar,charvar);
MPRINT(TEST):   numvar = age;
647     %test(name=name,ds=sashelp.class);
MPRINT(TEST):   length numvar 8 charvar $200 ;
MPRINT(TEST):   call missing(numvar,charvar);
MPRINT(TEST):   charvar = name;
648   run;

NOTE: There were 1 observations read from the data set SASHELP.CLASS.
NOTE: The data set WORK.TEST2 has 1 observations and 7 variables.
Ksharp
Super User
data class; 
 set sashelp.class;
run;



%macro xxxx(lib=,dsn=,vname=);
%let t="%sysfunc(prxchange(s/\s+/" "/,-1,%upcase(&vname)))";
data _null_;
 set sashelp.vcolumn(keep=libname memname name type 
where=(libname="%upcase(&lib)" and memname="%upcase(&dsn)" and upcase(name) in (&t))) end=last;
if _n_=1 then call execute("data test;set &lib..&dsn;");
if type='num' then call execute(catt('num_',name,'=',name,';'));
 else  call execute(catt('char_',name,'=',name,';'));
if last then call execute('run;');
run;
%mend;

%xxxx(lib=work,dsn=class,vname=age name sex)

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 8 replies
  • 1039 views
  • 0 likes
  • 6 in conversation