The MBC Procedure
Example 12.1 Storing and Scoring
When you use PROC MBC to fit a model, you can use the STORE statement to save the model for later application to a new data table. For example, you can score new observations to find their posterior probabilities of cluster membership for each cluster in the model. This example uses PROC CAS to run the mbcScore action to do this subsequent scoring.
The following statements generate a data table that represents a scenario in product marketing. The different market segments are distinguished by different levels of certain financial characteristics.
Segment 1 is a generally younger group with a higher debt-to-income ratio, and it might be attracted by newer, more luxurious goods. When marketing to this group, you would like to highlight newer lines with higher price points. In contrast, segment 2, with a low debt-to-income ratio and an average age between that of segments 1 and 3, might represent a group of bargain hunters who would prefer a product that offers the best value. The customers in segment 3 have a higher average age and a moderate debt-to-income ratio, and they represent a group that is not interested in luxury items and not strongly motivated by price. Finally, segment 4 represents customers who do not fit into these groups and so might respond to a variety of offers.
These statements assume that your CAS engine libref is named mycas, but you can substitute any appropriately defined CAS engine libref.
data mycas.marketdata;
label moninc = 'Monthly Income'
mondebt = 'Monthly Debt'
tenancy = 'Months at Current Residence'
ageyrs = 'Age';
call streaminit(869884);
* -- Segment 1 : Young Spenders ------------ ;
do j = 1 to 1000;
moninc = 12 + sqrt(2) * rand('normal');
mondebt = 6 + sqrt(1) * rand('normal');
tenancy = 18 + sqrt(2) * rand('normal');
ageyrs = 30 + sqrt(8) * rand('normal');
output;
end;
* -- Segment 2 : Middle-Aged Savers -------- ;
do j = 1 to 3000;
moninc = 10 + sqrt(2.5) * rand('normal');
mondebt = 2 + sqrt(0.25) * rand('normal');
tenancy = 24 + sqrt(3) * rand('normal');
ageyrs = 40 + sqrt(8) * rand('normal');
output;
end;
* -- Segment 3 : Comfortably Established --- ;
do j = 1 to 6000;
moninc = 15 + sqrt(2) * rand('normal');
mondebt = 3 + sqrt(0.5) * rand('normal');
tenancy = 32 + sqrt(4) * rand('normal');
ageyrs = 50 + sqrt(5) * rand('normal');
output;
end;
* -- Segment 4: Defying Classification ----- ;
do j = 1 to 300;
moninc = rand('uniform') * 15 + 5;
mondebt = rand('uniform') * 9 + 0;
tenancy = rand('uniform') * 30 + 10;
ageyrs = rand('uniform') * 40 + 20;
output;
end;
drop j;
;
The existence of outliers is a good motivation to include a noise component in the model, because the outliers can otherwise distort the cluster structure in the model. You can use the NOISE=YES option to specify this directly. The following statements fit a range of models to these data. Each model includes a noise component.
proc mbc data=mycas.marketdata nclusters=(2 3 4 5) noise=YES seed=1389035719;
var moninc mondebt tenancy ageyrs;
run;
Output 12.1.1 shows the "Fit Summary" table, indicating that the model with the best fit has three Gaussian clusters and a noise component.
Output 12.1.1: Mean and Covariance Estimates for Selected Model
| Model Selection Summary | |||||||
|---|---|---|---|---|---|---|---|
| Covariance Structure | Number of Clusters | Noise Component | Number of Parameters | -2 Log L | AIC | AICC | BIC |
| VVV | 3 | Y | 46 | 170967 | 171059 | 171059 | 171392 |
| VVV | 4 | Y | 61 | 170965 | 171087 | 171088 | 171529 |
| VVV | 5 | Y | 76 | 170942 | 171094 | 171095 | 171644 |
| VVV | 2 | Y | 31 | 182253 | 182315 | 182315 | 182539 |
The goal of this analysis is to identify segments within the group of customers, and you can use the OUTPUT statement to label each customer with its most strongly associated segment. The MAXPOST option in the following OUTPUT statement includes the index of the component that has the highest posterior clustering probability for each observation. The indices start at zero for a model that includes a noise component:
proc mbc data=mycas.marketdata nclusters=(2 3 4 5) noise=YES;
var moninc mondebt tenancy ageyrs;
output out=mycas.mktscore copyvars=(moninc mondebt tenancy ageyrs) maxpost;
run;
In Output 12.1.2, the "Mixing Estimates" table indicates a mixture component with index 0; this is the noise component.
Output 12.1.2: Mixing Estimates for Selected Model
| Mixing Probability Estimates for Selected Model | |
|---|---|
| Mixing Component | Mixing Probability |
| 0 | 0.02865 |
| 1 | 0.58248 |
| 2 | 0.09776 |
| 3 | 0.29110 |
Output 12.1.3 shows the parameter estimates for the selected model.
Output 12.1.3: Parameter Estimates for Selected Model
| Parameter Estimates for Selected Model | ||||||
|---|---|---|---|---|---|---|
| Cluster | Variable | Mean | Covariance | |||
| moninc | mondebt | tenancy | ageyrs | |||
| 1 | moninc | 15.00354 | 2.05211 | 0.01309 | -0.00126 | 0.02988 |
| mondebt | 3.00321 | 0.50819 | 0.05722 | 0.00485 | ||
| tenancy | 31.99125 | 4.01148 | -0.03269 | |||
| ageyrs | 50.04533 | 5.03305 | ||||
| 2 | moninc | 11.96829 | 2.12425 | 0.02417 | 0.07263 | -0.09442 |
| mondebt | 6.01248 | 1.06665 | -0.00584 | 0.05854 | ||
| tenancy | 17.99029 | 2.01793 | 0.03317 | |||
| ageyrs | 29.99537 | 7.41317 | ||||
| 3 | moninc | 9.99463 | 2.52433 | -0.01639 | 0.05279 | 0.10067 |
| mondebt | 1.99783 | 0.24534 | 0.00233 | 0.00226 | ||
| tenancy | 24.01474 | 2.93710 | 0.05776 | |||
| ageyrs | 39.91170 | 8.17630 | ||||
The clusters that the procedure has identified align well with the segments that are generated in the example.
To market effectively to these different groups, you want to be able to assign a cluster membership score to each potential customer. You can use the STORE statement as follows to save the fitted model for later scoring:
proc mbc data=mycas.marketdata nclusters=(2 3 4 5) noise=YES seed=1389035719;
var moninc mondebt tenancy ageyrs;
store mycas.mktgroups;
run;
The mycas.mktgroups store contains a representation of the model that you can apply to a new set of customer data. You can use the mbcScore action to score new observations according to this saved model.
The following statements show a possible set of new data:
data mycas.newcusts;
input moninc mondebt tenancy ageyrs;
datalines;
7 5 20 35
12 6 20 30
18 4 31 52
10 4 25 37
;
The following statements show how to score the new set by using PROC CAS to invoke the mbcScore action. The mbcScore action call takes several parameters. You specify the new observations to score by using the table parameter. The saved model mycas.mktgroups is specified in the restore parameter. You use the casOut parameter to designate that the data table mycas.newScores will contain the scores for the new data. The copyvars parameter includes the listed input variables in the output data table. The maxpost parameter requests that the variable Group in the output set identify the cluster that has the highest posterior weight for each observation. Finally, you use the nextclus parameter to specify the root of the variable name for each cluster weight.
proc cas;
action mbc.mbcScore /
table={name='newcusts'}
restore={name='mktgroups'}
casOut={name='newScores', replace=true}
copyvars={'moninc' , 'mondebt' , 'tenancy' , 'ageyrs'}
maxpost='group'
nextclus='cluswt';
run;
quit;
When the scoring is finished, you can examine the clusterings in the data table mycas.newScores. Output 12.1.4 shows the input data and the resulting weights and clustering.
Output 12.1.4: Weights and Scores for New Data
| cluswt0 | cluswt1 | cluswt2 | cluswt3 | group | moninc | mondebt | tenancy | ageyrs |
|---|---|---|---|---|---|---|---|---|
| 0.73350 | 0.00000 | 0.26650 | 0.00000 | 0 | 7 | 5 | 20 | 35 |
| 0.00096 | 0.00000 | 0.99904 | 0.00000 | 2 | 12 | 6 | 20 | 30 |
| 0.00176 | 0.99824 | 0.00000 | 0.00000 | 1 | 18 | 4 | 31 | 52 |
| 0.35676 | 0.00000 | 0.00001 | 0.64323 | 3 | 10 | 4 | 25 | 37 |
You can use the values in the Group variable to decide what action to take for each customer. You can also consider the strength of the association shown in the variables cluswt0 through cluswt3. In this case, each observation has a strong association with its assigned cluster, but one observation has a noticeable affinity for the noise cluster as well.