exporting in SAS multiple datasets - sas

I have a set of datasets which I would like to output in an Excel File. Is there a way to do this quickly rather than calling proc export each time for each dataset
%let MyDS = ('out.Ids', 'out.Vars', 'out.Places')
%let MyDSname = (Ids, Vars, Places)
I would like to create a macro that check if each dataset exists and then output to an Excel spreadsheet with the Tab name as specified in the corresponding MyDSname ...
Something like... %macro Out(MySpreadsheetName, MyDS, MyDSname);
Thanks very much for your help

Assuming you have Access to PC Files licensed, the easiest way to do this (export several datasets to one workbook) is with a libname.
libname mywbk excel 'c:\pathtomyexcel\excelfile.xlsx';
data mywbk.nameoftab;
set dataset;
run;
In terms of conditionally creating this, you should look at how you're arriving at the list of names to export. In general, you should have a dataset that contains one row per dataset to export, and two columns - DS name and tab name. You can then merge that to sashelp.vtables or dictionary.tables which are views containing the list of tables in the current SAS session; memname is the name of the table, libname is the name of the library. Then create a macro call from that:
proc sql;
select cats('%out(',dsname,',',tabname,')') into :calllist separated by ' '
from joinedds;
quit;
libname ... ;
&calllist.;

Related

SAS Selecting the most recent dataset in a lib automatically

i am working with a library that is updated every month or so, and i need a way to select the most recent dataset each month, i tried two methods that would show me the latest table, one that makes a table ordering from the modified date
proc sql;
create table tables as
select memname, modate
from dictionary.tables
where libname = 'SASHELP'
order by modate desc;
quit;
and one that gives me just the latest modified one
proc sql;
select memname into> latest_dataset
from dictionary.tables
where libname='WORK'
having crdate=max(crate);
%put &=latest_dataset;
and i would like to put these latest datasets in a table, but i don't know how, or if there is another easier way to do this, i am still very much new to SAS programming so i'm lost, any help is appreciated.
Use Proc APPEND to put the latest data set into a table. You are essentially accumulating rows.
Use SQL :INTO to obtain (place into a macro variable) the libname.memname of the data set that should be appended.
Example:
The task of determining the newest data set and appending it to a base table is also in a macro so the the code can be easily rerun in the example.
%macro append_newest;
%local newest_table;
proc sql noprint;
select catx('.', libname, memname) into :newest_table
from dictionary.tables
where libname = 'WORK'
and memtype = 'DATA'
having crdate = max(crdate);
%put NOTE: &=newest_table;
create view newest_view as
select "&newest_table" as row_source length=41, *
from &newest_table
;
proc append base=work.master data=newest_view;
run;
%mend;
* create an empty for accumulating new observations;
data work.master;
length row_source $41;
set one (obs=0);
run;
data work.one;
set sashelp.class;
where name between 'A' and 'E';
%append_newest;
data work.two;
set sashelp.class;
where name between 'Q' and 'ZZ';
%append_newest;
data work.three;
set sashelp.class;
where name between 'E' and 'Q';
%append_newest;
Will produce this master table that accumulates the little pieces that come in day by day.
You would want additional constraints such as a unique key in order to prevent appending the same data more than once.

join tables from a library

hi am trying to append the data-sets from a library which contain a specific column variable in them.for example i want to append those data-sets which contain the name column in them from myfile library.
below is my sample code--->
libname myfile'\c:data';
proc sql noprint ;
select distinct catx(".",libname,memname) into :DataList separated by " "
from dictionary.columns
where libname = upcase(myfile) and upcase(name);
quit;
Assuming that the type of the variable is consistent across all datasets something as simple as SET will work:
Data want;
Set &datalist;
Run;

How to obtain the number of records of a dataset in SAS

I want to count the number of records in a dataset in SAS. There is a function the make this thing in a simple way? I used R ed for obtain this information there was the length() function. Morover I need the number of record to compute some percetages so I need this value not in a table but in a value that can be used for other data step. How can I fix?
Thanks in advance
Here is another solution, using SAS dictionaries,
proc sql;
select nobs into: num_obs
from dictionary.tables
where libname = "WORK" and memname = "A"
;
quit;
It is easy to get the size of many datasets by modifying the above code,
proc sql;
create table test as
select memname, nobs
from dictionary.tables
where libname = "WORK" and memname like "A%"
;
quit;
data _null_;
set test;
call symput(memname, nobs);
run;
The above code will give you the sizes of all data sets with name starting with "a" in the temporary/work library.
Assuming this is a basic SAS table that you've created, and not modified or appended to, the best way is to use the meta data held in a dataset (the Number of tries is held in a piece of meta data called "nobs"), without reading through the dataset its self and place it in a macro variable. You can do this in the following way:
Data _null_;
i=1;
If i = 0 then set DATASETTOCOUNT nobs= mycount;
Call symput('mycount', mycount);
Run;
%put &mycount.;
You will now have a macro variable that contains the number of rows in your dataset, that you can call on in other data steps using &mycount.

Merge multiple tables in sas using loop or macro

I have generated the tables of Forecast_2013 to 2022 in a loop and then merged all datasets in to 1 Table. But now I want to do merge the datasets in a loop with irrespective of years, The next year will be 2023 or 2024...I dont want to do mannually to set Forecast_2023;set forecast_2024. How can I put in to loop using macro?
Data P_OT.FORECAST(DROP=td qq AGE1 AGE2 AGE3 AGE4 AGEBANDFCST020 AGEBANDFCST030 AGEBANDFCST035P
HSI1_2012 HSI1_2013 HSI1_2014 HSI1_2015 HSI1_2016 HSI1_2017 HSI1_2018 HSI1_2019 HSI1_2020 HSI1_2021 HSI1_2022);
set FORECAST_2013;set FORECAST_2014;set FORECAST_2015;set FORECAST_2016;
set FORECAST_2017;set FORECAST_2018;set FORECAST_2019;set FORECAST_2020;
set FORECAST_2021;set FORECAST_2022;
run;
An alternative to what Scott posted would be:
*Assign library to folder where FORECAST_ files are located;
libname NAME 'C:\Path to Folder';
*Data step to stack files;
Data P_OT.FORECAST(DROP=td qq AGE1 AGE2 AGE3 AGE4 AGEBANDFCST020
AGEBANDFCST030 AGEBANDFCST035P HSI1_:);
set NAME.FORECAST_:;
run;
This should give the same results as what Scott posted using name prefix lists instead of using SQL to produce lists of datasets to be merged and variables to be dropped.
The code above will stack all datasets in the libname library that start with "FORECAST_". It will also drop all variables in the created dataset that begin with "HSI1_".
You can use proc sql and the sashelp.vcolumn table to find the names of all your tables and create a macro variable containing them. To do this all your tables need to be in the same library, and any other tables in the library can not contain FORCAST_. The part of the sql code that says memname like '%FORECAST_%' is doing a search on all the tables in the library called libname to select the tables that contain FORCAST_. The sql step creates a list of your tables that you can then inject into your data step to stack them.
Again: Be careful there are not other tables with the name like FORECAST_ or it will try to stack tables you do not want. The easiest way to ensure this would be to put them in their own library when you create these tables.
If you have all these tables in the work library then replace libname with work
I'm on my phone and haven't checked the substr and index part, but if i recall correctly that should work.
proc sql noprint;
select "libname."||memname
into :stack_tables separated by ' '
from sashelp.vcolumn
where libname = upper("libname")
and
memname like '%FORECAST_%'
;
select "HSI1_"||substr(memname,index(memname,"_")+1,4)
into :drop_vars separated by ' '
from sashelp.vcolumn
where libname = upper("libname")
and
memname like '%FORECAST_%'
;
quit;
Data P_OT.FORECAST(DROP=td qq AGE1 AGE2 AGE3 AGE4 AGEBANDFCST020 AGEBANDFCST030 AGEBANDFCST035P
&drop_vars.);
set &stack_tables.;
run;

SAS - Creating variables from macro variables

I have a SAS dataset which has 20 character variables, all of which are names (e.g. Adam, Bob, Cathy etc..)
I would like a dynamic code to create variables called Adam_ref, Bob_ref etc.. which will work even if there a different dataset with different names (i.e. don't want to manually define each variable).
So far my approach has been to use proc contents to get all variable names and then use a macro to create macro variables Adam_ref, Bob_ref etc..
How do I create actual variables within the dataset from here? Do I need a different approach?
proc contents data=work.names
out=contents noprint;
run;
proc sort data = contents; by varnum; run;
data contents1;
set contents;
Name_Ref = compress(Name||"_Ref");
call symput (NAME, NAME_Ref);
%put _user_;
run;
If you want to create an empty dataset that has variables named like some values you have in a macro variables you could do something like this.
Save the values into macro variables that are named by some pattern, like v1, v2 ...
proc sql;
select compress(Name||"_Ref") into :v1-:v20 from contents;
quit;
If you don't know how many values there are, you have to count them first, I assumed there are only 20 of them.
Then, if all your variables are character variables of length 100, you create a dataset like this:
%macro create_dataset;
data want;
length %do i=1 %to 20; &&v&i $100 %end;
;
stop;
run;
%mend;
%create_dataset; run;
This is how you can do it if you have the values in macro variable, there is probably a better way to do it in general.
If you don't want to create an empty dataset but only change the variable names, you can do it like this:
proc sql;
select name into :v1-:v20 from contents;
quit;
%macro rename_dataset;
data new_names;
set have(rename=(%do i=1 %to 20; &&v&i = &&v&i.._ref %end;));
run;
%mend;
%rename_dataset; run;
You can use PROC TRANSPOSE with an ID statement.
This step creates an example dataset:
data names;
harry="sally";
dick="gordon";
joe="schmoe";
run;
This step is essentially a copy of your step above that produces a dataset of column names. I will reuse the dataset namerefs throughout.
proc contents data=names out=namerefs noprint;
run;
This step adds the "_Refs" to the names defined before and drops everything else. The variable "name" comes from the column attributes of the dataset output by PROC CONTENTS.
data namerefs;
set namerefs (keep=name);
name=compress(name||"_Ref");
run;
This step produces an empty dataset with the desired columns. The variable "name" is again obtained by looking at column attributes. You might get a harmless warning in the GUI if you try to view the dataset, but you can otherwise use it as you wish and you can confirm that it has the desired output.
proc transpose out=namerefs(drop=_name_) data=namerefs;
id name;
run;
Here is another approach which requires less coding. It does not require running proc contents, does not require knowing the number of variables, nor creating a macro function. It also can be extended to do some additional things.
Step 1 is to use built-in dictionary views to get the desired variable names. The appropriate view for this is dictionary.columns, which has alias of sashelp.vcolumn. The dictionary libref can be used only in proc sql, while th sashelp alias can be used anywhere. I tend to use sashelp alias since I work in windows with DMS and can always interactively view the sashelp library.
proc sql;
select compress(Name||"_Ref") into :name_list
separated by ' '
from sashelp.vcolumn
where libname = 'WORK'
and memname = 'NAMES';
quit;
This produces a space delimited macro vaiable with the desired names.
Step 2 To build the empty data set then this code will work:
Data New ;
length &name_list ;
run ;
You can avoid assuming lengths or create populated dataset with new variable names by using a slightly more complicated select statement.
For example
select compress(Name)||"_Ref $")||compress(put(length,best.))
into :name_list
separated by ' '
will generate a macro variable which retains the previous length for each variable. This will work with no changes to step 2 above.
To create populated data set for use with rename dataset option, replace the select statement as follows:
select compress(Name)||"= "||compress(_Ref")
into :name_list
separated by ' '
Then replace the Step 2 code with the following:
Data New ;
set names (rename = ( &name_list)) ;
run ;