SAS macro error more positional parameters found than defined - sas

I created a macro using SAS version 9.4 to read data from DTAQ zip dataset. the code looks like the following (it is reading data from DTAQ trade file one day at a time):
libname rasheek "D:\Rasheek I\";
*** Loop for processing DTAQ_2.1 data;
%macro ZipRead_vars(rawzip,rawfile,outdata,outdata1, ddt,round,macrofile);
%do rd=1 %to &round;
%global zipfile mb outdt outdt1 datadate;
%let zipfile=%scan(&rawzip,&rd,'*');
%let mb=%scan(&rawfile,&rd,'*');
%let datadate=%scan(&ddt,&rd,'*');
%let outdt=%scan(&outdata,&rd,'*');
%let outdt1=%scan(&outdata1,&rd,'*');
%scan(&macrofile,1,'#');
%end;
%mend ZipRead_vars;
%ZipRead_vars(D:\DTAQ_RAW\2014\201401\Trd\EQY_US_ALL_TRADE_20140102.zip*
D:\DTAQ_RAW\2014\201401\Trd\EQY_US_ALL_TRADE_20140103.zip*,
taqtrade20140102*taqtrade20140103*,
rasheek.trd_20140102*rasheek.trd_20140103*,
rasheek.trd_20140102_1*rasheek.trd_20140103_1*,
20140102*20140103*,2,%TRD_Read21;#);
%macro TRD_Read21;
filename trd zip "&zipfile" member="&mb";
Data &outdt;
Infile trd firstobs=2 missover;
input
#1 hh 2.
#3 mm 2.
#5 ss 2.
#7 ss1 3.
#1 time 9.
#10 Exchange $1.
#11 Symbol $16.
#27 SaleCondition $4.
#31 TradeVolume 9.
#40 TradePrice 11.4
date=&datadate;
    ticker=compress(symbol);
    stime=hh*3600+mm*60+ss+0.001*ss1;
    DollarVolume=TradeVolume*TradePrice;
    if stime<34200 or stime>57600 then delete;
run;
proc sql;
create table &outdt1 as
select date, symbol,SaleCondition, sum(tradevolume)as totaltradevolume, sum(dollarvolume)as totaldollarvolume
from &outdt
group by date, symbol, SaleCondition
;
quit;
%mend TRD_Read21;
When I run the code I get error: more positional parameters found than defined. However, if I change the last part of the code to the following it works. 
create table &outdt1 as
select * from &outdt
;
quit;
%mend TRD_Read21;
please help/guide me in fixing the code. Thank you in advance
 

One thing you can do to avoid the more positional parameters error message is to always call the macro using the parameter names. That way you know which parameter is getting which value in your call. SAS allows you to use the parameter names in the call even for parameters that are defined such that they can also be called by position.
So let's do that for your macro call:
%ZipRead_vars
(rawzip=D:\DTAQ_RAW\2014\201401\Trd\EQY_US_ALL_TRADE_20140102.zip
*D:\DTAQ_RAW\2014\201401\Trd\EQY_US_ALL_TRADE_20140103.zip*
,rawfile=taqtrade20140102*taqtrade20140103*
,outdata=rasheek.trd_20140102*rasheek.trd_20140103*
,outdata1=rasheek.trd_20140102_1*rasheek.trd_20140103_1*
,ddt=20140102*20140103*
,round=2
,macrofile=%TRD_Read21;#
);
The value for that last parameter, MACROFILE, looks strange. You want to put the text generated by the macro %TRD_READ21 as the string to pass into the macro %ZipRead_vars? Does that macro only generate text? If it generates code then the code it generates will be passed as the value of the MACROFILE parameter instead of being executed by SAS.
If you want to pass the NAME of a macro to call then do not include the % in the call.
...,macroname=TRD_READ21,....
Then in your macro definition you can use the name to call the macro.
%let macroname=%scan(&macroname,1,#);
%&macroname;

Related

SAS Macro variable escaping apostrophe in variable name Proc Http

I have been working on this for 3 days now and have tried all I can think of including %str(),%bquote(), translate() and tranwrd() to replace single apostrophe with double apostrophe or %’
The below data step and macro work fine until I hit a last name which contains an apostrophe e.g. O’Brien. I then encounter syntax errors due to un closed left parentheses. The below code I have left what I thought was closest to working with the tranwrd included.
Any assistance you can provide is greatly appreciated.
%macro put_data (object1,id);
Proc http
method=“put”
url=“https://myurl/submissionid/&id”
in=&object1;
Headers “content-type”=“application/json”;
Run;
%mend;
data _null_;
Set work.emp_lis;
call execute(catt(‘%put_data(‘,’%quote(‘’{“data”:{“employeeName”:”’,tranwrd(employeeName,”’’”,”’”),’”}}’’),’,id,’)’));
run;
Craig
There are a wide potential of problems in constructing or passing a json string in SAS macro. Proc JSON will produce valid json (in a file) from data and that file in turn can be specified as the input to be consumed by your web service.
Example:
data have;
length id 8 name $25;
input id& name&;
datalines;
1 Homer Simpson
2 Ned Flanders
3 Shaun O'Connor
4 Tom&Bob
5 'X Æ A-12'
6 Goofy "McDuck"
;
%macro put_data (data,id);
filename jsonfile temp;
proc json out=jsonfile;
export &data.(where=(id=&id));
write values "data";
write open object;
write values "name" name;
write close;
run;
proc http
method="put"
url="https://myurl/submissionid/&id"
in=jsonfile
;
headers "content-type"="application/json";
run;
%mend;
data _null_;
set have;
call execute(cats('%nrstr(%put_data(have,',id,'))'));
run;
I was able to find issue with my code with the tranwrd statement was backwards and it needed to be moved to the proc sql create table statement. I also needed to wrap &object1 in %bquote. This was the final code that worked.
When creating table wrap variables in tranwrd as below.
tranwrd(employeeName, “‘“,”’’”)
% macro put_data (object1,id);
Proc http
method=“put”
url=“https://myurl/submissionid/&id”
in=%bquote(&object1);
Headers “content-type”=“application/json”;
Run;
%mend;
data _null_;
Set work.emp_lis;
call execute(catt(‘%put_data(‘,’%quote(‘’{“data”:{“employeeName”:”’,employeeName,’”}}’’),’,id,’)’));
run;
Just use actual quotes and you won't have to worry about macro quoting at all.
So if your macro looks like this:
%macro put_data(object1,id);
proc http method="put"
url="https://myurl/submissionid/&id"
in=&object1
;
headers "content-type"="application/json";
run;
%mend;
Then the value of OBJECT1 would usually be a quoted string literal or a fileref. (There are actually other forms.) Looks like you are trying to generate a quoted string. So just use the QUOTE() function.
So if your data looks like:
data emp_lis;
input id employeeName $50.;
cards;
1234 O'Brien
4567 Smith
;
Then you can use a data step like this to generate one macro call for each observation.
data _null_;
set emp_lis;
call execute(cats
('%nrstr(%put_data)('
,quote(cats('{"data":{"employeeName":',quote(trim(employeeName)),'}}'))
,',',id
,')'
));
run;
And your SAS log will look something like:
NOTE: CALL EXECUTE generated line.
1 + %put_data("{""data"":{""employeeName"":""O'Brien""}}",1234)
NOTE: PROCEDURE HTTP used (Total process time):
real time 2.46 seconds
cpu time 0.04 seconds
2 + %put_data("{""data"":{""employeeName"":""Smith""}}",4567)
NOTE: PROCEDURE HTTP used (Total process time):
real time 2.46 seconds
cpu time 0.04 seconds

SAS : ERROR Attempt to change the value of a string in PRXCHANGE

I have this code :
%global nb_usag;
%global usager_entr;
%let nb_usag=0;
%syslput nb_usag=&nb_usag;
%let usager_entr=u;
PROC SQL noprint ;
select count(distinct no_usager_entr) into :nb_usag from &lib..INSCRITS_USA_1
;
quit;
data _null_;
if &nb_usag > 0 then do;
call execute
("PROC SQL noprint ;
select distinct no_usager_entr INTO :usager_entr separated by ','
from &lib..INSCRITS_USA_1;")
;
if &usager_entr ne "u" then do;
call prxchange('s/,/\",\"',-1,&usager_entr);
end;
end;
run;
%let usager_entr="&usager_entr";
%syslput usager_entr=&usager_entr;
%put &nb_usag;
%put &usager_entr;
But the code generate this error for the function prxchange:
ERROR 135-185: Attempt to change the value of the constant 's/,/\",\"' in the PRXCHANGE subroutine call.
What I am doing wrong?
I want to modify every , in the variable usager_entr with ",".
For example, if usager_entr = 12121212,34343434,56565656 it will become 12121212","34343434","56565656.
In my case the table &lib..INSCRITS_USA_1 is empty, then nb_usag=0.
Thanks!
Too much fooling around! Instead, create the double quoted list of values directly from Proc SQL
Presuming the list will be used in a later clause with the construct IN (&myList).
%let usager_entr_dq_csv_list = "redundant safety value that will never match anything";
PROC SQL noprint ;
select distinct quote(trim(no_usager_entr))
INTO :usager_entr_dq_csv_list separated by ','
from &lib..INSCRITS_USA_1;
The redundant value is needed because if it were empty you would later generate IN () and have a syntax error.
If the list is being used in pass-through SQL you will want to use the additional arguments of QUOTE function to bound the values with a single quote.
For modify your macro variable from usager_entr = 12121212,34343434,56565656 to "12121212","34343434","56565656", you could use prxchange:
%let usager_entr =12121212,34343434,56565656;
%put &usager_entr;
%let New_usager_entr=%sysfunc(prxchange(s/([1-9]+)/"$1"/, -1,%quote(&usager_entr)));
%put &New_usager_entr;

ERROR 180-322: Statement is not valid or it is used out of proper

I have searched some info about this error,but it seems none match mine,may someone familiar with this error take a look.
"Code generated by a SAS macro, or submitted with a "submit selected" operation in your editor, can leave off a semicolon inadvertently." it is still abstruse for me to explore in my code by this comment.although I got this error,the outcomes is right.may someone give any advice..thanks!
%let cnt=500;
%let dataset=fund_sellList;
%let sclj='~/task_out_szfh/fundSale/';
%let wjm='sz_fundSale_';
%macro write_dx;
options spool;
data _null_;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
if _n_=1 then i=cnt;
if _n_<=i then do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
end;
run;
%mend write_dx;
%write_dx();
and if I am not using macro,there is no error.
data _null_;
options spool;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
if _n_=1 then i=cnt;
if _n_<=i then do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
end;
run;
--------------------------------update----------------------------------
I add % to the keyword,but still get the same error
%macro write_dx;
options spool;
data _null_;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
%if _n_=1 %then i=cnt;
%if _n_<=i %then %do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
%end;
run;
%mend write_dx;
%write_dx();
Why did you add () to the macro call when the macro is not designed to accept any parameters? If you do that then the () are NOT processed by the macro processor and so are passed along to SAS to interpret. That is the same error message as you would get if you submitted (); by itself.
1 %macro xx ;
2 data _null_;
3 put 'Running data step in macro';
4 run;
5 %mend xx;
6 %xx();
Running data step in macro
NOTE: DATA statement used (Total process time):
real time 0.02 seconds
cpu time 0.00 seconds
6 %xx();
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
7 ****;
8 ();
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
But if you define it with 0 or more parameters.
%macro param();
generated code
%mend ;
%put |%param()|;
The macro processor will use the () and so they are not passed onto SAS to use.
|generated code|
Change
%macro write_dx;
to
%macro write_dx();
When creating a macro, you have to include the () even if no values are passed.

sas macro loop to rename variable

Hi I am trying to rename variables using SAS Macro loop.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data MilkNew;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
%Re(6)
This loop is mean to rename MT...Sp to MTSp.... Eg:MT20SP to MTSp20.
When I run my loop, there was no error but the variable names were not changed in MilkNew at all.
Where does the problem come? Thanks!
If the only purpose of the macro is to rename the variables in the data set, then why read the data with a set statement. Your data set is probably really small so you don't even realize the inefficiency of doing that. Instead use the modify statement in proc datasets to accomplish the same thing, but more efficiently. Here's an alternative macro for you.
%macro renamevar(dsname, time);
%local lib ds i;
%let lib = %sysfunc(coalescec(%scan(&dsname, -2, %str(.)), work));
%let ds = %scan(&dsname, -1, %str(.));
proc datasets lib=&lib nolist;
modify &ds;
rename
%do i = 1 %to &time;
mt&&t&i..Sp=MTSp&&t&i.
%end;
;
quit;
%mend;
%renamevar(milk, 6);
Here's the log after the macro call:
NOTE: Renaming variable mt12MthSp to MTSp12Mth.
NOTE: Renaming variable mt20Sp to MTSp20.
NOTE: Renaming variable mt30Sp to MTSp30.
NOTE: Renaming variable mt40Sp to MTSp40.
NOTE: Renaming variable mt50Sp to MTSp50.
NOTE: Renaming variable mt60Sp to MTSp60.
NOTE: MODIFY was successful for WORK.MILK.DATA.
NOTE: PROCEDURE DATASETS used (Total process time):
real time 0.00 seconds
cpu time 0.01 seconds
You should move the loop so that it only generates just the RENAME statement (or even just the old=new name pairs). What is happening now is that you keep overwriting MilkNew so only the last RENAME has any effect.
%macro Re(time);
data MilkNew;
set Milk;
%do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
run;
%mend Re;
%Re(6)
You should have seen the last variable name in the loop (so the 6th) changed. That's because you repeated the same data step with the same source dataset but a different destination - so each time you 'forgot' the changes made in the earlier step.
So, this would've worked, though I'll get in a minute to why this isn't a good way to do this.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data Milk;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
data milk;
input
MT12mthSP
MT20SP
MT30SP
MT40SP
MT50SP
MT60SP
;
datalines;
12 20 30 40 50 60
;;;;
run;
%Re(6)
Here I had it make all changes to Milk and save them back in that dataset. If you want to preserve Milk then first make Milk_New then have that in both set and data statements.
Second, you should not do a new data step for each change. Macros don't have to have a data step in them; they can be run inside the datastep.
So for example:
%macro Re(time);
%Do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
%mend Re;
data milk_new;
set milk;
%Re(6);
run;
Even better would be generating this list outside of a macro entirely - look up "generating code SAS" for suggestions on that.
If you didn't see any renames at all, you also may have an issue where a label is present on the column(s). That won't affect your usage of the variable name, but it will make it confusing. Use
label _all_;
Or include a label-clearing statement (label <varname>; where you pop in the same variable name as the original variable name before rename) inside your macro loop to fix that.

Calling a macro variable from libname

How do I call a macro variable in the from clause of proc sql if I wish to use it in a libname?
Let me show you what I mean:
options nofmterr;
libname FRST "/ecm/retail/mortgage/nbk6kra/LGD/data/frst_201312bkts";
libname HELN "/ecm/retail/mortgage/nbk6kra/LGD/data/heln_201312bkts";
libname HELC "/ecm/retail/mortgage/nbk6kra/LGD/data/helc_201312bkts";
%let pathLGD = /new/mortgage/2014Q4/LGD;
%let prod = FRST;
/**************** Segment calculation **************** Date filter to be consistent with model documentation for segmented tables****************/
%macro Performance(prod);
proc sql;
create table lgd_seg_&prod as
select distinct
SegDT_LGD_2013,
min(ScoreDT_LGD_2013) as min_range,
max(ScoreDT_LGD_2013) as max_range,
count(*) as count,
mean(lgd_ncl_adjusted) as LGD_actual,
mean(ScorePIT_LGD_2013) as LGD_pred_pit_1,
mean(ScoreDT_LGD_2013) as LGD_pred_dt_1
from "&prod."scored;
where coh_asof_yyyymm > 200612
group by 1;
quit;
PROC EXPORT DATA=lgd_seg_&prod._fs
OUTFILE= "&pathLGD./lgd_seg.xlsx"
DBMS=XLSX REPLACE;
SHEET="&prod._lgd_seg_fs";
RUN;
%mend;
%Performance(prod=FRST);
%Performance(prod=HELN);
%Performance(prod=HELC);
So in the "from" clause, the macro is supposed to read FRST.scored, HELN.scored, and HELC.scored respectively. Currently it cannot find the file, and if I were to remove the quotation marks, then it'd become "work.FRSTscored".
I hope I've made this clear. Any input and comment is appreciated.
When you want to follow the the resolved value of a macro variable with an immediate additional character you should escape the macro variable with a full stop (.). For example:
%let start = one;
%put &start.two;
%put &start..two;
%put &startend;
onetwo
one.two
WARNING: Apparent symbolic reference STARTEND not resolved.
So your code should read from &prod..scored;.
If you ever need to you can also delay the resolution of a macro variable with double ampersand (&&):
%let end = two;
%let onetwo = three;
%put &&one&end;
%put &&&start&end;
Three
Three
Or:
%let three = inception;
%put &&&&&&&start&end;
inception
Remove quotation marks applied outside the macro variable prod and use two dots after macro variable (one to signify end of macro variable name and second one to specify the table name after the libname reference.
from &prod..scored