<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/chaid-and-caret-a-good-combo-june-6-2018/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://ibecav.github.io/chaidtutor2/"> Chuck Powell</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p><a rel="nofollow" href="https://ibecav.github.io/chaidtutor1/" target="_blank">In an earlier post</a> I focused on<br>
an in depth visit with CHAID (Chi-square automatic interaction<br>
detection). There are lots of tools that can help you predict an<br>
outcome, or classify, but CHAID is especially good at helping you<br>
explain to <strong>any audience</strong> how the model arrives at it’s prediction or<br>
classification. It’s also incredibly robust from a statistical<br>
perspective, making almost no assumptions about your data for<br>
distribution or normality. This post I’ll focus on marrying CHAID with<br>
the awesome <a rel="nofollow" href="https://topepo.github.io/caret/index.html" target="_blank"><code>caret</code> package</a><br>
to make our predicting easier and hopefully more accurate. Although not<br>
strictly necessary you’re probably best served by reading the original<br>
post first.</p>
<p>We’ve been using a dataset that comes to us from the <a rel="nofollow" href="https://www.ibm.com/communities/analytics/watson-analytics-blog/hr-employee-attrition/" target="_blank">IBM Watson<br>
Project</a><br>
and comes packaged with the <code>rsample</code> library. It’s a very practical and<br>
understandable dataset. A great use case for a tree based algorithm.<br>
Imagine yourself in a fictional company faced with the task of trying to<br>
figure out which employees you are going to “lose” a.k.a. attrition or<br>
turnover. There’s a steep cost involved in keeping good employees, and<br>
training and on-boarding can be expensive. Being able to predict<br>
attrition even a little bit better would save you lots of money and make<br>
the company better, especially if you can understand exactly what you<br>
have to “watch out for” that might indicate the person is a high risk to<br>
leave.</p>
<h2>Setup and library loading</h2>
<p>If you’ve never used <code>CHAID</code> before you may also not have <code>partykit</code>.<br>
<code>CHAID</code> isn’t on <code>CRAN</code> but I have commented out the install command<br>
below. You’ll also get a variety of messages, none of which is relevant<br>
to this example so I’ve suppressed them.</p>
<div>
<div>
<pre><code><span># install.packages(&quot;partykit&quot;)</span><span>
</span><span># install.packages(&quot;CHAID&quot;, repos=&quot;http://R-Forge.R-project.org&quot;)</span><span>
</span><span>require</span><span>(</span><span>rsample</span><span>)</span><span> </span><span># for dataset and splitting also loads broom and tidyr</span><span>
</span><span>require</span><span>(</span><span>dplyr</span><span>)</span><span>
</span><span>require</span><span>(</span><span>CHAID</span><span>)</span><span>
</span><span>require</span><span>(</span><span>purrr</span><span>)</span><span> </span><span># we'll use it to consolidate some data</span><span>
</span><span>require</span><span>(</span><span>caret</span><span>)</span><span>
</span><span>require</span><span>(</span><span>kableExtra</span><span>)</span><span> </span><span># just to make the output nicer</span><span>
</span></code></pre>
</div>
</div>
<h2>Predicting attrition in a fictional company</h2>
<p><a rel="nofollow" href="https://ibecav.github.io/chaidtutor1/" target="_blank">Last time</a> I spent a great deal<br>
of time explaining the mechanics of loading the data. This time we’ll<br>
race right through. If you need an explanation of what’s going on please<br>
refer back. I’ve embedded some comments in the code to follow along and<br>
changing the data frame name to <code>newattrit</code> is not strictly necessary it<br>
just mimics the last post.</p>
<div>
<div>
<pre><code><span>str</span><span>(</span><span>attrition</span><span>)</span><span> </span><span># included in rsample</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## 'data.frame':    1470 obs. of  31 variables:
##  $ Age                     : int  41 49 37 33 27 32 59 30 38 36 ...
##  $ Attrition               : Factor w/ 2 levels &quot;No&quot;,&quot;Yes&quot;: 2 1 2 1 1 1 1 1 1 1 ...
##  $ BusinessTravel          : Factor w/ 3 levels &quot;Non-Travel&quot;,&quot;Travel_Frequently&quot;,..: 3 2 3 2 3 2 3 3 2 3 ...
##  $ DailyRate               : int  1102 279 1373 1392 591 1005 1324 1358 216 1299 ...
##  $ Department              : Factor w/ 3 levels &quot;Human_Resources&quot;,..: 3 2 2 2 2 2 2 2 2 2 ...
##  $ DistanceFromHome        : int  1 8 2 3 2 2 3 24 23 27 ...
##  $ Education               : Ord.factor w/ 5 levels &quot;Below_College&quot;&lt;..: 2 1 2 4 1 2 3 1 3 3 ...
##  $ EducationField          : Factor w/ 6 levels &quot;Human_Resources&quot;,..: 2 2 5 2 4 2 4 2 2 4 ...
##  $ EnvironmentSatisfaction : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 2 3 4 4 1 4 3 4 4 3 ...
##  $ Gender                  : Factor w/ 2 levels &quot;Female&quot;,&quot;Male&quot;: 1 2 2 1 2 2 1 2 2 2 ...
##  $ HourlyRate              : int  94 61 92 56 40 79 81 67 44 94 ...
##  $ JobInvolvement          : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 3 2 2 3 3 3 4 3 2 3 ...
##  $ JobLevel                : int  2 2 1 1 1 1 1 1 3 2 ...
##  $ JobRole                 : Factor w/ 9 levels &quot;Healthcare_Representative&quot;,..: 8 7 3 7 3 3 3 3 5 1 ...
##  $ JobSatisfaction         : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 4 2 3 3 2 4 1 3 3 3 ...
##  $ MaritalStatus           : Factor w/ 3 levels &quot;Divorced&quot;,&quot;Married&quot;,..: 3 2 3 2 2 3 2 1 3 2 ...
##  $ MonthlyIncome           : int  5993 5130 2090 2909 3468 3068 2670 2693 9526 5237 ...
##  $ MonthlyRate             : int  19479 24907 2396 23159 16632 11864 9964 13335 8787 16577 ...
##  $ NumCompaniesWorked      : int  8 1 6 1 9 0 4 1 0 6 ...
##  $ OverTime                : Factor w/ 2 levels &quot;No&quot;,&quot;Yes&quot;: 2 1 2 2 1 1 2 1 1 1 ...
##  $ PercentSalaryHike       : int  11 23 15 11 12 13 20 22 21 13 ...
##  $ PerformanceRating       : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Good&quot;&lt;&quot;Excellent&quot;&lt;..: 3 4 3 3 3 3 4 4 4 3 ...
##  $ RelationshipSatisfaction: Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 1 4 2 3 4 3 1 2 2 2 ...
##  $ StockOptionLevel        : int  0 1 0 0 1 0 3 1 0 2 ...
##  $ TotalWorkingYears       : int  8 10 7 8 6 8 12 1 10 17 ...
##  $ TrainingTimesLastYear   : int  0 3 3 3 3 2 3 2 2 3 ...
##  $ WorkLifeBalance         : Ord.factor w/ 4 levels &quot;Bad&quot;&lt;&quot;Good&quot;&lt;&quot;Better&quot;&lt;..: 1 3 3 3 3 2 2 3 3 2 ...
##  $ YearsAtCompany          : int  6 10 0 8 2 7 1 1 9 7 ...
##  $ YearsInCurrentRole      : int  4 7 0 7 2 7 0 0 7 7 ...
##  $ YearsSinceLastPromotion : int  0 1 0 3 2 3 0 0 1 7 ...
##  $ YearsWithCurrManager    : int  5 7 0 0 2 6 0 0 8 7 ...
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span># the easy to convert because they are integers with less than 10 levels</span><span>
</span><span>attrition</span><span> </span><span>&lt;-</span><span> </span><span>attrition</span><span> </span><span>%>%</span><span> </span><span>mutate_if</span><span>(</span><span>function</span><span>(</span><span>col</span><span>)</span><span> </span><span>length</span><span>(</span><span>unique</span><span>(</span><span>col</span><span>))</span><span> </span><span>&lt;=</span><span> </span><span>10</span><span> </span><span>&amp;</span><span> </span><span>is.integer</span><span>(</span><span>col</span><span>),</span><span> </span><span>as.factor</span><span>)</span><span>
</span><span># More difficult to get 5 levels</span><span>
</span><span>attrition</span><span>$</span><span>YearsSinceLastPromotion</span><span> </span><span>&lt;-</span><span> </span><span>cut</span><span>(</span><span>
</span><span>attrition</span><span>$</span><span>YearsSinceLastPromotion</span><span>,</span><span>
</span><span>breaks</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>-1</span><span>,</span><span> </span><span>0.9</span><span>,</span><span> </span><span>1.9</span><span>,</span><span> </span><span>2.9</span><span>,</span><span> </span><span>30</span><span>),</span><span>
</span><span>labels</span><span> </span><span>=</span><span> </span><span>c</span><span>(</span><span>&quot;Less than 1&quot;</span><span>,</span><span> </span><span>&quot;1&quot;</span><span>,</span><span> </span><span>&quot;2&quot;</span><span>,</span><span> </span><span>&quot;More than 2&quot;</span><span>)</span><span>
</span><span>)</span><span>
</span><span># everything else just five more or less even levels</span><span>
</span><span>attrition</span><span> </span><span>&lt;-</span><span> </span><span>attrition</span><span> </span><span>%>%</span><span> </span><span>mutate_if</span><span>(</span><span>is.numeric</span><span>,</span><span> </span><span>funs</span><span>(</span><span>cut_number</span><span>(</span><span>.</span><span>,</span><span> </span><span>n</span><span>=</span><span>5</span><span>)))</span><span>
</span><span>dim</span><span>(</span><span>attrition</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## [1] 1470   31
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>str</span><span>(</span><span>attrition</span><span>)</span><span> </span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## 'data.frame':    1470 obs. of  31 variables:
##  $ Age                     : Factor w/ 5 levels &quot;[18,29]&quot;,&quot;(29,34]&quot;,..: 4 5 3 2 1 2 5 2 3 3 ...
##  $ Attrition               : Factor w/ 2 levels &quot;No&quot;,&quot;Yes&quot;: 2 1 2 1 1 1 1 1 1 1 ...
##  $ BusinessTravel          : Factor w/ 3 levels &quot;Non-Travel&quot;,&quot;Travel_Frequently&quot;,..: 3 2 3 2 3 2 3 3 2 3 ...
##  $ DailyRate               : Factor w/ 5 levels &quot;[102,392]&quot;,&quot;(392,656]&quot;,..: 4 1 5 5 2 4 5 5 1 5 ...
##  $ Department              : Factor w/ 3 levels &quot;Human_Resources&quot;,..: 3 2 2 2 2 2 2 2 2 2 ...
##  $ DistanceFromHome        : Factor w/ 5 levels &quot;[1,2]&quot;,&quot;(2,5]&quot;,..: 1 3 1 2 1 1 2 5 5 5 ...
##  $ Education               : Ord.factor w/ 5 levels &quot;Below_College&quot;&lt;..: 2 1 2 4 1 2 3 1 3 3 ...
##  $ EducationField          : Factor w/ 6 levels &quot;Human_Resources&quot;,..: 2 2 5 2 4 2 4 2 2 4 ...
##  $ EnvironmentSatisfaction : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 2 3 4 4 1 4 3 4 4 3 ...
##  $ Gender                  : Factor w/ 2 levels &quot;Female&quot;,&quot;Male&quot;: 1 2 2 1 2 2 1 2 2 2 ...
##  $ HourlyRate              : Factor w/ 5 levels &quot;[30,45]&quot;,&quot;(45,59]&quot;,..: 5 3 5 2 1 4 4 3 1 5 ...
##  $ JobInvolvement          : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 3 2 2 3 3 3 4 3 2 3 ...
##  $ JobLevel                : Factor w/ 5 levels &quot;1&quot;,&quot;2&quot;,&quot;3&quot;,&quot;4&quot;,..: 2 2 1 1 1 1 1 1 3 2 ...
##  $ JobRole                 : Factor w/ 9 levels &quot;Healthcare_Representative&quot;,..: 8 7 3 7 3 3 3 3 5 1 ...
##  $ JobSatisfaction         : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 4 2 3 3 2 4 1 3 3 3 ...
##  $ MaritalStatus           : Factor w/ 3 levels &quot;Divorced&quot;,&quot;Married&quot;,..: 3 2 3 2 2 3 2 1 3 2 ...
##  $ MonthlyIncome           : Factor w/ 5 levels &quot;[1.01e+03,2.7e+03]&quot;,..: 4 3 1 2 2 2 1 1 4 3 ...
##  $ MonthlyRate             : Factor w/ 5 levels &quot;[2.09e+03,6.89e+03]&quot;,..: 4 5 1 5 3 3 2 3 2 3 ...
##  $ NumCompaniesWorked      : Factor w/ 10 levels &quot;0&quot;,&quot;1&quot;,&quot;2&quot;,&quot;3&quot;,..: 9 2 7 2 10 1 5 2 1 7 ...
##  $ OverTime                : Factor w/ 2 levels &quot;No&quot;,&quot;Yes&quot;: 2 1 2 2 1 1 2 1 1 1 ...
##  $ PercentSalaryHike       : Factor w/ 5 levels &quot;[11,12]&quot;,&quot;(12,13]&quot;,..: 1 5 3 1 1 2 5 5 5 2 ...
##  $ PerformanceRating       : Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Good&quot;&lt;&quot;Excellent&quot;&lt;..: 3 4 3 3 3 3 4 4 4 3 ...
##  $ RelationshipSatisfaction: Ord.factor w/ 4 levels &quot;Low&quot;&lt;&quot;Medium&quot;&lt;..: 1 4 2 3 4 3 1 2 2 2 ...
##  $ StockOptionLevel        : Factor w/ 4 levels &quot;0&quot;,&quot;1&quot;,&quot;2&quot;,&quot;3&quot;: 1 2 1 1 2 1 4 2 1 3 ...
##  $ TotalWorkingYears       : Factor w/ 5 levels &quot;[0,5]&quot;,&quot;(5,8]&quot;,..: 2 3 2 2 2 2 4 1 3 4 ...
##  $ TrainingTimesLastYear   : Factor w/ 7 levels &quot;0&quot;,&quot;1&quot;,&quot;2&quot;,&quot;3&quot;,..: 1 4 4 4 4 3 4 3 3 4 ...
##  $ WorkLifeBalance         : Ord.factor w/ 4 levels &quot;Bad&quot;&lt;&quot;Good&quot;&lt;&quot;Better&quot;&lt;..: 1 3 3 3 3 2 2 3 3 2 ...
##  $ YearsAtCompany          : Factor w/ 5 levels &quot;[0,2]&quot;,&quot;(2,5]&quot;,..: 3 4 1 4 1 3 1 1 4 3 ...
##  $ YearsInCurrentRole      : Factor w/ 5 levels &quot;[0,1]&quot;,&quot;(1,2]&quot;,..: 3 4 1 4 2 4 1 1 4 4 ...
##  $ YearsSinceLastPromotion : Factor w/ 4 levels &quot;Less than 1&quot;,..: 1 2 1 4 3 4 1 1 2 4 ...
##  $ YearsWithCurrManager    : Factor w/ 5 levels &quot;[0,1]&quot;,&quot;(1,2]&quot;,..: 4 4 1 1 2 4 1 1 5 4 ...
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>newattrit</span><span> </span><span>&lt;-</span><span> </span><span>attrition</span><span> </span><span>%>%</span><span> </span><span>select_if</span><span>(</span><span>is.factor</span><span>)</span><span>
</span><span>dim</span><span>(</span><span>newattrit</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## [1] 1470   31
</code></pre>
</div>
</div>
<p>Okay we have data on 1,470 employees. We have 30 potential predictor<br>
(features) or independent variables and the all important <code>attrition</code><br>
variable which gives us a yes or no answer to the question of whether or<br>
not the employee left. We’re to build the most accurate predictive model<br>
we can that is also simple (parsimonious) and explainable. The<br>
predictors we have seem to be the sorts of data we might have on hand in<br>
our HR files and thank goodness are labelled in a way that makes them<br>
pretty self explanatory.</p>
<p>Last post we explored the control options and built predictive models<br>
like the one below. <a rel="nofollow" href="https://ibecav.github.io/chaidtutor1/" target="_blank">For a review of what the output means and how CHAID<br>
works please refer back</a>.</p>
<div>
<div>
<pre><code><span># explore the control options</span><span>
</span><span>ctrl</span><span> </span><span>&lt;-</span><span> </span><span>chaid_control</span><span>(</span><span>minsplit</span><span> </span><span>=</span><span> </span><span>200</span><span>,</span><span> </span><span>minprob</span><span> </span><span>=</span><span> </span><span>0.05</span><span>)</span><span>
</span><span>ctrl</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## $alpha2
## [1] 0.05
## 
## $alpha3
## [1] -1
## 
## $alpha4
## [1] 0.05
## 
## $minsplit
## [1] 200
## 
## $minbucket
## [1] 7
## 
## $minprob
## [1] 0.05
## 
## $stump
## [1] FALSE
## 
## $maxheight
## [1] -1
## 
## attr(,&quot;class&quot;)
## [1] &quot;chaid_control&quot;
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>full_data</span><span> </span><span>&lt;-</span><span> </span><span>chaid</span><span>(</span><span>Attrition</span><span> </span><span>~</span><span> </span><span>.</span><span>,</span><span> </span><span>data</span><span> </span><span>=</span><span> </span><span>newattrit</span><span>,</span><span> </span><span>control</span><span> </span><span>=</span><span> </span><span>ctrl</span><span>)</span><span>
</span><span>print</span><span>(</span><span>full_data</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## 
## Model formula:
## Attrition ~ Age + BusinessTravel + DailyRate + Department + DistanceFromHome + 
##     Education + EducationField + EnvironmentSatisfaction + Gender + 
##     HourlyRate + JobInvolvement + JobLevel + JobRole + JobSatisfaction + 
##     MaritalStatus + MonthlyIncome + MonthlyRate + NumCompaniesWorked + 
##     OverTime + PercentSalaryHike + PerformanceRating + RelationshipSatisfaction + 
##     StockOptionLevel + TotalWorkingYears + TrainingTimesLastYear + 
##     WorkLifeBalance + YearsAtCompany + YearsInCurrentRole + YearsSinceLastPromotion + 
##     YearsWithCurrManager
## 
## Fitted party:
## [1] root
## |   [2] OverTime in No
## |   |   [3] YearsAtCompany in [0,2]
## |   |   |   [4] Age in [18,29], (29,34]: No (n = 129, err = 32.6%)
## |   |   |   [5] Age in (34,38], (38,45], (45,60]: No (n = 109, err = 6.4%)
## |   |   [6] YearsAtCompany in (2,5], (5,7], (7,10], (10,40]
## |   |   |   [7] WorkLifeBalance in Bad: No (n = 45, err = 22.2%)
## |   |   |   [8] WorkLifeBalance in Good, Better, Best
## |   |   |   |   [9] JobSatisfaction in Low: No (n = 153, err = 12.4%)
## |   |   |   |   [10] JobSatisfaction in Medium, High, Very_High
## |   |   |   |   |   [11] Age in [18,29], (29,34], (34,38], (38,45]
## |   |   |   |   |   |   [12] BusinessTravel in Non-Travel, Travel_Rarely
## |   |   |   |   |   |   |   [13] JobInvolvement in Low: No (n = 25, err = 12.0%)
## |   |   |   |   |   |   |   [14] JobInvolvement in Medium, High, Very_High
## |   |   |   |   |   |   |   |   [15] RelationshipSatisfaction in Low: No (n = 81, err = 3.7%)
## |   |   |   |   |   |   |   |   [16] RelationshipSatisfaction in Medium, High: No (n = 198, err = 0.0%)
## |   |   |   |   |   |   |   |   [17] RelationshipSatisfaction in Very_High: No (n = 105, err = 4.8%)
## |   |   |   |   |   |   [18] BusinessTravel in Travel_Frequently: No (n = 95, err = 8.4%)
## |   |   |   |   |   [19] Age in (45,60]: No (n = 114, err = 11.4%)
## |   [20] OverTime in Yes
## |   |   [21] JobLevel in 1: Yes (n = 156, err = 47.4%)
## |   |   [22] JobLevel in 2, 3, 4, 5
## |   |   |   [23] MaritalStatus in Divorced, Married: No (n = 188, err = 10.6%)
## |   |   |   [24] MaritalStatus in Single: No (n = 72, err = 34.7%)
## 
## Number of inner nodes:    11
## Number of terminal nodes: 13
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>plot</span><span>(</span><span>
</span><span>full_data</span><span>,</span><span>
</span><span>main</span><span> </span><span>=</span><span> </span><span>&quot;newattrit dataset, minsplit = 200, minprob = 0.05&quot;</span><span>,</span><span>
</span><span>gp</span><span> </span><span>=</span><span> </span><span>gpar</span><span>(</span><span>
</span><span>lty</span><span> </span><span>=</span><span> </span><span>&quot;solid&quot;</span><span>,</span><span>
</span><span>lwd</span><span> </span><span>=</span><span> </span><span>2</span><span>,</span><span>
</span><span>fontsize</span><span> </span><span>=</span><span> </span><span>10</span><span>
</span><span>)</span><span>
</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p><img src="https://i2.wp.com/ibecav.github.io/images/chaid22-1.png?w=456" data-recalc-dims="1"></p>
<h2>Over-fitting</h2>
<p>Okay we have a working predictive model. At this point, however, we’ve<br>
been <strong>cheating</strong> to a certain degree! We’ve been using every available<br>
piece of data we have to develop the best possible model. We’ve told the<br>
powerful all-knowing <code>algorithims</code> to squeeze every last bit of accuracy<br>
they can out of the data. We’ve told it to <code>fit</code> the best possible<br>
model. Problem is that we may have done that at the cost of being able<br>
to generalize our model to new data or to new situations. That’s the<br>
problem of over-fitting in a nutshell. If you want a fuller<br>
understanding please consider <a rel="nofollow" href="https://elitedatascience.com/overfitting-in-machine-learning" target="_blank">reading this post on<br>
EliteDataScience</a>.<br>
I’m going to move on to a solution for solving this limitation and<br>
that’s where <code>caret</code> comes in.</p>
<p>We’re going to use <code>caret</code> to employ <code>cross-validation</code> a.k.a. <code>cv</code> to<br>
solve this challenge for us, or more accurately to mitigate the problem.<br>
<a rel="nofollow" href="https://elitedatascience.com/overfitting-in-machine-learning#how-to-prevent" target="_blank">The same article<br>
explains</a><br>
it well so I won’t repeat that explanation here, I’ll simply show you<br>
how to run the steps in <code>R</code>.</p>
<p>This is also a good time to point out that <code>caret</code> has <a rel="nofollow" href="https://topepo.github.io/caret/" target="_blank">extraordinarily<br>
comprehensive documentation</a> which I<br>
used extensively and I’m limiting myself to the basics.</p>
<p>As a first step, let’s just take 30% of our data and put is aside for a<br>
minute. We’re not going to let chaid <em>see it</em> or know about it as we<br>
build the model. In some scenarios you have subsequent data at hand for<br>
checking your model (data from another company or another year or …). We<br>
don’t, so we’re going to self-impose this restraint. Why 30%? Doesn’t<br>
have to be, could be as low as 20% or as high as 40% it really depends<br>
on how conservative you want to be, and how much data you have at hand.<br>
Since this is just a tutorial we’ll simply use 30% as a representative<br>
number. We’ve already loaded both <code>rsample</code> and <code>caret</code> either of which<br>
is quite capable of making this split for us. I’m arbitrarily going to<br>
use <code>rsample</code> syntax which is the line with <code>initial_split(newattrit,<br>
prop = .7, strata = &quot;Attrition&quot;)</code> in it. That takes our data set<br>
<code>newattrit</code> makes a 70% split ensuring that we keep our outcome variable<br>
<code>Attrition</code> as close to 70/30 as we can. <em>This is important because our<br>
data is already pretty lop-sided</em> for outcomes. The two subsequent lines<br>
serve to take the data contained in <code>split</code> and produce two separate<br>
dataframes, <code>test</code> and <code>train</code>. They have 440 and 1030 staff members<br>
each. We’ll set <code>test</code> aside for now and focus on <code>train</code>.</p>
<div>
<div>
<pre><code><span># Create training (70%) and test (30%) sets for the attrition data.</span><span>
</span><span># Use set.seed for reproducibility</span><span>
</span><span>#####</span><span>
</span><span>set.seed</span><span>(</span><span>1234</span><span>)</span><span>
</span><span>split</span><span> </span><span>&lt;-</span><span> </span><span>initial_split</span><span>(</span><span>newattrit</span><span>,</span><span> </span><span>prop</span><span> </span><span>=</span><span> </span><span>.7</span><span>,</span><span> </span><span>strata</span><span> </span><span>=</span><span> </span><span>&quot;Attrition&quot;</span><span>)</span><span>
</span><span>train</span><span> </span><span>&lt;-</span><span> </span><span>training</span><span>(</span><span>split</span><span>)</span><span>
</span><span>test</span><span> </span><span>&lt;-</span><span> </span><span>testing</span><span>(</span><span>split</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p>The next step is a little counter-intuitive but quite practical. Turns<br>
out that many models do not perform well when you feed them a <code>formula</code><br>
for the model even if they claim to support a formula interface (as<br>
CHAID does). <a rel="nofollow" href="https://stackoverflow.com/questions/33088893/caret-random-forests-not-working-something-is-wrong-all-the-accuracy-metric" target="_blank">Here’s an SO<br>
link</a><br>
that discusses in detail but my suggestion to you is to always separate<br>
them and avoid the problem altogether. We’re just taking our<br>
<code>predictors</code> or <code>features</code> and putting them in <code>x</code> while we put our<br>
outcome in <code>y</code>.</p>
<div>
<div>
<pre><code><span># create response and feature data</span><span>
</span><span>features</span><span> </span><span>&lt;-</span><span> </span><span>setdiff</span><span>(</span><span>names</span><span>(</span><span>train</span><span>),</span><span> </span><span>&quot;Attrition&quot;</span><span>)</span><span>
</span><span>x</span><span> </span><span>&lt;-</span><span> </span><span>train</span><span>[,</span><span> </span><span>features</span><span>]</span><span>
</span><span>y</span><span> </span><span>&lt;-</span><span> </span><span>train</span><span>$</span><span>Attrition</span><span>
</span></code></pre>
</div>
</div>
<p>Alright, let’s get back on track. <code>trainControl</code> is the function within<br>
<code>caret</code> we need to use. Chapter 5 in the <code>caret</code> doco covers it in great<br>
detail. I’m simply going to pluck out a few sane and safe options.<br>
<code>method = &quot;cv&quot;</code> gets us cross-validation. <code>number = 10</code> is pretty<br>
obvious. I happen to like seeing the progress in case I want to go for<br>
coffee so <code>verboseIter = TRUE</code>, and I play it safe and explicitly save<br>
my predictions <code>savePredictions = &quot;final&quot;</code>. We put everything in<br>
<code>train_control</code> which we’ll use in a minute.</p>
<div>
<div>
<pre><code><span># set up 10-fold cross validation procedure</span><span>
</span><span>train_control</span><span> </span><span>&lt;-</span><span> </span><span>trainControl</span><span>(</span><span>method</span><span> </span><span>=</span><span> </span><span>&quot;cv&quot;</span><span>,</span><span>
</span><span>number</span><span> </span><span>=</span><span> </span><span>10</span><span>,</span><span>
</span><span>verboseIter</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>,</span><span>
</span><span>savePredictions</span><span> </span><span>=</span><span> </span><span>&quot;final&quot;</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p>Not surprisingly the <code>train</code> function in <code>caret</code> trains our model! It<br>
wants to know what our <code>x</code> and <code>y</code>’s are, as well as our training<br>
control parameters which we’ve parked in <code>train_control</code>. At this point<br>
we could successfully unleash the dogs of war (sorry Shakespeare) and<br>
train our model since we know we want to use <code>chaid</code>. But let’s change<br>
one other useful thing and that is <code>metric</code> which is what metric we want<br>
to use to pick the “best” model. Instead of the default “accuracy” we’ll<br>
use <code>Kappa</code> which as you may remember from the last post is more<br>
conservative measure of how well we did.</p>
<blockquote>
<p><strong>If you’re running this code yourself this is a good time to take a<br>
coffee break. I’ll tell you later how to find out how long it took<br>
more or less exactly. But there’s no getting around it we’re model<br>
building many more times so it takes longer.</strong></p>
</blockquote>
<div>
<div>
<pre><code><span># train model</span><span>
</span><span>chaid.m1</span><span> </span><span>&lt;-</span><span> </span><span>train</span><span>(</span><span>
</span><span>x</span><span> </span><span>=</span><span> </span><span>x</span><span>,</span><span>
</span><span>y</span><span> </span><span>=</span><span> </span><span>y</span><span>,</span><span>
</span><span>method</span><span> </span><span>=</span><span> </span><span>&quot;chaid&quot;</span><span>,</span><span>
</span><span>metric</span><span> </span><span>=</span><span> </span><span>&quot;Kappa&quot;</span><span>,</span><span>
</span><span>trControl</span><span> </span><span>=</span><span> </span><span>train_control</span><span>
</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## + Fold01: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold01: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold01: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold01: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold01: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold01: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold02: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold02: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold02: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold02: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold02: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold02: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold03: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold03: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold03: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold03: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold03: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold03: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold04: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold04: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold04: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold04: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold04: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold04: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold05: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold05: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold05: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold05: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold05: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold05: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold06: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold06: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold06: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold06: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold06: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold06: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold07: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold07: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold07: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold07: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold07: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold07: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold08: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold08: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold08: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold08: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold08: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold08: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold09: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold09: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold09: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold09: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold09: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold09: alpha2=0.01, alpha3=-1, alpha4=0.01 
## + Fold10: alpha2=0.05, alpha3=-1, alpha4=0.05 
## - Fold10: alpha2=0.05, alpha3=-1, alpha4=0.05 
## + Fold10: alpha2=0.03, alpha3=-1, alpha4=0.03 
## - Fold10: alpha2=0.03, alpha3=-1, alpha4=0.03 
## + Fold10: alpha2=0.01, alpha3=-1, alpha4=0.01 
## - Fold10: alpha2=0.01, alpha3=-1, alpha4=0.01 
## Aggregating results
## Selecting tuning parameters
## Fitting alpha2 = 0.05, alpha3 = -1, alpha4 = 0.05 on full training set
</code></pre>
</div>
</div>
<p>And…. we’re done. Turns out in this case the best solution was what<br>
<code>chaid</code> uses as defaults. The very last line of the output tells us<br>
that. But let’s use what we have used in the past for printing and<br>
plotting the results…</p>
<div>
<div>
<pre><code><span>chaid.m1</span><span> </span><span>#equivalent to print(chaid.m1)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## CHi-squared Automated Interaction Detection 
## 
## 1030 samples
##   30 predictor
##    2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 928, 927, 927, 926, 928, 926, ... 
## Resampling results across tuning parameters:
## 
##   alpha2  alpha4  Accuracy   Kappa    
##   0.01    0.01    0.8223292  0.1522392
##   0.03    0.03    0.8349699  0.1579585
##   0.05    0.05    0.8213958  0.1692826
## 
## Tuning parameter 'alpha3' was held constant at a value of -1
## Kappa was used to select the optimal model using the largest value.
## The final values used for the model were alpha2 = 0.05, alpha3 = -1
##  and alpha4 = 0.05.
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>plot</span><span>(</span><span>chaid.m1</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p><img alt="" src="https://i1.wp.com/ibecav.github.io/images/chaid27-1.png?w=456" data-recalc-dims="1"></p>
<p>Wait. What? These are not the output we’re used to. <code>caret</code> has changed<br>
the output from its’ work (an improvement actually) but we’ll have to<br>
change how we get the information out. Before we do that however, let’s<br>
inspect what we have so far. The output gives us a nice concise summary.<br>
1030 cases with 30 predictors. It gives us an idea of how many of the<br>
1030 cases were used in the individual folds <code>Summary of sample<br>
sizes: 928, 927, 927, 926, 928, 926, ...</code>.</p>
<p>The bit about <code>alpha2</code>, <code>alpha4</code>, and <code>alpha3</code> is somewhat mysterious.<br>
We saw those names when we looked at the <code>chaid_control</code> documentation<br>
last post but why are they here? We’ll come back to that in a moment.<br>
But it is clear that it thought Kappa of <code>0.1692826</code> was best.</p>
<p>The plot isn’t what we’re used to seeing, but is easy to understand.<br>
<code>Kappa</code> is on the y axis, <code>alpha2</code> on the x axis and it’s shaded/colored<br>
by <code>alpha4</code> (remember we left <code>alpha3</code> out of the mix). The plot is a<br>
bit of overkill for what we did but we’ll put it to better use later.</p>
<p>But what about the things we were used to seeing? Well if you remember<br>
that <code>caret</code> is reporting averages of all the folds it sort of makes<br>
sense that the <strong>best</strong> final model results are now in<br>
<code>chaid.m1$finalModel</code> so we need to use that when we <code>print</code> or <code>plot</code>.<br>
So in the next block of code let’s:</p>
<ol>
<li>Print the final model from <code>chaid</code> (<code>chaid.m1$finalModel</code>)</li>
<li>Plot the final model from <code>chaid</code> (<code>plot(chaid.m1$finalModel)</code>)</li>
<li>Produce the <code>confusionMatrix</code> across all folds<br>
(<code>confusionMatrix(chaid.m1)</code>)</li>
<li>Produce the <code>confusionMatrix</code> using the final model<br>
(<code>confusionMatrix(predict(chaid.m1), y)</code>)</li>
<li>Check on variable importance (<code>varImp(chaid.m1)</code>)</li>
<li>The best tuning parameters are stored in <code>chaid.m1$bestTune</code></li>
<li>How long did it take? Look in <code>chaid.m1$times</code></li>
<li>In case you forgot what method you used look here <code>chaid.m1$method</code></li>
<li>We’ll look at model info in a bit <code>chaid.m1$modelInfo</code></li>
<li>The summarized results are here in a nice format if needed later<br>
<code>chaid.m1$results</code></li>
</ol>
<p>Many of these you’ll never need but I wanted to at least give you a hint<br>
of how complete the <code>chaid.m1</code> object is</p>
<div>
<div>
<pre><code><span>chaid.m1</span><span>$</span><span>finalModel</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## 
## Model formula:
## .outcome ~ Age + BusinessTravel + DailyRate + Department + DistanceFromHome + 
##     Education + EducationField + EnvironmentSatisfaction + Gender + 
##     HourlyRate + JobInvolvement + JobLevel + JobRole + JobSatisfaction + 
##     MaritalStatus + MonthlyIncome + MonthlyRate + NumCompaniesWorked + 
##     OverTime + PercentSalaryHike + PerformanceRating + RelationshipSatisfaction + 
##     StockOptionLevel + TotalWorkingYears + TrainingTimesLastYear + 
##     WorkLifeBalance + YearsAtCompany + YearsInCurrentRole + YearsSinceLastPromotion + 
##     YearsWithCurrManager
## 
## Fitted party:
## [1] root
## |   [2] OverTime in No
## |   |   [3] YearsAtCompany in [0,2]
## |   |   |   [4] Age in [18,29], (29,34]
## |   |   |   |   [5] StockOptionLevel in 0: No (n = 43, err = 48.8%)
## |   |   |   |   [6] StockOptionLevel in 1, 2, 3
## |   |   |   |   |   [7] RelationshipSatisfaction in Low: Yes (n = 7, err = 42.9%)
## |   |   |   |   |   [8] RelationshipSatisfaction in Medium, High, Very_High: No (n = 38, err = 7.9%)
## |   |   |   [9] Age in (34,38], (38,45], (45,60]: No (n = 77, err = 7.8%)
## |   |   [10] YearsAtCompany in (2,5], (5,7], (7,10], (10,40]
## |   |   |   [11] WorkLifeBalance in Bad: No (n = 36, err = 19.4%)
## |   |   |   [12] WorkLifeBalance in Good, Better, Best
## |   |   |   |   [13] Department in Human_Resources, Sales
## |   |   |   |   |   [14] Age in [18,29], (29,34], (34,38], (38,45]
## |   |   |   |   |   |   [15] WorkLifeBalance in Bad, Good: No (n = 37, err = 16.2%)
## |   |   |   |   |   |   [16] WorkLifeBalance in Better, Best: No (n = 119, err = 4.2%)
## |   |   |   |   |   [17] Age in (45,60]: No (n = 27, err = 25.9%)
## |   |   |   |   [18] Department in Research_Development: No (n = 347, err = 4.0%)
## |   [19] OverTime in Yes
## |   |   [20] JobLevel in 1
## |   |   |   [21] JobRole in Healthcare_Representative, Human_Resources, Laboratory_Technician, Manager, Manufacturing_Director, Research_Director, Sales_Executive, Sales_Representative
## |   |   |   |   [22] JobInvolvement in Low, Medium: Yes (n = 19, err = 10.5%)
## |   |   |   |   [23] JobInvolvement in High, Very_High: Yes (n = 45, err = 44.4%)
## |   |   |   [24] JobRole in Research_Scientist: No (n = 53, err = 35.8%)
## |   |   [25] JobLevel in 2, 3, 4, 5
## |   |   |   [26] Gender in Female: No (n = 86, err = 9.3%)
## |   |   |   [27] Gender in Male
## |   |   |   |   [28] MaritalStatus in Divorced, Married: No (n = 71, err = 18.3%)
## |   |   |   |   [29] MaritalStatus in Single: No (n = 25, err = 44.0%)
## 
## Number of inner nodes:    14
## Number of terminal nodes: 15
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>plot</span><span>(</span><span>chaid.m1</span><span>$</span><span>finalModel</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<p><img src="https://i1.wp.com/ibecav.github.io/images/chaid28-1.png?w=456" data-recalc-dims="1"></p>
<div>
<div>
<pre><code><span>confusionMatrix</span><span>(</span><span>chaid.m1</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## Cross-Validated (10 fold) Confusion Matrix 
## 
## (entries are percentual average cell counts across resamples)
##  
##           Reference
## Prediction   No  Yes
##        No  79.0 13.0
##        Yes  4.9  3.1
##                             
##  Accuracy (average) : 0.8214
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>confusionMatrix</span><span>(</span><span>predict</span><span>(</span><span>chaid.m1</span><span>),</span><span> </span><span>y</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No Yes
##        No  839 120
##        Yes  25  46
##                                           
##                Accuracy : 0.8592          
##                  95% CI : (0.8365, 0.8799)
##     No Information Rate : 0.8388          
##     P-Value [Acc > NIR] : 0.03938         
##                                           
##                   Kappa : 0.3228          
##  Mcnemar's Test P-Value : 5.89e-15        
##                                           
##             Sensitivity : 0.9711          
##             Specificity : 0.2771          
##          Pos Pred Value : 0.8749          
##          Neg Pred Value : 0.6479          
##              Prevalence : 0.8388          
##          Detection Rate : 0.8146          
##    Detection Prevalence : 0.9311          
##       Balanced Accuracy : 0.6241          
##                                           
##        'Positive' Class : No              
## 
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>varImp</span><span>(</span><span>chaid.m1</span><span>)</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## ROC curve variable importance
## 
##   only 20 most important variables shown (out of 30)
## 
##                         Importance
## OverTime                    100.00
## YearsInCurrentRole           90.81
## YearsAtCompany               90.41
## MonthlyIncome                87.08
## JobLevel                     84.36
## TotalWorkingYears            80.04
## YearsWithCurrManager         79.78
## StockOptionLevel             69.51
## MaritalStatus                65.96
## Age                          59.31
## JobSatisfaction              44.86
## JobInvolvement               44.27
## DistanceFromHome             36.80
## EnvironmentSatisfaction      32.15
## WorkLifeBalance              31.63
## DailyRate                    30.23
## JobRole                      29.94
## NumCompaniesWorked           28.67
## Department                   25.79
## HourlyRate                   19.81
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>chaid.m1</span><span>$</span><span>bestTune</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>##   alpha2 alpha3 alpha4
## 3   0.05     -1   0.05
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>chaid.m1</span><span>$</span><span>times</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## $everything
##    user  system elapsed 
## 247.218   1.581 248.999 
## 
## $final
##    user  system elapsed 
##   9.612   0.055   9.674 
## 
## $prediction
## [1] NA NA NA
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>chaid.m1</span><span>$</span><span>method</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## [1] &quot;chaid&quot;
</code></pre>
</div>
</div>
<div>
<div>
<pre><code><span>chaid.m1</span><span>$</span><span>modelInfo</span><span>
</span></code></pre>
</div>
</div>
<div>
<div>
<pre><code>## $label
## [1] &quot;CHi-squared Automated Interaction Detection&quot;
## 
## $library
## [1] &quot;CHAID&quot;
## 
## $loop
## NULL
## 
## $type
## [1] &quot;Classification&quot;
## 
## $parameters
##   parameter   class
## 1    alpha2 numeric
## 2    alpha3 numeric
## 3    alpha4 numeric
##                                                                                     label
## 1                                                                       Merging Threshold
## 2                                                       Splitting former Merged Threshold
## 3 \n                                                    Splitting former Merged Threshold
## 
## $grid
## function (x, y, len = NULL, search = &quot;grid&quot;) 
## {
##     if (search == &quot;grid&quot;) {
##         out &lt;- data.frame(alpha2 = seq(from = 0.05, to = 0.01, 
##             length = len), alpha3 = -1, alpha4 = seq(from = 0.05, 
##             to = 0.01, length = len))
##     }
##     else {
##         out &lt;- data.frame(alpha2 = runif(len, min = 1e-06, max = 0.1), 
##             alpha3 = runif(len, min = -0.1, max = 0.1), alpha4 = runif(len, 
##                 min = 1e-06, max = 0.1))
##     }
##     out
## }
## 
## $fit
## function (x, y, wts, param, lev, last, classProbs, ...) 
## {
##     dat &lt;- if (is.data.frame(x)) 
##         x
##     else as.data.frame(x)
##     dat$.outcome &lt;- y
##     theDots &lt;- list(...)
##     if (any(names(theDots) == &quot;control&quot;)) {
##         theDots$control$alpha2 &lt;- param$alpha2
##         theDots$control$alpha3 &lt;- param$alpha3
##         theDots$control$alpha4 &lt;- param$alpha4
##         ctl &lt;- theDots$control
##         theDots$control &lt;- NULL
##     }
##     else ctl &lt;- chaid_control(alpha2 = param$alpha2, alpha3 = param$alpha3, 
##         alpha4 = param$alpha4)
##     if (!is.null(wts)) 
##         theDots$weights &lt;- wts
##     modelArgs &lt;- c(list(formula = as.formula(&quot;.outcome ~ .&quot;), 
##         data = dat, control = ctl), theDots)
##     out &lt;- do.call(CHAID::chaid, modelArgs)
##     out
## }
## </code></pre></div></div>