How to read User select Values of a static list of a SAS Prompt - static

My Program has five files, every file has a macro. I have tried to make a stored process that has a prompt to ask the user which file to open (I have no rights to save the files in the database, where the SAS-Folders are).
The Prompt is called Prompt_1 and has five values of a static List (1_First, 2_second, 3_third, 4_fourth, 5_fifth ).
In the Stored Process Manager > SAS Code Page > I have typed:
%if &Prompt_1= 1_First %then
%include '/home/admin/mohaddad/sasuser.v94/1_ First.sas';
%else %if & Prompt_1= 2_second %then
%include '/home/admin/mohaddad/sasuser.v94/2_ second.sas';
.....
The Error I get: The %IF statement is not valid in open code.
The Question is: How to read the value of Prompt_1 and compare it with “User select Values of a static list”?
P.S. Without IF Statement the program opens the file and excutes the Code
Thanks for help

SAS up to version 9.4M4 disallows open-code %if statements. The 9.4M5 documentation for %IF indicates updates to the SAS Foundation has removed the restriction.
There are many ways to process the prompt selection.
Macro
Write a macro that processes the selection in a conditional manner. I recommend placing the %include inside a %do block to ensure the statements needed semi-colon is part of the conditional statement.
%macro process_selection;
%if &Prompt_1 = 1_First %then %do;
%include '/home/admin/mohaddad/sasuser.v94/1_ First.sas';
%end;
%else
%if & Prompt_1= 2_second %then %do;
%include '/home/admin/mohaddad/sasuser.v94/2_ second.sas';
%end;
%* etc …;
%mend;
%* invoke the macro;
%process_selection;
DATA _ NULL _; CALL EXECUTE
Data step can conditionally execute statements with CALL EXECUTE. Best practice is to wrap the statement in %NRSTR to ensure it will execute after the DATA step completes.
You can also replace IF/THEN/ELSE with SELECT/WHEN statements.
data _null_;
if "&prompt_1" = "1_First" then
call execute ( '%nrstr(%include "/home/admin/mohaddad/sasuser.v94/1_ First.sas";)' );
else
if "&x" = "hello there2" then
call execute ( '%nrstr(%/home/admin/mohaddad/sasuser.v94/2_ second.sas";)' );
run;
MACRO 'associate array'
For the case of the prompt selection value having no space or special characters, you can specify the potential target files in a series of macro variables.
%let FILE_1_First = /home/admin/mohaddad/sasuser.v94/1_ First.sas;
%let FILE_2_Second = /home/admin/mohaddad/sasuser.v94/2_ Second.sas;
%include "%superq(FILE_&PROMPT_1)";
Prompt dialog selection values
You can change the prompt dialog to return the filename to include. Either the fullpath
%include "&prompt_1";
or a partial part of the path (such as just the name part "1_ First")
%include "/home/admin/mohaddad/sasuser.v94/&PROMPT_1..sas";
%SYSFUNC(IFC
A hackish way to conditionally process statements in open-code is to use %sysfunc(ifc(…
IFC is a function that evaluates an expression and returns one string for the true case, and a different (or no-string) for the false case. It won't hurt anything code or resource wise by having five of these statements in a row, wherein only one of the tests true.
%sysfunc(ifc(&Prompt_1 = 1_First
, %include '/home/admin/mohaddad/sasuser.v94/1_ First.sas';,));
%sysfunc(ifc(&Prompt_1 = 2_Second
, %include '/home/admin/mohaddad/sasuser.v94/2_ second.sas';,));

Open code macro logic isn't available until version 9.4m5. One option for you now is to wrap it in a macro and call it:
%macro mymacro()
%if &Prompt_1= 1_First %then %do;
%include '/home/admin/mohaddad/sasuser.v94/1_First.sas';
%end;
%else %if &Prompt_1= 2_second %then %do;
%include '/home/admin/mohaddad/sasuser.v94/2_second.sas';
%end;
%mend;
%mymacro()
Another option (given that it appears as though your program names are identical to the prompt values) is to simply use the prompt macro value in the %include path:
%include "/home/admin/mohaddad/sasuser.v94/&prompt_1..sas";

Related

SPSS loop ROC analysis for lots of variables

In SPSS, I would like to perform ROC analysis for lots of variables (989). The problem, when selecting all variables, it gives me the AUC values and the curves, but a case is immediately excluded if it has one missing value within any of the 989 variables. So, I was thinking of having a single-variable ROC analysis put into loop. But I don't have any idea how to do so. I already named all the variables var1, var2, var3, ..., var988, var989.
So, how could I loop a ROC analysis? (Checking "Treat user-missing values as valid" doesn't do the trick)
Thanks!
this sounds like a job for python. Its usually the best solution for this sort of job in SPSS.
So heres a framwork that might help you. I am woefully unfamiliar with ROC-Analysis, but this general pattern is applicable to all kinds of looping scenarios:
begin program.
import spss
for i in range(spss.GetVariableCount()):
var = spss.GetVariableName(i)
cmd = r'''
* your variable-wise analysis goes here --> use spss syntax, beetween the three ' no
* indentation is needed. since I dont know what your syntax looks like, we'll just
* run descriptives and frequencies for all your variables as an example
descriptives %(var)s
/sta mean stddev min max.
fre %(var)s.
'''%locals()
spss.Submit(cmd)
end program.
Just to quickly go over what this does: In line 4 we tell spss to do the following as many times as theres variables in the active dataset, 989 in your case. In line 5 we define a (python) variable named var which contains the variable name of the variable at index i (0 to 988 - the first variable in the dataset having index 0). Then we define a command for spss to execute. I like to put it in raw strings because that simplifies things like giving directories. A raw string is defined by the r''' and ends at the '''. in line 12. "spss.Submit(cmd)" gives the command defined after "cmd = " to spss for execution. Most importantly though, whenever the name of the variable would appear in your syntax, substitute it with "%(var)s"
If you put "set mprint on." a line above the "begin program." youll see exactly what it does in the viewer.

Conflict of global macro and loop

I'm trying to automate the process of running through a bunch of files that are sequentially named, manipulate them all in the same way and then save them.
I thought that using a forvalues loop with a global macro would be the best way to do this in Stata.
My code is something like:
global s=1988
forvalues i=${s}/2018 {
import excel "${s}.xlsx", sheet("Data") firstrow clear
.
.
.
save ${s}, replace
}
However, this gives me the error:
program error: code follows on the same line as open brace
It seems that Stata is reading the curly brace for the global macro as the start of the loop. I tried different variations of the loop to get around this but to no avail. Since I am using clear within the loop, I can't use a local macro or it goes into an infinite loop.
I have come across this issue before. For some reason Stata gets confused about the number of curly braces. If you remove them, the forvalues loop works as expected:
global s = 1988
forvalues i = $s / 2018 {
display `i'
}
1988
1989
1990
1991
.
.
.
2016
2017
2018
You can also nest the global macro s inside a local macro:
local s = ${s}
forvalues i = 1 / `s' {
display `i'
}

SAS batch jobs: executing multiple scripts through the same local server connection

When executing a single .sas script, I waste roughly 30 seconds on "establishing connection to a local server" and library loads, which are then followed by a 3-second execution of the sas script itself.
Is it possible to execute multiple sas scripts programmatically (I invoke them via the Windows shell) against the same established connection?
Like a keep-alive TCP channel?
Hello access_granted,
You are on the right track. You said in the comments to RawFocus, "it operates like socket server" and in your question "keep alive TCP connection". You can actually archieve thatin SAS with some clever programming.
SAS Socket server:
Here’s a SAS server in macro language, looping forever to
respond to requests. It’s worth noting while waiting for the
next request, the server is in a sleep state, consuming very
little resource on its host machine.
/* Port as input parameter */
%let portno= &sysparm;
%* let portno= 001;
%do %while ( 1 );
filename in_msg SOCKET " :&portno"
SERVER;
filename temp_pgm "%sysfunc(pathname(work))/a-ready.sas" ;
data _null_;
infile in_msg;
file temp_pgm;
input ;
put _infile_;
run;
filename in_msg;
/* Before execution, server can choose to
parse it first */
%inc temp_pgm;
run;
filename temp_pgm;
%end;
This client is supplied with a SAS server’s IP address, port number to call on, and the program file for the server to execute. It is able to send one request to a known available SAS server. Also note the client is responsible for directing the server on which port to send back the results (if required).
%let host= IP-address-for-server-machine;
%let portno= 001;
filename to_servr SOCKET “ &host:&portno” ;
filename input ‘SAS-program-to-be-run-onserver.sas’;
/* Simple scheme to come up with a different port
number to accept response from the server */
%let ret_port= %eval( &portno + 1 );
data _null_;
file to_servr;
infile input end= EOF;
if _n_ = 1 then do;
/* Required info for the server to return results
back to this client */
put ‘%let ret_port= ’ “ &ret_port;” ;
put ‘%let client= IP-address-for-clientmachine;’;
end;
input ;
put _infile_;
run;
filename input;
filename to_servr;
/* Receiving socket for the results */
filename back_rst SOCKET “ :&ret_port” SERVER;
data null_;
infile back_rst;
file print;
input ;
put _infile_;
run;
filename back_rst;
Source: http://www2.sas.com/proceedings/sugi24/Coders/p083-24.pdf
By the way, you could also consider reconfiguring the SAS server if possible. 30 seconds to start a session is a bit long, especially for the batch session.
If you're happy for them to run consecutively in the same sas session, you could just create a sas script to execute the other scripts..
options source2; /* puts the contents of the sas files below in the log */
%inc "C:\MyLoc\script1.sas";
%inc "C:\MyLoc\script2.sas";
%inc "C:\MyLoc\script3.sas";
etc.
The drawback with this approach is that your programs will have to be deal appropriately with any remaining work tables / macros / macro variables etc from the previous runs (they won't have clean sessions).

How to run operating system command from sas & read number of lines of a file?

data student;
infile "E:\student.txt";
input id name $ marks;
run;
data _NULL_;
set student nobs=TotalRecords end=Last;
if Last then
do;
put TotalRecords=;
/*---------Put your code here--------*/
/*Code to retrieve number of records */
/*of file from OS command */
end;
run;
Hi all, I am working on a program in which I am reading data from text file. After reading the data I want to check number of records(no. of lines) of file are equal to no. of observations of my data set or not, just for validation purpose.
If you're just wanting to validate yourself visually, this information is printed to the log - at least SAS's opinion of how many lines are in the file.
If you're trying to do this automatically, read on. You don't need OS tools to check this.
Typically, this sort of validation is intending to check a few specific problems:
Early termination of file reading due to ^Z character (EOF character)
Reading two SAS lines from one File line
Reading one SAS line from two File lines
File system failure causing SAS to prematurely end file read
All can be fairly easily checked - mostly - by doing a second pass of the file.
data linecheck;
infile yourfile ignoredoseof;
input;
lines+1;
run;
That will cause SAS to read the number of lines into a variable, which you could then call symput or otherwise use to check and see if the original read-in matches it. All four problems are potentially checked, if the 4th was a temporary failure; of course, if the disk is permanently damaged, no solution (including asking the OS) will help you (since any solution would require reading the file - text files don't have a header or anything that tells you their line length, so you have to read them in).
This example uses UNIX WC to gather info on a PARMCARDS file. While not particularly interesting it serves the example.
filename FT15F001 temp;
data wc_info;;
filevar = catx(' ','/usr/bin/wc',quote(pathname('FT15F001')));
infile dummy pipe filevar=filevar truncover end=eof;
do while(not eof);
input lines words characters filename $256.;
put _infile_;
output;
end;
stop;
parmcards;
how
many
lines
are
in this parmcards file
;;;;
run;
proc print;
run;

SAS ExportPackage command exceeds 8191 characters

We have an automated process for exporting metadata items for promotion, using the ExportPackage commandline utility (documented here).
The command is written to a .bat file, and then executed (in SAS) via a filename pipe.
We recently observed a strange behaviour when exporting multiple objects (around 60), that we believe is due to the windows line length limitation for batch commands.
Basically, one character would be removed (meaning that particular object would not be found), but the rest of the line (after 8191 chars) executes successfully.
Am interested to know:
Can the ExportPackage command be executed in a way that does not hit the 8191 limitation?
Alternatively, can the ExportPackage command be split over multiple lines somehow?
Or is there some way to pass a file to the -objects parameter, rather than space separated values?
Or is it possible to append to (rather than replace) an .spk file?
I doubt there's any answer to this that you're going to like. The documentation you linked states that existing package files with the same names are overwritten and does not mention any way of appending to one.
You can split the command over multiple lines within the batch file using ^ characters, but this still doesn't get around the overall 8191 character limit after recombining the pieces.
Therefore, you will need to do one or more of the following:
Export your items to separate packages with different filenames or in different folders, e.g. 20 at a time
Move your objects into a limited set of folders and subfolders before exporting, and export only the top level folders rather than the individual objects. It looks as though you can still use the other command line options to limit which objects are exported.
Silly option: create a dummy object with dependencies on all of the objects you want to export, mention only that one explicitly in the objects list, and use the -includeDep parameter to force the export utility to export all its dependencies.
Disclaimer: I have never actually used the export utility in question.
I got around this issue by inserting a dummy character at the 8191 point. Note that this 8191 length limit is everything in the command AFTER "ExportPackage".
One solution is therefore as follows:
/* If the ExportPackage command line is more than 8191 characters, it will
fail due to a windows line length limitation. To avoid this, add a
hash character at the 8191 point.
*/
%let log= \path\to\my.log;
%let profile= -profile "\path\to\my\dummy\profile.swa";
%let package= -package "\path\to\my\desired.spk";
%let str=&my_list_of_objects; /* previously defined */
%let breakpoint=%eval(
8191 - %length(%str(&profile &package -objects))-1);
%if %length(&str)>=&breakpoint %then %let objects=%substr(
&str,1,&breakpoint-1)#%substr(&str,&breakpoint,%length(&str)-&breakpoint+1);
%else %let objects=&str;
The command could then executed along the lines of:
ExportPackage &profile &package &objects -subprop -includeEmptyFolders -log &log
What DIDN'T work:
Inserting spaces between quotes at the 8191 point, eg:
"object1" "object2"
The extra spaces were ignored and a character was still removed from the second object.
Inserting spaces within the literal, eg:
"object1 " "object2"
Object 1 was not found, presumably due to the trailing spaces.

Resources