BookmarkSubscribeRSS Feed
alepage
Barite | Level 11

Hello I am using the following code:

 

%macro test;

%ARRAY(yrs,VALUES=1983-1986);

%let Temp= %DO_OVER(yrs);

%mend test;

%test;

%put &Temp;

 

Here's my sas log:

 

1 %_eg_hidenotesandsource;

5 %_eg_hidenotesandsource;

22

23 %macro test;

24 %ARRAY(yrs,VALUES=1983-1986);

25 %let Temp= %DO_OVER(yrs);

26 %mend test;

27 %test;

NOTE: Line generated by the invoked macro "TEST".

27 %ARRAY(yrs,VALUES=1983-1986);

_

180

WARNING: Apparent invocation of macro ARRAY not resolved.

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

WARNING: Apparent invocation of macro DO_OVER not resolved.

WARNING: Apparent invocation of macro DO_OVER not resolved.

28 %put &Temp;

%DO_OVER(yrs)

29

30 %_eg_hidenotesandsource;

42

43

44 %_eg_hidenotesandsource;

47

 

 

My software does not seems te recognized %array and %do_over;

 

First question: Why?

If it is not possible to correct this situation, what can I do to achieve the same task?

 

Regards,

Alain

12 REPLIES 12
RW9
Diamond | Level 26 RW9
Diamond | Level 26

"Why?" - this is simple, there is no such thing as %array in the SAS language, either in Base SAS or Macro SAS, therefore the operation is invalid.  Nor in fact is there a %do_over function in the SAS language.

 

Question from me, why do data processing in a text generation language in the first place?  Datastep (Base) is the language in which data processing is done, and that has all the constructs such as arrays for such a purpose.  Macro language only creates text, it doesn't do anything itself.  You can use a few constructs in Macro, such as:

%do i=1983 %to 1986;
...
%end;

However these are pretty basic and are only creating text at the end of the day.

Tom
Super User Tom
Super User

Those are not SAS provided macros. You need to tell SAS how to find the definition of those macros.  Either %INCLUDE the source code or setup SASAUTOS to point to the location where SAS can find the source code.

art297
Opal | Level 21

%array and %do_over do not, as far as I know, exist. Let us know what you're trying to accomplish (with example have and want datasteps) and someone will be able to show you how to do it.

 

Art, CEO, AnalystFinder.com

alepage
Barite | Level 11

What the purpose of the attached pdf document if the macro %do_over and %array do not exist?

 

Anyway...

 

What I would like to do is to make two macro variables which contains a list of values such as:

 

%let list1=15020 54330 54350 55080 65110 68020 80110
%let list2= 54090 54320 55020 80100 80110 80180 80200 90010

 

I would like to put those two macro variables at the begining of the sas program to permits the user to see those code and to change those if necessary.

 

There after, I would like to use those two macro variables in order to reproduce the code below, using a do loop statement.

It is why I have looked at the %array and %do_over, macro function that does not exist.

 

Does anyone could provide me such suggestions?

Regards

Alain

 

 

 

data donneesTrans (drop=totIndUnRisq totVed i);

 length ind15020 ind54330 ind54350 ind55080 ind65110 ind68020 ind80110 
           ved54090 ved54320 ved55020 ved80100 ved80110 ved80180 ved80200 ved90010 4;

 set donneesTrans;
 array unite{*} unite1-unite&DimVect;
 do i=1 to &DimVect;
  if unite(i)=15020 then ind15020=1;
  if unite(i)=54330 then ind54330=1;
  if unite(i)=54350 then ind54350=1;
  if unite(i)=55080 then ind55080=1;
  if unite(i)=65110 then ind65110=1;
  if unite(i)=68020 then ind68020=1;
  if unite(i)=80110 then ind80110=1;

  if unite(i)=54090 then ved54090=1;
  if unite(i)=54320 then ved54320=1;
  if unite(i)=55020 then ved55020=1;
  if unite(i)=80100 then ved80100=1;
  if unite(i)=80110 then ved80110=1;
  if unite(i)=80180 then ved80180=1;
  if unite(i)=80200 then ved80200=1;
  if unite(i)=90010 then ved90010=1;
 end;

 if ind15020=. then ind15020=0; 
 if ind54330=. then ind54330=0;
 if ind54350=. then ind54350=0;
 if ind55080=. then ind55080=0;
 if ind65110=. then ind65110=0; 
 if ind68020=. then ind68020=0;
 if ind80110=. then ind80110=0;

 if ved54090=. then ved54090=0;
 if ved54320=. then ved54320=0;
 if ved55020=. then ved55020=0;
 if ved80100=. then ved80100=0;
 if ved80110=. then ved80110=0;
 if ved80180=. then ved80180=0;
 if ved80200=. then ved80200=0;
 if ved90010=. then ved90010=0;
run;
 

 

 

art297
Opal | Level 21

Actually, they both exist but aren't macro functions, per se, but rather macros written by a SAS user. They can be downloaded from:http://www.sascommunity.org/wiki/File:Clay-TightLooping-macros.zip

 

If your not familiar with SAS macros, you have to first download and unzip the file, then run the two (or if you want, all three macros).

 

Then you can use them as described in the paper about them.

 

Art, CEO, AnalystFinder.com

 

alepage
Barite | Level 11

Thank you for providing me the web site where to download those macro.

Regards,

Alain

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Firstly, you can halve your code straight off by using ifn/ifc functions:

data donneesTrans (drop=totIndUnRisq totVed i);
 length ind15020 ind54330 ind54350 ind55080 ind65110 ind68020 ind80110 
           ved54090 ved54320 ved55020 ved80100 ved80110 ved80180 ved80200 ved90010 4;
 set donneesTrans;
 array unite{*} unite1-unite&DimVect;
 do i=1 to &DimVect;
    ind1520=ifn(unite(i)=15020,1,0);
    ind5430=ifn(unite(i)=54330,1,0);
...
 end;
run;

This way you don't need the second part for setting the zeros.

 

Second point, putting data into macro lists is rarely a good idea, it can both bloat the code you need to do (i.e. all the looping over an such like) and make hard to maintain code.  Put your lists into datasets - datasets are there to hold data.  Then you merge data together and process.  So say I have the lists as:
LIST_NO  LIST1  LIST2 ...

1               15020  54330...

2               54090  54320...

 

It is then a pretty simple task to merge either of those lists onto the data and then code the check as:

data want;
  merge have lists (where=(list_no=1));
  array unite{*} unite:;    /* Note the shorthand use of : to indicate */
  array list{*} list:;              /* all variables starting with the text */
  do i=1 to dim(unite);
    do j=1 to dim(list);
      if unite{i}=list{j} then found_value=list{j};
    end;
  end;
run;

      

This just loops over list and sets foun variable to value if it matches.  Very simple, short logic.

alepage
Barite | Level 11

Very good comments.  I will take that into consideration for the future.

Regards,
Alain

alepage
Barite | Level 11

Hello,

 

I agree with you, putting a list of values into a macro variable may not be in certain circumstance, the best thing to do.

I have read your message attentively.

What I have done its to put my code in a dataset as recommended. At the end, I have two different datasets, one containing the numeric code and another one containing the alphanumeric code. Ex;

Table1

10010

10020

And so on.

 

Table2

Ved10010

Ved10020

And so on

 

Thereafter, I have use proc transpose (out = Liste) with table2 which permit me to obtain the following dataset

 

Ved10010 Ved10020 … and so on.

Table3 contains the variables unite from Unite1 – Unite13;

 

I have made a merge of table3 and Liste to get a dataset (Table4) as below:

Unite1 Unite2 Unite3 …Unite13 ved10010 ved10020…Ind80800 and so so.

 

Thereafter, I would like to initialize all the variable starting by ved or ind to zero.

Then, what I would like to do its to check for each observation of dataset Table4 if the values for the variable Unite1 to Unite13 can be found in values of table1 and if so the variable ved or ind would have the value of one.

 

Ex;

If the value of 10010 is found in one of the value Unites1 to Unite13 then ind10010 will have the value of one otherwise, ind10010 is zero.

 

I have made few tests with the arrays and I am losing the original value of Unite1 to Unite13 (reset to zero) and I am not able to assign a value of one or zero to ind10010 and so on.

What’s wrong with my arrays??

Regards,

Alain.

 

 

 

 

 

 

Tom
Super User Tom
Super User

@alepage wrote:

Hello,

 

I agree with you, putting a list of values into a macro variable may not be in certain circumstance, the best thing to do.

I have read your message attentively.

What I have done its to put my code in a dataset as recommended. At the end, I have two different datasets, one containing the numeric code and another one containing the alphanumeric code. Ex;

Table1

10010

10020

And so on.

 

Table2

Ved10010

Ved10020

And so on

 

Thereafter, I have use proc transpose (out = Liste) with table2 which permit me to obtain the following dataset

 

Ved10010 Ved10020 … and so on.

Table3 contains the variables unite from Unite1 – Unite13;

 

I have made a merge of table3 and Liste to get a dataset (Table4) as below:

Unite1 Unite2 Unite3 …Unite13 ved10010 ved10020…Ind80800 and so so.

 

Thereafter, I would like to initialize all the variable starting by ved or ind to zero.

Then, what I would like to do its to check for each observation of dataset Table4 if the values for the variable Unite1 to Unite13 can be found in values of table1 and if so the variable ved or ind would have the value of one.

 

Ex;

If the value of 10010 is found in one of the value Unites1 to Unite13 then ind10010 will have the value of one otherwise, ind10010 is zero.

 

I have made few tests with the arrays and I am losing the original value of Unite1 to Unite13 (reset to zero) and I am not able to assign a value of one or zero to ind10010 and so on.

What’s wrong with my arrays??

Regards,

Alain. 

 


That sounds even worse than the original approach.  Instead make everything vertical. Then summarize and transpose if you want.

So the data you want to search looks something like this. So instead of columns named UNITE1 to UNITEn you have up to N rows with two columns one with the value, call it UNITE, and one with the index, call it ROW.

data facts ;
  input id row unite ;
cards;
1 1 101
1 2 102
2 1 201
2 2 101
2 3 102
;

Then the data you want to search for looks something like this.

data search ;
  input prefix $ value ;
cards;
ved 101
ved 102
ved 103
ved 201
;

Now combine them an aggregate to the ID* search term level.  Also generate a dummy set of zeros for the ones that are not found and re-merge to two sets.  You can then transpose back to your wide format if you need it for some other purpose.

proc sql ;
  create table matches as
    select a.id,b.prefix,b.value
         , max(case when b.value = a.unite then 1 else 0 end) as found
    from facts a inner join search b
    on a.unite = b.value
    group by 1,2,3
    order by 1,2,3
  ;
  create table dummy as
    select a.id,b.prefix,b.value,0 as found
    from (select distinct id from facts) a
    full join search b
    on 1=1
    order by 1,2,3
  ;
quit;
data want ;
  merge dummy matches ;
  by id prefix value ;
run;
proc print;
run;
proc transpose data=want out=report ;
  by id ;
  id prefix value ;
  var found ;
run;
proc print;
run;
Obs    id    prefix    value    found

 1      1     ved       101       1
 2      1     ved       102       1
 3      1     ved       103       0
 4      1     ved       201       0
 5      2     ved       101       1
 6      2     ved       102       1
 7      2     ved       103       0
 8      2     ved       201       1


Obs    id    _NAME_    ved101    ved102    ved103    ved201

 1      1    found        1         1         0         0
 2      2    found        1         1         0         1

 

Tom
Super User Tom
Super User

It is best not to use code generation (macros) to do things that you can do directly with SAS.

In this case it looks like you want to input two lists of code values (IND and VED) and generate series of flag variables to indicate if those codes appear in the set of input variables (UNITE1 ....).

It would probably be better to produce mulitple observations instead of multiple columns with the code value stored in the variable name.  But even with this wide structure you do not need to resort to macro looping. Instead just use macro functions to generate the dynamic variable names.

 

%* Set the code lists ;
%let indlist=15020 54330 54350 55080;
%let vedlist=54090 54320 55020 80100;

%* generate variable names from code lists;
%let indvars=ind%sysfunc(tranwrd(%sysfunc(compbl(&codelist)),%str( ),%str( ind)));
%let vedvars=ved%sysfunc(tranwrd(%sysfunc(compbl(&codelist)),%str( ),%str( ved)));

%* Need count to generate _temporary_ array ;
%let nflags=%sysfunc(countw(&indlist &vedlist));

data want;
  set have;
  array flags 4 &indvars &vedvars;
  array values (&nflags) _temporary_ (&indlist &vedlist);
  do i=1 to dim(flags);
    flags(i) = 0<whichn(values(i),of unite1-unite&DimVect);
  end;
  drop i;
run;

 

 

alepage
Barite | Level 11

Thanks for the SAS program.

Regards,

Alain

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

SAS Enterprise Guide vs. SAS Studio

What’s the difference between SAS Enterprise Guide and SAS Studio? How are they similar? Just ask SAS’ Danny Modlin.

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
  • 12 replies
  • 3275 views
  • 2 likes
  • 4 in conversation