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
| Model Information | |
|---|---|
| Data Source | ANALYSISDATA |
| Response Variable | y |
| Number of Observations Read | 1000000 |
|---|---|
| Number of Observations Used | 1000000 |
| Class Level Information | ||
|---|---|---|
| Class | Levels | Values |
| c1 | 8 | 1 2 3 4 5 6 7 8 |
| c2 | 2 | 0 1 |
| c3 | 5 | average big huge small tiny |
| Dimensions | |
|---|---|
| Number of Effects | 7 |
| Number of Parameters | 19 |
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 15 | 43122037 | 2874802 | 85947.2 | <.0001 |
| Error | 999984 | 33447919 | 33.44845 | ||
| Corrected Total | 999999 | 76569956 | |||
| Root MSE | 5.78346 |
|---|---|
| R-Square | 0.56317 |
| Adj R-Sq | 0.56317 |
| AIC | 4510024 |
| AICC | 4510024 |
| SBC | 3510211 |
| ASE | 33.44792 |
| Parameter Estimates | |||||
|---|---|---|---|---|---|
| Parameter | DF | Estimate | Standard Error | t Value | Pr > |t| |
| Intercept | 1 | 21.625780 | 0.184485 | 117.22 | <.0001 |
| c1 1 | 1 | 0.021762 | 0.023134 | 0.94 | 0.3469 |
| c1 2 | 1 | -0.003045 | 0.023134 | -0.13 | 0.8953 |
| c1 3 | 1 | 4.819376 | 0.023134 | 208.32 | <.0001 |
| c1 4 | 1 | -0.028183 | 0.023134 | -1.22 | 0.2231 |
| c1 5 | 1 | -0.026722 | 0.023134 | -1.16 | 0.2481 |
| c1 6 | 1 | 0.009117 | 0.023134 | 0.39 | 0.6935 |
| c1 7 | 1 | 7.980757 | 0.023134 | 344.98 | <.0001 |
| c1 8 | 0 | 0 | . | . | . |
| c2 0 | 1 | -0.994256 | 0.011808 | -84.20 | <.0001 |
| c2 1 | 0 | 0 | . | . | . |
| c3 average | 1 | 5.299590 | 0.183209 | 28.93 | <.0001 |
| c3 big | 1 | 10.269901 | 0.183437 | 55.99 | <.0001 |
| c3 huge | 1 | 20.296836 | 0.183893 | 110.37 | <.0001 |
| c3 small | 1 | 0.274441 | 0.183286 | 1.50 | 0.1343 |
| c3 tiny | 0 | 0 | . | . | . |
| x1 | 1 | 2.488900 | 0.020040 | 124.19 | <.0001 |
| x2 | 1 | -0.011993 | 0.020022 | -0.60 | 0.5492 |
| x3 | 1 | -0.004668 | 0.020031 | -0.23 | 0.8157 |
| Task Timing | ||
|---|---|---|
| Task | Seconds | Percent |
| Setup and Parsing | 0.03 | 25.71% |
| Levelization | 0.04 | 40.47% |
| Model Initialization | 0.00 | 0.58% |
| SSCP Computation | 0.03 | 31.05% |
| Model Fitting | 0.00 | 0.76% |
| Cleanup | 0.00 | 1.27% |
| Total | 0.10 | 100.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
| Selection Information | |
|---|---|
| Selection Method | Stepwise |
| Select Criterion | SBC |
| Stop Criterion | SBC |
| Effect Hierarchy Enforced | None |
| Stop Horizon | 3 |
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
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 18 | 49369167 | 2742731 | 100831 | <.0001 |
| Error | 999981 | 27200789 | 27.20131 | ||
| Corrected Total | 999999 | 76569956 | |||
| Root MSE | 5.21549 |
|---|---|
| R-Square | 0.64476 |
| Adj R-Sq | 0.64475 |
| AIC | 4303286 |
| AICC | 4303286 |
| SBC | 3303508 |
| ASE | 27.20079 |
| Parameter Estimates | |||||
|---|---|---|---|---|---|
| Parameter | DF | Estimate | Standard Error | t Value | Pr > |t| |
| Intercept | 1 | 12.413940 | 0.167096 | 74.29 | <.0001 |
| c3 average | 1 | 5.279719 | 0.165217 | 31.96 | <.0001 |
| c3 big | 1 | 10.253726 | 0.165423 | 61.98 | <.0001 |
| c3 huge | 1 | 20.270711 | 0.165834 | 122.24 | <.0001 |
| c3 small | 1 | 0.254210 | 0.165287 | 1.54 | 0.1241 |
| c3 tiny | 0 | 0 | . | . | . |
| c1 1 | 1 | 0.027386 | 0.020862 | 1.31 | 0.1893 |
| c1 2 | 1 | 0.003203 | 0.020862 | 0.15 | 0.8780 |
| c1 3 | 1 | 4.817146 | 0.020862 | 230.91 | <.0001 |
| c1 4 | 1 | -0.019003 | 0.020862 | -0.91 | 0.3623 |
| c1 5 | 1 | -0.016027 | 0.020862 | -0.77 | 0.4423 |
| c1 6 | 1 | 0.013329 | 0.020862 | 0.64 | 0.5229 |
| c1 7 | 1 | 7.983725 | 0.020862 | 382.69 | <.0001 |
| c1 8 | 0 | 0 | . | . | . |
| c2 0 | 1 | -0.995482 | 0.010648 | -93.49 | <.0001 |
| c2 1 | 0 | 0 | . | . | . |
| x1 | 1 | 2.492238 | 0.018072 | 137.90 | <.0001 |
| x5 | 1 | 1.987619 | 0.018055 | 110.08 | <.0001 |
| x6 | 1 | 3.974480 | 0.018063 | 220.04 | <.0001 |
| x7 | 1 | 5.506787 | 0.018055 | 304.99 | <.0001 |
| x10 | 1 | 3.003553 | 0.018056 | 166.35 | <.0001 |
| x20 | 1 | 3.971277 | 0.018061 | 219.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
| Selected Effects: | Intercept c3 c1 c2 x1 x5 x6 x7 x10 x20 |
|---|
| Parameter Estimates | |||||
|---|---|---|---|---|---|
| Parameter | DF | Estimate | Standard Error | t Value | Pr > |t| |
| Intercept | 1 | 12.413940 | 0.167096 | 74.29 | <.0001 |
| c3 average | 1 | 5.279719 | 0.165217 | 31.96 | <.0001 |
| c3 big | 1 | 10.253726 | 0.165423 | 61.98 | <.0001 |
| c3 huge | 1 | 20.270711 | 0.165834 | 122.24 | <.0001 |
| c3 small | 1 | 0.254210 | 0.165287 | 1.54 | 0.1241 |
| c3 tiny | 0 | 0 | . | . | . |
| c1 1 | 1 | 0.027386 | 0.020862 | 1.31 | 0.1893 |
| c1 2 | 1 | 0.003203 | 0.020862 | 0.15 | 0.8780 |
| c1 3 | 1 | 4.817146 | 0.020862 | 230.91 | <.0001 |
| c1 4 | 1 | -0.019003 | 0.020862 | -0.91 | 0.3623 |
| c1 5 | 1 | -0.016027 | 0.020862 | -0.77 | 0.4423 |
| c1 6 | 1 | 0.013329 | 0.020862 | 0.64 | 0.5229 |
| c1 7 | 1 | 7.983725 | 0.020862 | 382.69 | <.0001 |
| c1 8 | 0 | 0 | . | . | . |
| c2 0 | 1 | -0.995482 | 0.010648 | -93.49 | <.0001 |
| c2 1 | 0 | 0 | . | . | . |
| x1 | 1 | 2.492238 | 0.018072 | 137.90 | <.0001 |
| x5 | 1 | 1.987619 | 0.018055 | 110.08 | <.0001 |
| x6 | 1 | 3.974480 | 0.018063 | 220.04 | <.0001 |
| x7 | 1 | 5.506787 | 0.018055 | 304.99 | <.0001 |
| x10 | 1 | 3.003553 | 0.018056 | 166.35 | <.0001 |
| x20 | 1 | 3.971277 | 0.018061 | 219.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
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 18 | 49369167 | 2742731 | 100831 | <.0001 |
| Error | 999981 | 27200789 | 27.20131 | ||
| Corrected Total | 999999 | 76569956 | |||
| Root MSE | 5.21549 |
|---|---|
| R-Square | 0.64476 |
| Adj R-Sq | 0.64475 |
| AIC | 4303286 |
| AICC | 4303286 |
| SBC | 3303508 |
| ASE | 27.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
| Output CAS Tables | |||
|---|---|---|---|
| CAS Library | Name | Number of Rows | Number of Columns |
| CASUSERHDFS(tiarno) | NObs | 2 | 3 |
| CASUSERHDFS(tiarno) | Anova | 15 | 8 |
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
| Obs | Step | RowId | Source | DF | SS | MS | FValue | ProbF |
|---|---|---|---|---|---|---|---|---|
| 1 | 0 | MODEL | Model | 0 | 0.00 | . | . | . |
| 2 | 0 | ERROR | Error | 999999 | 76569955.77 | 76.57 | _ | _ |
| 3 | 0 | TOTAL | Corrected Total | 999999 | 76569955.77 | _ | _ | _ |
| 4 | 1 | MODEL | Model | 4 | 34038323.47 | 8509580.87 | 200075.52 | 0 |
| 5 | 1 | ERROR | Error | 999995 | 42531632.30 | 42.53 | _ | _ |
| 6 | 1 | TOTAL | Corrected Total | 999999 | 76569955.77 | _ | _ | _ |
| 7 | 2 | MODEL | Model | 11 | 42368768.31 | 3851706.21 | 112617.73 | 0 |
| 8 | 2 | ERROR | Error | 999988 | 34201187.46 | 34.20 | _ | _ |
| 9 | 2 | TOTAL | Corrected Total | 999999 | 76569955.77 | _ | _ | _ |
| 10 | 3 | MODEL | Model | 12 | 44902774.28 | 3741897.86 | 118161.74 | 0 |
| 11 | 3 | ERROR | Error | 999987 | 31667181.50 | 31.67 | _ | _ |
| 12 | 3 | TOTAL | Corrected Total | 999999 | 76569955.77 | _ | _ | _ |
| 13 | . | MODEL | Model | 12 | 44902774.28 | 3741897.86 | 118161.74 | 0 |
| 14 | . | ERROR | Error | 999987 | 31667181.50 | 31.67 | _ | _ |
| 15 | . | TOTAL | Corrected Total | 999999 | 76569955.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
| Obs | Step | RowId | Source | DF | SS | MS | FValue | ProbF |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | MODEL | Model | 4 | 34038323.47 | 8509580.87 | 200075.52 | 0 |
| 2 | 1 | ERROR | Error | 999995 | 42531632.30 | 42.53 | _ | _ |
| 3 | 1 | TOTAL | Corrected Total | 999999 | 76569955.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
| Number of Observations Read | 1000000 |
|---|---|
| Number of Observations Used | 1000000 |
| Number of Observations Used for Training | 333334 |
| Number of Observations Used for Validation | 333333 |
| Number of Observations Used for Testing | 333333 |
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 Effects | 215 |
| Number of Parameters | 242 |
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
| Selection Summary | |||||
|---|---|---|---|---|---|
| Step | Effect Entered | Effect Removed | Number Effects In | SBC | Validation ASE |
| 0 | Intercept | 1 | 1446393.76 | 76.3941 | |
| 1 | c3 | 2 | 1250317.65 | 42.5743 | |
| 2 | c1 | 3 | 1178532.55 | 34.1695 | |
| 3 | x7 | 4 | 1152107.92 | 31.6916 | |
| 4 | x20 | 5 | 1137648.47 | 30.3960 | |
| 5 | x6 | 6 | 1122927.08 | 29.0620 | |
| 6 | x10 | 7 | 1114257.83 | 28.3163 | |
| 7 | x1 | 8 | 1108187.50 | 27.7952 | |
| 8 | x6*x7 | 9 | 1102832.58 | 27.3630 | |
| 9 | x5 | 10 | 1098864.15 | 27.0251 | |
| 10 | c2 | 11 | 1096042.04 | 26.7808 | |
| 11 | c1*c2 | x5 | 11 | 1079175.02 | 25.4785 |
| 12 | x5 | 12 | 1074904.92 | 25.1392 | |
| 13 | x1*x7 | 13 | 1074007.70* | 25.0788* | |
| 14 | x12 | 14 | 1074010.64 | 25.0792 | |
| 15 | x10*x12 | 15 | 1074018.47 | 25.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
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 27 | 17196469 | 636906 | 25421.4 | <.0001 |
| Error | 333306 | 8350640 | 25.05397 | ||
| Corrected Total | 333333 | 25547109 | |||
| Root MSE | 5.00539 |
|---|---|
| R-Square | 0.67313 |
| Adj R-Sq | 0.67310 |
| AIC | 1407044 |
| AICC | 1407044 |
| SBC | 1074008 |
| 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 | |||||
|---|---|---|---|---|---|
| Parameter | DF | Estimate | Standard Error | t Value | Pr > |t| |
| Intercept | 1 | 15.001807 | 0.280999 | 53.39 | <.0001 |
| c3 average | 1 | 5.042310 | 0.274644 | 18.36 | <.0001 |
| c3 big | 1 | 10.022783 | 0.274987 | 36.45 | <.0001 |
| c3 huge | 1 | 20.024026 | 0.275669 | 72.64 | <.0001 |
| c3 small | 1 | 0.023062 | 0.274760 | 0.08 | 0.9331 |
| c3 tiny | 0 | 0 | . | . | . |
| c1 1 | 1 | 0.013898 | 0.044852 | 0.31 | 0.7567 |
| c1 2 | 1 | -0.017605 | 0.044768 | -0.39 | 0.6941 |
| c1 3 | 1 | 7.956872 | 0.044738 | 177.86 | <.0001 |
| c1 4 | 1 | -0.040781 | 0.044703 | -0.91 | 0.3616 |
| c1 5 | 1 | -0.055841 | 0.044731 | -1.25 | 0.2119 |
| c1 6 | 1 | 0.006792 | 0.044781 | 0.15 | 0.8795 |
| c1 7 | 1 | 7.927789 | 0.044636 | 177.61 | <.0001 |
| c1 8 | 0 | 0 | . | . | . |
| c2 0 | 1 | -0.067175 | 0.050100 | -1.34 | 0.1800 |
| c2 1 | 0 | 0 | . | . | . |
| c1 1 * c2 0 | 1 | 0.079213 | 0.070728 | 1.12 | 0.2627 |
| c1 1 * c2 1 | 0 | 0 | . | . | . |
| c1 2 * c2 0 | 1 | 0.060825 | 0.070791 | 0.86 | 0.3902 |
| c1 2 * c2 1 | 0 | 0 | . | . | . |
| c1 3 * c2 0 | 1 | -7.844872 | 0.070815 | -110.78 | <.0001 |
| c1 3 * c2 1 | 0 | 0 | . | . | . |
| c1 4 * c2 0 | 1 | 0.071977 | 0.070842 | 1.02 | 0.3096 |
| c1 4 * c2 1 | 0 | 0 | . | . | . |
| c1 5 * c2 0 | 1 | 0.163243 | 0.070819 | 2.31 | 0.0212 |
| c1 5 * c2 1 | 0 | 0 | . | . | . |
| c1 6 * c2 0 | 1 | 0.093567 | 0.070780 | 1.32 | 0.1862 |
| c1 6 * c2 1 | 0 | 0 | . | . | . |
| c1 7 * c2 0 | 1 | 0.095232 | 0.070898 | 1.34 | 0.1792 |
| c1 7 * c2 1 | 0 | 0 | . | . | . |
| c1 8 * c2 0 | 0 | 0 | . | . | . |
| c1 8 * c2 1 | 0 | 0 | . | . | . |
| x1 | 1 | 0.923338 | 0.060128 | 15.36 | <.0001 |
| x5 | 1 | 1.973562 | 0.030027 | 65.73 | <.0001 |
| x6 | 1 | -0.010948 | 0.060024 | -0.18 | 0.8553 |
| x7 | 1 | 0.014801 | 0.079434 | 0.19 | 0.8522 |
| x10 | 1 | 2.998046 | 0.030045 | 99.79 | <.0001 |
| x20 | 1 | 4.008839 | 0.030015 | 133.56 | <.0001 |
| x1 * x7 | 1 | 3.139149 | 0.103999 | 30.18 | <.0001 |
| x6 * x7 | 1 | 7.985084 | 0.103887 | 76.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
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 16 | 17196210 | 1074763 | 42898.0 | <.0001 |
| Error | 333317 | 8350899 | 25.05393 | ||
| Corrected Total | 333333 | 25547109 | |||
| Root MSE | 5.00539 |
|---|---|
| R-Square | 0.67312 |
| Adj R-Sq | 0.67310 |
| AIC | 1407032 |
| AICC | 1407032 |
| SBC | 1073878 |
| ASE (Train) | 25.05265 |
| ASE (Validate) | 25.07897 |
| ASE (Test) | 24.99008 |
| Parameter Estimates | |||||
|---|---|---|---|---|---|
| Parameter | DF | Estimate | Standard Error | t Value | Pr > |t| |
| Intercept | 1 | 14.984539 | 0.279452 | 53.62 | <.0001 |
| c3 average | 1 | 5.042886 | 0.274642 | 18.36 | <.0001 |
| c3 big | 1 | 10.023281 | 0.274984 | 36.45 | <.0001 |
| c3 huge | 1 | 20.024430 | 0.275666 | 72.64 | <.0001 |
| c3 small | 1 | 0.023506 | 0.274757 | 0.09 | 0.9318 |
| c3 tiny | 0 | 0 | . | . | . |
| c1_3 | 1 | 7.973492 | 0.034068 | 234.04 | <.0001 |
| c1_7 | 1 | 7.950113 | 0.026487 | 300.15 | <.0001 |
| c2 0 | 1 | 0.013611 | 0.018917 | 0.72 | 0.4718 |
| c2 1 | 0 | 0 | . | . | . |
| c1_3 * c2 0 | 1 | -7.925657 | 0.053503 | -148.14 | <.0001 |
| c1_3 * c2 1 | 0 | 0 | . | . | . |
| x1 | 1 | 0.923807 | 0.060127 | 15.36 | <.0001 |
| x5 | 1 | 1.973535 | 0.030027 | 65.73 | <.0001 |
| x6 | 1 | -0.010878 | 0.060024 | -0.18 | 0.8562 |
| x7 | 1 | 0.015273 | 0.079433 | 0.19 | 0.8475 |
| x10 | 1 | 2.998161 | 0.030044 | 99.79 | <.0001 |
| x20 | 1 | 4.008650 | 0.030014 | 133.56 | <.0001 |
| x1 * x7 | 1 | 3.138352 | 0.103997 | 30.18 | <.0001 |
| x6 * x7 | 1 | 7.984637 | 0.103886 | 76.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
| Class Level Information | ||
|---|---|---|
| Class | Levels | Values |
| c3 | 5 | average big huge small tiny |
| c1 | 8 | I II III IV V VI VII VIII |
| c2 | 2 | 0 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
| Output CAS Tables | |||
|---|---|---|---|
| CAS Library | Name | Number of Rows | Number of Columns |
| CASUSERHDFS(tiarno) | myOutputTable | 1000000 | 28 |
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
| Column Information for MYOUTPUTTABLE in Caslib CASUSERHDFS(tiarno) | ||||||||
|---|---|---|---|---|---|---|---|---|
| Column | Label | Id | Type | Length | Formatted Length | Format | Format Width | Format Decimal |
| Predicted | Predicted Value | 1 | double | 8 | 12 | 0 | 0 | |
| Residual | Residual | 2 | double | 8 | 12 | 0 | 0 | |
| CooksD | Cook's D Influence Statistic | 3 | double | 8 | 12 | 0 | 0 | |
| Leverage | Leverage | 4 | double | 8 | 12 | 0 | 0 | |
| c3 | 5 | char | 7 | 7 | 0 | 0 | ||
| c1 | 6 | double | 8 | 4 | ROMAN | 4 | 0 | |
| c2 | 7 | double | 8 | 12 | 0 | 0 | ||
| x1 | 8 | double | 8 | 12 | 0 | 0 | ||
| x2 | 9 | double | 8 | 12 | 0 | 0 | ||
| x3 | 10 | double | 8 | 12 | 0 | 0 | ||
| x4 | 11 | double | 8 | 12 | 0 | 0 | ||
| x5 | 12 | double | 8 | 12 | 0 | 0 | ||
| x6 | 13 | double | 8 | 12 | 0 | 0 | ||
| x7 | 14 | double | 8 | 12 | 0 | 0 | ||
| x8 | 15 | double | 8 | 12 | 0 | 0 | ||
| x9 | 16 | double | 8 | 12 | 0 | 0 | ||
| x10 | 17 | double | 8 | 12 | 0 | 0 | ||
| x11 | 18 | double | 8 | 12 | 0 | 0 | ||
| x12 | 19 | double | 8 | 12 | 0 | 0 | ||
| x13 | 20 | double | 8 | 12 | 0 | 0 | ||
| x14 | 21 | double | 8 | 12 | 0 | 0 | ||
| x15 | 22 | double | 8 | 12 | 0 | 0 | ||
| x16 | 23 | double | 8 | 12 | 0 | 0 | ||
| x17 | 24 | double | 8 | 12 | 0 | 0 | ||
| x18 | 25 | double | 8 | 12 | 0 | 0 | ||
| x19 | 26 | double | 8 | 12 | 0 | 0 | ||
| x20 | 27 | double | 8 | 12 | 0 | 0 | ||
| y | Response | 28 | double | 8 | 12 | 0 | 0 | |
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
| Number of Observations Read | 400875 |
|---|---|
| Number of Observations Used | 400875 |
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 32 | 19746621 | 617082 | 22696.4 | <.0001 |
| Error | 400842 | 10898319 | 27.18857 | ||
| Corrected Total | 400874 | 30644940 | |||
| Number of Observations Read | 599125 |
|---|---|
| Number of Observations Used | 599125 |
| Analysis of Variance | |||||
|---|---|---|---|---|---|
| Source | DF | Sum of Squares | Mean Square | F Value | Pr > F |
| Model | 32 | 29623784 | 925743 | 34022.3 | <.0001 |
| Error | 599092 | 16301231 | 27.20990 | ||
| Corrected Total | 599124 | 45925015 | |||
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
groupBysubparameter of thetableparameterpartitioning data into roles by using the
partByVarparametersplitting classification variables by using the
splitsubparameter of theclassparameterspecifying crossed and bar effects in the
effectssubparameter of themodelparameterusing Perl regular expressions to define variable lists
subsetting results by using the
displayparametersaving specified results as CAS tables by using the
outputTablesparametercreating an output CAS table of specified statistics and variables by using the
outputparameter
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
groupBysubparameter of thetableparameterpartitioning data into roles by using the
partByVarparametersplitting classification variables by using the
splitsubparameter of theclassparameterspecifying crossed and bar effects in the
effectssubparameter of themodelparameterusing Perl regular expressions to define variable lists
subsetting results by using the
displayparametersaving specified results as CAS tables by using the
outputTablesparametercreating an output CAS table of specified statistics and variables by using the
outputparameter
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
groupBysubparameter of thetableparameterpartitioning data into roles by using the
partByVarparametersplitting classification variables by using the
splitsubparameter of theclassparameterspecifying crossed and bar effects in the
effectssubparameter of themodelparameterusing Perl regular expressions to define variable lists
subsetting results by using the
displayparametersaving specified results as CAS tables by using the
outputTablesparametercreating an output CAS table of specified statistics and variables by using the
outputparameter
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.