I am trying to write a SAS output file from a work table. I inserted blank values ' ' (contains 4 spaces) for the output column. However, when I output to a text file, it shows only one space '' (Example below, Sequence_number)
Is there a way I can include a format and have all the four spaces as output:
Something like below(sequence number should have 4 blank spaces)
create table work.mech_temp_final as
(select put(mt.chq_srl_nbr, z12.) as chq_srl_nbr
, " " as sequence_number
from work.settl mt);
output as
12345| |
rather than
12345||
Instead of using PROC EXPORT, use a data step with a put statement to generate the pipe-delimited file. If you have many variables to output, you can generate the required code using dictionary tables, save it to a macro variable, and use it in the put statement.
data _null_;
set sashelp.cars(rename=(horsepower = chq_srl_nbr) );
file "C:\test.txt";
sequence_number = ' ';
/* Create a header */
if(_N_ = 1) then put 'chq_srl_nbr|sequence_number';
/* Write data in the desired format */
put chq_srl_nbr z12. '|' sequence_number $char4.;
run;
You can write directly write a fixed layout file from Proc SQL by using ODS LISTING. Not recommended for many use cases.
Example:
title;
options nocenter nodate nonumber ps=max formchar=' ';
ods listing file='c:\temp\listing.txt';
proc sql;
select
name format=$CHAR12.
, ' '
, age format=z9.
from sashelp.class;
quit;
ods listing close;
Related
This is the code that I have with me:
%let data=sashelp.cars;
proc transpose data=&data(obs=0) out=names;
var _all_;
run;
proc sql;
select cats('_',_name_,'=missing(',_name_,');') into: stmts separated by ' ' from names;
run;
data missing;
set &data;
_BIGN = 1;
/*%m_expand_varlist(data=&data,expr=cats('_',_name_,'=missing(',_name_,');'));*/
&stmts;
keep _:;
run;
proc summary data=missing;
var _numeric_;
output out=smry sum=;
run;
proc transpose data=smry(drop=_type_ _freq_) out=smry_;
run;
The goal of this code is to output the number of missing values for both character and numeric variables in the data set. The code accomplishes that objective but I have difficulty in understanding the purpose of certain lines in the code.
May I know what the following part of the code does?
select cats('_',_name_,'=missing(',_name_,');') into: stmts separated by ' ' from names;
I understand that the into part just stores the value into the macro variable stmts but what does "separated by ' ' from names" in the above line mean?
data missing;
set &data;
_BIGN = 1;
&stmts;
keep _:;
run;
And in the above portion of the code, what is the purpose of "keep :"? What does the ":" do in that? And is the "_BIGN = 1" necessary?
And also in the final output table called smry_, I get underscores before the names of the variables. But I don't need these underscores. What can I do to remove them? When I removed the underscore after the "keep :", the underscores in the smry table went away but I was left with only 10 rows instead of 15. Help would be appreciated. Thank you.
Before answering your questions, let me disclaim that this is not the way to go if you simply want a count of missing values for each numeric variable in your data.
However, this seems to be more of a practice assignment than an actual problem.
The Separated By Clause simply inserts the specific string between the values in the macro variable if the data source has >1 items. In this case the names data set has 15 items, so all 15 values are listed with a few spaces between them.
The colon operator in the keep statement tells the data step to keep only variables prefixed with an underscore.
_BIGN is not strictly necessary. However, it seems that the author of the code wants a simple count of observations in the final data set. That is all it does.
The underscores are applied to each variable name in the creation of the macro variable in. It is probably done to avoid conflicts between variable names (though this is technically still possible). Obviously, you can simply remove the underscore in the final data set.
As you know the "into" clause stores the values in the macro variable. The "separated by" leads to a list of values stored in the variable with spaces as delimiter here. If you don't use this you have only the value of the first row in your macro variable.
The ":" is a wildcard that means you keep all the variables starting with an underscore:
keep _:;
I managed to remove the underscores in the final data set using the following code:
data _smry_(drop = _name_);
set smry_;
name=compress(_name_, , 'kas');
run;
You could just rename the underscore variables back to original.
%let data=sashelp.heart;
proc transpose data=&data(obs=0) out=names;
var _all_;
run;
proc sql;
select cats('Label _',_name_,'=',quote(_label_),';') into: lb_stmts separated by ' ' from names;
select cats('_',_name_,'=missing(',_name_,');') into: stmts separated by ' ' from names;
select cats('_',_name_,'=',_name_) into: rn_stmts separated by ' ' from names;
run;
options symbolgen=1;
data missingV / view=missingV;
set &data;
&stmts;
&lb_stmts;
rename &rn_stmts;
keep _:;
run;
options symbolgen=0;
ods output summary=summary;
proc means data=missingV Sum Mean N STACKODSOUTPUT;
var _numeric_;
run;
ods output close;
proc print width=minimum label;
label sum='#Missing' mean='%Missing';
format sum 8. mean percentn8.1;
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;
I got a SAS dataset DATA which contains 100 variables. Unfortunately, this dataset dont contain the name of each variable. It just name the variable as VAR1 - Var100. I got a seperate file which list the name of each variable Name (one name per cell). I donot want to rename it one by one so the following code is not an option.
data lib.test (rename = (var1= truename1 var2 = truename2 ...) ;
set lib.test;
run;
Following Reeze's suggestions, I try to implement the following solution http://stackoverflow.com/questions/29006915/rename-variable-regardless-of-its-name-in-sas.
proc sql;
/* Write the individual rename assignments */
select strip(name) || " = " || substr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", varnum , 1)
/* Store them in a macro variable and separate them by spaces */
into :vars separated by " "
/* Use a sas dictionary table to find metadata about the dataset */
from sashelp.vcolumn
where libname = "LIB"
and memname = "TEST"
and 1 <= varnum <= 100;
quit;
proc datasets lib=lib nolist nodetails;
modify test;
rename &vars.;
quit;
Now, instead of using a,b,c,d ... to rename my variable, I want to use the name on datasetName as new names. Dataset Name looks like the following (I can transpose it if it is easier to use). The order of Name is the same as variable sequence in dataset lib.test. How can I change the code above to achieve this?
Name
name1
anc
sjsjd
mdmd
You can convert your NAME dataset to have both the old and new names and then use that to generate the rename pairs.
data name_pairs;
set name ;
old_name = cats('VAR',_n_);
run;
proc sql noprint ;
select catx('=',old_name,name)
into :vars separated by ' '
from name_pairs
;
quit;
You can then use the macro variable VARS in your rename statement.
I have an excel file that needs to be imported periodically to sas. The names of the columns are in row 2 and the number of columns can change. I'm using the following query:
proc import file = "file.xlsx"
out = sasfile
dbms= excel replace;
sheet = "sheet1";
range = "sheet1$A2:BE2000";
getnames = yes;
run;
However, I keep getting F variables in the sas output. How can I dynamically input only the columns that have names?
Are you saying that if the column doesn't have a name in the second row then you want to remove that column from the resulting table?
It is a bit of a pain to get PROC IMPORT to read an XLSX file that is not formatted as a table since it does not support NAMEROW, STARTROW, DATAROW, etc. But you might be able to do it by just reading the names and the data separately.
First let's create some macro variables to make the solution easy to modify.
%let sheetname=SHEET1;
%let startrow=2;
%let lastrow=2000;
%let startcol=A;
%let lastcol=BE;
Now let's read in the variable names from &STARTROW.
proc import datafile='c:\users\abernathyt\downloads\book1.xlsx' replace
dbms=xlsx out=names1;
range="&sheetname.$&startcol.&startrow:&lastcol.&startrow";
getnames=no;
run;
And then transpose it.
proc transpose data=names1 out=names2;
var _all_;
run;
Now let's generate old=new pairs for the columns we want to rename and also the list of columns that we want to drop.
proc sql noprint ;
select case when col1 ne ' ' then catx('=',_name_,nliteral(trim(col1))) else ' ' end
, case when col1 ne ' ' then ' ' else _name_ end
into :rename separated by ' '
, :drop separated by ' '
from names2
;
quit;
Now let's read in the data and add dataset options to rename and/or drop columns on the way out.
proc import datafile='c:\users\abernathyt\downloads\book1.xlsx' replace
dbms=xlsx out=want(rename=(&rename) drop=&drop)
;
range="&sheetname.$&startcol.%eval(&startrow+1):&lastcol.&lastrow";
getnames=no;
run;
I think you are getting those because you are explicitly giving sheet and range just made a simple file and did import as expected with sas code given below
PROC IMPORT OUT= WORK.imported_file DATAFILE= "file.xlsx"
DBMS=EXCEL REPLACE;
GETNAMES=YES;
RUN;
If you are trying to start from a certain row you can achieve that using
namerow=2;
startrow=3;
I don't think there's an easy way to prevent proc import from creating the named F variables. But it's not hard to remove them after the import.
First, create a macro variable containing the F vars. I've chosen to use the dictionary.columns table to find variables that begin with "F" and only contain digits from the 2nd position to the end of name. You don't want to drop variables with names such as "flag", "F12_23" or "f2var".
* imported table in work.xl;
proc sql noprint;
select name into :fvars separated by ', '
from dictionary.columns
where
libname = 'WORK' and
memname = 'XL' and
name like 'F%' and
notdigit(strip(name), 2) = 0
;
quit;
Then use alter table to drop the variables.
proc sql;
alter table xl
drop &fvars;
quit;
It's pretty straight-forward.
I'm trying to use a double pipe delimiter "||" when I export a file from SAS to txt. Unfortunately, it only seems to correctly delimit the header row and uses the single version for the data.
The code is:
proc export data=notes3 outfile='/file_location/notes3.txt'
dbms = dlm;
delimiter = '||';
run;
Which results in:
ID||VAR1||VAR2
1|0|STRING1
2|1|STRING2
3|1|STRING3
If you want to use a two character delimiter, you need to use dlmstr instead of dlm in the file statement in data step file creation. You can't use proc export, unfortunately, as that doesn't support dlmstr.
You can create your own proc export fairly easily, by using dictionary.columns or sashelp.vcolumn to construct the put statement. Feel free to ask more specific questions on that side if you need help with it, but search around for data driven output and you'll most likely find what you need.
The reason proc export won't use a double pipe is because it generates a data step to do the export, which uses a file statement. This is a known limitation - quoting the help file:
Restriction: Even though a character string or character variable is
accepted, only the first character of the string or variable is used
as the output delimiter. This differs from INFILE DELIMITER=
processing.
The header row || works because SAS constructs it as a string constant rather than using a file statement.
So I don't think you can fix the proc export code, but here's a quick and dirty data step that will transform the output into the desired format, provided that your dataset has no missing values and doesn't contain any pipe characters:
/*Export as before to temporary file, using non-printing TAB character as delimiter*/
proc export
data=sashelp.class
outfile="%sysfunc(pathname(work))\temp.txt"
dbms = dlm;
delimiter = '09'x;
run;
/*Replace TAB with double pipe for all rows beyond the 1st*/
data _null_;
infile "%sysfunc(pathname(work))\temp.txt" lrecl = 32767;
file "%sysfunc(pathname(work))\class.txt";
input;
length text $32767;
text = _infile_;
if _n_ > 1 then text = tranwrd(text,'09'x,'||');
put text;
run;
/*View the resulting file in the log*/
data _null_;
infile "%sysfunc(pathname(work))\class.txt";
input;
put _infile_;
run;
As Joe suggested, you could alternatively write your own delimiter logic in a dynamically generated data step, e.g.
/*More efficient option - write your own delimiter logic in a data step*/
proc sql noprint;
select name into :VNAMES separated by ','
from sashelp.vcolumn
where libname = "SASHELP" and memname = "CLASS";
quit;
data _null_;
file "%sysfunc(pathname(work))\class.txt";
set sashelp.class;
length text $32767;
text = catx('||',&VNAMES);
put text;
run;