Regression Action Set

Model Selection with Validation

This section contains PROC CAS code.

Note: Input data must be accessible in your CAS session, either as a CAS table or as a transient-scope table. A CAS table has a two-level name: the first level is your CAS engine libref, and the second level is the table name. You refer to this table in the CAS procedure by specifying only the second level. For more information about two-level names, see Chapter 2, Shared Concepts. A transient-scope table is called directly from the action and exists in memory for the duration of the action. For more information about accessing data, see SAS Viya: System Programming Guide. For more information about PROC CAS and programming in CASL, see SAS Cloud Analytic Services: CASL Programmer’s Guide and SAS Cloud Analytic Services: CASL Reference.

This example explores ways that you can use the glm action to develop linear regression models. It shows a sequence of steps that highlight various aspects of the syntax available in the glm action. In some cases, you can use the ODS system in conjunction with PROC CAS as an alternative to techniques shown in this example. This example uses features of the action instead of using ODS because these features are available on Lua, Python, and Java clients, where ODS is not available.

Efficient Data Simulation on the CAS Server

The following DATA step runs on the CAS server and creates the data table mycas.analysisData, which contains one million observations and 28 variables. This DATA step assumes that your CAS engine libref is named mycas and your CAS session is named mysess, but you can substitute any appropriately defined CAS engine libref and session name.

The DATA step forms the response y by adding an error term to a true model. Variables x1–x20 are used as the continuous regressors, and variables c1–c3 are used as classification variables. The true model response, yTrue, is a linear combination of the main effects, x1, x5, x10, x20, and c1(only for levels 3 and 7), and the interaction effects, x1*x7, x6*x7, and c1*c3.

%let nTotalObs=1000000;
%let seed=1;
data mycas.analysisData / sessref=mysess single=no;
    drop i j c3Num nObsPerThread nExtras rew;
    length c3$ 7;

    array x{20} x1-x20;
    call streamInit(&seed);
    nObsPerThread  = int(&nTotalObs/_nthreads_);
    nExtras        = mod(&nTotalObs,_nthreads_);
    if _threadid_ <= nExtras then nObsPerThread =  nObsPerThread + 1;

    do i=1 to nObsPerThread;

       id = (_threadid_-1)*nObsPerThread +i;
       if _threadid_ > nExtras then id=id+nExtras;
       rew = rand('rewind', id);

       do j=1 to 20;
          x{j} = rand('UNIFORM');
       end;

       if  rand('UNIFORM') < 0.4 then byVar='A';
                                 else byVar='B';

       c1 = 1 + mod(id,8);
       c2 = rand('BERNOULLI',0.6);

       if      id < (0.001 * &nTotalObs) then do; c3 = 'tiny';     c3Num=1;end;
       else if id < (0.3   * &nTotalObs) then do; c3 = 'small';    c3Num=1;end;
       else if id < (0.7   * &nTotalObs) then do; c3 = 'average';  c3Num=2;end;
       else if id < (0.9   * &nTotalObs) then do; c3 = 'big';      c3Num=3;end;
       else                                   do; c3 = 'huge';     c3Num=5;end;

       yTrue = 10 + x1 + 2*x5 + 3*x10 + 4*x20  + 3*x1*x7 + 8*x6*x7
                  + 5*c3Num   + 8*(c1=7) + 8*(c1=3)*c2;

       error = 5*rand('NORMAL');

       y = yTrue + error;

            if mod(id,3)=1 then Role = 'TRAIN';
       else if mod(id,3)=2 then Role = 'VAL';
       else                     Role = 'TEST';

       output;
   end;
run;

There are several noteworthy aspects of the preceding DATA step. When you submit a DATA step with a CAS engine libref, a determination is made whether this DATA step can be run directly on the CAS server. If the DATA step uses any features that are not supported by the CAS DATA step that runs on the CAS server, then the client executes the DATA step and then the CAS engine sends the results to the server. The preceding DATA step can execute directly on the CAS server. By default, the CAS server executes this DATA step in a single thread on a single node and then distributes the results across the worker nodes in your CAS session. When you specify the SINGLE=NO option in a DATA step that executes on the CAS server, the DATA step code executes in parallel in every thread on every node in your session. For example, if your DATA step creates 10 observations and executes in parallel on a session that has three worker nodes each of which uses 16 threads, then the resulting CAS table has 160 observations on each worker node, yielding a CAS table with a total of 480 observations.

The following excerpt from the preceding DATA step uses this technique to create a table with a specified number of observations in parallel on your CAS server: It divides the total number of observations (n) by the total number of threads (_nthreads_); if the number of observations cannot be exactly divided by the number of threads, the code adds one observation to each of a subset of the threads until all observations are accounted for.

 nObsPerThread  = int(1000000/_nthreads_);
 nExtras        =  mod(1000000,_nthreads_);
 if _threadid_ <= nExtras then nObsPerThread =  nObsPerThread + 1;
 do i=1 to nObsPerThread;
    . . .
    output;
 end;
Machine-Learning Syntax Mode for Model Specification

The glm action supports two mutually exclusive modes for specifying the variables in a model: one mode uses target, inputs, and nominals parameters; the other mode uses class and model parameters. You can use the target, inputs, and nominals parameters to specify main-effect models that use the default settings for modeling and classification variable properties. This mode for specifying the model effects is called "machine-learning" mode because it mirrors the syntax for specifying models that is used by many machine-learning learning actions in which more complex model specifications are not needed. The following statements show how you can use PROC CAS to run the glm action with machine-learning mode syntax:

proc cas;
   regression.glm  /
      table='analysisData',
      target='y',
      inputs={'c1','c2','c3','x1','x2','x3'},
      nominals={'c1','c2'};
run;

The target parameter names the response variable, and the inputs parameter names the regressor variables. By default, the numeric variables that you specify in the inputs parameter are treated as continuous regressors and all character variables that you specify in the inputs parameter are treated as classification (nominal) variables. If you want to use a numeric variable in the inputs parameter as a classification variable, then you must also specify that variable in the nominals parameter. The preceding code explicitly defines the numeric variables c1 and c2 as classification variables. The character variable c3 is automatically treated as a classification variable.

The output from this analysis is displayed by default, and is presented in Output 19.3.1. For an explanation of the tables shown in Output 19.3.1, see the example in Getting Started: REGSELECT Procedure (SAS Visual Statistics: Procedures).

Output 19.3.1: Default Output

Results from regression.glm

Model Information
Data SourceANALYSISDATA
Response Variabley

Number of Observations Read1000000
Number of Observations Used1000000

Class Level Information
ClassLevelsValues
c181 2 3 4 5 6 7 8
c220 1
c35average big huge small tiny

Dimensions
Number of Effects7
Number of Parameters19

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model1543122037287480285947.2<.0001
Error9999843344791933.44845  
Corrected Total99999976569956   

Root MSE5.78346
R-Square0.56317
Adj R-Sq0.56317
AIC4510024
AICC4510024
SBC3510211
ASE33.44792

Parameter Estimates
ParameterDFEstimateStandard
Error
t ValuePr > |t|
Intercept121.6257800.184485117.22<.0001
c1 110.0217620.0231340.940.3469
c1 21-0.0030450.023134-0.130.8953
c1 314.8193760.023134208.32<.0001
c1 41-0.0281830.023134-1.220.2231
c1 51-0.0267220.023134-1.160.2481
c1 610.0091170.0231340.390.6935
c1 717.9807570.023134344.98<.0001
c1 800...
c2 01-0.9942560.011808-84.20<.0001
c2 100...
c3 average15.2995900.18320928.93<.0001
c3 big110.2699010.18343755.99<.0001
c3 huge120.2968360.183893110.37<.0001
c3 small10.2744410.1832861.500.1343
c3 tiny00...
x112.4889000.020040124.19<.0001
x21-0.0119930.020022-0.600.5492
x31-0.0046680.020031-0.230.8157

Task Timing
TaskSecondsPercent
Setup and Parsing0.0325.71%
Levelization0.0440.47%
Model Initialization0.000.58%
SSCP Computation0.0331.05%
Model Fitting0.000.76%
Cleanup0.001.27%
Total0.10100.00%


Model Specification by Using class and model Parameters

The glm action also supports a more general form of model specification that uses class and model parameters as alternatives to the target, inputs, and nominals parameters that are used in the machine-learning mode. By using the class and model parameters, you can specify models that cannot be expressed in the machine-learning mode. The following functionalities (which are not available in machine learning mode) are supported:

  • variable lists that are defined by Perl regular expressions

  • variable-specific nonsingular parameterizations of classification variables

  • variable-specific ordering options for classification variables

  • models that include interaction and nested effects

  • models that include constructed collection, multimember, polynomial, and spline effects

  • models that include starting and included effects for use with effect selection

  • models that do not include an intercept

  • model fit and influence diagnostics

The remaining examples in this section use the class and model parameters to specify regression models.

Specifying Main Effect models by Using class and model Parameters

The following code fits a model that is identical to the model shown Output 19.3.1. In the class-and-model syntax, the target variable and model effects are specified as subparameters of the model parameter and the classification variables are identified by using the class parameter. Because PROC CAS is an interactive procedure, it is not necessary to resubmit the PROC CAS statement.

   regression.glm
      table='analysisData',
      class={'c1','c2','c3'},
      model={depVar='y',
             effects={'c1','c2','c3','x1','x2','x3'}
            };
run;
Specifying Sets of Variables by Using Perl Regular Expressions

You can use Perl regular expressions to specify sets of variables in all the variable lists that you can specify in the class and model parameters. Any string that is enclosed in forward slashes is treated as a Perl regular expression and is matched against the variable names in the input CAS table that you specify in the table parameter. For example, the following code uses regular expressions to specify the same model that is shown in Output 19.3.1:

   regression.glm /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x[1-3]$/'}
            };
run;

The Perl regular expression /c/ matches any variable in the analysisData table whose name contains the character "c" ( c1, c2, and c3), and the regular expression /x[1-3]$/ matches variable names that end in the character "x" followed by a digit in the range 1–3 (x1, x2, and x3).

Obtaining the Return Status of an Action Submission

The status of each action submission is available in the _status variable, which contains several fields that you can access programmatically. You can display the contents of _status by using the PRINT statement as follows:

   print _status;
run;

For an action that runs without error, this PRINT statement displays the following line in the SAS log:

   {severity=0,reason=0,,statusCode=0}

You can access and print individual fields in the status by using dot notation as follows:

   print _status.severity;
run;

This PRINT statement displays the number 0 (the contents of _status.severity) in the SAS log.

The following code submits the glm action without specifying a response variable (which is an error). It also illustrates how you can assign the status of the action to a variable that you name.

   regression.glm status=myStatus/
      table='analysisData',
      model={ effects='x1'};
 run;
    print myStatus;
 run;

This code produces the following errors in the SAS log:

   ERROR: A response variable is required.
   ERROR: The action stopped due to errors.

The nature of the error is also saved in the myStatus status variable, which you can display as follows:

    print myStatus;
 run;

This PRINT statement produces the following line in the SAS log:

   {severity=2,reason=6,status=A response variable is required.,statusCode=3240083}

For more information about severity and reason codes, see the section "Programming with Severity and Reason Codes" in SAS Viya: System Programming Guide.

Performing Model Selection

The following code shows how you request stepwise selection by using the selection parameter in the glm action:

   regression.glm  /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x/'}
            },
      selection={method='STEPWISE'};
 run;

In this case, the regular expression /x/ matches all variables in the analysisData table that contain the character "x", namely x1–x20.

The "Selection Information" table is shown in Output 19.3.2.

Output 19.3.2: Selection Information

Results from regression.glm

Selection Information
Selection MethodStepwise
Select CriterionSBC
Stop CriterionSBC
Effect Hierarchy EnforcedNone
Stop Horizon3


Output 19.3.3 displays the "Stop Reason," "Selection Reason," and "Selected Effects" tables, which are displayed without any titles. The "Stop Reason" table indicates that selection stopped because adding or removing any effect would worsen the SBC value that is used as the selection criterion. In this case, no choose subparameter is specified in the selection parameter, so the final model is the selected model, as indicated in the "Selection Reason" table. The "Selected Effects" table lists the effects in the selected model.

Output 19.3.3: Stopping and Selection Reasons

Stepwise selection stopped because adding or removing an effect does not improve the SBC criterion.

The model at step 9 is selected.

Selected Effects:Intercept c3 c1 c2 x1 x5 x6 x7 x10 x20


The "Analysis of Variance," "Fit Statistics" (which is displayed without a title), and "Parameter Estimates" tables shown in Output 19.3.4 display details of the selected model.

Output 19.3.4: Details of the Selected Model

Results from regression.glm
 
Selected Model

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model18493691672742731100831<.0001
Error9999812720078927.20131  
Corrected Total99999976569956   

Root MSE5.21549
R-Square0.64476
Adj R-Sq0.64475
AIC4303286
AICC4303286
SBC3303508
ASE27.20079

Parameter Estimates
ParameterDFEstimateStandard
Error
t ValuePr > |t|
Intercept112.4139400.16709674.29<.0001
c3 average15.2797190.16521731.96<.0001
c3 big110.2537260.16542361.98<.0001
c3 huge120.2707110.165834122.24<.0001
c3 small10.2542100.1652871.540.1241
c3 tiny00...
c1 110.0273860.0208621.310.1893
c1 210.0032030.0208620.150.8780
c1 314.8171460.020862230.91<.0001
c1 41-0.0190030.020862-0.910.3623
c1 51-0.0160270.020862-0.770.4423
c1 610.0133290.0208620.640.5229
c1 717.9837250.020862382.69<.0001
c1 800...
c2 01-0.9954820.010648-93.49<.0001
c2 100...
x112.4922380.018072137.90<.0001
x511.9876190.018055110.08<.0001
x613.9744800.018063220.04<.0001
x715.5067870.018055304.99<.0001
x1013.0035530.018056166.35<.0001
x2013.9712770.018061219.88<.0001


Saving Action Output in a Named Result

In the previous examples, the output of the glm action is immediately displayed. If you route the results to a named result, then the output is saved in the named result and is not displayed. The following code shows how you can do this:

   regression.glm  result=myresult /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x/'}
            },
      selection={method='STEPWISE'};
 run;

After you run the preceding code, the results of running the glm action are stored in the result object named myresult, but no output is displayed. You can display the tables stored in myresult by using the PRINT statement in PROC CAS as follows:

   print myresult;
run;

The output of running this PRINT statement is the same as when you run the glm action without assigning the results and is not reproduced here. You can also display specific tables that are stored in myresult. The following code shows how you can print the "Selected Effects" and "Parameter Estimates" tables:

    print myresult['Summary.SelectedEffects']
          myresult['SelectedModel.ParameterEstimates'];
 run;

The requested output is shown in Output 19.3.5.

Output 19.3.5: Requested Tables from myresult

Summary.SelectedEffects: Results from regression.glm
 
Selection Details

Selected Effects:Intercept c3 c1 c2 x1 x5 x6 x7 x10 x20

SelectedModel.ParameterEstimates: Results from regression.glm
 
Selected Model

Parameter Estimates
ParameterDFEstimateStandard
Error
t ValuePr > |t|
Intercept112.4139400.16709674.29<.0001
c3 average15.2797190.16521731.96<.0001
c3 big110.2537260.16542361.98<.0001
c3 huge120.2707110.165834122.24<.0001
c3 small10.2542100.1652871.540.1241
c3 tiny00...
c1 110.0273860.0208621.310.1893
c1 210.0032030.0208620.150.8780
c1 314.8171460.020862230.91<.0001
c1 41-0.0190030.020862-0.910.3623
c1 51-0.0160270.020862-0.770.4423
c1 610.0133290.0208620.640.5229
c1 717.9837250.020862382.69<.0001
c1 800...
c2 01-0.9954820.010648-93.49<.0001
c2 100...
x112.4922380.018072137.90<.0001
x511.9876190.018055110.08<.0001
x613.9744800.018063220.04<.0001
x715.5067870.018055304.99<.0001
x1013.0035530.018056166.35<.0001
x2013.9712770.018061219.88<.0001


In order to use the PRINT statement to print specific tables in a stored result, you need to know the paths of those tables in the stored result. One way you can get this information is to specify the ODS TRACE ON command and then print the entire result. Another way to get the paths is to use the DESCRIBE statement in PROC CAS as follows:

   describe myresult;
 run;

This displays in the SAS log a dictionary of the tables and the columns in those tables that are stored in myresult. A subset of this dictionary for the "ModelInfo" and "SelectionSummary" tables follows:

   dictionary ( 13 entries, 13 used);
    [ModelInfo] Table ( [2] Rows [3] columns
   Column Names:
    [1] RowId            [                ] (string)
    [2] Description      [Description     ] (string)
    [3] Value            [Value           ] (string)
    [Summary.SelectionSummary] Table[SelectionSummary] ( [9] Rows [7] columns
   Column Names:
    [1] Control          [                ] (string)
    [2] Step             [Step            ] (int32)
    [3] EffectEntered    [Effect Entered  ] (string)
    [4] EffectRemoved    [Effect Removed  ] (string)
    [5] nEffectsIn       [Number Of Effects] (int32)
    [6] SBC              [SBC             ] (double)
    [7] OptSBC           [Optimal SBC     ] (int32)

This dictionary contains the paths (such as Summary.SelectionSummary) of all tables in myresult. You must use the complete path to request specific tables in the PRINT statement.

Using the display Parameter

The glm action supports a display parameter, which provides functionality that corresponds to that of ODS SELECT, ODS EXCLUDE, and ODS TRACE statements on a SAS client. The major advantage of using the action’s display parameter is that you can use it with Lua, Python, and Java clients, where the ODS system is not available. The following code shows how you can use the traceNames subparameter of the display parameter to obtain the paths of the tables that the glm action produces:

   regression.glm  result=myresult /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x/'}
            },
      selection={method='STEPWISE'},
      display={traceNames=true};
run;

This code produces the following notes in the SAS log:

   NOTE: Name:'ModelInfo' Label:'Model Information' Path:'ModelInfo'
   NOTE: Name:'SelectionInfo' Label:'Selection Information' Path:'SelectionInfo'
   NOTE: Name:'NObs' Label:'Number of Observations' Path:'NObs'
   NOTE: Name:'ClassInfo' Label:'Class Level Information' Path:'ClassInfo'
   NOTE: Name:'Dimensions' Label:'Dimensions' Path:'Dimensions'
   NOTE: Name:'SelectionSummary' Label:'Selection Summary'
         Path:'Summary.SelectionSummary'
   NOTE: Name:'StopReason' Label:'Stop Reason' Path:'Summary.StopReason'
   NOTE: Name:'SelectionReason' Label:'Selection Reason'
         Path:'Summary.SelectionReason'
   NOTE: Name:'SelectedEffects' Label:'Selected Effects'
         Path:'Summary.SelectedEffects'
   NOTE: Name:'ANOVA' Label:'Analysis of Variance' Path:'SelectedModel.ANOVA'
   NOTE: Name:'FitStatistics' Label:'Fit Statistics'
         Path:'SelectedModel.FitStatistics'
   NOTE: Name:'ParameterEstimates' Label:'Parameter Estimates'
         Path:'SelectedModel.ParameterEstimates'
   NOTE: Name:'Timing' Label:'Task Timing' Path:'Timing'

You can also use the display parameter to select which tables produced by the glm action are returned to the client. The following code uses the display parameter to select the "ANOVA" and "Fit Statistics" tables:

    regression.glm  /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x/'}
            },
      selection={method='FORWARD'},
      display={names={'ANOVA','FitStatistics'} };
run;

Specifying full paths is optional—the path-matching rules that the display parameter uses are consistent with rules that the ODS SELECT statement uses. The tables that you request by using the display parameter match all tables whose paths end with the specified path. For example, display={names={’ANOVA"} } matches all the paths ANOVA, SelectedModel.ANOVA, and BYGroup1.SelectedModel.ANOVA, and display={names={’SelectedModel.ANOVA"} } matches SelectedModel.ANOVA and BYGroup1.SelectedModel.ANOVA, but not ANOVA.

Output 19.3.6 shows the filtered output that contains only "Analysis of Variance" and "Fit Statistics" tables.

Output 19.3.6: Filtered Output from the glm Action

Results from regression.glm
 
Selected Model

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model18493691672742731100831<.0001
Error9999812720078927.20131  
Corrected Total99999976569956   

Root MSE5.21549
R-Square0.64476
Adj R-Sq0.64475
AIC4303286
AICC4303286
SBC3303508
ASE27.20079


If you enclose the strings in the names subparameter of the display parameter within forward slashes, then those strings are treated as Perl regular expressions that are matched against the full paths of the tables that the action produces. For example, display={names={’/Summary/’}} selects the paths Summary.SelectionSummary, Summary.StopReason, Summary.SelectionReason, and Summary.SelectedEffects.

Saving Displayed Tables as CAS Tables by Using the outputTables Parameter

The glm action supports an OutputTables parameter, which you can use to save any displayed table as a CAS table in the active caslib. This functionality is analogous to the ODS OUTPUT functionality, which saves ODS output as SAS data sets. The following code provides an example of using this parameter:

    regression.glm  /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x/'}
            },
      selection={method='STEPWISE',details='ALL',maxsteps=3},
      outputTables={names={'NObs','Anova'}};
run;

Output 19.3.7 displays the "Output CAS Tables," which provide information about all the CAS tables that are produced by the action. You see that the preceding code created CAS tables named "NObs" and "Anova."

Output 19.3.7: Output Cas Tables

Results from regression.glm
 
Selected Model

Output CAS Tables
CAS LibraryNameNumber
of Rows
Number
of Columns
CASUSERHDFS(tiarno)NObs23
CASUSERHDFS(tiarno)Anova158


The following PROC PRINT code displays the "Anova" CAS table on the SAS client by using the CAS engine to bring the table back to the SAS client.

 proc print data=mycas.anova;
 run;

Output 19.3.8: PROC PRINT of mycas.anova

ObsStepRowIdSourceDFSSMSFValueProbF
10MODELModel00.00...
20ERRORError99999976569955.7776.57__
30TOTALCorrected Total99999976569955.77___
41MODELModel434038323.478509580.87200075.520
51ERRORError99999542531632.3042.53__
61TOTALCorrected Total99999976569955.77___
72MODELModel1142368768.313851706.21112617.730
82ERRORError99998834201187.4634.20__
92TOTALCorrected Total99999976569955.77___
103MODELModel1244902774.283741897.86118161.740
113ERRORError99998731667181.5031.67__
123TOTALCorrected Total99999976569955.77___
13.MODELModel1244902774.283741897.86118161.740
14.ERRORError99998731667181.5031.67__
15.TOTALCorrected Total99999976569955.77___


Output 19.3.8 shows contents of the "Anova" CAS table. Because you specified the details='ALL' subparameter of the selection parameter, ANOVA tables are produced at each step of the selection process. Notice that the CAS tables that you request in the outputTables parameter consist of all the tables whose paths end with the specified name and that grouping variables are added to the CAS table so that you can distinguish between the tables. This behavior matches the behavior of ODS OUTPUT. If you want to see a specific table, you can specify the complete path to the table. Furthermore, you can optionally supply your own name for the CAS table that is created, as illustrated in the following code:

 proc cas;
    regression.glm  /
      table='analysisData',
      class={'/c/'},
      model={ depVar='y',
              effects={'/c/','/x/'}
            },
      selection={method='FORWARD',details='ALL',maxsteps=3},
      outputTables={names={'Step1.Anova'='myStep1Anova'}};
 run;

The following PROC PRINT code displays the myStep1Anova CAS table on the SAS client by using the CAS engine to bring the table back to the SAS client.

  proc print data=mycas.myStep1Anova;
  run;

Output 19.3.9 shows the ANOVA table at step 1 that is saved in the myStep1Anova CAS table.

Output 19.3.9: PROC PRINT of myStep1Anova

ObsStepRowIdSourceDFSSMSFValueProbF
11MODELModel434038323.478509580.87200075.520
21ERRORError99999542531632.3042.53__
31TOTALCorrected Total99999976569955.77___


Partitioning Data and Specifying Models with Interactions

The following code shows how you can add all two-way interactions of the continuous regressor variables and an interaction of c1 and c2 to the effects from which the model is selected. Because there is a danger of overfitting your data when you add these interactions, this example uses the Role variable in the analysisData table to partition the data into training, validation, and test sets and uses the ASE (average square error) on validation data to choose the final model.

 proc cas;
    regression.glm  /
      table='analysisData',
      partByVar={name='Role',train='TRAIN',validate='VAL',test='TEST'},
      class={'/c/'},
      model={ depVar='y',
              effects={  '/c/',
                         { vars={'c1','c2'},interact='CROSS' },
                         { vars='/x/'      ,interact='BAR'   ,maxinteract=2 }
                      }
            },
      selection={method='FORWARDSWAP',choose='VALIDATE',hierarchy='SINGLE'};
 run;

The partByVar parameter identifies the Role as the variable whose values are used to split the data into training, validation, and test roles. If your data do not contain a suitable variable to specify for the partitioning, then you can instead use the partByFrac parameter to randomly assign observations to roles in proportions that you specify.

Output 19.3.10 shows the "Number of Observations" table. You see that each role contains approximately one third of the observations.

Output 19.3.10: Number of Observations by Role

Results from regression.glm

Number of Observations Read1000000
Number of Observations Used1000000
Number of Observations Used for Training333334
Number of Observations Used for Validation333333
Number of Observations Used for Testing333333


The effects subparameter of the model parameter enables you to add interactions in your model. The effects subparameter defines a list of terms enclosed in braces and separated by commas. Each such term contains a list of variables defined by the vars parameter and optionally other parameters that specify how the variables in that term interact. In this example, the terms are {vars={’/c/’}} , {vars={’c1’,’c2’},interact=’CROSS’}, and {vars={’/x/’},interact=’BAR’,maxinteract=2}. These terms define the model effects that correspond to the following syntax that you would use in the GLM procedure in SAS/STAT:

c1 c2 c3
c1*c2
x1|x2|x3|x4|x5|x6|x7|x8|x9|x10|x11|x12|x13|x14|x15|x16|x17|x18|x9|x20@2

If a term contains only a vars parameter, then you can directly specify the variables named in the vars parameter in the effects parameter without the enclosing braces. This type of simplification is a general property of action syntax that is referred to as "coercion." You can exploit coercion to simplify the specification {vars={’/c/’}} to be simply ’/c/’. You can also use coercion to simplify the specification of the vars subparameter when the list within braces that it defines consists of a single string. In this case, you can omit the braces and specify the string directly in the vars subparameter. Thus the term {vars={’/x/’},interact=’BAR’,maxinteract=2} can be specified as {vars=’/x/’,interact=’BAR’,maxinteract=2}.

For more examples of specifying complex models, see Chapter 3, Specifying Linear Models for SAS Viya Analytical Actions.

Output 19.3.11 shows the "Dimensions" table. You see that the model specification defines a model that has 215 effects and 242 parameters.

Output 19.3.11: Number of Effects and Parameters

Dimensions
Number of Effects215
Number of Parameters242


Output 19.3.12 shows the "Selection Summary" table. Both the SBC and Validation ASE statistics achieve their optimal value at step 13, and the model at step 13 is the selected model.

Output 19.3.12: Selection Summary

Results from regression.glm
 
Selection Details

Selection Summary
StepEffect
Entered
Effect
Removed
Number
Effects In
SBC Validation
ASE
0Intercept 11446393.7676.3941
1c3 21250317.6542.5743
2c1 31178532.5534.1695
3x7 41152107.9231.6916
4x20 51137648.4730.3960
5x6 61122927.0829.0620
6x10 71114257.8328.3163
7x1 81108187.5027.7952
8x6*x7 91102832.5827.3630
9x5 101098864.1527.0251
10c2 111096042.0426.7808
11c1*c2x5111079175.0225.4785
12x5 121074904.9225.1392
13x1*x7 131074007.70*25.0788*
14x12 141074010.6425.0792
15x10*x12 151074018.4725.0798
* Optimal Value Of Criterion


Output 19.3.13 shows the "Selected Effects" table. You see that the selected effects are precisely the effects that by construction are systematically related to the response.

Output 19.3.13: Selection Effects

Selected Effects:Intercept c3 c1 c2 c1*c2 x1 x5 x6 x7 x10 x20 x1*x7 x6*x7


Output 19.3.14: Model Fit Diagnostics

Results from regression.glm
 
Selected Model

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model271719646963690625421.4<.0001
Error333306835064025.05397  
Corrected Total33333325547109   

Root MSE5.00539
R-Square0.67313
Adj R-Sq0.67310
AIC1407044
AICC1407044
SBC1074008
ASE (Train)25.05187
ASE (Validate)25.07878
ASE (Test)24.99062


The "Analysis of Variance" and "Fit Statistics" tables shown in Output 19.3.14 confirm that the selected model is highly significant. Furthermore, the average square error (ASE) on the training, validation, and test data are approximately equal, indicating that the model is not overfitting the training data.

Output 19.3.15 shows the "Parameter Estimates" table for the selected model.

Output 19.3.15: Parameter Estimates

Parameter Estimates
ParameterDFEstimateStandard
Error
t ValuePr > |t|
Intercept115.0018070.28099953.39<.0001
c3 average15.0423100.27464418.36<.0001
c3 big110.0227830.27498736.45<.0001
c3 huge120.0240260.27566972.64<.0001
c3 small10.0230620.2747600.080.9331
c3 tiny00...
c1 110.0138980.0448520.310.7567
c1 21-0.0176050.044768-0.390.6941
c1 317.9568720.044738177.86<.0001
c1 41-0.0407810.044703-0.910.3616
c1 51-0.0558410.044731-1.250.2119
c1 610.0067920.0447810.150.8795
c1 717.9277890.044636177.61<.0001
c1 800...
c2 01-0.0671750.050100-1.340.1800
c2 100...
c1 1 * c2 010.0792130.0707281.120.2627
c1 1 * c2 100...
c1 2 * c2 010.0608250.0707910.860.3902
c1 2 * c2 100...
c1 3 * c2 01-7.8448720.070815-110.78<.0001
c1 3 * c2 100...
c1 4 * c2 010.0719770.0708421.020.3096
c1 4 * c2 100...
c1 5 * c2 010.1632430.0708192.310.0212
c1 5 * c2 100...
c1 6 * c2 010.0935670.0707801.320.1862
c1 6 * c2 100...
c1 7 * c2 010.0952320.0708981.340.1792
c1 7 * c2 100...
c1 8 * c2 000...
c1 8 * c2 100...
x110.9233380.06012815.36<.0001
x511.9735620.03002765.73<.0001
x61-0.0109480.060024-0.180.8553
x710.0148010.0794340.190.8522
x1012.9980460.03004599.79<.0001
x2014.0088390.030015133.56<.0001
x1 * x713.1391490.10399930.18<.0001
x6 * x717.9850840.10388776.86<.0001


If you examine the p-values of the parameters of the classification variable c1, you see that the only the parameters that correspond to levels 3 and 7 of this variable are significant. This suggests that a more parsimonious model might be obtained if your model could select parameters for this effect independently. The following code shows how you can do this:

   regression.glm  /
      table='analysisData',
      partByVar={name='Role',train='TRAIN',validate='VAL',test='TEST'},
      class={ 'c2','c3',
              {vars='c1',split=true}
            },
      model={ depVar='y',
              effects={  '/c/',
                         { vars={'c1','c2'},interact='CROSS' },
                         { vars='/x/'      ,interact='BAR'   ,maxinteract=2 }
                      }
            },
      selection={method='FORWARDSWAP',choose='VALIDATE',hierarchy='SINGLE'};
 run;

As in the definition of the effects parameter, the class parameter specifies a list of terms enclosed in braces and separated by commas. Each such term contains a list of variables that are defined by the vars subparameter and optionally contains other subparameters that specify additional properties of the variables that are defined by the vars parameter. Similarly, you can use the coercion property of the action syntax to simplify the specification of terms when the optional parameters are not needed. The uncoerced version of the class specification is as follows:

      class={ {vars={'c2','c3'}},
              {vars={'c1'},split=true} }

By using coercion, this simplifies to the following:

      class={ 'c2','c3',
              {vars='c1',split=true} }

Output 19.3.16 shows details of the selected model.

Output 19.3.16: Details of the Selected Model

Results from regression.glm
 
Selected Model

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model1617196210107476342898.0<.0001
Error333317835089925.05393  
Corrected Total33333325547109   

Root MSE5.00539
R-Square0.67312
Adj R-Sq0.67310
AIC1407032
AICC1407032
SBC1073878
ASE (Train)25.05265
ASE (Validate)25.07897
ASE (Test)24.99008

Parameter Estimates
ParameterDFEstimateStandard
Error
t ValuePr > |t|
Intercept114.9845390.27945253.62<.0001
c3 average15.0428860.27464218.36<.0001
c3 big110.0232810.27498436.45<.0001
c3 huge120.0244300.27566672.64<.0001
c3 small10.0235060.2747570.090.9318
c3 tiny00...
c1_317.9734920.034068234.04<.0001
c1_717.9501130.026487300.15<.0001
c2 010.0136110.0189170.720.4718
c2 100...
c1_3 * c2 01-7.9256570.053503-148.14<.0001
c1_3 * c2 100...
x110.9238070.06012715.36<.0001
x511.9735350.03002765.73<.0001
x61-0.0108780.060024-0.180.8562
x710.0152730.0794330.190.8475
x1012.9981610.03004499.79<.0001
x2014.0086500.030014133.56<.0001
x1 * x713.1383520.10399730.18<.0001
x6 * x717.9846370.10388676.86<.0001


You see that the selected model consists of precisely the parameters that by construction are systematically related to the response. Furthermore, the selected effects for c1 correspond to levels 3 and 7 only. This yields a model with 16 model degrees of freedom as compared to the 27 model degrees of freedom in the selected model where the classification variable c1 is not split. Finally, by examining the fit statistics of this model, you see that it achieves this parsimony without loss in predictive accuracy on the training data, and yet produces a slightly better generalization on the validation and test data.

Specifying Variable Attributes

The attributes parameter provides similar functionality to that of LABEL and FORMAT statements that you can use with SAS procedures. You can use the attributes parameter to specify formats and labels for variables in the input CAS table as shown in the following code:

   regression.glm  /
      table='analysisData',
      attributes={ {name='c1',format='roman4.'} },
      class='/c/',
      model={ depVar='y',
              effects={  '/c/', '/x/'}
            };
 run;

The "Class Level Information" table in Output 19.3.17 shows that the glm action applies the specified format for the c1 variable.

Attributes that you specify for a variable in the attributes parameter override those same attributes for that variable in the input data. However, the input data attributes are unchanged after the action completes.

Output 19.3.17: Details of the Selected Model

Results from regression.glm

Class Level Information
ClassLevelsValues
c35average big huge small tiny
c18I II III IV V VI VII VIII
c220 1


Creating an Output CAS Table

You can use the output parameter to create an output CAS table that contains observationwise statistics, which are computed after the model is fitted. The variables in the input data table are not included in the output data table in order to avoid data duplication for large data tables; however, variables that you specify in the copyVars subparameter are included. Strings enclosed in forward slashes that you specify in the copyVars subparameter are interpreted as Perl regular expressions.

The output statistics are computed based on the parameter estimates for the selected model. For observations in which only the response variable is missing, predicted values are computed even though these observations do not affect the model fit. This enables, for example, predicted values to be computed for new observations.

You name the statistics to be included and identify which variables to copy from the input CAS table as shown in the following code:

   regression.glm  /
      table='analysisData',
      attributes={ {name='c1',format='roman4.'} , {name='y',label='Response'} },
      class='/c/',
      model={ depVar='y',
              effects={  '/c/', '/x/'}
            },
      output={casout='myOutputTable',pred='Predicted',resid='Residual',
              h='Leverage',cooksD='CooksD', copyVars={'/c/','/x/','y'}
             };
 run;

Output 19.3.18 displays the "Output CAS Tables," which provide information about all the CAS tables that are produced by the action. You see that the preceding code created a CAS table named "myOutputTable."

Output 19.3.18: Output CAS Tables

Results from regression.glm

Output CAS Tables
CAS LibraryNameNumber
of Rows
Number
of Columns
CASUSERHDFS(tiarno)myOutputTable100000028


The following code runs the columnInfo action in the table action set to display information about the columns in the myOutputTable:

    table.columnInfo / table='myOutputTable';
run;

Output 19.3.19 shows the columns in the myOutputTable CAS table. You see that the format for the variable c1 and the label for y that you specified in the attributes parameter are retained in the output CAS table.

Output 19.3.19: Columns in the myOutputTable CAS Table

Results from table.columnInfo

Column Information for MYOUTPUTTABLE in Caslib CASUSERHDFS(tiarno)
ColumnLabelIdTypeLengthFormatted
Length
FormatFormat
Width
Format
Decimal
PredictedPredicted Value1double812 00
ResidualResidual2double812 00
CooksDCook's D Influence Statistic3double812 00
LeverageLeverage4double812 00
c3 5char77 00
c1 6double84ROMAN40
c2 7double812 00
x1 8double812 00
x2 9double812 00
x3 10double812 00
x4 11double812 00
x5 12double812 00
x6 13double812 00
x7 14double812 00
x8 15double812 00
x9 16double812 00
x10 17double812 00
x11 18double812 00
x12 19double812 00
x13 20double812 00
x14 21double812 00
x15 22double812 00
x16 23double812 00
x17 24double812 00
x18 25double812 00
x19 26double812 00
x20 27double812 00
yResponse28double812 00


Instead of explicitly listing variables to copy in the copyVars parameter, you can instead use one of the following shortcuts:

copyVars='ALL'

copies all input variables

copyVars='ALL_NUMERIC'

copies all numeric input variables

copyVars='ALL_MODEL'

copies all the input variables that are used in the modeling

BY Processing

You can use the groupby subparameter of the table parameter to specify BY processing in the glm action. Unlike BY processing in SAS procedures, the input table is not required to be sorted by the variable or variables that you name in the groupBy subparameter. In the preceding code snippets in this example, the table parameter is specified simply as table=’analysisData’. This form is the coerced simplification of the general specification of the table parameter when only the name subparameter is needed. You can use a partially coerced form to specify BY processing with a single BY variable as shown in the following code:

   regression.glm /
      table={ name='analysisData', groupBy='byVar'},
      attributes={ {name='c1',format='roman4.'} , {name='y',label='Response'} },
      class='/c/',
      model={ depVar='y',
              effects={  '/c/', '/x/'}
            },
      display={names={'NObs','ANOVA'},traceNames=true};
  run;

Output 19.3.20 shows the output. You see that the output of BY group is identified by a title that contains the name and value of the BY variable.

Output 19.3.20: BY Processing Output

Results from regression.glm
 
byVar=A

Number of Observations Read400875
Number of Observations Used400875

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model321974662161708222696.4<.0001
Error4008421089831927.18857  
Corrected Total40087430644940   

Results from regression.glm
 
byVar=B

Number of Observations Read599125
Number of Observations Used599125

Analysis of Variance
SourceDFSum of
Squares
Mean
Square
F ValuePr > F
Model322962378492574334022.3<.0001
Error5990921630123127.20990  
Corrected Total59912445925015   


Furthermore, the following notes in the SAS log (which you request with the traceNames subparameter of the display parameter) show that the table paths contain a prefix that identifies the BY group index:

   NOTE: Name:'NObs' Label:'Number of Observations' Path:'ByGroup1.NObs'
   NOTE: Name:'ANOVA' Label:'Analysis of Variance' Path:'ByGroup1.ANOVA'
   NOTE: Name:'NObs' Label:'Number of Observations' Path:'ByGroup2.NObs'
   NOTE: Name:'ANOVA' Label:'Analysis of Variance' Path:'ByGroup2.ANOVA'
Model Selection with Validation

This section contains Lua code that creates the same analysisData table that is shown in the CASL example. It highlights sections of the example where the Lua syntax differs significantly from the CASL syntax. See the CASL version of this example for discussions of the data, syntax features, and the output from running these examples. For more information about coding in Lua, see Getting Started with SAS Viya for Lua and SAS Viya: System Programming Guide.

Because this example uses simulated data that are produced from DATA step code, you can submit the runCode action on your Lua client as follows to create the simulated data in your CAS session:

s:runCode{single='NO',code="\
   data analysisData;\
      drop i j c3Num nObsPerThread nExtras;\
      length c3$ 7;\
      array x{20} x1-x20;\
      call streamInit(1);\
      nObsPerThread  = int(1000000/_nthreads_);\
      nExtras        =  mod(1000000,_nthreads_);\
      if _threadid_ <= nExtras then nObsPerThread =  nObsPerThread + 1;\
      do i=1 to nObsPerThread;\
         do j=1 to 20;\
            x{j} = rand('UNIFORM');\
         end;\
         if  rand('UNIFORM') < 0.4 then byVar='A';\
                                   else byVar='B';\
         c1 = 1 + mod(i,8);\
         c2 = rand('BERNOULLI',0.6);\
         if      i < 50   then do; c3 = 'tiny';     c3Num=1;end;\
         else if i < 250  then do; c3 = 'small';    c3Num=1;end;\
         else if i < 600  then do; c3 = 'average';  c3Num=2;end;\
         else if i < 1200 then do; c3 = 'big';      c3Num=3;end;\
         else                  do; c3 = 'huge';     c3Num=5;end;\
         yTrue = 10 + x1 + 2*x5 + 3*x10 + 4*x20  + 3*x1*x7 + 8*x6*x7\
                    + 5*c3Num   + 8*(c1=7) + 8*(c1=3)*c2;\
         error = 5*rand('NORMAL');\
         y = yTrue + error;\
              if mod(i,3)=1 then Role = 'TRAIN';\
         else if mod(i,3)=2 then Role = 'VAL';\
         else                    Role = 'TEST';\
         output;\
     end;\
   run;"
}
Loading the regression Action Set

You can specify an action by using the action set name and the action name, separated by an underbar (actionset_action). For example, you can specify the name regression_glm to run the glm action in the regression action set, as shown in the following code:

s:regression_glm{
   table='analysisData',
   class={'c1','c2','c3'},
   model={ depVar='y',
           effects={'c1','c2','c3','x1','x2','x3'}
         }
   }

When you use the actionset_action form of specifying the action, the action set is loaded automatically into your CAS session if it is not already loaded. The following code shows how you can explicitly load the regression action set:

s:loadactionset{actionset='regression'}

After the regression action set is loaded, you can run the glm action without also having to name the regression action set as shown in the following code:

s:glm{
   table='analysisData',
   class={'c1','c2','c3'},
   model={ depVar='y',
           effects={'c1','c2','c3','x1','x2','x3'}
         }
   }
Saving the Results of an Action Submission

As in the CASL case, you can submit actions from Lua without specifying a named result, as shown in the following code:

 s:glm{
   table='analysisData',
   class={'/c/'},
   model={ depVar='y',
           effects={'/c/','/x/'}
         },
    selection={method='STEPWISE'}
  }

When you submit the preceding code, the results are sent to the Lua client and are displayed immediately as the action runs. If you want to save the results, you can name the results as shown in the following code:

 myResult=s:glm{
   table='analysisData',
   class={'/c/'},
   model={ depVar='y',
           effects={'/c/','/x/'}
         },
    selection={method='STEPWISE'}
  }

In this case, the results are not displayed but are stored in a variable named myResult. You can display the contents of the myResult variable by entering myResult at the Lua prompt. This displays all the tables that are produced by this action call along with their full pathnames. You can specify table pathnames in a print command to display specific tables as shown in the following code:

print(myResult.SelectionInfo)
print(myResult["Summary.SelectionSummary"])
print(myResult["Summary.StopReason"])
print(myResult["Summary.SelectionReason"])
print(myResult["Summary.SelectedEffects"])
print(myResult["SelectedModel.Dimensions"])
print(myResult["SelectedModel.FitStatistics"])
print(myResult["SelectedModel.ParameterEstimates"])
Saving the Status of an Action Submission

The following code shows how you can save the status of an action call by providing a name for the status variable after you specify a name for the results:

 myResult,myStatus=s:glm{
   table='analysisData',
   class={'/c/'},
   model={ depVar='y',
           effects={'/c/','/x/'}
         },
    selection={method='STEPWISE'}
  }

The preceding code saves the status of the action submission in the variable myStatus. You can display the contents of the myStatus variable by entering myStatus at the Lua prompt.

For more information about severity and reason codes, see the section "Programming with Severity and Reason Codes" in SAS Viya: System Programming Guide.

A Full-Featured Example

The next block of code shows many features of the glm action, including the following:

  • processing groups by using the groupBy subparameter of the table parameter

  • partitioning data into roles by using the partByVar parameter

  • splitting classification variables by using the split subparameter of the class parameter

  • specifying crossed and bar effects in the effects subparameter of the model parameter

  • using Perl regular expressions to define variable lists

  • subsetting results by using the display parameter

  • saving specified results as CAS tables by using the outputTables parameter

  • creating an output CAS table of specified statistics and variables by using the output parameter

myResult,myStatus=s:glm{
   table={name = 'analysisData', groupBy='byVar'},
      partByVar={name='Role',train='TRAIN',validate='VAL',test='TEST'},
      class={ 'c2','c3',
              {vars='c1',split=true}
            },
      model={ depVar='y',
              effects={  '/c/',
                         { vars={'c1','c2'},interact='CROSS' },
                         { vars='/x/'      ,interact='BAR'   ,maxinteract=2 }
                      }
            },
      selection={method='FORWARDSWAP',choose='VALIDATE',hierarchy='SINGLE'},
      display={names={'/SelectedModel/'}
              },
      outputTables={names={NObs='myNObs',dimensions='myDimensions',
                           parameterEstimates='myParmEsts'
                          }
                   },
      output={casout='myOutputTable',pred='Predicted',resid='Residual',h='Leverage',
                                     cooksD='CooksD', copyVars="ALL"}
   }

The outputTables parameter saves the "Number of Observations," "Dimensions," and "Parameter Estimates" tables as CAS tables named myNObs, myDimensions, and myParmEsts respectively. The output parameter saves the specified statistics along with all the variables in the input analysisData table in a CAS table named myOutputTable. You can obtain details about the myOutputTable table by running the tableInfo action as shown in the following code:

 s:tableInfo{table='myOutputTable'}

For explanations and output of other features highlighted in this section, see the CASL example.

Model Selection with Validation

This section contains Python code that creates the same analysisData table that is shown in the CASL example. It highlights sections of the example where the Python syntax differs significantly from the CASL syntax. See the CASL version of this example for discussions of the data, syntax features, and the output from running these examples. For more information about coding in Python, see Getting Started with SAS Viya for Python and SAS Viya: System Programming Guide.

Because this example uses simulated data that are produced from DATA step code, you can submit the runCode action on your Python client as follows to create the simulated data in your CAS session:

s.runCode(single='NO',code="\
   data analysisData;\
      drop i j c3Num nObsPerThread nExtras;\
      length c3$ 7;\
      array x{20} x1-x20;\
      call streamInit(1);\
      nObsPerThread  = int(1000000/_nthreads_);\
      nExtras        =  mod(1000000,_nthreads_);\
      if _threadid_ <= nExtras then nObsPerThread =  nObsPerThread + 1;\
      do i=1 to nObsPerThread;\
         do j=1 to 20;\
            x{j} = rand('UNIFORM');\
         end;\
         if  rand('UNIFORM') < 0.4 then byVar='A';\
                                   else byVar='B';\
         c1 = 1 + mod(i,8);\
         c2 = rand('BERNOULLI',0.6);\
         if      i < 50   then do; c3 = 'tiny';     c3Num=1;end;\
         else if i < 250  then do; c3 = 'small';    c3Num=1;end;\
         else if i < 600  then do; c3 = 'average';  c3Num=2;end;\
         else if i < 1200 then do; c3 = 'big';      c3Num=3;end;\
         else                  do; c3 = 'huge';     c3Num=5;end;\
         yTrue = 10 + x1 + 2*x5 + 3*x10 + 4*x20  + 3*x1*x7 + 8*x6*x7\
                    + 5*c3Num   + 8*(c1=7) + 8*(c1=3)*c2;\
         error = 5*rand('NORMAL');\
         y = yTrue + error;\
              if mod(i,3)=1 then Role = 'TRAIN';\
         else if mod(i,3)=2 then Role = 'VAL';\
         else                    Role = 'TEST';\
         output;\
     end;\
   run;"
)
Loading the regression Action Set and Running the glm Action

The following code loads the regression action set:

s.loadactionset(actionset='regression')

You can specify an action by using the action set name and the action name, separated by a dot (actionset.action). For example, you can use the name regression.glm to run the glm action in the regression action set, as shown in the following code:

s.regression.glm(
   table='analysisData',
   classVars=['c1','c2','c3'],
   model={ 'depVar':'y',
           'effects':['c1','c2','c3','x1','x2','x3']
         }
)

The following code shows that you do not have to specify the action set name when you run the glm action:

s.glm(
   table='analysisData',
   classVars=['c1','c2','c3'],
   model={ 'depVar':'y',
           'effects':['c1','c2','c3','x1','x2','x3']
         }
)
Saving the Results of an Action Submission

As in the CASL case, you can submit actions from Python without specifying a named result, as shown in the following code:

s.glm(
   table='analysisData',
   classVars='/c/',
   model={ 'depVar':'y',
           'effects':['/c/','/x/']
         },
    selection={'method':'STEPWISE'}
)

When you submit the preceding code, the results are sent to the Python client and are displayed immediately as the action runs. If you want to save the results, you can name the results as shown in the following code:

myResult = s.glm(
   table='analysisData',
   classVars='/c/',
   model={ 'depVar':'y',
           'effects':['/c/','/x/']
         },
    selection={'method':'STEPWISE'}
)

In this case, the results are not displayed but are stored in a variable named myResult. The following command displays all the tables that are produced by this action call along with their full pathnames:

print(myResult)

The following commands individually display some of the tables that are produced by this action call:

print(myResult.SelectionInfo)
print(myResult["Summary.SelectionSummary"])
print(myResult["Summary.StopReason"])
print(myResult["Summary.SelectionReason"])
print(myResult["Summary.SelectedEffects"])
print(myResult["Dimensions"])
print(myResult["SelectedModel.FitStatistics"])
print(myResult["SelectedModel.ParameterEstimates"])

Status information about the action submission is also saved in myResult. You can display the severity, reason, and status fields by using the print command as follows:

 print("Severity code:", myResult.severity)
 print("Reason:       ", myResult.reason)
 print("Status:       ", myResult.status)

For more information about severity and reason codes, see the section "Programming with Severity and Reason Codes" in SAS Viya: System Programming Guide.

A Full-Featured Example

The next block of code shows many features of the glm action, including the following:

  • processing groups by using the groupBy subparameter of the table parameter

  • partitioning data into roles by using the partByVar parameter

  • splitting classification variables by using the split subparameter of the class parameter

  • specifying crossed and bar effects in the effects subparameter of the model parameter

  • using Perl regular expressions to define variable lists

  • subsetting results by using the display parameter

  • saving specified results as CAS tables by using the outputTables parameter

  • creating an output CAS table of specified statistics and variables by using the output parameter

myResult=s.glm(
   table={'name':'analysisData','groupBy':'byVar'},
   partByVar={'name':'Role','train':'TRAIN','validate':'VAL','test':'TEST'},
   classVars=['c1','c2',
              {'vars':{'c3'},'split':True}],
   model={ 'depVar':'y',
           'effects':['/c/',
                      { 'vars':{'c1','c2'},'interact':'CROSS'},
                      { 'vars':'/x/',      'interact':'BAR','maxinteract':2}]
         },
    selection={'method':'FORWARDSWAP','choose':'VALIDATE','hierarchy':'SINGLE'},
    display={'names':{'/SelectedModel/'}
            },
    outputTables={'names':{'NObs':'myNObs','dimensions':'myDimensions',
                           'parameterEstimates':'myParmEsts'
                          }
                 },
    output={'casout':'myOutputTable','pred':'Predicted','resid':'Residual',
            'h':'Leverage', 'cooksD':'CooksD', 'copyVars':'ALL'}
)

The outputTables parameter saves the "Number of Observations," "Dimensions," and "Parameter Estimates" tables as CAS tables named myNObs, myDimensions, and myParmEsts respectively. The output parameter saves the specified statistics along with all the variables in the input analysisData table in a CAS table named myOutputTable. You can obtain details about the myOutputTable table by running the tableInfo action as shown in the following code:

 s:tableInfo(table='myOutputTable')

For explanations and output of other features highlighted in this section, see the CASL example.

Model Selection with Validation

This section contains R code that creates the same analysisData table that is shown in the CASL example. It highlights sections of the example where the R syntax differs significantly from the CASL syntax. See the CASL version of this example for discussions of the data, syntax features, and the output from running these examples. For more information about coding in R, see Getting Started with SAS Viya for R and SAS Viya: System Programming Guide.

Because this example uses simulated data that are produced from DATA step code, you can submit the runCode action on your R client as follows to create the simulated data in your CAS session:

m <- cas.builtins.loadActionSet(s, actionset='dataStep')
m <- cas.dataStep.runCode(s,single='NO',code="
   data analysisData;
      drop i j c3Num nObsPerThread nExtras;
      length c3$ 7;
      array x{20} x1-x20;
      call streamInit(1);
      nObsPerThread  = int(1000000/_nthreads_);
      nExtras        =  mod(1000000,_nthreads_);
      if _threadid_ <= nExtras then nObsPerThread =  nObsPerThread + 1;
      do i=1 to nObsPerThread;
         do j=1 to 20;
            x{j} = rand('UNIFORM');
         end;
         if  rand('UNIFORM') < 0.4 then byVar='A';
                                   else byVar='B';
         c1 = 1 + mod(i,8);
         c2 = rand('BERNOULLI',0.6);
         if      i < 50   then do; c3 = 'tiny';     c3Num=1;end;
         else if i < 250  then do; c3 = 'small';    c3Num=1;end;
         else if i < 600  then do; c3 = 'average';  c3Num=2;end;
         else if i < 1200 then do; c3 = 'big';      c3Num=3;end;
         else                  do; c3 = 'huge';     c3Num=5;end;
         yTrue = 10 + x1 + 2*x5 + 3*x10 + 4*x20  + 3*x1*x7 + 8*x6*x7
                    + 5*c3Num   + 8*(c1=7) + 8*(c1=3)*c2;
         error = 5*rand('NORMAL');
         y = yTrue + error;
              if mod(i,3)=1 then Role = 'TRAIN';
         else if mod(i,3)=2 then Role = 'VAL';
         else                    Role = 'TEST';
         output;
      end;
   run;"
)
Loading the regression Action Set and Running the glm Action

The following code loads the regression action set:

cas.builtins.loadActionSet(s, actionset='regression')

You can specify an action by using the action set name and the action name, separated by a dot (actionset.action). For example, you can use the name regression.glm to run the glm action in the regression action set, as shown in the following code:

cas.regression.glm(s,
   table='analysisData',
   class=list('c1','c2','c3'),
   model=list(depVar='y',
              effects=list('c1','c2','c3','x1','x2','x3'))
)
Saving the Results of an Action Submission

As in the CASL case, you can submit actions from R without specifying a named result, as shown in the following code:

cas.regression.glm(s,
   table='analysisData',
   class='/c/',
   model=list(depVar='y',
              effects=list('/c/','/x/')),
   selection=list(method='STEPWISE')
)

When you submit the preceding code, the results are sent to the R client and are displayed immediately as the action runs. If you want to save the results, you can name the results as shown in the following code:

myResult <- cas.regression.glm(s,
   table='analysisData',
   class='/c/',
   model=list(depVar='y',
              effects=list('/c/','/x/')),
   selection=list(method='STEPWISE')
)

In this case, the results are not displayed but are stored in a variable named myResult. The following command displays all the tables that are produced by this action call along with their full pathnames:

myResult

The following commands individually display some of the tables that are produced by this action call:

print(myResult$SelectionInfo)
print(myResult$Summary.SelectionSummary)
print(myResult$Summary.StopReason)
print(myResult$Summary.SelectionReason)
print(myResult$Summary.SelectedEffects)
print(myResult$SelectedModel.Dimensions)
print(myResult$SelectedModel.FitStatistics)
print(myResult$SelectedModel.ParameterEstimates)

Status information about the action submission is also saved in the CAS session object s. You can display the severity, reason, and status fields by using the print command as follows:

 print(paste("Severity code:", s$severity))
 print(paste("Reason:       ", s$reason))
 print(paste("Status:       ", s$status))

For more information about severity and reason codes, see the section "Programming with Severity and Reason Codes" in SAS Viya: System Programming Guide.

A Full-Featured Example

The next block of code shows many features of the glm action, including the following:

  • processing groups by using the groupBy subparameter of the table parameter

  • partitioning data into roles by using the partByVar parameter

  • splitting classification variables by using the split subparameter of the class parameter

  • specifying crossed and bar effects in the effects subparameter of the model parameter

  • using Perl regular expressions to define variable lists

  • subsetting results by using the display parameter

  • saving specified results as CAS tables by using the outputTables parameter

  • creating an output CAS table of specified statistics and variables by using the output parameter

myResult <- cas.regression.glm(s,
   table=list(name='analysisData',groupBy='byVar'),
   partByVar=list(name='Role',train='TRAIN',validate='VAL',test='TEST'),
   class=list('c1','c2',list(vars='c3',split=TRUE) ),
   model=list(depVar='y',
              effects=list('/c/',
                           list(vars=list('c1','c2'),interact='CROSS'),
                           list(vars='/x/', interact='BAR', maxinteract=2)) ),
   selection=list(method='FORWARDSWAP',choose='VALIDATE',hierarchy='SINGLE'),
   display=list(names='/SelectedModel/'),
   outputTables=list(names=list(NObs='myNObs',dimensions='myDimensions',
                                parameterEstimates='myParmEsts') ),
   output=list(casout='myOutputTable',pred='Predicted',resid='Residual',
               h='Leverage', cooksD='CooksD', copyVars='ALL')
)

The outputTables parameter saves the "Number of Observations," "Dimensions," and "Parameter Estimates" tables as CAS tables named myNObs, myDimensions, and myParmEsts respectively. The output parameter saves the specified statistics along with all the variables in the input analysisData table in a CAS table named myOutputTable. You can obtain details about the myOutputTable table by running the tableInfo action as shown in the following code:

cas.table.tableInfo(s,table='myOutputTable')

For explanations and output of other features highlighted in this section, see the CASL example.

Last updated: October 29, 2020