I have just started learning SAS, and I'm using the following code to read xlsx files:
proc import out = data_lib.dataset_1
datafile = 'C:\data_folder\data_file_1.xlsx'
dbms = xlsx replace;
sheet = 'Sheet1';
getnames = yes;
run;
This has been working fine for me, but I'd like to supply the code with a list of filenames to read and a list of dataset names to create, so that the code need only appear once. I have looked at several instructional web pages about using macros, but I've been unable to translate that information into working code. Any help would be greatly appreciated. I'm using SAS 9.4, 64 bit.
I'd offer a modified version of kl78's suggestion, avoiding macros. Again, assuming you have the file names in a SAS data set, use a data step to read the list of file names and use call execute to run your proc import code for each file name.
data _null_;
set t_list;
call execute (
"proc import out = " || datasetname || "
datafile = '"|| filename ||"'
dbms = xlsx replace;
sheet = 'Sheet1';
getnames = yes;
run;");
run;
So, suppose you have your filenames and datanames in a table called t_list with variablename datasetname and filename, you could try something like this:
%macro readexcels;
data _null_;
set t_list (nobs=nobs);
call symputx(cat("libname_",_n_), datasetname);
call symputx(cat("filename_",_n_), filename);
if _n_=1 then
call symputx("nobs", nobs);
run;
%do i=1 %to &nobs;
proc import out = &&libname_&i;
datafile = "&&filename_&i"
dbms = xlsx replace;
sheet = 'Sheet1';
getnames = yes;
run;
%end;
%mend;
%readexcels;
In the datastep you read every entry of your table with datasetname and listname and create macrovariables with a numeric suffix. You only need to create a macrovariable for the number of entries once, so i did it when n = 1, you could also do this at eof.
Then you have a do loop, and with every loop you read the specific excel and write it in the specific dataset.
You need to write it like &&libname&i, because at first this resolves to &libname_1, and after this resolves to the variablevalue...
Related
I've imported the excel file and it says the data file work.one has been created, but how am i able to manipulate this file?
What im trying to do is transform PSA to ln(PSA+1) in most analyses using PSA, where PSA is the excel file I imported. I am using base SAS.
This is my code so far
data excl;
proc import out = psa
datafile= "H:\MinitabFiles\Morrell\SAS-Data\psa.xls"
dbms = xls replace;
sheet = "psa";
getnames = yes;
run;
data logs;
set excl;
sheet = log(sheet+1);
run;'
What do you want to do with it?
You'll find it in the work library under the explorer tree assuming you are using Enterprise Guide. To 'manipulate' it, many of your most obvious options will be covered by proc sql; or a datastep.
Please elaborate on the question if you want a more useful answer:-
1 which sas product are you using? EG, Base, etc?
2 have you located the file and established it has been imported correctly?
3 `manipulate'? What does this mean? Filter, perform calculations, functions, conversions or one of many other things.
Welcome btw, but please provide plenty more detail.
You have issues with your current code. For example the data excl doesn't do anything. PROC IMPORT creates the data, the data step around the PROC IMPORT is useless.
data excl; *REMOVE THIS, DOES NOT DO ANYTHING;
proc import out = psa /*PSA is the name of the output data set*/
datafile= "H:\MinitabFiles\Morrell\SAS-Data\psa.xls"
dbms = xls replace;
sheet = "psa";
getnames = yes;
run; *THIS GOES WITH THE PROC IMPORT;
data logs;
set excl; *YOUR DATASET IS CALLED PSA, replace EXCL with PSA;
sheet = log(sheet+1); *This assumes your PSA data set has a variable with sheet;
run;'
I'm writing a SAS program to interact with an API. I'm trying to use SAS to capture a specific field from a text file generated by the API.
The generated text "resp" looks like this:
{"result":{"progressId":"ab12","percentComplete":0.0,"status":"inProgress"},"meta":{"requestId":"abcde123","httpStatus":"200 - OK"}}
The field I want to capture is "progressID". In this case, it would be "ab12". If the length of progressID will change, what's the easiest way to capture this field?
My current approach is as follows:
/* The following section will import the text into a SAS table,
seperated by colon. The third column would be "ab12","percentCompelte"
*/
proc import out = resp_table
datafile= resp
dbms = dlm REPLACE;
delimiter = ':';
GETNAMES = NO;
run;
/* The following section will trim off the string ,"percentCompete"*/
data resp_table;
set resp_table;
Progress_ID = SUBSTR(VAR3,2,LENGTH(VAR3)-20);
run;
Do you have an easier/ more concise solution?
Thanks!
Shawn
You can use the JSON library engine to read a json document, and copy the contents to SAS datasets. Work with the data items that the engine creates.
Example:
filename myjson "c:\temp\sandbox.json";
data _null_;
file myjson;
input;
put _infile_;
datalines;
{"result":{"progressId":"ab12","percentComplete":0.0,"status":"inProgress"},"meta":{"requestId":"abcde123","httpStatus":"200 - OK"}}
run;
libname jsondoc json "c:\temp\sandbox.json";
proc copy in=jsondoc out=work;
run;
proc print data=work.Alldata;
where P1='result' and P2='progressId';
run;
Question: How can I export subsets of a data set to individual tabs of an Excel workbook (preferably .xlsx) without running PROC EXPORT several times?
My Solution
The data set contains 15 indicators. Unfortunately, the indicators do not have names which can be indexed. This means I cannot put the export procedure into a macro which loops over a counter 15 times and appends the name with the index. The indicators are (not really) things like "car", "truck", "bicycle", "dinosaur", etc.
The solution I came up with was like this:
proc export data = data_set
(where = (indicator = "car"))
outfile = "c:\workbook.xlsx"
dbms = xlsx replace;
sheet = car;
run;
...
proc export data = data_set
(where = (indicator = "dinosaur"))
outfile = "c:\workbook.xlsx"
dbms = xlsx replace;
sheet = dinosaur;
run;
However, this is obviously inefficient and begs for some sort of automation.
You can use the libname facility, which is what proc export uses in the background usually.
libname myexcel xlsx "c:\outwhatever\myfile.xlsx"; *can use XLSX if 9.4+ or EXCEL if earlier;
That gives you a regular libname just as if it were a SAS libname, and you can write to it like so:
data myexcel.sheetname;
set whatever;
run;
Or use PROC COPY or similar.
There are other options (using OLEDB or similar, for example), but libname is simplest. See the documentation for more details.
If you have SAS 9.4 ODS Excel is quite simple and nice, set the sheet_interval option to bygroup and add a prefix for the sheet name.
proc sort data=sashelp.class out=class;
by age;
run;
ods excel file='/folders/myfolders/sample.xlsx' options (sheet_interval='bygroup' sheet_label='Age');
proc print data=class noobs label;
by age;
run;
ods excel close;
I've run into an odd SAS quirk that I can't figure out - hopefully you can help.
I have a simple macro loop that imports CSV files and for some reason if I use a libref statement in the "out=" part of the import procedure, SAS doesn't recognize the libref as a valid name. But if I use the same libref in a data step, it works just fine.
The specific error it gives is: "ERROR: "TESTDB." is not a valid name."
I'd like to figure this out because I work with pretty big files and want to avoid reading through them more times than is necessary.
Here's the code that works, with some comments in it. I got around the issue by reading in the files, then writing them to permanent SAS datasets in a second step, but ideally I'd like to import the files directly into the "TESTDB" library. Any idea how to get SAS to recognize a libref in the "out=" statement of the import procedure?
libname testdb "C:\SAS test";
%let filepath = C:\SAS test\;
%macro loop(values);
%let count=%sysfunc(countw(&values));
%do i = 1 %to &count;
%let value = %qscan(&values,&i,%str(,));
proc import datafile = "&filepath.&value..csv"
out = &value dbms=csv replace; getnames=yes;
/*"out=testdb.&value" in the line above does not work*/
run;
data testdb.&value; set &value; run;
/*here the libref testdb works fine*/
%end;
%mend;
%loop(%str(test_a,test_b,test_c));
Thanks in advance for your help!
john
Perhaps try:
out=testdb.%unquote(&value)
Sometimes the macro language does not unquote values automatically. With result that the extra quoting characters introduced by a quoting function (%qscan %str %bquote %superq etc) cause problems.
Strange error. I am not able to pin it. My guess is that it has something to do with how the value macro variables are being created. When I moved the value variable creation to a data step and used Call Symputx, it works.
%macro loop(files);
/* Create macro variables for files.*/
data _null_;
count = countw("&files.",",");
call symputx("count",count,"L");
do i = 1 to count;
call symputx(cats("file",i),scan("&files.",i,","),"L");
end;
run;
/* Read and save each CSV as a sas table. */
%do i=1 %to &count.;
proc import datafile = "&filepath.&&file&i...csv"
out = testdb.&&file&i. dbms=csv replace; getnames=yes;
run;
%end;
%mend;
%loop(%str(test_a,test_b));
Situation: i have a workbook .xls with 4 spreadsheets named "SheetA", "SheetB", "SheetC", "SheetD".
For import one spreadsheet i do as following.
proc import
out = outputtableA
datafile = "C:\User\Desktop\excel.xls"
dbms = xls replace;
sheet = 'SheetA';
namerow = 3;
startrow = 5;
run;
All spreadsheet have same number of variables and format. I would like to combine all four outputtableX together using data step:
data combinedata;
set outputtableA outputtableB outputtableC outputtableD;
run;
I am new to SAS, i m thinking whether array and do-loop can help.
I would not use a do loop (as they're almost always overly complicated). Instead, I would make it data driven. I also would use Reese's solution if you can; but if you must use PROC IMPORT due to the namerow/datarow options, this works.
First, create the libname.
libname mylib excel "c:\blah\excelfile.xls";
We won't actually use it, if you prefer the xls options, but this lets us get the sheets.
proc sql;
select cats('%xlsimport(sheet=',substr(memname,1,length(memname)-1),')')
into :importlist separated by ' '
from dictionary.tables
where libname='MYLIB' and substr(memname,length(memname))='$';
quit;
libname mylib clear;
Now we've got a list of macro calls, one per sheet. (A sheet is a dataset but it has a '$' on the end.)
Now we need a macro. Good thing you wrote this already. Let's just substitute a few things in here.
%macro xlsimport(sheet=);
proc import
out = out&sheet.
datafile = "C:\User\Desktop\excel.xls"
dbms = xls replace;
sheet = "&sheet.";
namerow = 3;
startrow = 5;
run;
%mend xlsimport;
And now we call it.
&importlist.
I leave as an exercise for the viewers at home wrapping all of this in another macro that is able to run this given a filename as a macro parameter; once you have done so you have an entire macro that operates with little to no work to import an entire excel libname.
If you an xls file and are using a 32 bit version of SAS something like this would work:
libname inxls excel 'C:\User\Desktop\excel.xls';
proc datasets library=excel;
copy out=work;
run; quit;
libname inxls;
Then you can do your step above to append the files together. I'm not sure Proc Import with excel recognizes the option name row and start row so you may need to modify your code somehow to accommodate that, possibly using firstobs and then renaming the variables manually.
What you have will work assuming the variable names are the same. If they are not use the rename statement to make them all the same.
data combinedata;
set outputtableA(rename=(old_name1=new_name1 old_name2=new_name2 ... ))
outputtableB(...)
...
;
run;
Obviously, fill in the ellipses.