invoke function in SAS macro - sas

I have the following SAS prorgram with bugs.
proc sql
create table sigmav_n as
select std(V) as sigmav_new from bb
quit
...
sysfunc(abs(-sigmac_new))<0.01
Here V is a column name in table bb. And the program throws error as follows,
Argument I to function ABS referenced by %SYSFUNC or %QSYSFUNC macro
function is not a number.
Any body know the root cause of this?

Try this:
proc sql
select std(V) into :sigmav_new from bb
quit
...
%sysfunc(abs(-&sigmav_new.))<0.01
This would take the std dev of v, put it into a macro variable (numeric) that could then be used by abs() in the sysfunc command.
You'll probably need a let statement or something else going on or you're going to peform the abs() and the result will just 'float off' into the memory.
It's hard to tell what exactly you want to do with the value without seeing a bit more of your code.
A good SUGI paper on the %sysfunc %SYSFUNC: Extending the SAS Macro Language

Firstly why whould you negate a value before passing it to abs() ?
Nonetheless, try this:
proc sql ;
select case when (abs(std(v)) <0.01) then 1 else 0 end into :sigmav_new from bb ;
quit ;

Related

Issue Creating a Table based on Macro's Parameter Name

So I will briefly explain my code structure before I dive into the issue.
I've a macro
%Sales (Outdata= , dt =, Outdata2= , Outdata3= );
(
I create a table &outdata by (Select * from XYZ);
Proc SQL;
Create table &Outdata._1 as
(
)
%mend Sales
Now I call the macro
%Sales (Outdata = sales_final_Oct17, dt='2017-10-01');
Libname ABCDEFG
I Create a data set
Data ABCDEFG.all_sales_test;
Set ABCDEFG. all_Sales
sales_final_Oct17_1;
incur_month = month(rept_dt);
run;
Above (1 to 3) is the original code flow and it works fine.
My Problem:
I'm using a dynamic way of generating file name for each month (so that each month I do not manually enter file_name_month and date.
File name code
%let Last_Month = intnx('month', current_date,-1, "beginning");
Name = 'Sales_final';
Last_Month_Name = name|| put(&last_month, monyy7.);
Call SYMPUTX('Last_Month_Name_v', Last_Month_Name);
run;
Call Macro
%Sales(outdata=&Last_Month_Name, dt = 'Dynamic date');
Till this point everything works fine. The moment I create a data set similar to step 3 (above), the code breaks.
Libname ABCDEFG
Data ABCDEFG.all_sales_test;
Set ABCDEFG.all_Sales
Last_Month_Name_1;
incur_month = month(rept_dt);
run;
> Error Message: File ABCDEFG.LAST_MONTH_NAME_1.DATA does not exist.
What should I do to get rid of this error? It seems, if I pass a static name in the macro and then use the same name with "_1" it works fine but when I pass dynamic reference, then the data Set step fails with the above error message.
Any help is much appreciated. I'm new to SAS so excuse me if it's a silly question. Thanks.
In (1) the macro code is using the value of the macro parameter (or local macro variable) OUTDATA to create a dataset. In (2) you are supplying a value for OUTDATA in your call and in (3) you are using the same value again in the set statement.
One way to not have to type the value twice is to store the value into a macro variable then just reference that macro variable's value in steps (2) and (3).
So in (4) you did create a macro variable,Last_Month_Name_v , but you then used the value of a different macro variable, &Last_Month_Name in the macro call. But instead of using the macro variable in the set statement you just referenced some other dataset,Last_Month_Name_1 , that you never mentioned before at all.
Here are the simplified key steps in the process you want for how to create and use the macro variable. I have put in ... to show where I have left out parts of a statement or statements so we can concentrate on the flow of the macro variable and its value.
First you set the macro variable to some name that you want to use. Let's just use anything as the name for this example.
%let last_month_name= anything;
Then you use the value in the macro call to create the dataset. Notice the & before the name, that is what tells the macro processor to replace the name with the value. The period after the name tells the macro processor that is the end of the macro variable name.
%sales(outdata=&last_month_name. .... )
Then you can use the value again later when you want to tell the set statement which dataset to read.
set .... &last_month_name. ;
Now your posted macro %sales does not actually create the dataset named anything. Instead it appears to create a dataset named anything_1. Personally I don't know why that is there, but if you keep it that way then you need add the _1 back to the end of the macro variable's value in the set statement. Just do it in the same way that you did in the macro's code.
set .... &last_month_name._1 ;
Working with macros is one of the harder parts of SAS and can be very confusing to newcomers. Below is a simplified working example that demonstrates the approach I would take.
First we dynamically calculate the name of the table we want to save the results to:
%let current_date = %sysfunc(date());
%let last_month = Sales_final_%sysfunc(intnx(month, &current_date, -1, beginning), monyy7.);
%put &=last_month;
The output from the put statement in the above step is:
LAST_MONTH=Sales_final_OCT2017
Note that in the above code, I'm passing two parameters to %sysfunc(). The first parameter is the call to the intnx() function. The second parameter (monyy7.) is what format to apply to the result being returned from the function call.
Also note that there is no need to concatenate the prefix (Sales_final_) to the %sysfunc() result because when working with the macro language, the result of %sysfunc() is subsituted in place. There is no concatenation operator in the macro language at all - everything is based off of macro substitution.
Then it's a simple case of passing that value into the macro as shown below:
%macro sales(outdata=);
proc sql;
create table &outdata._1 as select * from sashelp.class;
quit;
%mend;
%sales(outdata=&last_month);
You should be able to modify the above into what you need.

SAS - Dynamically creating macro variable via concatenation of text to a current macro variable

I'm looking to create a macro variable. The macro variable name needs to be made by concatenating text to an already existing variable after it has resolved. Specifically, I want to do this in a PROC SQL block using INTO:. Here's a snippet to explain what I want to do.
%macro MyMacro(process);
PROC SQL;
SELECT
COUNT(*) INTO: &process._new_text
FROM
DataSetHere
;QUIT;
%mend MyMacro;
If I call on this macro and I pass the word "cat" into process, I want to have now declared/initialized a variable with the name cat_new_text, and it should return the COUNT(*) selected in that query whenever &cat_new_text is referenced.
I've done a little reading around, looked into using multiple ampersands, trying to resolved &process within quotes first - nothing has really solved my exact problem. Does anyone know of a clear cut way to accomplish this?
Thanks in advance!
Your code seems fine and it seems like it would do exactly what you describe.
However if your trying to "access" that new macro variable outside the macro, e.g.,
%MyMacro(cat);
%put &cat._new_text.;
then indeed it will not work because the variable was created locally in your macro and does not exist outside the scope of that macro.
To fix that, you simply need to add a %global statement in your macro definition:
%macro MyMacro(process);
%global &process._new_text;
PROC SQL;
SELECT COUNT(*)
INTO: &process._new_text
FROM DataSetHere
;
QUIT;
%mend MyMacro;

In SAS how do i assign a macro variable to the current week number?

I'm assigning a macro variable as such:
%let weeks2=31 ;
but instead of 31 i want the variable to automatically be the current weeknumber.
I've tried using weeks2=week(today(), 'u') but this returns an error.
Any ideas?
Thanks in advance!!
You've got the right idea. However, you are trying to use SAS functions (week and today) in a macro statement (%let). When doing so, you have to call each SAS function using the %sysfunc macro function:
%let weeks2=%sysfunc(week(%sysfunc(today()), u));
Also, arguments need not be quoted in macro functions (the u argument in this case).

Cycling through all variables

I started to learn SAS here fairly recently and am getting the basics down pretty well, but have a question regarding something that is a little outside of my current realm of knowledge. Does anyone happen to know of a way to cycle through all variables in a SAS dataset? I know how to run a do loop/array on variables in a range (x1-x99), but ideally would like to look at every variable without having to rename any variables. Basically, I'm looking to run through a dataset and change variable values when the current value = 'True'/'False'. My guess is that I'll need to use proc contents in someway here, but not really sure how to go about using it correctly. Any tips/insight would be greatly appreciated. Thanks!
You can create an array of non-similarly-named variables. You're on the right track with PROC CONTENTS, although you also can use dictionary.columns or sashelp.vcolumn, which contain basically the same information.
proc sql;
select name into :collist separated by ' '
from dictionary.columns
where memname='DATASETNAME' and libname='LIBNAME' and <other criteria>;
quit;
The variables have to be all of the same type (char/numeric) so you may want to include a criterion of variable type in your query, plus any other limiting factor you may need.
That will create a list, &collist., in a macro variable you can use in your array
array vars &collist.;
and now you can loop over the array.
You may also be able to cheat things, if all of your variables are the same type, and you know the order is fixed . The double dash list (x1--x99) is 'in variable order, all variables from x1 to x99' and doesn't require numeric suffixes or anything like that.
Finally, you also might be able to write a format in PROC FORMAT to accomplish what you need, depending on what you are intending to do (mapping TRUE to 1 and FALSE to 0 or something like that).
Adding to Joe's answer: you can overcome the requirement that all variables should be of the same type. For that you can use macro loop instead of array. Firstly you need to define the macro:
%macro loop;
%do i=1 %to %sysfunc(countw(&collist));
....
<here goes your code for changing values, where instead of a variable name
you use macro function %scan(&collist,&i)>
....
%end;
%mend loop;
and now you can paste %loop into the DATA step where you're going to process all variables.

How does SAS macro quoting interact with format literals?

Executing locally in a clean session:
%let x = %str(put(age, best.));
proc sql;
select &x from sashelp.class;
quit;
This generates the following error:
1 put(age, best.)
----
22
----
76
ERROR 22-322: Syntax error, expecting one of the following: a format name, ?.
ERROR 76-322: Syntax error, statement will be ignored.
But this "manually-resolved" version runs without notes, warnings or errors:
proc sql;
select put(age, best.) from sashelp.class;
quit;
Can somebody explain exactly what %str() is doing in this program that causes an issue at execution time? Apologies for the vague question, but I am unsure what the relevant interactions are; I cannot replicate using equivalent data-step syntax so perhaps proc SQL peculiarities are involved?
The %str() function masks a character string during macro compilation. Remove the %str() function in the let statement or add an %unquote() function in the sql select to have if resolve correctly.
Answered at this question on runsubmit.com:
I'm going to mark this answer as
correct because it led me to this page
of documentation:
http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#tw3514-unquote.htm
- "In rare cases, masking text with a macro quoting function changes the way
the word scanner tokenizes the text
... The word scanner does not use it
as the boundary of a literal token in
the input stack". Sounds like a bug,
frankly, but if the tokenizer
algorithm is as ancient and hairy as I
imagine, I'd spin it as a quirk too!
Can you use a format statement instead? For example, this works just fine.
%let x = %str( age format=best.);
proc sql;
select &x. from sashelp.class;
quit;
For some reason SAS doesn't like the "best." format.
i.e. when I try this, your code works
%let x = %str(put(age, 8.));
????
If you add this to your code
%put _user_ ;
you will see how &x is quoted by %str, in the log. That is why the proc sql code doesn't work. Using %Unquote in the select portion of the proc sql statement will allow the code to run.
http://www2.sas.com/proceedings/forum2007/152-2007.pdf