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

Hello,

 

I would like to use the macro function ExtractACell to put some values from one dataset to another but it is not working.

I hope someone could help me with that.

 

Here's the sample code;

The %test2 is not working properly.  Also, I am getting many warning.

 

options spool;
Data test;
length filename $100;
do i=1 to 100;
filename=cats('Data',i);
output;
End;
run;
%macro ExtractACell(dataset=,rownum=, VarName=);
data _null_;
set &dataset. (obs=&rownum. firstobs=&rownum. keep = &VarName.);
call symputx('gvalue', &VarName., 'G');
stop;
run;
%mend ExtractACell;
%ExtractACell(dataset=test,rownum=5, VarName=filename);
%put &gvalue.;
%symdel gvalue;
%macro test1;
Data new;
length obs 8. datasetname $100.;
%do k = 1 %to 100;
obs=&k.;
output;

%end;
run;


%mend test1;
%test1;
%macro test2;
Data new2;
length obs 8. datasetname $100.;
%do k = 1 %to 100;
obs=&k.;
call execute('%ExtractACell(dataset=test ,rownum= &k. , VarName=filename);');
datasetname=symget('gvalue');
output;

%end;
run;

%mend test2;
%test2;

1 ACCEPTED SOLUTION

Accepted Solutions
ballardw
Super User

Sense you say all of these sets are in a specific subfolder then I would expect then all to be in the same SAS library.

You get call of the datasets and how many observations they have with this code:

data want;
   set sashelp.vtable;
   where libname='WORK';
   keep libname memname nobs;
run;
/* or */
proc sql;
   create table want as
   select libname,memname, nobs
   from dictionary.tables
   where libname='WORK'
   ;
quit;

Replace WORK with the name of your library. The name is stored in the SAS metadata in uppercase so make sure to use upper case spelling.

Then to create the final wanted data set:

Proc sql;
   create table final as 
   select a.libname, a.memname, a.nobs
          ,case upcase(b.name)
             when 'YOURVARIABLE' then 'Yes'
             else 'No'
           end as YOURVARIABLE
   from want as a
        left join
        (select * from dictionary.columns
         where upcase(name)='YOURVARIABLE' ) as b
        on a.libname=b.libname
        and a.memname=b.memname
   ;
quit;
          

The Procedure SQL allows use of SQL statements to join data sets in many manners (LOTS of books out there). In this case the previous data set contains the bits you had, library, memname (data set name) and nobs (number of observations). The SAS metadata view holding all the information about variable properties such as name, which can be stored in mixed case so I force an upper case comparison, type, label, format, length and other bits then is used to select when the variable name is the one you want, using "YOURVARIABLENAME" above, only in that library using a WHERE clause. The "as A" and "as B" after the data set name or the query (select * from dictionary.columns ... ) are alias so you can refer to the variables with the prefix a.<variable name> or b<variable name>. The Left Join will select all the records from the A set, the ON clause provides conditions on matching, the library and member name this time. The CASE clause examines the contributing value of the B.Name to the desired value. If it is there then we assign a value of 'Yes' as requested, else assign "No" to a column with the name of the variable you asked about.

 

Personally instead of 'Yes' and 'No' I would use numeric 1/0 for Yes/No with code like this:

Proc sql;
   create table final as 
   select a.libname, a.memname, a.nobs
          ,( upcase(b.name) = 'YOURVARIABLE') as YOURVARIABLE
   from want as a
        left join
        (select * from dictionary.columns
         where upcase(name)='YOURVARIABLE' ) as b
        on a.libname=b.libname
        and a.memname=b.memname
   ;
quit;
          

because there are several things that are easier to report when you only want to count/sum/calculate with the "yes" values that don't work quite as nice with character values.

 

There are lots of metadata available in SAS. Knowing it is there is the first thing. Then learning tools to use it.

 

This would be how to check on TWO variables:

Proc sql;
   create table final as 
   select a.libname, a.memname, a.nobs
          ,( upcase(b.name) = 'YOURVARIABLE') as YOURVARIABLE
          ,( upcase(b.name) = 'OTHERVAR') as OTHERVAR
   from want as a
        left join
        (select * from dictionary.columns
         where upcase(name)in ('YOURVARIABLE' 'OTHERVAR') ) as b
        on a.libname=b.libname
        and a.memname=b.memname
   ;
quit;

Another way would be to create a data set with the names of the variables and do a join on the dictionary.columns to get just the sets that have those variables and then do a similar join with the data of members and obs.

View solution in original post

6 REPLIES 6
PaigeMiller
Diamond | Level 26

@alepage wrote:

Hello,

 

I would like to use the macro function ExtractACell to put some values from one dataset to another but it is not working.

I hope someone could help me with that.


Re-run your code, with this as the first line:

options mprint;

This adds useful information into the log so you should be able to see why your code is not running. If you still need help, please SHOW us the log by pasting it as text into the window that appears when you click on the </> icon (and not by any other method).

 

For your future benefit, when you say "it is not working", we cannot help you. We need to see the log and/or the incorrect output.

 

For your future benefit, when you say "Also, I am getting many warning", we cannot help you, you didn't even tell us what warnings you are getting, and we need to see the log.

--
Paige Miller
ballardw
Super User

@alepage wrote:

Hello,

 

I would like to use the macro function ExtractACell to put some values from one dataset to another but it is not working.

I hope someone could help me with that.

 

Here's the sample code;

The %test2 is not working properly.  Also, I am getting many warning.

 

options spool;
Data test;
length filename $100;
do i=1 to 100;
filename=cats('Data',i);
output;
End;
run;
%macro ExtractACell(dataset=,rownum=, VarName=);
data _null_;
set &dataset. (obs=&rownum. firstobs=&rownum. keep = &VarName.);
call symputx('gvalue', &VarName., 'G');
stop;
run;
%mend ExtractACell;
%ExtractACell(dataset=test,rownum=5, VarName=filename);
%put &gvalue.;
%symdel gvalue;
%macro test1;
Data new;
length obs 8. datasetname $100.;
%do k = 1 %to 100;
obs=&k.;
output;

%end;
run;


%mend test1;
%test1;
%macro test2;
Data new2;
length obs 8. datasetname $100.;
%do k = 1 %to 100;
obs=&k.;
call execute('%ExtractACell(dataset=test ,rownum= &k. , VarName=filename);');
datasetname=symget('gvalue');
output;

%end;
run;

%mend test2;
%test2;


There are several issues with

%do k = 1 %to 100;
obs=&k.;
call execute('%ExtractACell(dataset=test ,rownum= &k. , VarName=filename);');
datasetname=symget('gvalue');
output;

Several have to do with timing issues between the macro processor and data step execution.

If you want a data step to Call execute something multiple times then almost certainly a macro %do loop is not what you want. A simple data step loop.

Second the &k in the %ExtracACell call will not resolve because it is inside single quotes.

It is quite often easy to create a string value using a datstep variable such as:

 

string= cats('%ExtractACell(dataset=test ,rownum=', obs, ', VarName=filename);');

Then use that variable in the call execute:

call execute (string);

Note that there is NOT need for the macro loop with K, at least without seeing a parameter passed. and if &K is a parameter then

do obs=1 to &k;
   string= cats('%ExtractACell(dataset=test ,rownum=', obs, ', VarName=filename);');
call execute (string); end;

will work to call that macro

 

 

And the actual major, as in it will not work at all:

the Symget will never see the result from %ExtractAcell. The Call execute creates lines of code that are placed in an "to execute" buffer that does not start executing anything until the end of the data step. So the symget either has exactly one value of Gvalue to look at, the one the existed before the data step started, or none at all.

 

I strongly suggest that you provide an example of a source data set, what values you expect to extract from it, the target data set and what that target should look like after the extraction.

There are special SAS functions such as OPEN with POINT that let you do random access to records in a data set.

alepage
Barite | Level 11

Hello,

 

Thank you for the information provided.

Many time, i need to extract information from one or two dataset then make a new one.

Maybe I don't have the good approach.  I have already create a basic code to show what I often need to do.  

**********************

Data test;
length filename $100;
do i=1 to 100;
filename=cats('Data',i);
output;
End;
run;

In the dataset test, we have a list of dataset that we could find into a subfolder. In our example, there name goes from Data1  to Data100 but it could be any name.

 

***********************

From there, I have the macro function ExtractACell, where we put the dataset name of interest, the rownumber and the variable name.  Then this value is put into a global varialbe named gvalue.

%macro ExtractACell(dataset=,rownum=, VarName=);
data _null_;
set &dataset. (obs=&rownum. firstobs=&rownum. keep = &VarName.);
call symputx('gvalue', &VarName., 'G');
stop;
run;
%mend ExtractACell;
%ExtractACell(dataset=test,rownum=5, VarName=filename);
%put &gvalue.;
%symdel gvalue;

*************************************

From that point, I want to create using the macro function test2 to put the following information into the dataset New2

obs

corresponding filename from dataset new (rownumber = obs)

there after, I would like to call another funciton to check if a specific variable is prensent in each dataset from Data1 to Data100.

 

********************

Here's want I wish to do.

You have into subfolder, a list of sas dataset and I want to put in a single dataset, the name of each of those datasets present into that subfolder, get the number of obs for each dataset then check if a specific variable (ex: vartatou) is present in each dataset , yes or no

 

the end result will be

 

Data final;

dataset name     obs   vartatou

data1                   51       yes

data2                   26       no

...

data1000           200        yes

Wha'ts the best way to do that.

I though that the use of macro will do the job but it seem it is not the case

What's can we do?

 

 

 

 

 

 

ballardw
Super User

Sense you say all of these sets are in a specific subfolder then I would expect then all to be in the same SAS library.

You get call of the datasets and how many observations they have with this code:

data want;
   set sashelp.vtable;
   where libname='WORK';
   keep libname memname nobs;
run;
/* or */
proc sql;
   create table want as
   select libname,memname, nobs
   from dictionary.tables
   where libname='WORK'
   ;
quit;

Replace WORK with the name of your library. The name is stored in the SAS metadata in uppercase so make sure to use upper case spelling.

Then to create the final wanted data set:

Proc sql;
   create table final as 
   select a.libname, a.memname, a.nobs
          ,case upcase(b.name)
             when 'YOURVARIABLE' then 'Yes'
             else 'No'
           end as YOURVARIABLE
   from want as a
        left join
        (select * from dictionary.columns
         where upcase(name)='YOURVARIABLE' ) as b
        on a.libname=b.libname
        and a.memname=b.memname
   ;
quit;
          

The Procedure SQL allows use of SQL statements to join data sets in many manners (LOTS of books out there). In this case the previous data set contains the bits you had, library, memname (data set name) and nobs (number of observations). The SAS metadata view holding all the information about variable properties such as name, which can be stored in mixed case so I force an upper case comparison, type, label, format, length and other bits then is used to select when the variable name is the one you want, using "YOURVARIABLENAME" above, only in that library using a WHERE clause. The "as A" and "as B" after the data set name or the query (select * from dictionary.columns ... ) are alias so you can refer to the variables with the prefix a.<variable name> or b<variable name>. The Left Join will select all the records from the A set, the ON clause provides conditions on matching, the library and member name this time. The CASE clause examines the contributing value of the B.Name to the desired value. If it is there then we assign a value of 'Yes' as requested, else assign "No" to a column with the name of the variable you asked about.

 

Personally instead of 'Yes' and 'No' I would use numeric 1/0 for Yes/No with code like this:

Proc sql;
   create table final as 
   select a.libname, a.memname, a.nobs
          ,( upcase(b.name) = 'YOURVARIABLE') as YOURVARIABLE
   from want as a
        left join
        (select * from dictionary.columns
         where upcase(name)='YOURVARIABLE' ) as b
        on a.libname=b.libname
        and a.memname=b.memname
   ;
quit;
          

because there are several things that are easier to report when you only want to count/sum/calculate with the "yes" values that don't work quite as nice with character values.

 

There are lots of metadata available in SAS. Knowing it is there is the first thing. Then learning tools to use it.

 

This would be how to check on TWO variables:

Proc sql;
   create table final as 
   select a.libname, a.memname, a.nobs
          ,( upcase(b.name) = 'YOURVARIABLE') as YOURVARIABLE
          ,( upcase(b.name) = 'OTHERVAR') as OTHERVAR
   from want as a
        left join
        (select * from dictionary.columns
         where upcase(name)in ('YOURVARIABLE' 'OTHERVAR') ) as b
        on a.libname=b.libname
        and a.memname=b.memname
   ;
quit;

Another way would be to create a data set with the names of the variables and do a join on the dictionary.columns to get just the sets that have those variables and then do a similar join with the data of members and obs.

alepage
Barite | Level 11
it is more a sql approach than using a way to select data into a dataset using SAS code but I have read your code and I am quit sure it will do the job. Thank you very much for your help. I though that with SAS we could write small macro and use those in a main program to gather the information we need but it seems not the good approach.

Thank again.
Reeza
Super User
Data new2;
length obs 8. datasetname $100.;
%do k = 1 %to 100;
obs=&k.;
call execute('%ExtractACell(dataset=test ,rownum= &k. , VarName=filename);');
datasetname=symget('gvalue');
output;

%end;
run;

 You need to be using a do loop, not a %DO loop. You need to build the string up using concatenate functions, the macro parameter won't resolve correctly in the same step. And you're always overwriting your NEW data set each time so this whole process isn't going to work as you expect as your results are overwritten each time.

 

You've only provided a rough outline so not sure what you're trying to do here overall or why. Usually, we recommend starting with a working use case. Does your base use case, without any loops do what you need it do? Can you show that code and how you want to generalize it?

 



Tutorial on converting a working program to a macro
This method is pretty robust and helps prevent errors and makes it much easier to debug your code. Obviously biased, because I wrote it 🙂 https://github.com/statgeek/SAS-Tutorials/blob/master/Turning%20a%20program%20into%20a%20macro.md

Examples of common macro usage
https://communities.sas.com/t5/SAS-Communities-Library/SAS-9-4-Macro-Language-Reference-Has-a-New-Ap...

 

 

 

 

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

Creating Custom Steps in SAS Studio

Check out this tutorial series to learn how to build your own steps in SAS Studio.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 6 replies
  • 1733 views
  • 4 likes
  • 4 in conversation