SAS PROC IMPORT with Dynamic Named Ranges - excel

I have a workbook containing database information split into various tabs that I want to import into SAS for further manipulation.
The dimensions of these database tables vary from tab to tab and in addition, there might be further changes to the dimensions of the table throughout the tenure of this project.
As such, I've set up dynamic named ranges in every tab such that it picks up the exact dimensions I require i.e. My named range TBL_SHEET1
=OFFSET(SHEET1!$A$1,0,0,COUNTA(SHEET1!$A:$A),COUNTA(SHEET1!$1:$1)-2)
Now the SAS issue is that when I use the proc import code, it does not seem to think these named ranges "exist" and returns a failure message in the log.
Is there a way around this? Or just a limitation in the way SAS communicates with Excel?
(As a side note, the reason why I am not just importing the entire tab is because there are some extra columns to the right which I want to exclude from my import)
My SAS import code:
proc import
out= rwork.SHEET1
datafile = "C:\My User\Sample.xlsx"
dbms= excel replace;
range=TBL_SHEET1.;
getnames=yes;
run;

Adding your code will really help. However, if I understood your question correct, the problem is with importing named ranges. Here's an example on how to import user defined named range into SAS.
In the example below, I am selecting Range A1:C7 and naming the range as Test
Now, to import the names range Test into SAS,
PROC IMPORT OUT= WANT
DATAFILE= "C:\Desktop\Test.xls"
DBMS=XLS REPLACE;
RANGE="Test";
GETNAMES=YES;
RUN;
In the above code, Range tells SAS to import data from excel under named range Test. You can also automate the code and import multiple excel sheets with dynamic name range into SAS.
For example, I have multiple sheets in an Excel which I want to import into SAS, I'll use the following code to bring the spreadsheets to SAS.
# Assigning Library #
Libname Test "C:\Desktop\Test.xls";
# Creating a table to select sheet names in above library #
Proc sql;
create table test_excel as
select *
from dictionary.tables
where libname="Test" ;
Quit;
# Assigning count and Sheet names to macros#
## Sheet names ##
Proc sql;
select memname into: list seperated by '*'
from test_excel
;
quit;
## Number of sheets ##
Proc sql;
select count(memname) into:count
from test_excel
;
quit;
# Macro to import #
options mprint;
%macro import;
%do i=1 %to &count;
%let var=%scan(&list,&i,*);
PROC IMPORT OUT= %substr(&var,1,%length(&var)-1)
DATAFILE= "C:\Desktop\Test.xls"
DBMS=EXCEL REPLACE;
RANGE="&var";
GETNAMES=YES;
RUN;
%end;
%mend import;
%import ;
I hope this is clear.

I replicated the problem and also can't import a dynamic (not fixed) range from Excel. It doesn't work on either EXCEL or XLSX engines.
If you need a quick solution and don't necessarily have to use Excel ranges, here is a workaround for the problem:
Allocate one sheet's cell, e.g A1 for storing what you currently store in the dynamic range TBL_sheet1, i.e. the range of cells to import.
Execute proc import only to import this range of cells:
PROC IMPORT DATAFILE="C:\test.xlsx" DBMS=xlsx out=work.x
replace;
getnames=no;
RANGE="Sheet1$A1:A1";
RUN;
Next, put the imported range to a macro variable and execute proc import again with proper RANGE= definition.
%let range=;
proc sql noprint;
select A into :range
from work.x;
quit;
%let range = %sysfunc(compress(&range.,$,));
%put &range.;
/* resolves to: B2:B4 */
%macro import(x);
PROC IMPORT DATAFILE="C:\test.xlsx" DBMS=xlsx out=work.y
replace;
getnames=no;
RANGE="Sheet1$&x.";
RUN;
%mend;
%import(&range.);

Related

"ALL"-Option for BY (SAS PROC TABULATE)

ODS EXCEL FILE="/mypoath/myfile.xlsx" options(
frozen_headers="3"
sheet_name="#byval1");
PROC TABULATE data=out;
BY byVariable;
CLASS varA varB;
TABLES varA, varB;
RUN;
ODS EXCEL CLOSE;
The code above creates an excel-file with different sheets. There is one sheet for each value of the variable byVariable. Is there a way to create an additional sheet "ALL" which contains the results for all values of the byVariable together? I mean something like "ALL" (used in the TABLES-section). I tried BY ALL byVar already (which doesn't work).
Thanks for help!
The simple answer is NO. If you want all of the data then don't use the BY statement.
ODS EXCEL FILE="/mypoath/myfile.xlsx" options(frozen_headers="3");
ODS EXCEL options(sheet_name="ALL");
PROC TABULATE data=out;
CLASS varA varB;
TABLES varA, varB;
RUN;
ODS EXCEL options(sheet_name="#byval1");
PROC TABULATE data=out;
BY byVariable;
CLASS varA varB;
TABLES varA, varB;
RUN;
ODS EXCEL CLOSE;
There is no such option.
You can:
rerun the report without BY, or
stack the data on itself modifying the by variable to be ALL -- such that it is higher than all existant by values.
data stacked / view=stacked;
set
have
have (in=stackflag)
;
if stackflag then do;
byvar = 'A0'x || 'ALL'; * A0 forces value to be 'after' other original byVar values;
end
run;
proc tabulate data=stacked;
by byvar;
…
Note: 'A0'x is a hard space ASCII character

SAS export subset of column to worksheet with a column name

Given a SAS dataset with columns named n1,n2,..nN.
Is there a simple way to export common set of columns and unique subset of columns to a workbook, where each column is exported to the worksheet with the same name as the last column name?
Example:
For the SAS dataset above, the columns:
n1, n2, n5 -> Worksheet n5
n1, n2, n9 -> Worksheet n9
n1, n2, n13 -> Worksheets n13
are exported to a Excel workbook, with worksheets named as above.
Appreciate any suggestions.
Use the SHEET= statement in a Proc EXPORT step.
For example:
filename myxl 'c:\temp\sandbox.xlsx';
proc export replace file=myxl dbms=excel
data=sashelp.class (keep=name)
;
sheet='Name';
run;
proc export replace file=myxl dbms=excel
data=sashelp.class (keep=name age weight)
;
sheet='Weight';
run;
A macro can be coded to generate repetitive parts
%macro excel_push (file=, data=, always=, each=);
%local i n var;
%let n = %sysfunc(countw(&each));
%do i = 1 %to &n;
%let var = %scan(&each,&i);
proc export replace file=&file dbms=excel
data=&data(keep=&always &var)
;
sheet="&var";
run;
%end;
%mend;
options mprint;
filename myxl2 'c:\temp\sandbox2.xlsx';
%excel_push (
file=myxl2,
data=sashelp.class,
always=name age sex,
each=height weight
)
If you open the Excel output, leave it open and rerun the code, you will get an error, albeit slightly obscure:
ERROR: Error attempting to CREATE a DBMS table. ERROR: Execute: The Microsoft Access database
engine could not find the object ********. Make sure the object exists and that you spell
its name and the path name correctly. If ******** is not a local object, check your
network connection or contact the server administrator..
I guess, What I understood from your question is how to have sheet name with last variable of SAS dataset. One way to do this is to use dictionary.columns and find which column position (varnum in dictionary.columns)is max in a dataset which will give the last variable and you can make a macro variable out of this and use this for sheet in proc export.
/* pick up the last variable*/
proc sql ;
select name into :mysheet TRIMMED from dictionary.columns
where libname = "SASHELP"
and memname = "CLASS"
and varnum = (select max(varnum) from dictionary.columns
where libname = "SASHELP"
and memname = "CLASS");
/* use the macrovariable in your sheet statement*/
PROC EXPORT DATA= Sashelp.Class /*Sheet 1*/
outfile= "/folders/myfolders/class.xlsx "
dbms=xlsx replace;
sheet="&mysheet";
run;

How to open .sas7bdat in SAS and export to excel?

I am quite new to SAS. I have got a file with file extension .sas7bdat which contains daily stocks prices and percentage changes. It has almost 2 million line items. I know that I can simply double click the file and open it with SAS 9.4. But, I am looking for codes which I can type in Editor and open this file. Please help me.
After I open this file, I need to export it to excel. Since it has 2 million data, I can not export everything in a single excel tab. So, What I want to do it randomly pick (say 10,000 or 20,000) data and export only this randomly picked data to excel.
My .sas7bdat file is on desktop.
Please help.
You can use surveyselect and specify the number of records you want in your subset the use proc export.
In my Example below I create a table of 10 rows and only wanted 5 row in the subset. just change the value in my macro variable from 5 to 100000
Code:
data have;
input value;
datalines;
1
2
3
4
5
6
7
8
9
10
;
run;
%let subset=5;
proc surveyselect data=have
method=srs n=&subset. out=want;
run;
Output:
value=1
value=2
value=5
value=6
value=10
Exporting:
proc export data=sashelp.class
outfile='c:\myfiles\want.csv'
dbms=csv
replace;
run;
You can also filter on the data you are exporting, dummy example below:
proc export data=want (where=(value > 100 or location='X'))
outfile='c:\myfiles\want.csv'
dbms=csv
replace;
run;
You can use ODS. This will be simpler, but generate a file which will give warning in opening first time
libname rd_data "<Your Path to dataset>"
data temp;
set rd_data.<dataset name>;
rnd = ranuni(123456);
run;
proc sort data = temp out = temp(drop=rnd);
by rnd;
run;
**** Remember this is .xls file, not .xlsx
ods html file = <xls file path - full path>;
proc print data = temp(obs=1000);
run;
ods html close;
Alternatively, you can use DDE (Dynamic Data Exchange)
First, create a library to point to the location on the file system where the data set resides. This is a pointer (in C terms) to the directory.
libname myData "<path to folder>";
From there you can use a random number and a data step to get N random values. Alternatively, PROC SURVEYSELECT can be used, but you might not have it licensed.
data temp;
set myData.<Data Set Name>;
__rnd = ranuni(1);
run;
proc sort data=temp ;
by __rnd;
run;
data toOutput;
set temp(obs=10000 drop=__rnd);
run;
The last Data Step reads in just the first 10,000 records which you randomized above.
Then you can use PROC EXPORT to export the values.
proc export data=toOutput outfile="c:\temp\output.xlsx" dbms=xlsx replace;
sheet="MyData";
run;
The great thing here is that you can create other sheets in the file with additional exports.
proc export data=toOutput outfile="c:\temp\output.xlsx" dbms=xlsx replace;
sheet="MyData2";
run;
This would allow you to create multiple samples or even export all the data across multiple sheets.

Change sheet names in SAS ODS dynamically

Is there a way to dynamically change the sheet names in SAS ODS using macros? Inside a macro, I have the following code:
%DO N = 1 %TO &SQLOBS;
ODS TAGSETS.ExcelXP
OPTIONS(SHEET_NAME = &&TABLEVAR&N
EMBEDDED_TITLES = 'NO'
EMBEDDED_FOOTNOTES = 'NO'
PRINT_HEADER = 'CURRENT &&TABLEVAR&N AS OF &D';
PROC PRINT
DATA = WORK.&&TABLEVAR&N
NOOBS;
RUN;
%END;
Which basically loops through an array of table names and for each table outputs a separate Excel Sheet. And by calling a macro variable &TABLEVAR, it is supposed to dynamically change the sheet name for each table. However, I get the following error:
ERROR 22-322: Expecting a quoted string.
So SAS does resolve the macro variable, I can see the table names are right in the log, but the problem is they are not a quoted string. So I tried it like this with quotes around it:
OPTIONS(SHEET_NAME = '&&TABLEVAR&N'
But then the macro variable is not being resolved by SAS and all the sheets are named &&TABLEVAR&1, 2, etc.
Is there a way around this? (And each sheet is a separate table with a different name so I can't use a BY group.) What good macros are if they can't interact with ODS?
I just found out placing %sysfunc and quote in front of &&TABLEVAR&N does the trick.
So the final code looks like this:
%DO N = 1 %TO &SQLOBS;
ODS TAGSETS.ExcelXP
OPTIONS(SHEET_NAME = %sysfunc(quote(&&TABLEVAR&N))
EMBEDDED_TITLES = 'NO'
EMBEDDED_FOOTNOTES = 'NO'
PRINT_HEADER = 'CURRENT &&TABLEVAR&N AS OF &D';
PROC PRINT
DATA = WORK.&&TABLEVAR&N
NOOBS;
RUN;
%END;
I don't think you need all that. If you use double quotes around the macro variable expression, you should get the same result as the %SYSFUNC(QUOTE()) function:
"&&TABLEVAR&N"
That is, unless you somehow plan to have double quotes in the sheet names....

Exporting multiple sas datasets into multiple excel sheets with macro?

I am looking if there is a macro that would export multiple datasets into separate excel worksheets within a workbook. Would be great if I could have 10 sheets per workbook.
I can do it the usual way as below, but I have more than 100 datasets to export:
PROC EXPORT DATA=HAVE;
OUTFILE= "S:\MYEXCEL.xlsx"
DBMS=EXCEL REPLACE;
SHEET="NEW_SHEET";
RUN;
Thank you!
The general concept is that you would do something like this:
%macro export_data(file=,data=,sheet=);
proc export data=&data.
outfile="&file."
dbms=excel replace;
sheet="&sheet.";
run;
%mend export_data;
Then you need to construct your export macro calls however you want. Get a dataset with one row per dataset (use dictionary.tables in SQL or sashelp.vtable in data step) and work out however you want to the logic of the sheet names and how many workbooks you need. Google data-driven macro calls for more information.
Provided you have 'SAS/Access to PC FIle formats' licensed, this little macro does it effortlessly:
%macro SASToExcel(ImportLibrary=, ExportLocation=);
ods output members = _Members;
proc datasets lib = &ImportLibrary; run; quit;
proc sql;
select count(Name) into :NumOfDatasets from _Members;
select Name into :Dataset1-:Dataset%trim(%left(&NumOfDatasets)) from _Members;
quit;
%do index = 1 %to &NumOfDatasets;
proc export data=&ImportLibrary..&&Dataset&index.
outfile="&ExportLocation"
dbms=excel replace;
sheet="&&Dataset&index";
run;
%end;
proc datasets;
delete _Members;
quit;
%mend;
%SASToExcel(ImportLibrary=raw, ExportLocation = c:\test.xlsx);

Resources