I need to form a line concatenating numbers and strings, but separated by a space.
I tried it in 5 ways, but it didn't give the desired result.
%LET lim1 = 113;
%LET lim2 = 166;
Test 1:
%LET linha = %SYSFUNC(CATS(De,&lim1,a,&lim2,clientes));
%PUT &linha;
Out 1:
De113a166clientes
Test 2:
%LET linha = %SYSFUNC(CATS('De ',&lim1,' a ',&lim2,' clientes'));
%PUT &linha;
Out 2 (Error):
30 %LET linha = %SYSFUNC(CATS('De ',&lim1,' a ',&lim2,' clientes'));
NOTE: Line generated by the macro function "SYSFUNC".
30 'De '113' a '166' clientes'
_____ _____
49 49
NOTE 49-169: The meaning of an identifier after a quoted string might change in a future SAS release. Inserting white space
between a quoted string and the succeeding identifier is recommended.
Test 3:
%LET linha = %SYSFUNC(CATX(' ','De ',&lim1,' a ',&lim2,' clientes'));
%PUT &linha;
Out 3 (Error):
29 %LET linha = %SYSFUNC(CATX(' ','De ',&lim1,' a ',&lim2,' clientes'));
NOTE: Line generated by the macro function "SYSFUNC".
29 'De '' '113' '' a '' '166' '' clientes'
________ ___________
49 49
NOTE 49-169: The meaning of an identifier after a quoted string might change in a future SAS release. Inserting white space
between a quoted string and the succeeding identifier is recommended.
Test 4:
%LET linha = %SYSFUNC(CATX(' ',De,&lim1,a,&lim2,clientes));
%PUT &linha;
Out 4 (Error):
NOTE: Line generated by the macro function "SYSFUNC".
29 De' '113' 'a' '166' 'clientes
___ ___ ___ ___
49 49 49 49
NOTE 49-169: The meaning of an identifier after a quoted string might change in a future SAS release. Inserting white space
between a quoted string and the succeeding identifier is recommended.
Test 5:
%LET linha = %SYSFUNC(CATX(*,De,&lim1,a,&lim2,clientes));
%PUT &linha;
Out 5:
De*113*a*166*clientes
Test 5 is as close as I need, but I need to replace * with a blank space.
I need: De 113 a 166 clientes
Unfortunately, I was not successful.
In macro you don't need to use CAT for assembling a source code text.
Just resolve the macro variables in the context desired.
%LET lim1 = 113;
%LET lim2 = 166;
%LET linha = De &lim1 a &lim2 clientes;
%PUT &=linha;
----- LOG -----
LINHA=De 113 a 166 clientes
If using a macro variable value in the DATA step context of a quoted string or string value computation the resolution should be within double quotes of a string literal (unless the macro value is already literally double quoted text)
data have;
input (part1-part3) ($);
datalines;
De a clientes
Si o consumer
Mr A Sky
;
%LET lim1 = 113;
%LET lim2 = 166;
data want;
set have;
result = catx(' ', part1, "&lim1", part2, "&lim2", part3);
put result=;
run;
----- LOG -----
result=De 113 a 166 clientes
result=Si 113 o 166 consumer
result=Mr 113 A 166 Sky
Related
Do somebody know why the number stocked in "numero" isn't the same that the one I put in the let ?
I use SAS Enterprise Guide 7.1.
Here's my program :
%let ident = 4644968792486317489 ;
data _null_ ;
numero= put(&ident.,z19.);
call symputx('numero',numero);
run;
%put &numero. ;
And the log :
30 %let ident = 4644968792486317489 ;
31
32 data _null_ ;
33 numero= put(&ident.,z19.);
34 call symputx('numero',numero);
35 run;
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
36
37 %put &numero. ;
4644968792486317056
Thanks by advance !
SAS stores numbers as 8 byte floating point values. Therefore there is a limit to the maximum integer that can be stored exactly (or really exactly without gaps). They even publish a table with the maximum value.
And a function you can use to determine the maximum value.
3 %put %sysfunc(constant(exactint),comma23.);
9,007,199,254,740,992
Looks like your "number" is really an identifier. So store it as character to begin with and you will not have these problems.
data want;
length numero $19;
numero = "&ident";
numero = translate(right(numero),'0',' ');
run;
Use the SAS MD5 function to anonymize strings. Don't forget MACRO is really just text processing.
%let ident = 4644968792486317489 ;
%let numero = %sysfunc(MD5(&ident));
or in DATA Step
data ... ;
numero = MD5("&ident");
In certain situations you might associate a monotonic serial value to an identity value.
%let ident = 4644968792486317489 ;
%if not %symexist(i&ident) %then %do;
%let i&ident = %sysfunc(monotonic());
%put new serial;
%end;
%put i&ident=&&i&ident;
----- LOG -----
i4644968792486317489=1
I have a macro array &start_num
+-------+
| start |
+-------+
| 25.5 |
| 33.5 |
| 42.5 |
| 54.5 |
| 98 |
+-------+
but when I am using
%put %scan(&start_num,1);
it returns :25
%put %scan(&start_num,2);
returns me:533
why and how to fix it?
You can specify delimiter in scan function.
For example,if macro variable were initialized from variable start in table:
proc sql noprint;
select start into:start_num separated by ' ' from have;
quit;
START_NUM=25.5 33.5 42.5 54.5 98
Or in %let statement:
%let START_NUM=25.5 33.5 42.5 54.5 98;
Using %scan function:
%let var1 = %scan(&start_num,1,%str( ));
%put &=var1;
VAR1=25.5
%let var2 = %scan(&start_num,2,%str( ));
%put &=var2;
VAR2=33.5
because %scan thinks dot as seperator, that is why you get 25 and 533 respectively. checkout below example
%let start_num= 25.533.545.554.598;
%let var1 = %scan(&start_num,1);
%put value of with dot as separator &var1;
%put &var1 yields 25
%let var2 = %scan(&start_num,2);
%put value of with dot as separator &var2;
%put &var2 yields 533
Note: code edited after remarks from #user667489 but issues remain.
I built a fairly simple macro to return the intersection of 2 space separated lists as a new space separated list but for some reason the definition of the macro returns errors.
The macro loops through both lists and keep an element if a match is found (very straightforward, no handling of duplicates or optimization).
I cannot make sense of the log, which shows a combination of following error messages:
ERROR: Macro keyword LET appears as text.
ERROR: Macro keyword MACRO appears as text.
%macro list_intersection
(list1= /* space separated list, or unique term */
,list2= /* space separated list, or unique term */
);
%local output;
%local i;
%local j;
%let i = 1;
%let j = 1;
%do %while (%length(%scan(&list1,&i)));
%do %while (%length(%scan(&list2,&j)));
%if (%scan(&list1,&i) = %scan(&list2,&j)) %then
%let output = &output %scan(&list1,&i);
%let j = %eval(&j+1);
%end;
%let i = %eval(&i+1);
%end;
&output
%mend;
Can you help me circling the issue ?
I'm also open to a more efficient/robust/simple way of achieving the same output.
Reproducible log
Using SAS 9.3, I put above code in a separate program for it not to be polluted, save project, close and reopen. Open program, click run button, and here is the complete log:
1 The SAS System 09:50 Monday, January 22, 2018
1 ;*';*";*/;quit;run;
2 OPTIONS PAGENO=MIN;
3 %LET _CLIENTTASKLABEL='Program3';
ERROR: Macro keyword LET appears as text.
4 %LET _CLIENTPROJECTPATH='F:\CI\Projects\Wealth Indicators\106 Explore DIM_PHYSICALPERSON\SAS\Rework_table_auto2.egp';
ERROR: Macro keyword LET appears as text.
5 %LET _CLIENTPROJECTNAME='Rework_table_auto2.egp';
ERROR: Macro keyword LET appears as text.
6 %LET _SASPROGRAMFILE=;
ERROR: Macro keyword LET appears as text.
7
8 ODS _ALL_ CLOSE;
9 OPTIONS DEV=ACTIVEX;
10 GOPTIONS XPIXELS=0 YPIXELS=0;
11 FILENAME EGSR TEMP;
12 ODS tagsets.sasreport13(ID=EGSR) FILE=EGSR STYLE=HtmlBlue
12 ! STYLESHEET=(URL="file:///C:/Program%20Files/SASHome/x86/SASEnterpriseGuide/5.1/Styles/HtmlBlue.css") NOGTITLE NOGFOOTNOTE
12 ! GPATH=&sasworklocation ENCODING=UTF8 options(rolap="on");
13
14 GOPTIONS ACCESSIBLE;
15 %macro list_intersection
ERROR: Macro keyword MACRO appears as text.
16 (list1= /* space separated list, or unique term */
17 ,list2= /* space separated list, or unique term */
18 );
19 %local output;
ERROR: Macro keyword LOCAL appears as text.
20 %local i;
ERROR: Macro keyword LOCAL appears as text.
21 %local j;
ERROR: Macro keyword LOCAL appears as text.
22 %let i = 1;
ERROR: Macro keyword LET appears as text.
23 %let j = 1;
ERROR: Macro keyword LET appears as text.
24 %do %while (%length(%scan(&list1,&i)));
ERROR: Macro keyword DO appears as text.
25 %do %while (%length(%scan(&list2,&j)));
ERROR: Macro keyword DO appears as text.
26 %if (%scan(&list1,&i) = %scan(&list2,&j)) %then
ERROR: Macro keyword IF appears as text.
27 %let output = &output %scan(&list1,&i);
28 %let j = %eval(&j+1);
ERROR: Macro keyword LET appears as text.
29 %end;
ERROR: Macro keyword END appears as text.
30 %let i = %eval(&i+1);
ERROR: Macro keyword LET appears as text.
31 %end;
ERROR: Macro keyword END appears as text.
32 &output
33 %mend;
ERROR: Macro keyword MEND appears as text.
34
35 GOPTIONS NOACCESSIBLE;
36 %LET _CLIENTTASKLABEL=;
ERROR: Macro keyword LET appears as text.
37 %LET _CLIENTPROJECTPATH=;
2 The SAS System 09:50 Monday, January 22, 2018
ERROR: Macro keyword LET appears as text.
38 %LET _CLIENTPROJECTNAME=;
ERROR: Macro keyword LET appears as text.
39 %LET _SASPROGRAMFILE=;
ERROR: Macro keyword LET appears as text.
40
41 ;*';*";*/;quit;run;
42 ODS _ALL_ CLOSE;
43
44
45 QUIT; RUN;
46
Initial code before edit:
%macro list_intersection
(list1= /* space separated list, or unique term */
,list2= /* space separated list, or unique term */
);
%local output =;
%local i = 1;
%local j = 1;
%do %while (%length(%scan(&list1,&i)));
%do %while (%length(%scan(&list2,&j)));
%if (%scan(&list1,&i) = %scan(&list2,&j) %then
%local output = &output %scan(&list1,&i);
%let j = %eval(&j+1);
%end;
%let i = %eval(&i+1);
%end;
&output
%mend;
A few things immediately stand out:
You cannot use %local to set a value for a macro variable. Instead of %local i=1; you must write two separate statements: %local i; %let i = 1;. %local initialises macro variables to an empty string.
You have unbalanced brackets in your %if statement.
Try moving %let j = %eval(&j+1); into the outer %do %while loop.
Also, you probably want to make sure that %scan only uses space as a delimiter - it defaults to space plus . < ( + & ! $ * ) ; ^ - / , % |
Here's a working version:
%macro list_intersection
(list1= /* space separated list, or unique term */
,list2= /* space separated list, or unique term */
);
%local output;
%local i;
%local j;
%let i = 1;
%do %while (%length(%scan(&list1,&i,%str( ))));
%let j = 1;
%do %while (%length(%scan(&list2,&j,%str( ))));
%if %scan(&list1,&i,%str( )) = %scan(&list2,&j,%str( )) %then
%let output = &output %scan(&list1,&i,%str( ));
%let j = %eval(&j+1);
%end;
%let i = %eval(&i+1);
%end;
&output
%mend;
%put %list_intersection(list1=1 2 3,list2=2 3 4);
You can use SAS functions to make that much easier. Use the COUNTW() function to find the upper bound for the %DO loop. Use the FINDW() function to test if word is found in the other list.
%macro list_intersection
(list1 /* space separated list of terms */
,list2 /* space separated list of terms */
);
%local output i next ;
%do i=1 %to %sysfunc(countw(&list1,%str( ))) ;
%let next=%scan(&list1,&i,%str( ));
%if %sysfunc(findw(&list2,&next,,s)) %then %let output=&output &next ;
%end;
&output
%mend;
You could include the i modifier in the findw() call to make it case insensitive. You could also test if the word is already in the output string to eliminate duplicates.
%macro list_intersection_nodups
(list1 /* space separated list of terms */
,list2 /* space separated list of terms */
);
%local output i next ;
%do i=1 %to %sysfunc(countw(&list1,%str( ))) ;
%let next=%scan(&list1,&i,%str( ));
%if %sysfunc(findw(&list2,&next,,si)) and not %sysfunc(findw(&output,&next,,si))
%then %let output=&output &next ;
%end;
&output
%mend;
Example:
274 %put %list_intersection_nodups(A B a C,a c d);
A C
For an assignment I am asked to create a do loop in Proc Sql statement. My program is not recognizing the m1sales and m2sales. Here are the data sets and the macros that I had to create. The First macro is to set allow people to set the qtr to a number. The second macro set Months One, Two, and Three depending on the months. The first proc sql is doing what I want. The second is not when I add in the do statement. I was given the additional statement, "The %do loop isn't 'within' a create table command. The %do loop is replaced with a sequence of text that follows the create table command; that text may be though of as 'within' the create table statement." Can anyone tell me how to create that do loop correctly?
data Month1;
input Name $ sales;
cards;
Joyce 235
Marsha 352
Bill 491
Vernon 210
Sally 418
;
data Month2;
input Name $ sales;
cards;
Joyce 169
Marsha 281
Bill 315
Vernon 397
Sally 305
;
data Month3;
input Name $ sales;
cards;
Joyce 471
Marsha 314
Bill 394
Vernon 291
Sally 337
;
data Month4;
input Name $ sales;
cards;
Joyce 338
Marsha 259
Bill 310
Vernon 432
Sally 362
;
data Month5;
input Name $ sales;
cards;
Joyce 209
Marsha 355
Bill 302
Vernon 416
Sally 475
;
data Month6;
input Name $ sales;
cards;
Joyce 306
Marsha 472
Bill 351
Vernon 405
Sally 358
;
options symbolgen;
%Macro quarter(quarter);
%Global qtr;
%Let qtr = &quarter;
%Mend quarter;
%quarter (1);
options mprint symbolgen;
%Macro Month(day);
%Global One;
%Global Two;
%Global Three;
%if &qtr = %eval(1) %then %do;
%Let One = 1;
%Let Two = 2;
%Let Three = 3;
%end;
%Else %if &qtr = %eval(2) %then %do;
%Let One = 4;
%Let Two = 5;
%Let Three = 6;
%end;
%Else %if &qtr = %eval(3) %then %do;
%Let One = 7;
%Let Two = 8;
%Let Three = 9;
%end;
%Else %if &qtr = %eval(4) %then %do;
%Let One = 10;
%Let Two = 11;
%Let Three = 12;
%end;
%Mend Month;
%Month(&qtr);
Correct code:
%Macro qtrearn(x);
proc sql;
create table qtr&x as
select Month&One..name, month&One..sales as m&One.sales, month&Two..sales as m&Two.sales,
month&Three..sales as m&Three.sales, sum(month&One..sales, month&Two..sales, month&Three..sales) as qtr&x.sales
from month&One, month&Two, month&Three
where month&One..name=month&Two..name=month&Three..name;
select sum(m&One.sales) as m&One.total, sum(m&Two.sales) as m&Two.total, sum(m&Three.sales) as m&Three.total,
sum(qtr&x.sales) as qtr&x.total
from qtr&x;
%Mend qtrearn;
%qtrearn(&qtr);
Code that is not working with the do loop. I need to insert the do loop for an assignment.
options mprint symbolgen;
%Macro qtrearn(x);
proc sql;
%do i = &One %to &Three;
create table qtr&x as
select Month&i..name, month&&i..sales as m&&i.sales,
sum(month&One..sales, month&Two..sales, Month&Three..sales) as qtr&x.sales
from month&One, month&Two, month&Three
where month&One..name=month&Two..name=month&Three..name;
%end;
select sum(m&One.sales) as m&One.total, sum(m&Two.sales) as m&Two.total, sum(m&Three.sales) as m&Three.total,
sum(qtr&x.sales) as qtr&x.total
from qtr&x;
%Mend qtrearn;
%qtrearn(&qtr);
I was able to solve it. In case someone else wants it. Here is the answer:
%Macro qtrearn(x);
proc sql;
create table qtr&x as
select Month&One..name,
%do i = &One %to &Three;
month&&i..sales as m&&i.sales,
%end;
sum(month&One..sales, month&Two..sales, month&Three..sales) as qtr&x.sales
from month&One, month&Two, month&Three
where month&One..name=month&Two..name=month&Three..name;
create table totals_qtr&x as
select %do i = &One %to &Three;
sum(m&&i.sales) as m&&i.total,
%end;
sum(qtr&x.sales) as qtr&x.total
from qtr&x;
proc print data=work.qtr&x;
run;
proc print data=work.totals_qtr&x;
run;
%Mend qtrearn;
%qtrearn(&qtr);
I want to store a list of macro references in another macro variable and then change the content of one of the referenced variables.
As example:
%LET String=FirstString;
%LET KeepMacroNotString=&String;
%PUT &String = &KeepMacroNotString ?;
%LET String=String changed;
%PUT &String = &KeepMacroNotString?;
In the end I would like that %PUT &KeepMacroNotString resolves to "String changed". However it sticks to the first assignment.
Any ideas?
Thx, Lubenja
Much easier to do with a data step.
data _null_;
call symputx('KeepMacroNotString','&String');
run;
I found the solution:
A combination of the %NRSTR function and the %UNQUOTE function do the trick:
%LET String=FirstString;
%LET KeepMacroNotString=%NRSTR(&String);
%PUT &String = &KeepMacroNotString ?;
%LET String=String changed;
%PUT &String = %UNQUOTE(&KeepMacroNotString)?;
Explanation: First you have to mask the "&" to prevent the macro from being resolved (%NRSTR()).
But when you want to use the marco, then you have to unquote it again (%UNQUOTE()).
I wouldn't use this approach, but if you don't mind warning messages in the log (I do), you could in theory just change the order of your statements:
WARNING: Apparent symbolic reference STRING not resolved.
57
58 %LET KeepMacroNotString=&String;
59
60 %LET String=FirstString;
61 %PUT &String = &KeepMacroNotString ?;
FirstString = FirstString ?
62
63 %LET String=String changed;
64 %PUT &String = &KeepMacroNotString?;
String changed = String changed?
This is basically an ugly way of accomplishing the same sort of indirection that Tom did more gracefully. Key point being that that the macro variable KeepMacroNotString is given a value of &String, not the resolved value.
In a macro setting, this can also be accomplished by assigning default value as a macro variable reference, e.g.:
59 %macro indirect(String=
60 ,KeepMacroNotString=&string
61 );
62 %put _local_;
63 %put &string = &keepmacronotstring;
64 %mend indirect;
65 %indirect(string=Hi)
INDIRECT KEEPMACRONOTSTRING &string
INDIRECT STRING Hi
Hi = Hi