Chapter 16. Interactive Techniques

Contents

  • 16.1 Overview of Interactive Techniques 385

  • 16.2 Pausing a Program to Enable Interaction 385

  • 16.3 Attaching Menus to Graphs 386

  • 16.4 Linking Related Data 390

  • 16.5 Dialog Boxes in SAS/IML Studio 396

    • 16.5.1 Displaying Simple Dialog Boxes 396

    • 16.5.2 Displaying a List in a Dialog Box 399

  • 16.6 Creating a Dialog Box with Java 402

  • 16.7 Creating a Dialog Box with R 404

    • 16.7.1 The Main Idea 404

    • 16.7.2 A First Modal Dialog Box 405

    • 16.7.3 A Modal Dialog Box with a Checkbox 406

    • 16.7.4 Case Study: A Modal Dialog Box for a Correlation Analysis 408

  • 16.8 References 411

16.1 Overview of Interactive Techniques

This chapter describes IMLPlus features that enable programmers to add interactive features to IMLPlus programs. This includes the following:

  • pausing a program so that the person who is running the program can interact with the data

  • attaching a menu to a graph that calls a module when the menu is selected

  • creating dialog boxes that prompt the user for input

16.2 Pausing a Program to Enable Interaction

The PAUSE statement is described in Section 5.9.4. By default, when the PAUSE statement is executed, the Auxiliary Input window appears. However, if the text string in the PAUSE statement begins with the word "NoDialog:", then the Auxiliary Input window is not displayed, but the program still pauses its execution. The remainder of the message is displayed in the SAS/IML Studio status bar, which is located in the lower left corner of the SAS/IML Studio application.

When a program is paused, you can still interact with the SAS/IML Studio GUI. In particular, you can interact with plots and data tables, which means that you can select observations. Consequently, when the program resumes, the program can get the selected observations and use them in some computation. Alternatively, the program can modify characteristics of the selected observations.

For example, the following statements create a scatter plot. The program then pauses and prompts the user to select certain observations. When the program resumes, the selected observations are colored red.

/* pause program to enable user to interact with the data */
declare DataObject dobj;
dobj = DataObject.CreateFromServerDataSet("Sasuser.Movies");

declare ScatterPlot plot;
plot = ScatterPlot.Create(dobj, "ReleaseDate", "US_Gross");

pause "NoDialog: Select observations. Click Resume to color them red.";
dobj.GetSelectedObsNumbers(SelObsIdx);
if ncol(SelObsIdx) > 0 then
   dobj.SetMarkerColor(SelObsIdx, RED);

The PAUSE message is displayed in the status bar, as shown in Figure 16.1. When the user clicks the resume icon (16.2 Pausing a Program to Enable Interaction) or presses ALT+F5, the program resumes and the selected observations are colored red.

A Message in the Status Bar

Figure 16.1. A Message in the Status Bar

16.3 Attaching Menus to Graphs

In SAS/IML Studio, you can attach a menu to any data table or graph. You can associate IMLPlus statements with a menu item, which means that SAS/IML Studio executes those statements when you select the menu item. In this way, you can attach an analysis to a data table or a graph. The menu is called an action menu because it enables you to define some action that is applied to the data or to the graph.

Usually the "action" is to compute some statistic or to draw some feature on the graph. You could add a kernel density estimate to a histogram as described in Section 4.5, add a loess smoother to a scatter plot as described in Section 9.7, or add a rug plot to a histogram as described in Section 9.5.

This section describes how to create an action menu that is attached to a histogram. The action menu calls the RugPlot module that is described in Section 9.5. This section assumes that the RugPlot module is stored in a directory on the SAS/IML Studio module search path, as described in Section 5.7.

In the following example, the AppendActionItem method in the Data View class specifies the text and action for a menu item on a histogram:

/* add action menu to histogram */
declare Histogram hist;
hist = Histogram.Create(dobj, "Budget");
hist.AppendActionMenuItem("Draw a Rug Plot",        /* 1 */
                          "run OnRugPlot();"        /* 2 */
                          );

start OnRugPlot();                                  /* 3 */
   declare Histogram h;
   h = DataView.GetInitiator();                     /* 4 */
   run RugPlot(h);
finish;

The action menu in this example consists of a single menu item. The program contains the following main steps:

  1. The first string to the AppendActionMenuItem method specifies the text that appears on the action menu item, as shown in Figure 16.2.

  2. The second string specifies the statement that is executed when the menu item is selected. This is usually a module call. In this case, selecting the action menu item executes the statement run OnRugPlot();.

  3. The OnRugPlot module is defined. The module does not take any arguments.

  4. Inside the OnRugPlot module, the DataView.GetInitiator method is called. This method returns the graph or data table object from which the action menu item was selected. In this example, the method returns a Histogram object. That Histogram object is passed to the RugPlot module, which draws the rug plot on the histogram.

The action menu item is not visible until you press F11 in the window that contains the action menu. When you press F11, the action menu appears as shown in Figure 16.2. When you select Draw a Rug Plot, the OnRugPlot module is called. This causes the rug plot to appear on the histogram, as shown in Figure 9.18.

An Action Menu

Figure 16.2. An Action Menu

The next example extends the previous example by creating an action menu that defines two menu items. The first menu item calls a module that draws a rug plot. The second menu item calls a module that removes the rug plot from the histogram.

/* add action menu with two menu items to histogram */
declare Histogram hist2;
hist2 = Histogram.Create(dobj, "Budget");
hist2.AppendActionMenuItem("Draw a Rug Plot",       /* 1 */
                           "run OnRugPlot2();"
                           );
hist2.AppendActionMenuItem("Remove Rug Plot",       /* 2 */
                           "run OnRemoveRugPlot();"
                           );
start OnRugPlot2();                                 /* 3 */
   declare Histogram h;
   h = DataView.GetInitiator();
   h.DrawRemoveCommands("RugBlock");
   h.DrawBeginBlock("RugBlock");                    /* 4 */
   run RugPlot(h);
   h.DrawEndBlock();
finish;
start OnRemoveRugPlot();                            /* 5 */
   declare Histogram h;
   h = DataView.GetInitiator();
   h.DrawRemoveCommands("RugBlock");                /* 6 */
finish;

The program contains the following main steps:

  1. The first call to the AppendActionMenuItem method specifies that the OnRugPlot2 module is called when the Draw a Rug Plot menu item is selected.

  2. The second call to the AppendActionMenuItem method specifies that the OnRemoveRugPlot module is called when the Remove Rug Plot menu item is selected.

  3. The OnRugPlot2 module is defined. The module uses the DataView.GetInitiator method to obtain the Histogram object from which the action menu item was selected.

  4. Inside the module, there are three new method calls that define and name a block of drawing statements. All of the drawing methods in the RugPlot module are contained within this block. The name of the block is "RugBlock."

    1. The DrawRemoveCommands method ensures that any previous block of statements with the same name is removed. You cannot have two blocks of statements with the same name.

    2. The DrawBeginBlock method marks the beginning of the statement block and names the block.

    3. The DrawEndBlock method marks the end of the statement block.

    The purpose of creating this block is so that these drawing statements can be removed in the OnRemoveRugPlot module.

  5. The OnRemoveRugPlot module is defined. This module also calls the DataView.GetInitiator method to obtain the Histogram object.

  6. The OnRemoveRugPlot module contains a call to the DrawRemoveCommand method. This method removes all drawing associated with the "RugBlock" statements. The result is that the histogram no longer displays the tick marks that were drawn by the RugPlot module. After the call to DrawRemoveCommands, the Histogram is in the same state as before the RugPlot module was called.

You can now press F11 and select the first menu item to draw a rug plot. When you no longer want the rug plot to be displayed, you can press F11 and select the second menu item to erase the rug plot, as shown in Figure 16.3.

An Action Menu That Removes the Rug Plot

Figure 16.3. An Action Menu That Removes the Rug Plot

You can implement many other interesting techniques with action menus. The SAS/IML Studio online Help contains several sections that describe action menus. To view the documentation, select HelpAn Action Menu That Removes the Rug PlotHelp Topics from the SAS/IML Studio main menu. Action menus are described in two sections:

  • Select the chapter titled "The Plots" and the section titled "The Action Menu."

  • Select the chapter titled "IMLPlus Class Reference" and the documentation for the action menu methods in the DataView class.

16.4 Linking Related Data

This book primarily focuses on analyzing and displaying one data set at a time. However, it is also possible to use the interactive graphics of SAS/IML Studio to analyze several related data sets in a single program. This section describes how to link graphics from two related data sets by using the action menus that are described in Section 16.3.

The example in this section involves comparing different nonparametric smoothers for scatter plot data. When you fit a model to data, you often choose a single model from among a family of possible models. It is common to choose the model that optimizes some criterion that measures the agreement between the data and the model. For example, when choosing among several loess models, it is common to choose the model that minimizes the corrected Akaike's information criterion (AICC).

Figure 16.4 shows two plots. The "observation plot" on the left shows observations from the Movies data set. The "criterion plot" on the right displays the AICC criterion versus the smoothing parameter for a series of loess models. Every point in the criterion plot represents a possible curve through the points in the observation plot. In that sense, these two plots are related to each other. However, the data for these plots reside in different data objects.

Observation Plot (left) and a Criterion Plot (right)

Figure 16.4. Observation Plot (left) and a Criterion Plot (right)

If you want to visualize and compare the smoothers that are associated with a particular set of smoothing parameters, you can use the following technique:

  1. Create the two plots.

  2. Attach an action menu to the criterion plot.

  3. Select several parameter values in the criterion plot.

  4. When the action menu is chosen, do the following:

    1. Get the selected parameter values.

    2. Graph the associated loess curves on the observation plot.

Section 16.3 shows that an action menu can be used to modify the plot that is displaying the action menu. The new idea in this section is that the criterion plot, which displays the action menu, can modify a different plot (the observation plot). In IMLPlus, you can attach the observation plot to the criterion plot by using the AddAttachedObject method in the DataView class.

The following statements begin this example by creating the observation plot in Figure 16.4:

/* create a scatter plot of the observations */
DSName = "Sasuser.Movies";
XVarName = "ReleaseDate";
YVarName = "Budget";

declare DataObject dobj;
dobj = DataObject.CreateFromServerDataSet(DSName);
declare ScatterPlot p;
p = ScatterPlot.Create(dobj, XVarName, YVarName);

To create the criterion plot, you need to call the LOESS procedure and use the SMOOTH= option in the MODEL statement to fit models for a list of values of the smoothing parameter, as shown in the following module:

/* compute AICC criterion for a range of smoothing values */
start ComputeLinkedCriterionPlot(ScatterPlot CritPlot, ScatterPlot p);
   /* 1. write data to server */
   p.GetVars(ROLE_X, XVarName);
   p.GetVars(ROLE_Y, YVarName);
   declare DataObject dobj = p.GetDataObject();
   dobj.WriteVarsToServerDataSet(XVarName//YVarName, "Work", "LoessIn", true);

   /* 2. call PROC loess to compute data for criterion plot */
   submit XVarName YVarName;
   proc loess data=Work.LoessIn;
      model &YVarName = &XVarName / select = AICC
                                            smooth = 0.05 to 0.9 by 0.01;
      ods output ModelSummary=LoessCriteria;
   run;
   endsubmit;

   /* 3. create data object from SmoothingCriterion table */
   declare DataObject CritDobj;
   CritDobj = DataObject.CreateFromServerDataSet("Work.LoessCriteria");
   CritPlot = ScatterPlot.Create(CritDobj, "Smooth", "AICC");

   /* 4. give the criterion plot a link to the original scatter plot */
   CritPlot.AddAttachedObject("ObsPlot", p);

   /* 5. create an action menu */
   CritPlot.AppendActionMenuItem("Draw Loess Curves for Selected Parameters",
                                 "run OnDrawLoessParam();" );
finish;

/* call the module to create the criterion plot */
declare ScatterPlot CritPlot;
run ComputeLinkedCriterionPlot(CritPlot, p);

The ComputeLinkedCriterionPlot module takes two arguments. The first argument is the criterion plot. The second argument is the observation plot. The criterion plot is created in the module and returned as an output parameter. The module has five main steps:

  1. Get the data object that is associated with the observation plot. Write the X and Y variables to the LoessIn data set.

  2. Call PROC LOESS to compute smoothers for a sequence of values of the smoothing parameter. The sequence is specified by using the SMOOTH= option in the MODEL statement. The ODS OUTPUT statement creates the LoessCriteria data set, which contains AICC values for each value of the smoothing parameter.

  3. Create a data object from the LoessCriteria data set and create the criterion plot.

  4. Attach the observation plot to the criterion plot. This is the new idea in this section. The AddAttachedObject method stores a reference to the observation plot object. The reference is associated with the name "ObsPlot." The plot reference can be obtained at a later time by calling the GetAttachedObject method, as is done later in Step 7.

  5. Create an action menu item. When the item is selected, the OnDrawLoessParam module will be executed. (That module is not yet defined.)

A user can select parameter values in the criterion plot (by clicking on them) and then press F11 to display the action menu, as shown in Figure 16.5. When the user selects the action menu item, the action menu will call the OnDrawLoessParam module. This module needs to get the selected parameters and draw the associated loess curves on the observation plot.

Smoothing Parameters versus AICC Criterion

Figure 16.5. Smoothing Parameters versus AICC Criterion

The OnDrawLoessParam module is defined by the following statements:

/* compute loess curves for selected smoothing parameters */
start OnDrawLoessParam();
   /* 6. get selected parameters */
   declare ScatterPlot CritPlot;
   CritPlot = DataView.GetInitiator();
   declare DataObject CritDobj;
   CritDobj = CritPlot.GetDataObject();
   CritDobj.GetVarSelectedData("Smooth", s);
   if ncol(s)=0 then return;

   /* 7. get linked scatter plot and names of variables */
   declare ScatterPlot p;
   p = CritPlot.GetAttachedObject("ObsPlot");
   p.GetVars(ROLE_X, XVarName);
   p.GetVars(ROLE_Y, YVarName);

   /* 8. call PROC loess to compute smoothers (assume LoessIn exists) */
   submit XVarName YVarName s;
   proc loess data=Work.LoessIn;
      model &YVarName = &XVarName / smooth = &s;
      score / ;
      ods output ScoreResults=LoessOut;
   run;

   proc sort data=LoessOut;
      by SmoothingParameter &XVarName;
   run;
   endsubmit;

   /* 9. read data for loess curves */
   PredVarName = "P_" + YVarName;
   use LoessOut;
   read all var "SmoothingParameter" into smooth;
   read all var XVarName into allX;
   read all var PredVarName into allPred;
   close LoessOut;

   /* 10. overlay loess smoothers on scatter plot */
   uSmooth = unique(smooth);
   p.DrawRemoveCommands("LoessCurves");
   p.DrawBeginBlock("LoessCurves");
   p.DrawUseDataCoordinates();
   do i = 1 to ncol(uSmooth);
      idx = loc(smooth=uSmooth[i]);
      x = allX[idx];
      pred = allPred[idx];
      p.DrawLine(x, pred);
   end;
   p.DrawEndBlock();
finish;

Because the module is called from an action menu, the module does not have any arguments; all of the data that module needs is obtained from the criterion plot. The OnDrawLoessParam module consists of the following main steps:

  1. The Getlnitiator and GetDataObject methods enable you to get the data that underlie the criterion plot. The GetVarSelectedData method retrieves the smoothing parameter values that are currently selected. These values are stored in the vector s.

  2. The GetAttachedObject method retrieves a reference to the observation plot. The GetVars method obtains the names of the X and Y variables.

  3. The names of the variables and the selected smoothing values are passed as parameters to PROC LOESS. The LOESS procedure computes and scores the models for those parameters. The predicted values are saved to the LoessOut data set. For each value of the smoothing parameter, the SORT procedure sorts the output values by the X variable.

  4. The data for the loess curves are read into SAS/IML vectors.

  5. As described in Section 16.3, the curves are drawn inside a named block of statements. (If the action menu is called a second time, the DrawRemoveCommands method removes all drawing that is associated with the first call to the action menu.) A DO loop is used to plot each loess curve on the observation plot. For each unique value of the smoothing parameter, the module extracts the relevant X and Y values and draws the curve.

Figure 16.6 shows the result of selecting the action menu for the highlighted parameters shown in Figure 16.5. The curve with many undulations corresponds to the smaller value of the smoothing parameter; the gently sloping curve corresponds to the larger value. You could improve the OnDrawLoessParam module by also drawing a legend on the observation plot. This is left as an exercise.

Loess Curves That Correspond to the Selected Parameters

Figure 16.6. Loess Curves That Correspond to the Selected Parameters

This technique is useful for comparing multiple loess curves when the criterion plot has multiple local minima. It is also useful for visualizing statistics that are associated with maps. You can write a program that enables you to select a country or region and to choose an action menu that carries out an analysis on data that are associated with the selected region.

16.5 Dialog Boxes in SAS/IML Studio

SAS/IML Studio is distributed with modules that create and display several simple dialog boxes. All of these modules begin with the prefix "DoDialog" or "DoMessageBox." You can use these dialog boxes to query for user input or to display information to the user. These dialog boxes are modal, meaning that the program halts execution until the dialog box is dismissed.

All of the modules (and others) in this section are documented in the SAS/IML Studio online Help, in the chapter titled "IMLPlus Module Reference."

16.5.1 Displaying Simple Dialog Boxes

This section describes three simple dialog boxes that you can use to query for user input. The three examples in this section are part of a single program that simulates rolling two six-sided dice a user-determined number of times, and enables you to display a graph that shows the distribution of the values for the dice rolls.

The simplest dialog box is a message box. This dialog box presents the user with information and then waits until the user clicks OK. The following statements use the DoMessageBoxOK module to display a message box with an OK button:

title = "Rolling Dice";
message = "This game simulates rolling two six-sided dice.";
run DoMessageBoxOK(title, message);

The message box is displayed in Figure 16.7. The DoMessageBoxOK has two arguments. The first is a string that appears in the title bar of the window. The second is the message that is displayed to the user.

A Message Box That Contains an OK Button

Figure 16.7. A Message Box That Contains an OK Button

Another simple dialog box is one that contains a single edit field, as shown in Figure 16.8. The DoDialogModifyDouble module takes a default numerical value and enables the user to accept or modify that value, as shown in the following statements:

/* prompt user for a value to use in an algorithm */
message = "Please enter the number of times to roll the dice:";
N = 100;                            /* default number of rolls */
rc = DoDialogModifyDouble(N, title, message);
if rc & N>1 then do;         /* valid specification?           */
    N = int(N);                     /* truncate value to integer      */
    d = j(N, 2);                    /* allocate Nx2 vector (2 dice)   */
    call randgen(d, "uniform");     /* fill with values in (0,1)      */
    d = ceil(6*d);                  /* convert to integers in 1-6     */
    sum = d[,+];                    /* values of the N rolls          */
end;
else
   print "Good-bye";                /* canceled or invalid value      */

For this part of the program, the value 100 is the default number of dice rolls. The user can modify that value. When the user clicks OK, the value in the dialog box is copied into the scalar matrix N. The value returned by the module (called the return code) is stored in the rc matrix. The return code indicates whether the user entered a valid number and clicked OK. A value of 0 indicates that you should not use the value of n; a nonzero value indicates a successful return from the dialog box module.

A Dialog Box with a Numerical Input Field

Figure 16.8. A Dialog Box with a Numerical Input Field

The program uses the value from the dialog box to simulate rolling two dice N number of times. The RANDGEN function returns an N × 2 matrix of values in the range (0,1). If you call this matrix t, then 6t is a matrix with values in the range (0, 6). The CEIL function returns the closest integer greater than a given value, which results in uniformly distributed integers with values one through six. Finally, the subscript operator (+) sums the two columns of the matrix, as described in the section "Writing Efficient SAS/IML Programs" on page 79.

The vector d contains the values of the simulated dice rolls. The following statements use the DoMessageBoxYesNo module to ask the user whether the program should display a bar chart that shows the distribution of the values:

/* prompt user to determine the behavior of an algorithm */
message = "Would you like to see the distribution of the results?";
rc = DoMessageBoxYesNo(title, message);
if rc then do;                             /* create the bar chart */
   declare BarChart bar;
   bar = BarChart.Create(title, sum);
   bar.SetAxisLabel(XAXIS, "Sum");
   bar.SetQtherThreshold(0);
end;

The dialog box is shown in Figure 16.9. If the user clicks Yes, the program creates and displays a bar chart similar to the one shown in Figure 16.10. As the number of simulated rolls gets larger, the distribution of the sample approaches a triangular distribution.

A Message Box That Contains Yes and No Buttons

Figure 16.9. A Message Box That Contains Yes and No Buttons

Possible Results from a Simulation of Rolling Dice

Figure 16.10. Possible Results from a Simulation of Rolling Dice

16.5.2 Displaying a List in a Dialog Box

When you want to present the user with a list of possible choices, you can use the DoDialogGet-ListItem or DoDialogGetListItems modules. The DoDialogGetListItem module enables the user to choose a single item from the list, whereas the DoDialogGetListItems module enables the user to select one or more items from a list.

The two modules have a similar syntax. The first argument is a matrix that serves as an output argument. When the module returns, the matrix contains the indices of the items that the user selected. The second and third arguments are the title and message for the dialog box; these arguments are identical to the arguments for the DoMessageBoxOK dialog box. The fourth argument is a character matrix; each row contains the text for the list. The last argument is a character matrix that labels the columns in the list.

The following statements present the user with a list of all the numeric variables in a data set, and then run a correlation analysis on the variables that the user selects:

/* prompt user to choose variables for an analysis */
declare DataObject dobj;
dobj = DataObject.CreateFromServerDataSet("Sasuser.Vehicles");

/* get vector of numeric variables */
do i = 1 to dobj.GetNumVar();             /* 1 */
   name = dobj.GetVarName(i);             /* get the name              */
   if dobj.IsNumeric(name) then           /* if variable is numeric    */
      varNames = varNames // name;        /*    add it to the vector   */
end;

rc = DoDialogGetListItems(selections,     /* 2 */
        "Correlation Analysis",
        "Please select variables for correlation analysis.",
        varNames, "Name");

if rc then do;                            /* 3 */
   vars = varNames[selections];           /* 4 */
   dobj.WriteVarsToServerDataSet(vars,    /* 5 */
                    "Work", "In", true);

   submit vars;                           /* 6 */
      proc corr data=In nosimple noprob;
         var &vars;                       /* analyze selected vars */
      run;
   endsubmit;
end;

This example uses a data object to read the Vehicles data. The main steps of the program are as follows:

  1. For each variable in the data object, the program queries the data object to determine the variable's name and whether the variable is numeric. If so, the variable name is appended to a vector. (After the DO loop, a careful programmer will check that the varNames vector has at least two elements.)

  2. The vector is passed to the DoDialogGetListItems module, which displays a list of the variables and prompts the user to select variables for a correlation analysis. A possible choice is shown in Figure 16.11.

    A Dialog Box That Contains a List

    Figure 16.11. A Dialog Box That Contains a List

  3. The user can dismiss the Correlation Analysis dialog box by clicking OK or Cancel. The value of the matrix rc reflects that choice. If the user clicks OK, the matrix rc is nonzero.

  4. The numeric vector selections contains the list items that the user selected. For example, the vector returned from the selections shown in Figure 16.11 is {1,2,3,4}. The program gets the names of the selected variables by extracting elements from the varNames vector into the vector vars.

  5. In order to call a procedure, you must make sure that the data are in a SAS data set in a libref. That is already the case for this example, but in general a data object can be created from many sources. Consequently, you should write the selected variables to a SAS data set such as Work.In.

  6. The names of the selected variables are passed to the SAS System by listing the vars vector in the SUBMIT statement. Inside the SUBMIT block, the expression &vars refers to the contents of this vector. The output from PROC CORR is shown in Figure 16.12.

Results of a Correlation Analysis on Selected Variables

Figure 16.12. Results of a Correlation Analysis on Selected Variables

If you need more control over the number of items the user can select or if you want to supply a default set of values, use the DoDialogGetListItemsEx module.

The preceding example creates a vector by concatenating character strings within a loop. However, Section 2.9 advises that you avoid doing this. In the example, the loop is over variables in a data set. When the number of variables is small (say, less than 100), the inefficiency does not affect the overall performance of the program. However, if your data contain thousands of variables, you should avoid concatenating character strings within a loop. The following code gives an alternative formulation:

/* get vector of numeric variables (more efficient) */
k = dobj.GetNumVar();                 /* number of variables          */
blank32 = subpad("", 1, 32);          /* string with 32 blanks        */
varNames = j(k, 1, blank32);          /* allocate result vector       */
count = 0;                            /* keep count of numeric vars   */
do i = 1 to k;                        /* for each variable...         */
   name = dobj.GetVarName(i);         /* get the name                 */
   if dobj.IsNumeric(name) then do;   /* if the variable is numeric   */
      count = count + 1;              /*    increment the counter     */
      varNames[count] = name;         /*    add name to the vector    */
   end;
end;
varNames = strip(varNames[1:count]);  /* shrink & strip blanks        */

Analyzing the logic of the alternative formulation is left as an exercise for the reader.

16.6 Creating a Dialog Box with Java

This example shows how to call Java classes to create a modal dialog box. This feature might be useful to the statistical programmer who wants to create dialog boxes as part of an IMLPlus program. This section does not attempt to explain how to program in Java, but rather assumes that the reader is familiar with Java programming.

This book primarily describes the Java classes that are provided as part of SAS/IML Studio, such as those in com.sas.imlplus.client.graphics. However, you can call any public method in any Java class, regardless of its origin. The documentation of a class lists the public methods for the class.

For example, if you are interested in using Java Swing components to create a GUI for an IMLPlus program, you can run the following statements directly from a SAS/IML Studio program window:

/* create a modal dialog by using Java Swing classes */
import javax.swing.JOptionPane;  /* import statements MUST be first */

/* Text for the buttons */
declare String[] distrib = {"Normal", "Uniform", "Exponential"};

response = JOptionPane.showOptionDialog(
           null,                              /* parent component   */
           "Sample from what distribution?",  /* message            */
           "Pseudorandom Sampling",           /* title of dialog box*/
           JOptionPane.DEFAULT_OPTION,        /* option type        */
           JOptionPane.PLAIN_MESSAGE,         /* messageType        */
           null,                              /* no icon            */
           distrib,                           /* text for buttons   */
           distrib[0] );                      /* default selection  */

msg = "Note: Modal dialog. Program is blocked until the dialog is closed.";
print msg;
print "The selected choice was " response " = " (distrib[response]);
call randseed(12345);                         /* set seed           */
x = j(5, 1);                                  /* allocate vector    */
call randgen(x, distrib[response]); /* generate from chosen distrib */
print x;

The program uses the import statement to import the JOptionPane class in the javax.swing package. The import statement enables you to refer to Java classes without always specifying the package that contains the class. Like the declare keyword, the import keyword must be specified in lowercase characters. An import statement must precede other IMLPlus statements.

The Java String class is used to create an array of character strings. Because IMLPlus automatically imports the java.lang package (which contains the String class), you do not need to explicitly import the package that contains the String class.

The arguments to the Java showOptionsDialog method are explained in comments in the previous program. A simple internet search readily provides links to the documentation and examples for the JOptionPane class.

The program displays a dialog box as shown in Figure 16.13. If you click the "Normal" button, then the dialog box returns the value 0, which is stored in the response variable. Because the index for Java arrays is zero-based, the expression distrib[response] is a string that can be used as an argument to the RANDGEN subroutine. Figure 16.14 shows output from running the program.

Modal Dialog Box Created in Java

Figure 16.13. Modal Dialog Box Created in Java

Result of Calling a Java Modal Dialog Box

Figure 16.14. Result of Calling a Java Modal Dialog Box

If you want to use classes defined by yourself or that you downloaded from another source, you can do that as well. The instructions are given in the online Help, in the chapter "Extending IMLPlus: Using Java."

16.7 Creating a Dialog Box with R

As described earlier in this chapter, SAS/IML Studio provides several simple dialog boxes that you can use to write programs that present options and choices for users of the programs. However, SAS/IML Studio does not provide a toolkit that enables you to design and build arbitrary dialog boxes from within an IMLPlus program. This section describes an advanced topic: how to use R packages to build modal dialog boxes that can be incorporated into IMLPlus programs.

The examples in this section use the tcltk package (Dalgaard 2001), which is distributed with R. The Tcl/Tk toolkit used by the tcltk package is included with the Microsoft Windows versions of R by default. There are other packages that hide some of the low-level details of creating GUI components, but installing these packages can be a complicated undertaking. For example, some R users prefer the gWidgets package, which uses the GTK2 toolkit. However, it is not trivial to install the GTK2 toolkit, so this section uses the tcltk package.

If you are not familiar with Tcl and Tk, these tools are standards in building GUI components. The Tool Command Language (Tcl) is a scripting language often used for building dialog boxes and even complete user interfaces. The associated toolkit of graphical user interface components is known as Tk.

16.7.1 The Main Idea

Suppose you want to enable the user to choose some option that is associated with an analysis. The default value for the option is off, which is encoded by 0, but the user can turn the option on, which is encoded by 1. You can create a dialog box in R so that the user can select a value for the option. The dialog box records the choice in an R variable, which can be transferred to a SAS/IML matrix. The following statements implement this idea without creating the dialog box:

/* send default value to R; retrieve new value */
x = 0;
run ExportMatrixToR(x, "Value");
submit / R;
   # The user's choice is recorded in the Value variable.
   Value <- 1
endsubmit;
run ImportMatrixFromR(x, "Value");
print x;

The main idea is to create one or more R variables that contain the chosen options. These variables are then imported into SAS/IML matrices, and the algorithm continues based on the chosen options.

16.7.2 A First Modal Dialog Box

Using the previous program as a template, the following program creates a button on a Tk window. The window is created as a modal dialog box, which means that the user must dismiss the window before the program continues. When the button on the window is pressed, the dialog box sets the Value variable to 1.

/* create modal dialog box in R */
x = 0;
run ExportMatrixToR(x, "Value");
submit / R;
   library(tcltk)
   win <- tktoplevel()                  # 1. create a container window
   tktitle(win) <- "Main Window"        #    set title for container

   OnButton <- function() {             # 2. define handler for button
      Value <<- 1                       #    set value at global scope
      tkdestroy(win)                    #    close the parent window
   }
   set.but <- tkbutton(                 # 3. create button
       win,                             #    specify parent window
       text = "Set Return Value",       #    button text
       command = OnButton               #    func to call upon button press
       )
   tkgrid(set.but)                      # 4. add button to window

   tkfocus(win)                         # 5. move the focus to window
   tkwait.window(win)                   #    wait until window is closed
endsubmit;
run ImportMatrixFromR(x, "Value");
print x;

The window created by the program is shown in Figure 16.15. The following list briefly describes the main R statements:

  1. The tktoplevel function creates a window that contains the GUI elements. (For this program, the window contains a single button.) The variable win is an object that can be used to set properties of the window. The tktitle function adds the title "Main Window" to the window, although only the first few characters of the title are visible in Figure 16.15.

  2. The button needs to call a function when it is pressed. This function is called OnButton in the program. The function does two things:

    1. It sets the Value variable to the value 1. Note that the «- operator ensures that the Value variable is set at the global scope of the program, rather than being local to the OnButton function.

    2. It calls the tkdestroy function, which closes the main window.

  3. The tkbutton function creates a button with the text "Set Return Value." When the button is pressed, the OnButton function is called.

  4. The tkgrid function arranges the button in the main window.

  5. The tkfocus function gives the keyboard focus to the window.

  6. The tkwait.window function tells the program to wait until the window is closed, which occurs in the OnButton function.

Modal Dialog Box Created in R

Figure 16.15. Modal Dialog Box Created in R

The flow of the program is as follows. The ExportMatrixToR module creates the Value variable with the value 0. The R statements create the dialog box in Figure 16.15 and wait until the window is dismissed. When you click Set Return Value, the Value variable is set to the value 1 and the window is destroyed. This causes the program to continue and to execute the statement immediately following the tkwait function call. The ImportMatrixToR module reads the value of the Value variable into the SAS/IML matrix x, and this value is printed.

16.7.3 A Modal Dialog Box with a Checkbox

The previous section showed how to create a modal dialog box by using the tcltk package. This section extends the example by creating a checkbox and adding OK and Cancel buttons to the dialog box.

Suppose you want to call the CORR procedure to examine the relationships between several variables. The CORR procedure has several options, one of which is the COV option that specifies whether to compute the variance-covariance matrix of the variables. You can write a program that displays a dialog box with a checkbox that gives the user the option of computing the covariance matrix. The dialog box might look like Figure 16.16. When the user clicks OK, the state of the checkbox determines whether to specify the COV option in the PROC CORR statement.

Modal Dialog Box with Checkbox

Figure 16.16. Modal Dialog Box with Checkbox

The following statements use the tcltk package to create the dialog box in Figure 16.16. The state of the checkbox is recorded in the cov.option variable in R:

/* create dialog box in R; retrieve options that the user selects */
covOption = 0;
run ExportMatrixToR(covOption, "cov.option");
submit / R;
   require(tcltk)
   OK.pressed <- FALSE
   win <- tktoplevel()                     # 1. create a container window
   tktitle(win) <- "CORR Options"          #    set title for container
   tcl.cov.option <- tclVar(cov.option)    # 2. create Tcl var for widget

   cb.cov.option <- tkcheckbutton(win)     # 3. create checkbox for option
   label.cov.option <- tklabel(win, text="Show covariances")
   tkconfigure(cb.cov.option, variable=tcl.cov.option)

   tkgrid(cb.cov.option,                   # 4. arrange widgets
          label.cov.option, sticky="w")
   onOK <- function() {                    # 5. define handler for buttons
       cov.option <<- as.numeric(tclvalue(tcl.cov.option))
       OK.pressed <<- TRUE
       tkdestroy(win)
   }
   onCancel <- function() {
       tkdestroy(win)
   }

   buttonsFrame <- tkframe(win)            # 6. create and layout OK/Cancel
   OK.but <- tkbutton(buttonsFrame, text="OK",
                      command=onOK, default="active")
   cancel.but <- tkbutton(buttonsFrame, text="Cancel", command=onCancel)
   tkgrid(OK.but, tklabel(buttonsFrame, text=" "), cancel.but)
   tkgrid(buttonsFrame, columnspan=2)

   tkfocus(win)                            # 7. move the focus to window
   tkwait.window(win)                      # wait until window is closed
endsubmit;

The program begins by defining a default value (0, which means "off") for the covariance option. This value is exported to an R variable named cov.option. The following list briefly describes the main R statements:

  1. The main window is created as described in the previous section.

  2. The tclVar function creates a special object of class tclVar called tcl.cov.option. This variable is associated with the checkbox and is automatically updated whenever the state of the checkbox changes.

  3. The program creates two widgets: a checkbox and a label. The tkconfigure function ensures that the value of the tcl.cov.option variable reflects the state of the checkbox.

  4. The tkgrid function arranges the checkbox and label in the main window. Each widget is left-aligned by using the sticky="w" option.

  5. The onOK and onCancel functions are defined. These functions are called when the OK or Cancel buttons are clicked. Note that the onOK function gets the value of the tcl.cov.option variable and copies it to the numeric variable cov.option.

  6. The OK and Cancel buttons are packed into a frame that is then added to the bottom of the dialog box.

  7. As explained previously, the dialog box is set to be modal.

After the dialog appears, the user must press either OK or Cancel. If OK is pressed, the onOK function is called, which sets the cov.option variable to reflect the state of the checkbox and then sets the OK.pressed variable to TRUE. If the window is closed by clicking Cancel or by clicking the close icon (X) in the window's title bar, then the value of the OK.pressed variable is FALSE.

The following statements continue the program by checking the value of the OK.pressed variable. If the variable indicates that OK was pressed, then the value of cov.option determines whether to set the COV option in the PROC CORR statement.

/* retrieve user's choices from R variables */
run ImportMatrixFromR(pressed, "OK.pressed");

if pressed then do;                       /* OK was pressed          */
   run ImportMatrixFromR(covOption, "cov.option");
   if covOption then
      covParam = "COV";                   /* checkbox was selected   */
   else
      covParam="";                        /* not selected            */
   submit covParam;                       /* pass option to CORR     */
      proc corr data=Sasuser.Movies noprob &covParam;
         var Sex Violence Profanity;
      run;
   endsubmit;
end;
else do;
   /* Cancel button was pressed */
end;

16.7.4 Case Study: A Modal Dialog Box for a Correlation Analysis

The previous section describes how to create a dialog box with a single checkbox that specifies whether to use the COV option in the PROC CORR statement. This section builds on the previous example by adding two more widgets to the dialog box. The first is another checkbox that determines whether to use the NOSIMPLE option. The second is a radio button group that specifies how the CORR procedure should handle missing values. By default, the CORR procedure computes the correlation for each pair of variables and deletes missing values in a pairwise fashion. Alternatively, the NOMISS option specifies that an entire observation be deleted if any variable has a missing value for that observation. This is called listwise deletion of missing values. The dialog box is shown in Figure 16.17.

Modal Dialog Box for a Correlation Analysis

Figure 16.17. Modal Dialog Box for a Correlation Analysis

The following statements define the default values for the various options and transfer these values to R:

/* define and display modal dialog box in R */
descOption = 1;
covOption = 0;
missOption = "pair";
run ExportMatrixToR(descOption, "desc.option");
run ExportMatrixToR(covOption,  "cov.option");
run ExportMatrixToR(missOption, "miss.option");

The next portion of the program is long, but only because the dialog box now contains five widgets: two checkboxes, a radio button group, and the OK and Cancel buttons. However, the main steps of the program are similar to the steps explained in the previous section.

submit / R;
   require(tcltk)
   OK.pressed <- FALSE

   # 1. create Tcl variables for each widget
   tcl.desc.option <- tclVar(desc.option)
   tcl.cov.option  <- tclVar(cov.option)
   tcl.miss.option <- tclVar(miss.option)

   # 2. create a container window; set window title
   win <- tktoplevel()
   tktitle(win) <- "CORR Options"

   # 3. create descriptive statistics checkbox
   cb.desc.option <- tkcheckbutton(win)
   label.desc.option <- tklabel(win, text="Descriptive statistics")
   tkconfigure(cb.desc.option, variable=tcl.desc.option)

   # 4. create covariance checkbox
   cb.cov.option <- tkcheckbutton(win)
   label.cov.option <- tklabel(win, text="Show covariances")
   tkconfigure(cb.cov.option, variable=tcl.cov.option)

   # 5. create radio button for handling missing values
   label.miss.option <- tklabel(win, text="-- Exclude missing values --")
   rb.miss.pair <- tkradiobutton(win)
   rb.miss.list <- tkradiobutton(win)
   label.miss.pair <- tklabel(win, text="Pairwise")
   label.miss.list <- tklabel(win, text="Listwise")
   tkconfigure(rb.miss.pair, variable=tcl.miss.option, value="pair")
   tkconfigure(rb.miss.list, variable=tcl.miss.option, value="list")

   # 6. all widgets created. Arrange them on window
   tkgrid(cb.desc.option, label.desc.option, sticky="w")
   tkgrid(cb.cov.option,  label.cov.option, sticky="w")
   tkgrid(label.miss.option, columnspan=2)
   tkgrid(rb.miss.pair, label.miss.pair, sticky="w")
   tkgrid(rb.miss.list, label.miss.list, sticky="w")

   # 7. define handler for buttons
   onOK <- function() {
       desc.option <<- as.numeric(tclvalue(tcl.desc.option))
       cov.option  <<- as.numeric(tclvalue(tcl.cov.option))
       miss.option <<- as.character(tclvalue(tcl.miss.option))
       OK.pressed  <<- TRUE
       tkdestroy(win)
   }
   onCancel <- function() {
       tkdestroy(win)
   }

   # 8. create and layout OK/Cancel buttons
   buttonsFrame <- tkframe(win)
   OK.but <- tkbutton(buttonsFrame, text="OK",
                      command=onOK, default="active")
   cancel.but <- tkbutton(buttonsFrame, text="Cancel", command=onCancel)
   tkgrid(OK.but, tklabel(buttonsFrame, text=" "), cancel.but)
   tkgrid(buttonsFrame, columnspan=2)

   # 9. move the focus to window and wait until window is closed
   tkfocus(win)
   tkwait.window(win)
endsubmit;

The only new step is the creation of the radio button group. Two radio buttons are created by using the tkradiobutton function. Both of them are configured to update the tcl.miss.option variable. The first radio button sets the value of that variable to the string "pair"; the other sets the value to "list."

The remainder of the program reads the variables that are set by the dialog box. The program checks whether the OK button was clicked and, if so, reads the relevant R variables into SAS/IML matrices. Their values determine whether the NOSIMPLE, COV, and NOMISS options are specified in the PROC CORR statement.

/* retrieve user's choices from R variables; call SAS procedure */
run ImportMatrixFromR(pressed, "OK.pressed");
if pressed then do;
   run ImportMatrixFromR(descOption, "desc.option");
   run ImportMatrixFromR(covOption,  "cov.option");
   run ImportMatrixFromR(missOption, "miss.option");
   descParam = choose(descOption, "", "NOSIMPLE");
   covParam  = choose(covOption,  "COV", "");
   missParam = choose(upcase(missOption)="PAIR", "", "NOMISS");

   submit descParam covParam missParam;
      proc corr data=Sasuser.Movies noprob
                &descParam &covParam &missParam;
         var Sex Violence Profanity;
      run;
   endsubmit;
end;
else do;
   /* Cancel button was pressed */
end;

The examples in this section have demonstrated that you can use R to construct a dialog box in an IMLPlus program. For more information on creating dialog boxes with the tcltk package, see the Web page of examples from Wettenhall (2004).

16.8 References

[bibch16_01] P. Dalgaard, (2001), "A Primer on the R-Tcl/Tk Package," R News, 1(3), 27-31. URL http://cran.r-project.org/doc/Rnews/

[bibch16_02] J. Wettenhall, (2004), "R TclTk Coding Examples," Last accessed July 12, 2010. URL http://www.sciviews.org/_rgui/tcltk/

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset