BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Konkordanz
Pyrite | Level 9

Hi,

 

Im writing an syntax that starts with a macro (1) like this:

 

 

%let quarter=4;

In this macro the User (that will use that Syntax in the near future) shall enter a quarter of the year; this quarter will be the base of the output.

Now I want to create another macro, that references of the first macro. For example:

 

Suppose a User just want to check the fourth quarter (like in the macro above), the macro should create a new macro "quarter_final" with the text-value ="4". Something like this:

 

if quarter in(4) than do %let quarter_final="4"

 

If some want to get the SAS-Output for the quarter 1, 2 and/or 3, the macro should create a new macro "quarter_final" with the text-value ="1". Something like this:

if quarter in(1,2,3) than do %let quarter_final="1"

But: I didnt get a solution for this. Could anyone help me? Thank you!

 

1 ACCEPTED SOLUTION
21 REPLIES 21
PaigeMiller
Diamond | Level 26

@Konkordanz wrote:

 

Im writing an syntax that starts with a macro (1) like this:

 

%let quarter=4;

First, let's straighten out the terminology. &quarter is not a macro. It is a macro variable.

 

In this macro the User (that will use that Syntax in the near future) shall enter a quarter of the year; this quarter will be the base of the output.

Now I want to create another macro, that references of the first macro. For example:

Again, macro variables.

 

Suppose a User just want to check the fourth quarter (like in the macro above), the macro should create a new macro "quarter_final" with the text-value ="4". Something like this:

 

if quarter in(4) than do %let quarter_final="4"

You can't mix and match data step code with macro code like this. (There are ways to do it, this is not the way).

 

Perhaps what you want is this:

 

%if &quarter=4 %then %let quarter_final=4;

 

If some want to get the SAS-Output for the quarter 1, 2 and/or 3, the macro should create a new macro "quarter_final" with the text-value ="1". Something like this:

if quarter in(1,2,3) than do %let quarter_final="1"

But: I didnt get a solution for this. Could anyone help me? Thank you!

Again, you cannot mix and match data step and macro code the way you are doing it. How about this:

 

%if &quarter=1 or &quarter=2 or &quarter=3 %then %let quarter_final=1;

 

Now, let's take a big step back, and let me ask for the big picture. What are you trying to do here? Explain the goal, without reference to SAS or macro variables, please.

 

--
Paige Miller
Konkordanz
Pyrite | Level 9

Hey Paige Miller, thanks for helping!

 

In my dataset I’ve different price-information about german cities for several years and all quarters. There is also the population-size of each city, that differents between the years and quarters. What I’m doing is writing a syntax for a bunch of people, that want to create outputs with individual entered years and quarters.

My goal simplified: In the first %let-Command a user should enter the necessary quarter(s). For example: If one need a result for quarter 1 and 2, one would write both digits into the code:

%let quarter=1,2;

Another %let-command should “read” this and adapt automatically: If the user wants the quarter 1, 2 and/or 3, the new %let-command should bring the quarter_final=“1”. If the user wants also/only the fourth quarter, the new %let-command should bring quarter_final=“4”.

 

Why do I need that?

I want to create a new population-size-dummy-variable, that depends of the information that was entered by the users. For the first 3 quarters, the syntax should use the population size of the first quarter. If the fourth quarter is also involved, the syntax should use the population size of the fourth quarter.

 

For example, my syntax start is something like this: A Guy want to get an output for the first two quarter of 2019 and 2020. So he would edit the %let-codes like this:

%let year1=2019;
%let year2=2020;
%let quarter=“1“,“2“;

After this code, the main dataset gets filtered with the information. Like this:

Data want;
set have;
where (year="&year2." or year="&year1.") and Quarter in(&Quarter.);
run;

The result is a new table with all values of 2019 (Q1 & Q2) and 2020 (Q1 & Q2). That works. But:

Now I want to create the new population-variable for a certain city and depending on the users input. I guess, the best way doing that is to refer to the %let-command. If the user wrote a 1 and a 2 (for quarter 1 and quarter 2) into that command, the new %let-command should be: %let quarter_final=”1”; Afterwards I would create the variable:

Data want2;
set want;
if Quarter="&Quarter_final." and state="1" then population_new = population;
run;

This step doesn’t work. The code is not correct, I know. I just want to illustrate what I mean...Do you have another solution for me?

Your last code wont work, cause the user writes several quarter in the code, not only one.

 

PaigeMiller
Diamond | Level 26

What are the rules for determining the value of &quarter_final? You have told us &quarter=1 should produce &quarter_final=1. You now allow &quarter=1,2 or &quarter=1,2,3, what should the value of &quarter_final have? What value of &quarter_final gets assigned in other cases? Please cover all cases.

 

I guess, the best way doing that is to refer to the %let-command.

Not necessarily, it can be a data step calculation based upon &quarter.

 

By the way, why do you have character values of YEAR and character values of QUARTER in your data set? That seems to have changed since your first post. Shouldn't those be numeric? In the data set, are these variables numeric or character?

 

--
Paige Miller
Konkordanz
Pyrite | Level 9

Thanks for feedback!

 

By the way, why do you have character values of YEAR and character values of QUARTER in your data set? Shouldn't those be numeric?

Yes, these are character-variables.

 

 


What are the rules for determining the value of &quarter_final? You have told us &quarter=1 should produce &quarter_final=1. You now allow &quarter=1,2 or &quarter=1,2,3, what should the value of &quarter_final have? What value of &quarter_final gets assigned in other cases? Please cover all cases.

 

In every case, where the first, second or third quarter is involved, &quarter_final should bring the 1: (1) or (1,2) or (1,2,3)

As soon the fourth quarter is involved, &quarter_final should bring the 4: (4) or (4,3) or (4,2) or (4,1,2) and so on.

 

 

PaigeMiller
Diamond | Level 26

@Konkordanz wrote:

 

By the way, why do you have character values of YEAR and character values of QUARTER in your data set? Shouldn't those be numeric?

Yes, these are character-variables.


Sorry, but this is a poor decision, and makes the rest of the coding more difficult. If it is something you have control over, then you ought to make these variables numeric. Otherwise, if you have no control over the data (perhaps because you are getting it from a database or Excel) then I would suggest you write code to change YEAR to a numeric and QUARTER to numeric.

 

In every case, where the first, second or third quarter is involved, &quarter_final should bring the 1: (1) or (1,2) or (1,2,3)

As soon the fourth quarter is involved, &quarter_final should bring the 4: (4) or (4,3) or (4,2) or (4,1,2) and so on.

Ok, thanks.

 

Here is my solution, assuming you create new NUMERIC variables for year and quarter.

 

%let quarter = 1,2;
%let year1 = 2020;
%let year2 = 2019;
data want;
    set have;
    where (year=&year2. or year=&year1.) and quarter in(&quarter.);
    if find("&quarter",'4')>0 then quarter_final=4; 
    else quarter_final = 1;
    if quarter = quarter_final and state = "1" then population_new = population;
    drop quarter_final;
run;
--
Paige Miller
Kurt_Bremser
Super User

So it comes down to this:

The user creates a macro variable quarter that contains either a single quarter or a list of quarters.

From this, you derive a second macro variable final_quarter that is either 1 if the value 4 is not present in &quarter, or 4 if 4 is present there.

Is that correct?

Konkordanz
Pyrite | Level 9

Right. My thinking is: The Dataset contains a variable „quarter“ (1-4). The user writes into the %let-command, which quarter SAS should use for the calculation. Another %let-command „reads“ this. If the entered quarternumbers contains a „4“, the new %let-command shall produce a „4“. If the „4“ not part oft he users input, the new %let-command should produce the „1“.

Konkordanz
Pyrite | Level 9

@Kurt_Bremser  and @PaigeMiller : Thank you for your help! The solution of KurtBremser is short and works pretty well!

Konkordanz
Pyrite | Level 9

@Kurt_Bremser, Sry, but I must ask again:

 

your code inspired me, thank you. But ive noticed, that the code doesnt work and I dont know why. Can you help me once more? 🙂

 

The structure:

First: The User shall enter the years and quarters that are neccessary:

 

 

/* Year1:*/ 
%let year1=2019;

/*Year2*/
%let Year2=2020;

/*Which quarter shall be involved? */
%let Quarter2="1","2","3","4";

Afterwards, the dataset gets filtered with the entered data:

 

 

Data want;
set have;
where (Year="&Year2." or Year="&Year1.") and Quarter in(&quarter2.);
run;

 

 

That works. Now, the call symput command follows:

 

data _null_; 
call symputx ("quarter_final", ifc(indexc(&Quarter2.,"4"),"4","1"));
run;

My interpretation: If the macro "quarter2" contains the "4" (for example: "2","3","4"), the macro called "quarter_final" should produce the "4". If not (for example ["1","2","3"], it produces the "1". Thats what I want.

 

 

Based on this, the next data-step should use the population-value of the quarter "4". But it doesnt. Why? Where is the error in my code?

 

 

Data want2;
set want;
if year="&year2." and Quarter="&Quarter_final." and Region="1" then population_2 = population;
run;

 

 

PS:I also tried it with the apostroph like below:

 

 

data _null_;
call symputx ("quarter_final", ifc(indexc("&Quarter2.","4"),"4","1"));
run;

...but it produces an error:

 

""1","2","3","4"
           __     ___ ___
           49     49  49
              ___
              49
             _
             388
             _
             76
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.

ERROR 388-185: Expecting an arithmetic operator.

ERROR 76-322: Syntax error, statement will be ignored.

Thank you for helping again!

 

Edit:

If I enter only the "4", the macro produces the the "4"; thats correct.

If i enter the "4" in combination with an other digit (for example "1","4"), I got the "1". Thats not correct. It actually has to produce the "4", cause the 4 is one of the entered information.

 

 

PaigeMiller
Diamond | Level 26

@Konkordanz wrote:

 

Based on this, the next data-step should use the population-value of the quarter "4". But it doesnt. Why? Where is the error in my code?

 

Data want2;
set want;
if year="&year2." and Quarter="&Quarter_final." and Region="1" then population_2 = population;
run;

 

 

PS:I also tried it with the apostroph like below:

 

 

data _null_;
call symputx ("quarter_final", ifc(indexc("&Quarter2.","4"),"4","1"));
run;

...but it produces an error:

 


I think the code does not use quotes properly. How about this:

 

%let Quarter2=1,2,3,4;

data _null_; 
    call symputx ("quarter_final", ifc(indexc("&Quarter2","4"),4,1));
run;
%put &=quarter_final;

It was never clear to me why you have macro variables with quotes in the variable value. Usually, this is not necessary and in this case it appears to cause problems.

 

Please note that &quarter_final now has no quotes around the value, this makes sense to me ... but it may be that you need to modify later parts of the program to account for this.

--
Paige Miller
Kurt_Bremser
Super User

Do NOT do this:

%let Quarter2="1","2","3","4";

One simply does not want quotes in macro variables, they only interfere with later handling.

See this working example:

%let Quarter2=1,2,3,4;

data _null_; 
call symputx ("quarter_final", ifc(indexc("&Quarter2.","4"),"4","1"));
run;

%put &=quarter_final;

You also don't need the commas. 1234 would also work.

FreelanceReinh
Jade | Level 19

@Konkordanz wrote:

(...)

%let Quarter2="1","2","3","4";

(...)

data _null_; 
call symputx ("quarter_final", ifc(indexc(&Quarter2.,"4"),"4","1"));
run;

My interpretation: If the macro "quarter2" contains the "4" (for example: "2","3","4"), the macro called "quarter_final" should produce the "4". If not (for example ["1","2","3"], it produces the "1". Thats what I want.

 

 

Based on this, the next data-step should use the population-value of the quarter "4". But it doesnt. Why? Where is the error in my code?

Hello @Konkordanz,

 

I agree with the others that defining Quarter (in dataset HAVE) as a character variable was probably not the best idea.

 

Just to add the explanation why your code above doesn't work as intended and how it could be fixed:

  1. After resolution of the macro variable reference &Quarter2. -- i.e., after replacing it by the text "1","2","3","4" (including all these quotation marks) -- the INDEXC function call looks like this:
    indexc("1","2","3","4","4")
    This happens to be syntactically correct (hence no error messages). The documentation of the INDEXC function explains what this code does: It searches the one-character string "1" (in the first argument) for the first occurrence of any character present in the subsequent arguments. Since none of the three characters "2", "3" and "4" is found in "1", the function returns a value of 0. This, in turn, is interpreted by the IFC function as the Boolean value FALSE so that it returns the string in the third argument ("1"), as you have observed.
  2. You wanted to pass "1","2","3","4" as one character string to the INDEXC function, as in
    indexc('"1","2","3","4"',"4")
    (Note the use of outer single quotes to avoid the compiler's confusion you observed when using nested double quotes.) Since macro variable references are normally not resolved in single quotes, you could use functions to retrieve the macro variable value: the RESOLVE function (resolve('&quarter2')) or the SYMGET function:
    call symputx ("quarter_final", ifc(indexc(symget('quarter2'),"4"),"4","1"));
    Now the first argument of the INDEXC function is one character string and the digit character "4" is found in it, as intended. However, it would be found in "1","2","3","14" as well, so why not use a more specific condition using the IN operator:
    call symputx ("quarter_final", ifc("4" in (&quarter2),"4","1"));
    which is also simpler.
Konkordanz
Pyrite | Level 9

Thx for your replys. In general, it works without these quotes. But: The reason for using the macro variable with quotes is the step #2 (pls see above): After a user entered the quarter, the dataset should be filtered depending of this information:

 

Data want;
set have;
where (Year="&Year2." or Year="&Year1.") and Quarter in(&quarter2.);
run;

With quotes the where command would be for example: "[...] quarter in("1","2","3","4");...If I avoid the quotes, this command wouldnt work. If I had another way for filtering the dataset without the quotes, It would be great. Do you have one?

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 21 replies
  • 2105 views
  • 12 likes
  • 4 in conversation