<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Machine Learning on R Views</title>
    <link>https://rviews.rstudio.com/tags/machine-learning/</link>
    <description>Recent content in Machine Learning on R Views</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 19 Apr 2022 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://rviews.rstudio.com/tags/machine-learning/" rel="self" type="application/rss+xml" />
    
    
    
    
    <item>
      <title>MLDataR - Real-world Datasets for Machine Learning Applications</title>
      <link>https://rviews.rstudio.com/2022/04/19/mldatar-real-world-datasets-for-machine-learning-applications/</link>
      <pubDate>Tue, 19 Apr 2022 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2022/04/19/mldatar-real-world-datasets-for-machine-learning-applications/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;This is a guest post from Gary Hutson, lead of Machine Learning at Crisp Thinking, a company that provides AI solutions to moderate and detect offensive and abusive content online. His website is available at &lt;a href=&#34;https://hutsons-hacks.info/&#34;&gt;https://hutsons-hacks.info/&lt;/a&gt; and he can be reached through Twitter, &lt;a href=&#34;https://twitter.com/StatsGary&#34;&gt;@StatsGary&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;div id=&#34;mldatar-package-motivation&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;MLDataR package motivation&lt;/h2&gt;
&lt;p&gt;I love all things Machine Learning. The &lt;a href=&#34;https://github.com/StatsGary/MLDataR&#34;&gt;MLDataR&lt;/a&gt; package was driven by the need to have example datasets across the healthcare system for machine learning problems. I have been a machine learning practitioner for over nine years; however, I still find it interesting to explore new examples and datasets related to supervised machine learning classification and regression.&lt;/p&gt;
&lt;p&gt;Because the package contains clinical examples and examples from real hospital systems, it allows the potential machine learning engineer to practice all things related to supervised machine learning.&lt;/p&gt;
&lt;p&gt;Despite the package initially being aimed at healthcare, I have expanded it to new territories and domains.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;what-does-the-package-contain&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;What does the package contain?&lt;/h2&gt;
&lt;p&gt;The package contains several datasets for modelling. This is just a start and I am working with the &lt;a href=&#34;https://nhsrcommunity.com/&#34;&gt;NHS-R community&lt;/a&gt; to build it out even further. It is a sort of a call to arms to equip the package with even more examples of excellent datasets that can be used for machine learning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Diabetes disease prediction&lt;/strong&gt; - This dataset contains key variables, gathered from hospital research and papers in the British Medical Journal to identify the drivers behind diabetes disease. This dataset is useful for working with supervised classification machine learning problems or statistical problems. It uses past historical patient information to train and classify a model, with the aim to classify if a patient will have diabetes when they first present to the service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Diabetes early onset&lt;/strong&gt; - Gathered by Asif Laldin, the package contributor from Gloucestershire Clinical Commissioning Group, this dataset contains information on the time between a prediabetes diagnosis and the onset of diabetes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Failing care home prediction&lt;/strong&gt; - Using measures from the NHS incident reporting databases, this dataset contains data to classify if a care home will fail based on results from retrospective inspections.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Heart disease prediction&lt;/strong&gt; - This dataset is intended for supervised machine learning classification problems on which patients are likely to present with heart disease. This uses independent variables, such as resting blood pressure, maximum heart rate, history of angina, and other metrics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thyroid disease classification&lt;/strong&gt; - This one has a personal effect on me, as I am a sufferer of this disease. This drove me to source this dataset from the Garavan Institute, based on a collection of studies this institute did around thyroid disease. This contains 28 independent or predictor variables of patients with or without the disease. It is also covered in the &lt;a href=&#34;https://cran.r-project.org/web/packages/MLDataR/vignettes/MLDataR.html&#34;&gt;vignette supporting this package&lt;/a&gt; and the supporting YouTube tutorial using tidymodels with various ML techniques.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Counter Strike Global Offensive (CSGO)&lt;/strong&gt; - This dataset was kindly contributed by Asif Laldin, and is a detraction from the healthcare datasets in the package. I never intended the package to be purely healthcare ML datasets, and I plan to include credit card fraud examples, tabular playground examples from Kaggle, and many more, so watch this space…&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;run-a-tidymodels-routine-with-heart-disease-dataset&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Run a tidymodels routine with heart disease dataset&lt;/h2&gt;
&lt;p&gt;Let’s explore the heart disease dataset contained in MLDataR using a logistic regression model.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# install.packages(&amp;quot;MLDataR&amp;quot;)
library(MLDataR)
library(dplyr)
library(tidyr)
library(tidymodels)
library(data.table)
library(ConfusionTableR)
library(OddsPlotty)
glimpse(MLDataR::heartdisease)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Rows: 918
## Columns: 10
## $ Age              &amp;lt;dbl&amp;gt; 40, 49, 37, 48, 54, 39, 45, 54, 37, 48, 37, 58, 39, 4…
## $ Sex              &amp;lt;chr&amp;gt; &amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;M&amp;quot;, &amp;quot;F&amp;quot;, &amp;quot;F&amp;quot;…
## $ RestingBP        &amp;lt;dbl&amp;gt; 140, 160, 130, 138, 150, 120, 130, 110, 140, 120, 130…
## $ Cholesterol      &amp;lt;dbl&amp;gt; 289, 180, 283, 214, 195, 339, 237, 208, 207, 284, 211…
## $ FastingBS        &amp;lt;dbl&amp;gt; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ RestingECG       &amp;lt;chr&amp;gt; &amp;quot;Normal&amp;quot;, &amp;quot;Normal&amp;quot;, &amp;quot;ST&amp;quot;, &amp;quot;Normal&amp;quot;, &amp;quot;Normal&amp;quot;, &amp;quot;Normal…
## $ MaxHR            &amp;lt;dbl&amp;gt; 172, 156, 98, 108, 122, 170, 170, 142, 130, 120, 142,…
## $ Angina           &amp;lt;chr&amp;gt; &amp;quot;N&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;Y&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;Y&amp;quot;, &amp;quot;N&amp;quot;, &amp;quot;N&amp;quot;…
## $ HeartPeakReading &amp;lt;dbl&amp;gt; 0.0, 1.0, 0.0, 1.5, 0.0, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0…
## $ HeartDisease     &amp;lt;dbl&amp;gt; 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,…&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A glimpse into the dataset gives us a quick overview of our dataset. We have 918 rows and 10 columns. We can see our outcome, heart disease, and our nine predictors.&lt;/p&gt;
&lt;p&gt;There are a couple of things we want to clean up. Notice that our outcome variable &lt;code&gt;HeartDisease&lt;/code&gt; loads as a double variable. We want to convert it into a factor variable for our machine learning model.&lt;/p&gt;
&lt;p&gt;The variables &lt;code&gt;Sex&lt;/code&gt;, &lt;code&gt;RestingECG&lt;/code&gt;, and &lt;code&gt;AnginaY&lt;/code&gt; are character variables. For creating models, it is better to encode characters as factors.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;hd &amp;lt;- heartdisease %&amp;gt;%
  mutate(across(where(is.character), as.factor),
         HeartDisease = as.factor(HeartDisease)) %&amp;gt;% 
  # Remove any non complete cases
  na.omit()
is.factor(hd$HeartDisease)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] TRUE&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Machine Learning models, it is generally recommended to split your data into a training and testing set, or if you are using hyperparameter tuning and updating your model, a training / test and validation set. Other methods are available, such a K-Fold Cross Validation; however, we will stick to a basic training and testing split for the purposes of this walkthrough.&lt;/p&gt;
&lt;p&gt;To do this, and to make sure that the results are repeatable, we will use the &lt;code&gt;set.seed(123)&lt;/code&gt; value - which essentially says when we are randomly splitting this data, make sure that the random pattern is the same as the walkthrough., i.e. give me the same split as this post:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(123)
split_prop &amp;lt;- 0.8
testing_prop &amp;lt;- 1 - split_prop
split &amp;lt;- rsample::initial_split(hd, prop = split_prop)
training &amp;lt;- rsample::training(split)
testing &amp;lt;- rsample::testing(split)
# Print a custom message to show the samples involved
training_message &amp;lt;- function() {
  message(
    cat(
      &amp;#39;The training set has: &amp;#39;,
      nrow(training),
      &amp;#39; examples and the testing set has:&amp;#39;,
      nrow(testing),
      &amp;#39;.\nThis split has &amp;#39;,
      paste0(format(100 * split_prop), &amp;#39;%&amp;#39;),
      &amp;#39; in the training set and &amp;#39;,
      paste0(format(100 * testing_prop), &amp;#39;%&amp;#39;),
      &amp;#39; in the testing set.&amp;#39;,
      sep = &amp;#39;&amp;#39;
    )
  )
}
training_message()&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## The training set has: 734 examples and the testing set has:184.
## This split has 80% in the training set and 20% in the testing set.&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can fit a &lt;code&gt;parsnip&lt;/code&gt; model to the training set and then we can evaluate the performance.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;lr_hd_fit &amp;lt;- logistic_reg() %&amp;gt;%
  set_engine(&amp;quot;glm&amp;quot;) %&amp;gt;% 
  set_mode(&amp;quot;classification&amp;quot;) %&amp;gt;% 
  fit(HeartDisease ~ ., data = training)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we want to see the summary results of our model in a tidy way (i.e., a data frame with standard column names), we can use the &lt;code&gt;tidy()&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tidy(lr_hd_fit)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## # A tibble: 11 × 5
##    term             estimate std.error statistic  p.value
##    &amp;lt;chr&amp;gt;               &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;    &amp;lt;dbl&amp;gt;
##  1 (Intercept)       0.925     1.29        0.718 4.73e- 1
##  2 Age               0.0175    0.0123      1.42  1.55e- 1
##  3 SexM              1.17      0.241       4.84  1.29e- 6
##  4 RestingBP        -0.00164   0.00544    -0.301 7.63e- 1
##  5 Cholesterol      -0.00335   0.00104    -3.23  1.25e- 3
##  6 FastingBS         0.944     0.248       3.81  1.39e- 4
##  7 RestingECGNormal -0.291     0.261      -1.12  2.64e- 1
##  8 RestingECGST     -0.383     0.343      -1.12  2.64e- 1
##  9 MaxHR            -0.0192    0.00450    -4.28  1.88e- 5
## 10 AnginaY           1.54      0.229       6.72  1.78e-11
## 11 HeartPeakReading  0.689     0.113       6.12  9.54e-10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see the statistically significant ones by pulling out those with p &amp;lt; 0.05: Male (SexM), cholesterol (Cholesterol), fasting blood sugar (FastingBS), maximum heart rate (MaxHR), having angina (AnginaY), and peak heart rate reading (HeartPeakReading).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tidy(lr_hd_fit) %&amp;gt;% 
  filter(p.value &amp;lt; 0.05) %&amp;gt;% 
  pull(term)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [1] &amp;quot;SexM&amp;quot;             &amp;quot;Cholesterol&amp;quot;      &amp;quot;FastingBS&amp;quot;        &amp;quot;MaxHR&amp;quot;           
## [5] &amp;quot;AnginaY&amp;quot;          &amp;quot;HeartPeakReading&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s convert the probabilities from the fitted GLM model into odds ratios (ORs). An OR measures the association between an exposure and an outcome.&lt;a href=&#34;#fn1&#34; class=&#34;footnote-ref&#34; id=&#34;fnref1&#34;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; In this case, the OR represents the odds of heart disease will occur given a particular condition, compared to the odds of heart disease occurring in the absence of that condition.&lt;/p&gt;
&lt;p&gt;We can visualize the results using the &lt;a href=&#34;https://cran.r-project.org/web/packages/OddsPlotty/index.html&#34;&gt;OddsPlotty&lt;/a&gt; package and the &lt;code&gt;fit&lt;/code&gt; list object from the model.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tidy_oddsplot &amp;lt;- OddsPlotty::odds_plot(
  lr_hd_fit$fit,
  title = &amp;quot;Heart Disease Odds Plot&amp;quot;,
  point_col = &amp;quot;#6b95ff&amp;quot;,
  h_line_color = &amp;quot;red&amp;quot;
)
tidy_oddsplot &amp;lt;- tidy_oddsplot$odds_plot +
  theme(legend.position = &amp;quot;none&amp;quot;) +
  geom_text(
    label = round(tidy_oddsplot$odds_plot$data$OR, digits = 3),
    hjust = -0.5,
    vjust = 1,
    cex = 2.8
  )
tidy_oddsplot&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/2022/04/19/mldatar-real-world-datasets-for-machine-learning-applications/index_files/figure-html/unnamed-chunk-6-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We can also pull out the odds ratio data:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;tidy_oddsplot$data&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##                      OR  lower  upper             vars
## Age              1.0176 0.9935 1.0426              Age
## SexM             3.2156 2.0172 5.2026             SexM
## RestingBP        0.9984 0.9877 1.0090        RestingBP
## Cholesterol      0.9967 0.9946 0.9987      Cholesterol
## FastingBS        2.5710 1.5912 4.2114        FastingBS
## RestingECGNormal 0.7472 0.4471 1.2452 RestingECGNormal
## RestingECGST     0.6817 0.3474 1.3348     RestingECGST
## MaxHR            0.9809 0.9722 0.9895            MaxHR
## AnginaY          4.6768 2.9989 7.3842          AnginaY
## HeartPeakReading 1.9914 1.6056 2.4982 HeartPeakReading&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, odds ratios are used to compare the relative odds of the occurrence of heart disease, given exposure to the variable of interest. Looking at the results from &lt;code&gt;odds_plot()&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Having angina (AnginaY), peak heart rate reading (HeartPeakReading), Male (SexM), and fasting blood sugar (FastingBS) have an OR of greater than 1, implying that these conditions are associated with higher odds of heart disease. For example, people with Angina are 4.677 times more likely to get heart disease than those without this condition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Normal resting electrocardiogram (RestingECGNormal) and normal ST segment of an electrocardiogram (RestingECGST) have an OR of less than 1, implying that these conditions are associated with lower odds of heart disease, however due to the length of the error bars these conditions do not have as significant effect on heart disease, as those outlined with odds greater than 1.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note the confidence intervals, indicating the precision of the OR. The large CIs around RestingECGNormal and RestingECGST indicate a low level of precision of the OR. The small CIs around Age, RestingBP, Cholesterol, and MaxHRindicate a higher precision of the ORs. This is normally due to the representation of the encoded items within the model, i.e. the presence of an effect code.&lt;/p&gt;
&lt;p&gt;I evaluate the outputs of logistic regression models by using the sampling probability values that the variables have been selected by chance (p-values) for the cut off, but then use the odds ratios of the effect of the predictor variable on my outcome, using the odds plots to make the final decision.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;evaluating-with-testing-set&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Evaluating with testing set&lt;/h2&gt;
&lt;p&gt;The final way we can evaluate how well our model fits, before pushing this into production, would be to use a confusion matrix to collect how well our testing partition performs, against our training model predictions. Here we are trying to get a sense of, if we pushed this into the wild, how well would it do on unseen observations i.e. those new items that we don’t have a label for, a label in this sense is whether someone has had heart disease, or not.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(ConfusionTableR)
# Use our model to predict labels on to testing set
predictions &amp;lt;- cbind(predict(lr_hd_fit, new_data = testing),
                     testing)
# Create confusion matrix and output to record level for storage to monitor concept drift
cm &amp;lt;- ConfusionTableR::binary_class_cm(
  predictions$.pred_class,
  predictions$HeartDisease,
  mode = &amp;#39;everything&amp;#39;,
  positive = &amp;#39;1&amp;#39;
)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [INFO] Building a record level confusion matrix to store in dataset&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## [INFO] Build finished and to expose record level cm use the record_level_cm list item&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to expose the confusion matrix to view how well our model did on estimating the labels on the testing set:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# Access the confusion matrix list object
cm$confusion_matrix&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 70  6
##          1 16 92
##                                         
##                Accuracy : 0.88          
##                  95% CI : (0.825, 0.924)
##     No Information Rate : 0.533         
##     P-Value [Acc &amp;gt; NIR] : &amp;lt;2e-16        
##                                         
##                   Kappa : 0.758         
##                                         
##  Mcnemar&amp;#39;s Test P-Value : 0.055         
##                                         
##             Sensitivity : 0.939         
##             Specificity : 0.814         
##          Pos Pred Value : 0.852         
##          Neg Pred Value : 0.921         
##               Precision : 0.852         
##                  Recall : 0.939         
##                      F1 : 0.893         
##              Prevalence : 0.533         
##          Detection Rate : 0.500         
##    Detection Prevalence : 0.587         
##       Balanced Accuracy : 0.876         
##                                         
##        &amp;#39;Positive&amp;#39; Class : 1             
## &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our model did relatively well. Picking apart some of the metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;True positives (&lt;code&gt;TP&lt;/code&gt;) we have 92 correctly classified instances of heart failure&lt;/li&gt;
&lt;li&gt;True negatives(&lt;code&gt;TN&lt;/code&gt;) we have 70 cases classified as not having heart disease&lt;/li&gt;
&lt;li&gt;False negatives (&lt;code&gt;FN&lt;/code&gt;) we have 6 cases were our model said the patient didn’t have heart disease and they did&lt;/li&gt;
&lt;li&gt;False positives (&lt;code&gt;FP&lt;/code&gt;) we have 16 cases were our model said a patient did have heart disease, but they actually didn’t&lt;/li&gt;
&lt;li&gt;Recall (also called Sensitivity) is 0.9388 meaning from all the patients that had heart disease - how many did we predict correctly - this equation is &lt;code&gt;Recall=TP / (TP + FN)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Precision (also called Positive Predictive Value) is 0.8519 meaning from all the classes we predicted as positive, how many were actually positive. The equation here is &lt;code&gt;Precision=TP/(TP + FP)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I run many ML experiments, I wanted a way to store this data into a record-level extract. I actually stored this in a model metrics table on Postgres SQL database, but could be stored in any proprietary system. To get these metrics easily in record level, the package I created helps you do that with ease:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;# As we used the binary record level cm method, this stores the model data down
record_level_cm &amp;lt;- cm$record_level_cm %&amp;gt;%
  dplyr::mutate(user_name = Sys.getenv(&amp;quot;USERNAME&amp;quot;))
glimpse(record_level_cm)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## Rows: 1
## Columns: 24
## $ Pred_0_Ref_0         &amp;lt;int&amp;gt; 70
## $ Pred_1_Ref_0         &amp;lt;int&amp;gt; 16
## $ Pred_0_Ref_1         &amp;lt;int&amp;gt; 6
## $ Pred_1_Ref_1         &amp;lt;int&amp;gt; 92
## $ Accuracy             &amp;lt;dbl&amp;gt; 0.8804
## $ Kappa                &amp;lt;dbl&amp;gt; 0.7581
## $ AccuracyLower        &amp;lt;dbl&amp;gt; 0.8246
## $ AccuracyUpper        &amp;lt;dbl&amp;gt; 0.9235
## $ AccuracyNull         &amp;lt;dbl&amp;gt; 0.5326
## $ AccuracyPValue       &amp;lt;dbl&amp;gt; 4.902e-24
## $ McnemarPValue        &amp;lt;dbl&amp;gt; 0.05501
## $ Sensitivity          &amp;lt;dbl&amp;gt; 0.9388
## $ Specificity          &amp;lt;dbl&amp;gt; 0.814
## $ Pos.Pred.Value       &amp;lt;dbl&amp;gt; 0.8519
## $ Neg.Pred.Value       &amp;lt;dbl&amp;gt; 0.9211
## $ Precision            &amp;lt;dbl&amp;gt; 0.8519
## $ Recall               &amp;lt;dbl&amp;gt; 0.9388
## $ F1                   &amp;lt;dbl&amp;gt; 0.8932
## $ Prevalence           &amp;lt;dbl&amp;gt; 0.5326
## $ Detection.Rate       &amp;lt;dbl&amp;gt; 0.5
## $ Detection.Prevalence &amp;lt;dbl&amp;gt; 0.587
## $ Balanced.Accuracy    &amp;lt;dbl&amp;gt; 0.8764
## $ cm_ts                &amp;lt;dttm&amp;gt; 2022-04-19 11:18:51
## $ user_name            &amp;lt;chr&amp;gt; &amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;#Export to csv
data.table::fwrite(record_level_cm, file = &amp;#39;heart_disease_cm_record_level.csv&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you were happy with your model now, you could put this into production.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;can-i-contribute-my-own-dataset-to-mldatar&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Can I contribute my own dataset to MLDataR?&lt;/h2&gt;
&lt;p&gt;The answer is you can, and I would greatly encourage it. To boot, you will become a package contributor. I am looking for ML datasets from across a wide range of industries and organisations.&lt;/p&gt;
&lt;p&gt;Suitable datasets for machine learning applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have sufficient predictive variables for feature engineering&lt;/li&gt;
&lt;li&gt;Have a nominal outcome variable&lt;/li&gt;
&lt;li&gt;May have missing values&lt;/li&gt;
&lt;li&gt;Consist of interesting features&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have an idea, please submit a pull request to the &lt;a href=&#34;https://github.com/StatsGary/MLDataR&#34;&gt;GitHub repository&lt;/a&gt; and add your dataset.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;final-thoughts&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;I have really enjoyed putting this package together and I hope you can use it to:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learn tidymodels or caret.&lt;/strong&gt; I have put together a few tutorials on these in the past:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Building a tidymodels classification model from scratch: &lt;a href=&#34;https://www.youtube.com/watch?v=hxRx7ozLNKw&amp;amp;t=2583s&#34; class=&#34;uri&#34;&gt;https://www.youtube.com/watch?v=hxRx7ozLNKw&amp;amp;t=2583s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Advanced modelling with caret for supervised machine learning: &lt;a href=&#34;https://www.youtube.com/watch?v=rO40vvKXU-4&amp;amp;t=3085s&#34; class=&#34;uri&#34;&gt;https://www.youtube.com/watch?v=rO40vvKXU-4&amp;amp;t=3085s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Reticulate - R and Python a happy union: &lt;a href=&#34;https://www.youtube.com/watch?v=8WE-EU5k97Q&amp;amp;t=235s&#34; class=&#34;uri&#34;&gt;https://www.youtube.com/watch?v=8WE-EU5k97Q&amp;amp;t=235s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Collapsing a caret confusion matrix with ConfusionTableR: &lt;a href=&#34;https://youtu.be/9zcUlgLySZo&#34; class=&#34;uri&#34;&gt;https://youtu.be/9zcUlgLySZo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Put your models into production:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploying a caret machine learning model as an API with Plumber: &lt;a href=&#34;https://youtu.be/WMCkV_J5a0s&#34; class=&#34;uri&#34;&gt;https://youtu.be/WMCkV_J5a0s&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Creating a microservice with Docker and serving as a restful API: &lt;a href=&#34;https://youtu.be/JK6VLAKRjO4&#34; class=&#34;uri&#34;&gt;https://youtu.be/JK6VLAKRjO4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&#34;footnotes footnotes-end-of-document&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn1&#34;&gt;&lt;p&gt;Szumilas M. (2010). Explaining odds ratios. Journal of the Canadian Academy of Child and Adolescent Psychiatry = Journal de l’Academie canadienne de psychiatrie de l’enfant et de l’adolescent, 19(3), 227–229.&lt;a href=&#34;#fnref1&#34; class=&#34;footnote-back&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2022/04/19/mldatar-real-world-datasets-for-machine-learning-applications/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>A Guide to Binge Watching R / Medicine 2021</title>
      <link>https://rviews.rstudio.com/2021/09/09/a-guide-to-binge-watching-r-medicine/</link>
      <pubDate>Thu, 09 Sep 2021 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2021/09/09/a-guide-to-binge-watching-r-medicine/</guid>
      <description>
        

&lt;p&gt;&lt;a href=&#34;https://r-medicine.org/&#34;&gt;R / Medicine&lt;/a&gt; is a big deal. This year, the conference grew by 13% with 665 people from over 60 countries signing up for the virtual event which was held last month. 34% percent of the registrants were from outside of the United States and 17% identified as physicians.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;rmed.png&#34; height = &#34;300&#34; width=&#34;500&#34; alt=&#34;Global map with locations of R Medicine registrants indicated&#34;&gt;&lt;/p&gt;

&lt;p&gt;The conference is now an established international event where experts report on the advanced use of the R language, Machine Learning, and statistical analysis, and discuss the successes and challenges associated with bringing these technologies to day-to-day medical practice.&lt;/p&gt;

&lt;p&gt;Almost all of the talks, including keynotes, regular talks, lightning talks, pre-conference workshops and poster sessions are available online. &lt;a href=&#34;https://r-medicine.org/schedule/&#34;&gt;Find the links&lt;/a&gt; on the R / Medicine site or look through the &lt;a href=&#34;https://www.youtube.com/playlist?list=PL4IzsxWztPdmHxCpS_c2l_jbMfrywWciZ&#34;&gt;playlist &lt;/a&gt; on the &lt;a href=&#34;https://www.r-consortium.org/&#34;&gt;R Consortium Youtube&lt;/a&gt; Channel. Note that the posters can be viewed by going to the &lt;a href=&#34;https://spatial.chat/s/R-Medicine2021?room=231308&#34;&gt;conference spatial.chat site&lt;/a&gt;. (If you and a friend visit at the same time you should be able to &amp;ldquo;walk around&amp;rdquo; the posters and chat about what you see.)&lt;/p&gt;

&lt;p&gt;To kick off an evening of binge watching the conference I would begin with the keynotes.&lt;/p&gt;

&lt;h3 id=&#34;the-keynotes&#34;&gt;The Keynotes&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://medicine.umich.edu/dept/lhs/karandeep-singh-md-mmsc&#34;&gt;Dr. Karandeep Singh&lt;/a&gt; sets the hook for his talk, &lt;a href=&#34;https://www.youtube.com/watch?v=l71wLKUr26E&amp;amp;list=PL4IzsxWztPdmHxCpS_c2l_jbMfrywWciZ&amp;amp;index=7&#34;&gt;Bringing Machine Learning Models to the Bedside at Scale&lt;/a&gt;, two minutes into the video when he asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who are the twenty sickest patients in the hospital right now who are not in the ICU?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This straightforward question immediately gets to the promise and the problems of introducing large scale machine learning algorithms into the hospital, and indicates how medical practice interacts with big money questions about allocating resources. Both physicians and administrators would like to identify high risk patients and treat them proactively while being able to confidently spend less on unnecessary test for low risk patients. About (5:10) into the talk, Karandeep begins discussing the challenges associated with introducing machine learning models.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;chal.png&#34; height = &#34;300&#34; width=&#34;500&#34; alt=&#34;Slide with list of challenges discussed. Is there infrastructure to support models? Should we implement a model? Once implemented, how do we measure model performance? Is a model “good enough” to use? Do users agree on how to use the model? Is the model effective when used? What does governance look like for machine learning models?&#34;&gt;&lt;/p&gt;

&lt;p&gt;In the remainder of the talk he describes the technical infrastructure and then the governance or &amp;ldquo;social infrastructure&amp;rdquo; needed for success.&lt;/p&gt;

&lt;p&gt;If you enjoy a good detective story, and take pride in your ability to interpret a well-done statistical plot you are certainly going to want to watch &lt;a href=&#34;http://ziadobermeyer.com/&#34;&gt;Ziad Obermeyer&amp;rsquo;s&lt;/a&gt; keynote  &lt;a href=&#34;https://www.youtube.com/watch?v=JfKYO1W4uuA&amp;amp;list=PL4IzsxWztPdmHxCpS_c2l_jbMfrywWciZ&amp;amp;index=27&#34;&gt;Dissecting Algorithmic Bias&lt;/a&gt;. About two minutes into the video Professor Obermeyer sets the stage with the warning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The single greatest threat to all of the gains that we can make in using algorithms in medicine is letting them go wrong in increasingly well known ways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and the observation that due to the focus of the US health care management on &amp;ldquo;high risk care management&amp;rdquo; an estimated 150 to 200 million Americans are sorted by algorithms every year. He goes on to work through a case study that illustrates how an algorithm built with good intentions had the effect of scaling up racial bias.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;bias.png&#34; height = &#34;300&#34; width=&#34;600&#34; alt=&#34;Dot plot with regression line of algorithm risk score versus realized cost to show the racial bias in high risk care management&#34;&gt;&lt;/p&gt;

&lt;p&gt;A second case study features an algorithm that &amp;ldquo;fights against&amp;rdquo; racial bias. Along the way, Ziad weaves two common themes into his presentation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;So many of the ways that algorithms can go wrong come from training algorithms with the wrong target variables, often &amp;ldquo;convenient and tempting proxies&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;The necessity of follow-up work to fix underlying problems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the remainder of this post, I have organized the talks into six categories that you may find helpful for setting your viewing program: Clinical Practice, Clinical Trials, Medical Data, R in Production, R Tools, and Short Courses. The majority of the talks have a machine learning angle. There is quite a bit of Shiny and several R packages, not all of them on CRAN, are featured. I have provided links when I could find them. I don&amp;rsquo;t want to spoil anyone&amp;rsquo;s fun in searching through the videos for &amp;ldquo;Easter Eggs&amp;rdquo;, but the &lt;em&gt;Reproducible Research with R&lt;/em&gt; short course contains the first preview on the &lt;a href=&#34;https://quarto.org/&#34;&gt;Quarto&lt;/a&gt; Publishing system in a talk from anyone at RStudio. (Note that the video needs some editing. Start watching at 9 minutes.)&lt;/p&gt;

&lt;h3 id=&#34;clinical-practice&#34;&gt;Clinical Practice&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Building an Interpretable ML Model API for Interpretation of CNVs in Patients with Rare Diseases -    Francisco Requena&lt;/li&gt;
&lt;li&gt;Subgroup Identification and Precision Medicine with the personalized R Package -  Jared Huling&lt;/li&gt;
&lt;li&gt;R and Shiny Dashboards to Facilitate Quality Improvement in Anesthesiology and Periopeartive Care -   Robert Lobato&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tidytof&lt;/code&gt;: Predicting Patient Outcomes from Single-cell Data using Tidy Data Principles   - Timothy Keyes&lt;/li&gt;
&lt;li&gt;Assessing ML Model Performance in DIverse Populations and Across Time - Victor Castro, Roy Perlis&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;clinical-trials&#34;&gt;Clinical Trials&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Designing Early Phase Clinical Trials with &lt;a href=&#34;https://github.com/zabore/ppseq&#34;&gt;&lt;code&gt;ppseq&lt;/code&gt;&lt;/a&gt; -   Emily Zabor&lt;/li&gt;
&lt;li&gt;Collaborative, Reproducible Exploration of Clinical Trial Data -  Michael Kane&lt;/li&gt;
&lt;li&gt;Graphical Displays in R for Clinical Trials - Steven Schwager&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.gitmemory.com/presagia-analytics/ctrialsgov&#34;&gt;&lt;code&gt;ctrialsgov&lt;/code&gt;&lt;/a&gt;: Access, Visualization, and Discovery of the ClinicalTrials.gov Database - Taylor Arnold&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;medical-data&#34;&gt;Medical Data&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Scaling Up and Deploying Shiny and Text Mining for National Health Decisions - Andreas Soteriade, Chris Beeley&lt;/li&gt;
&lt;li&gt;Mapping African Health Data with &lt;a href=&#34;https://afrimapr.github.io/afrimapr.website/&#34;&gt;&lt;code&gt;afrimapr&lt;/code&gt;&lt;/a&gt; Package, Training &amp;amp; Community -   Andy South&lt;/li&gt;
&lt;li&gt;You R What You Measure: Digital Biomarkers for Insights in Personalized Health - Irene van den Broek&lt;/li&gt;
&lt;li&gt;Shiny and REDCap for a Global Research Consortium - Judith Lewis, Stephany Duda&lt;/li&gt;
&lt;li&gt;Diving into Registry Data: Using R for Large Norwegian Health Registries -    Julia Romanowska&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cran.r-project.org/web/packages/ReviewR/index.html&#34;&gt;&lt;code&gt;ReviewR&lt;/code&gt;&lt;/a&gt;: A Shiny App for Reviewing Clinical Records   - Laura Wiley,  David Mayer&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cran.r-project.org/package=DOPE&#34;&gt;&lt;code&gt;DOPE&lt;/code&gt;&lt;/a&gt;: An R package for Processing and Classifying Drug Names -   Layla Bouzoubaa&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cran.r-project.org/package=medicaldata&#34;&gt;&lt;code&gt;medicaldata&lt;/code&gt;&lt;/a&gt; for Teaching #Rstats -    Peter Higgins&lt;/li&gt;
&lt;li&gt;Stem Cell Transplant Outcomes Reporting using R/Shiny -   Richard Hanna,  Stephan Kadauke&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;r-in-production&#34;&gt;R in Production&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Second Server to the Right and Straight On &amp;lsquo;til Production: Deploying a GxP Shiny Application -   Marcus Adams&lt;/li&gt;
&lt;li&gt;Target Markdown and &lt;a href=&#34;https://docs.ropensci.org/stantargets/&#34;&gt;&lt;code&gt;stantargets&lt;/code&gt;&lt;/a&gt; for Bayesian model validation pipelines - Will Landau&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.themillerlab.io/publication/genetex/&#34;&gt;&lt;code&gt;GENETEX&lt;/code&gt;&lt;/a&gt;: A Genomics Report Text Mining R Package to Capture Real-world Clinico-genomic Data - David Miller, Sophia Shalhout&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;r-tools&#34;&gt;R Tools&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generalized Additive Models for Longitudinal Biomedical Data  -   Ariel Mundo&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Multistate Data Using the &lt;a href=&#34;https://cran.r-project.org/package=survival&#34;&gt;&lt;code&gt;survival&lt;/code&gt;&lt;/a&gt; Package   - Beth Atkinson&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Bayesian Random-Effects Meta-analysis using &lt;a href=&#34;https://cran.r-project.org/package=survival&#34;&gt;&lt;code&gt;bayesmeta&lt;/code&gt;&lt;/a&gt; -  Christian Rover&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;An &lt;a href=&#34;https://cran.r-project.org/package=arsenal&#34;&gt;&lt;code&gt;arsenal&lt;/code&gt;&lt;/a&gt; of R Functions for Statistical Summaries - Ethan Heinzen,  Beth Atkinson,  Jason Sinnwell&lt;/li&gt;
&lt;li&gt;R Markdown and &lt;a href=&#34;https://cran.r-project.org/package=officedown&#34;&gt;&lt;code&gt;officedown&lt;/code&gt;&lt;/a&gt; to Automate Clinical Trial Reporting -   Damian Rodziewicz&lt;/li&gt;
&lt;li&gt;Creating and Styling PPTX Slides with &lt;a href=&#34;https://cran.r-project.org/package=rmarkdown&#34;&gt;&lt;code&gt;rmarkdown&lt;/code&gt;&lt;/a&gt; -   Emil Hvitfeldt&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/ML4LHS/runway&#34;&gt;&lt;code&gt;runway&lt;/code&gt;&lt;/a&gt;: an R Package to Visualize Prediction Model Performance -    Jie Cao,    Karandeep Singh&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://CRAN.R-project.org/package=clinspacy&#34;&gt;&lt;code&gt;clinspacy&lt;/code&gt;&lt;/a&gt;: An R package for Clinical Natural Language Processing -  Jie Cao,    Karandeep Singh&lt;/li&gt;
&lt;li&gt;Data Visualization for Machine Learning Practitioners -   Julie Silge&lt;/li&gt;
&lt;li&gt;Animated Data Visualizations with &lt;a href=&#34;https://CRAN.R-project.org/package=gganimate&#34;&gt;&lt;code&gt;gganimate&lt;/code&gt;&lt;/a&gt; for Science Communication during the Pandemic - Kristen Panthagani&lt;/li&gt;
&lt;li&gt;Incorporating Risk-of-Bias Assessments into Evidence Syntheses with &lt;a href=&#34;https://cran.r-project.org/package=robvis&#34;&gt;&lt;code&gt;robvis&lt;/code&gt;&lt;/a&gt; -   Luke McGuinness,    Randall Boyes,  Alex Fowler&lt;/li&gt;
&lt;li&gt;&amp;lsquo;gpmodels&amp;rsquo;: A Grammar of Prediction Models -  Sean Meyer, Karandeep Singh&lt;/li&gt;
&lt;li&gt;CONSORT Diagrams in R with &lt;a href=&#34;https://github.com/tgerke/ggconsort&#34;&gt;&lt;code&gt;ggconsort&lt;/code&gt;&lt;/a&gt; -   Travis Gerke&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;short-courses&#34;&gt;Short Courses&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Secure Medical Data Collection: Best Practices with Excel, and Leveling Up to REDCap and &lt;a href=&#34;https://github.com/kamclean/collaborator&#34;&gt;&lt;code&gt;CollaboratoR&lt;/code&gt;&lt;/a&gt; - Peter Higgins,  Will Beasley,   Kenneth MacLean, Amanda Miller&lt;/li&gt;
&lt;li&gt;Introduction to R for Medical Data -  Ted Laderas, Daniel Chen,   Mara Alexeev&lt;/li&gt;
&lt;li&gt;An Introductory R Guide for Targeted Maximum Likelihood Estimation in Medical Research - Ehsan Karim, Hanna Frank&lt;/li&gt;
&lt;li&gt;Mapping Spatial Health Data   - Marynia Kolak,    Susan Paykin&lt;/li&gt;
&lt;li&gt;From SAS to R - Joe Krsszun&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;Reproducible Research with R - Alison Hill, Stephan Kaduke,   Paul Villanueva&lt;/li&gt;
&lt;/ul&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2021/09/09/a-guide-to-binge-watching-r-medicine/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>March 2021: &#34;Top 40&#34; New CRAN Packages</title>
      <link>https://rviews.rstudio.com/2021/04/22/march-2021-top-40-new-cran-packages/</link>
      <pubDate>Thu, 22 Apr 2021 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2021/04/22/march-2021-top-40-new-cran-packages/</guid>
      <description>
        

&lt;p&gt;By my count, two hundred twenty-one new packages &lt;em&gt;stuck&lt;/em&gt; to CRAN in March 2021.&lt;sup&gt;1&lt;/sup&gt; Here are my &amp;ldquo;Top 40&amp;rdquo; selections in twelve categories: Computational Methods, Data, Engineering, Genomics, Machine Learning, Medicine, Music, Networks, Science, Statistics, Utility, and Visualization. Two of these categories Engineering and Music have only one entry each. However, I decided to give them their own category in order to draw attention to  the use of R outside of the mainstream, and I have always lamented the fate of the &lt;em&gt;Miscellaneous&lt;/em&gt;. In the same spirit, note that the complete works of &lt;em&gt;the Bard&lt;/em&gt; appear in the Data category and that due to &lt;code&gt;tidypaleo&lt;/code&gt; &lt;em&gt;Paleoenvironmental&lt;/em&gt; is now &lt;em&gt;a thing&lt;/em&gt; in R.&lt;/p&gt;

&lt;h3 id=&#34;computational-methods&#34;&gt;Computational Methods&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=gamlss.foreach&#34;&gt;gamlss&lt;/a&gt; v1.0-5: Implements computationally intensive calculations for Generalized Additive Models for location, scale, and shape as described in &lt;a href=&#34;https://rss.onlinelibrary.wiley.com/doi/full/10.1111/j.1467-9876.2005.00510.x&#34;&gt;Rigby &amp;amp; Stasinopoulos (2005)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=waydown&#34;&gt;waydown&lt;/a&gt; v1.1.0: Implements an algorithm based on the classical Helmholtz decomposition to obtain an approximate potential function for non gradient fields. See &lt;a href=&#34;https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1007788&#34;&gt;Rodríguez-Sánchez (2020)&lt;/a&gt; for background and the &lt;a href=&#34;https://cran.r-project.org/web/packages/waydown/vignettes/examples.pdf&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;waydown.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;data&#34;&gt;Data&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=aopdata&#34;&gt;aopdata&lt;/a&gt; v0.2.1: Provides functions to download data from the &lt;a href=&#34;https://www.ipea.gov.br/acessooportunidades/en/&#34;&gt;Access to Opportunities Project&lt;/a&gt; (AOP) which includes annual estimates of access to employment, health and education services by transport mode, as well as data on the spatial distribution of population, schools and health-care facilities at a fine spatial resolution for all cities included in the study. There is an &lt;a href=&#34;https://cran.r-project.org/web/packages/aopdata/vignettes/intro_to_aopdata.html&#34;&gt;Introduction&lt;/a&gt; to the package, and there are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/aopdata/vignettes/access_inequality.html&#34;&gt;Analyzing Inequality&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/aopdata/vignettes/access_maps.html&#34;&gt;Mapping Urban Accessibility&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/aopdata/vignettes/landuse_maps.html&#34;&gt;Mapping Pooulation and Land Use&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;aopdata.png&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=bardr&#34;&gt;bardr&lt;/a&gt; v0.0.9: Provides R data structures for Shakespeare&amp;rsquo;s complete works, as provided by &lt;a href=&#34;https:www.gutenberg.org/ebooks/100&#34;&gt;Project Gutenberg&lt;/a&gt;. See &lt;a href=&#34;https://cran.r-project.org/web/packages/bardr/readme/README.html&#34;&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=metro&#34;&gt;metro&lt;/a&gt; v0.9.1: Provides access to the &lt;a href=&#34;https://developer.wmata.com/&#34;&gt;Metro Transparent Data Sets API&lt;/a&gt; published by the Washington Metropolitan Area Transit Authority, the  government agency operating light rail and passenger buses in the Washington D.C. area. See &lt;a href=&#34;https://cran.r-project.org/web/packages/metro/readme/README.html&#34;&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=RAQSAPI&#34;&gt;RAQSAPI&lt;/a&gt; v2.0.1: Provides functions to retrieve air monitoring data and associated metadata from the US Environmental Protection Agency&amp;rsquo;s &lt;a href=&#34;https://aqs.epa.gov/aqsweb/documents/data_api.html&#34;&gt;Air Quality System Service&lt;/a&gt;. There are several short vignettes including an &lt;a href=&#34;https://cran.r-project.org/web/packages/RAQSAPI/vignettes/Intro.html&#34;&gt;Introduction&lt;/a&gt; and a vignette on &lt;a href=&#34;https://cran.r-project.org/web/packages/RAQSAPI/vignettes/RAQSAPIusagetipsandprecautions.html&#34;&gt;Usage tips and precautions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=troopdata&#34;&gt;troopdata&lt;/a&gt; v0.1.3: Provides access to U.S. Department of Defense data on overseas military deployments and includes functions for pulling country-year troop deployment and basing data. See &lt;a href=&#34;https://cran.r-project.org/web/packages/troopdata/readme/README.html&#34;&gt;README&lt;/a&gt; to get started&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;troopdata.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;engineering&#34;&gt;Engineering&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=pipenostics&#34;&gt;pipenostics&lt;/a&gt; v0.1.7: Implements empirical and data-driven models of heat losses, corrosion diagnostics, reliability and predictive maintenance of pipeline systems which should be of interest to the engineering departments of heat generating and heat transferring companies. See &lt;a href=&#34;https://link.springer.com/book/10.1007%2F978-3-319-25307-7&#34;&gt;Timashev et al. (2016)&lt;/a&gt; and &lt;a href=&#34;https://www.sciencedirect.com/science/article/pii/S2214785317313755?via%3Dihub&#34;&gt;Reddy (2017)&lt;/a&gt; for the methods used and &lt;a href=&#34;https://cran.r-project.org/web/packages/pipenostics/readme/README.html&#34;&gt;README&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;pipenostics.svg&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;genomics&#34;&gt;Genomics&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=glmmSeq&#34;&gt;glmmSeq&lt;/a&gt; v0.1.0: Provides functions to fit negative binomial mixed effects models with matched samples to model expression data. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/glmmSeq/vignettes/glmmSeq.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;glmmSeq.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=ondisc&#34;&gt;ondisc&lt;/a&gt; v1.0.0: Implements a method to allow researchers to analyze large-scale single-cell data as and R object stored on disk. There is a tutorial on the the &lt;a href=&#34;https://cran.r-project.org/web/packages/ondisc/vignettes/tutorial_odm_class.html&#34;&gt;ondisc matrix class&lt;/a&gt; and another on &lt;a href=&#34;https://cran.r-project.org/web/packages/ondisc/vignettes/tutorial_other_classes.html&#34;&gt;Metadata&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=SignacX&#34;&gt;SignacX&lt;/a&gt; v2.2.0: Implements a neural network trained with flow-sorted gene expression data to classify cellular phenotypes in single cell RNA-sequencing data. See &lt;a href=&#34;https://www.biorxiv.org/content/10.1101/2021.02.01.429207v3&#34;&gt;Chamberlain et al. (2021)&lt;/a&gt; for background. There are seven vignettes including an &lt;a href=&#34;https://cran.r-project.org/web/packages/SignacX/vignettes/signac-Seurat_AMP.html&#34;&gt;Analysis of Kidney Lupus Data&lt;/a&gt; and an &lt;a href=&#34;https://cran.r-project.org/web/packages/SignacX/vignettes/signac-Seurat_pbmcs.html&#34;&gt;Analysis of PBMCs from 10X Genomics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;SignacX.png&#34; height = &#34;200&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;machine-learning&#34;&gt;Machine Learning&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=opitools&#34;&gt;opitools&lt;/a&gt; v1.0.3: Implements a tool to analyze opinions inherent in a text document relating to a specific subject (A) and assess how opinions expressed with respect to another subject (B) may affect the opinions on subject A. This package has been designed specifically for application to social media datasets, such as Twitter and Facebook. See &lt;a href=&#34;https://osf.io/preprints/socarxiv/c32qh/&#34;&gt;Adepeju and Jimoh (2021)&lt;/a&gt; for an extended example that demonstrates the utility of the approach and the &lt;a href=&#34;https://cran.r-project.org/web/packages/opitools/vignettes/opitools-vignette.html&#34;&gt;vignette&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;opitools.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=poems&#34;&gt;poems&lt;/a&gt; v1.0.1: Provides a framework of interoperable R6 classes for building ensembles of viable models via the &lt;a href=&#34;https://en.wikipedia.org/wiki/Pattern-oriented_modeling&#34;&gt;pattern-oriented modeling&lt;/a&gt; (POM) approach. The package includes classes for encapsulating and generating model parameters, and managing the POM workflow which includes: model setup; generating model parameters via Latin hyper-cube sampling; running multiple sampled model simulations; collating summary results; and validating and selecting an ensemble of models that best match known patterns. There are two vignettes: &lt;a href=&#34;https://cran.r-project.org/web/packages/poems/vignettes/simple_example.pdf&#34;&gt;Simple Example&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/poems/vignettes/thylacine_example.pdf&#34;&gt;Thylacine Example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;poems.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;medicine&#34;&gt;Medicine&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=dampack&#34;&gt;dampack&lt;/a&gt; v1.0.0: Implements a suite of functions for analyzing and visualizing the health economic outputs of mathematical models. See &lt;a href=&#34;https://www.cambridge.org/core/books/decision-making-in-health-and-medicine/31FD197195DAE2A6321409568BEFA2DD&#34;&gt;Hunink et al. (2014)&lt;/a&gt; for the theoretical underpinnings. There are five vignettes including &lt;a href=&#34;https://cran.r-project.org/web/packages/dampack/vignettes/basic_cea.html&#34;&gt;Basic Cost Effectiveness Analysis&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/dampack/vignettes/psa_analysis.html&#34;&gt;Probabilistic Sensitivity Analysis: Analysis&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/dampack/vignettes/voi.html&#34;&gt;Value of Information Analysis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;dampack.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=rdecision&#34;&gt;rdecision&lt;/a&gt; v1.0.3: Provides classes and functions for using decision trees to model health care interventions using cohort models. See &lt;a href=&#34;https://www.amazon.com/Decision-Modelling-Economic-Evaluation-Handbooks/dp/0198526628&#34;&gt;Briggs et al.&lt;/a&gt; for theory and terminology. There are five vignettes including &lt;a href=&#34;https://cran.r-project.org/web/packages/rdecision/vignettes/DT01-Sumatriptan.html&#34;&gt;Elementary decision tree (Evans 1997)&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/rdecision/vignettes/DT02-Tegaderm.html&#34;&gt;Decision tree with PSA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;rdecision.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;music&#34;&gt;Music&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=gm&#34;&gt;gm&lt;/a&gt; v1.0.2: Implements a high-level language to create music including converting your music to musical scores and audio files. It works with &lt;a href=&#34;https://rmarkdown.rstudio.com/&#34;&gt;R Markdown&lt;/a&gt;, R &lt;a href=&#34;https://jupyter.org/&#34;&gt;Jupyter Notebooks&lt;/a&gt;, and RStudio. There vignette is available in &lt;a href=&#34;https://cran.r-project.org/web/packages/gm/vignettes/gm.html&#34;&gt;English&lt;/a&gt; and in &lt;a href=&#34;https://cran.r-project.org/web/packages/gm/vignettes/cn.html&#34;&gt;Chinese&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;gm.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;networks&#34;&gt;Networks&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=sfnetworks&#34;&gt;sfnetworks&lt;/a&gt; v0.5.1: Provides a tidy approach to spatial network analysis in the form of classes and functions that enable a seamless interaction between the network analysis package &lt;code&gt;tidygraph&lt;/code&gt; and the spatial analysis package &lt;code&gt;sf&lt;/code&gt;. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/sfnetworks/vignettes/structure.html&#34;&gt;sf network structure&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/sfnetworks/vignettes/preprocess_and_clean.html&#34;&gt;Preprocessing&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/sfnetworks/vignettes/join_filter.html&#34;&gt;Spatial joins and filters&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/sfnetworks/vignettes/routing.html&#34;&gt;Routing&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/sfnetworks/vignettes/morphers.html&#34;&gt;Spatial morphers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;sfnetworks.png&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=valhallr&#34;&gt;valhallr&lt;/a&gt; v0.1.0: Implements an interface to the &lt;a href=&#34;https://github.com/valhalla/valhalla&#34;&gt;Valhalla&lt;/a&gt; routing engine’s API for turn-by-turn routing, isochrones, and origin-destination analyses. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/valhallr/vignettes/valhallr.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;valhallr.jpeg&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;science&#34;&gt;Science&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=asteRisk&#34;&gt;asteRisk&lt;/a&gt; v0.99.4: Provides functions to calculate the positions of satellites given a known state vector. It includes implementations of the SGP4 and SDP4 simplified perturbation models to propagate orbital state vectors. See &lt;a href=&#34;https://celestrak.com/NORAD/documentation/spacetrk.pdf&#34;&gt;Hoots et al. (1988)&lt;/a&gt;, &lt;a href=&#34;https://arc.aiaa.org/doi/10.2514/6.2006-6753&#34;&gt;Vallado et al. (2012)&lt;/a&gt;, and &lt;a href=&#34;https://arc.aiaa.org/doi/10.2514/1.9161&#34;&gt;Hoots et al. (2014)&lt;/a&gt; for background and the &lt;a href=&#34;https://cran.r-project.org/web/packages/asteRisk/vignettes/asteRisk.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;asteRisk.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=forImage&#34;&gt;forImage&lt;/a&gt; v0.1.0: Implements a tool to measure the size of foraminifera and other unicellulars and includes functions to guide foraminiferal test biovolume calculations and cell biomass estimations. The volume function includes several microalgae models geometric adaptations based on &lt;a href=&#34;https://onlinelibrary.wiley.com/doi/abs/10.1046/j.1529-8817.1999.3520403.x&#34;&gt;Hillebrand et al. (1999)&lt;/a&gt;, &lt;a href=&#34;https://academic.oup.com/plankt/article/25/11/1331/1490055&#34;&gt;Sun &amp;amp; Liu (2003)&lt;/a&gt;, and &lt;a href=&#34;http://siba-ese.unisalento.it/index.php/twb/article/view/106&#34;&gt;Vadrucci et al. (2007)&lt;/a&gt;. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/forImage/vignettes/forImage_vignette.html&#34;&gt;vignette&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;forImage.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=OpenSpecy&#34;&gt;OpenSpecy&lt;/a&gt; v0.9.1: Provides functions to analyze, process, identify and share Raman and (FT)IR spectra with functions to implement Savitzky-Golay smoothing in accordance with &lt;a href=&#34;https://journals.sagepub.com/doi/10.1366/000370207782597003&#34;&gt;Zhao et al. (2007)&lt;/a&gt; and identify spectra using an onboard reference library, see &lt;a href=&#34;https://journals.sagepub.com/doi/10.1177/0003702820929064&#34;&gt;Cowger et al. 2020&lt;/a&gt;. Analyzed spectra can be shared via &lt;a href=&#34;https://wincowger.shinyapps.io/OpenSpecy/&#34;&gt;Shiny App&lt;/a&gt;. There is a &lt;a href=&#34;https://cran.r-project.org/web/packages/OpenSpecy/vignettes/sop.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;OpenSpecy.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=tidypaleo&#34;&gt;tidypaleo&lt;/a&gt; v0.1.1: Provides functions with a common framework for age-depth model management, stratigraphic visualization, and common statistical transformations with a focus on stratigraphic visualization using &lt;code&gt;ggplot2&lt;/code&gt;. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/tidypaleo/vignettes/age_depth.html&#34;&gt;Age-depth Models&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/tidypaleo/vignettes/nested_analysis.html&#34;&gt;Nested Analyses&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/tidypaleo/vignettes/strat_diagrams.html&#34;&gt;Stratigraphic Diagrams&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;tidypaleo.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=VulnToolkit&#34;&gt;VulnToolkit&lt;/a&gt; v1.1.2: Provides functions to analyze and summarize tidal data sets and to access to NOAA mean sea level data. See &lt;a href=&#34;https://www.sciencedirect.com/science/article/abs/pii/S0272771415002139?via%3Dihub&#34;&gt;Hill &amp;amp; Anisfeld (2015)&lt;/a&gt; for background and the &lt;a href=&#34;https://cran.r-project.org/web/packages/VulnToolkit/vignettes/Tidal_data.html&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;VulnToolkit.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;statistics&#34;&gt;Statistics&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=corncob&#34;&gt;corncob&lt;/a&gt; v0.2.0: Implements functions for modeling correlated count data using the beta-binomial distribution, described in &lt;a href=&#34;https://projecteuclid.org/journals/annals-of-applied-statistics/volume-14/issue-1/Modeling-microbial-abundances-and-dysbiosis-with-beta-binomial-regression/10.1214/19-AOAS1283.short&#34;&gt;Martin et al. (2020)&lt;/a&gt;. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/corncob/vignettes/corncob-intro.pdf&#34;&gt;vignette&lt;/a&gt; for an introduction.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;corncob.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=hawkesbow&#34;&gt;hawkesbow&lt;/a&gt; v1.0.2: Implements an estimation method for &lt;a href=&#34;https://arxiv.org/pdf/1507.02822.pdf#:~:text=The%20Hawkes%20process%20(HP)%20is,trade%20orders%2C%20or%20bank%20defaults.&#34;&gt;Hawkes processes&lt;/a&gt; when count data are only observed in discrete time, using a spectral approach derived from the Bartlett spectrum. See &lt;a href=&#34;https://arxiv.org/abs/2003.04314&#34;&gt;Cheysson and Lang (2020)&lt;/a&gt; for background and the &lt;a href=&#34;https://cran.r-project.org/web/packages/hawkesbow/vignettes/hawkesbow.pdf&#34;&gt;vignette&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=LMMELSM&#34;&gt;LMMELSM&lt;/a&gt; v0.1.0: Implements two-level mixed effects location scale models on multiple observed or latent outcomes, and between-group variance modeling. See &lt;a href=&#34;https://econtent.hogrefe.com/doi/10.1027/1015-5759/a000624&#34;&gt;Williams et al. (2020)&lt;/a&gt; and &lt;a href=&#34;https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1541-0420.2007.00924.x&#34;&gt;Hedeker et al. (2008)&lt;/a&gt; for background and &lt;a href=&#34;https://cran.r-project.org/web/packages/LMMELSM/readme/README.html&#34;&gt;README&lt;/a&gt; for an example.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=mixpoissonreg&#34;&gt;mixpoissinreg&lt;/a&gt; v1.0.0: Provides functions to fit mixed Poisson regression models (Poisson-Inverse Gaussian or Negative-Binomial) with count data response variables. See &lt;a href=&#34;https://link.springer.com/article/10.1007%2Fs11222-015-9601-6&#34;&gt; Barreto-Souza and Simas (2016)&lt;/a&gt; for background. There are five vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/mixpoissonreg/vignettes/influence-mixpoissonreg.html&#34;&gt;Global and Local Influence&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/mixpoissonreg/vignettes/intervals-mixpoissonreg.html&#34;&gt;Confidence and Prediction Intervals&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/mixpoissonreg/vignettes/ml-mixpoissonreg.html&#34;&gt;MLE&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/mixpoissonreg/vignettes/tidyverse-mixpoissonreg.html&#34;&gt;Tidy Methods&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/mixpoissonreg/vignettes/tutorial-mixpoissonreg.html&#34;&gt;Overdispersed Count Data&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;mixpoissinreg.png&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=ppdiag&#34;&gt;ppdiag&lt;/a&gt; v0.1.0: Provides a suite of diagnostic tools for univariate point processes including tools for simulating and fitting both common and more complex temporal point processes and the diagnostic tools described in &lt;a href=&#34;https://direct.mit.edu/neco/article/14/2/325/6578/The-Time-Rescaling-Theorem-and-Its-Application-to&#34;&gt;Brown et al. (2002)&lt;/a&gt; and &lt;a href=&#34;https://arxiv.org/abs/2001.09359&#34;&gt;Wu et al. (2020)&lt;/a&gt;. There is a vignette on &lt;a href=&#34;https://cran.r-project.org/web/packages/ppdiag/vignettes/fitting_markov_modulated.html&#34;&gt;Markov Modulated Point Processes&lt;/a&gt; and another on &lt;a href=&#34;https://cran.r-project.org/web/packages/ppdiag/vignettes/ppdiag.html&#34;&gt;Diagnostic Tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=robustlm&#34;&gt;robustlm&lt;/a&gt; v0.1.0: Implements a computationally efficient exponential squared loss algorithm for variable selection proposed by &lt;a href=&#34;https://www.tandfonline.com/doi/abs/10.1080/01621459.2013.766613&#34;&gt;Wang et al.(2013)&lt;/a&gt;. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/robustlm/vignettes/vignette.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;robustlm.png&#34; height = &#34;200&#34; width=&#34;300&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://CRAN.R-project.org/package=smmR&#34;&gt;smmR&lt;/a&gt; v1.0.2: Provides functions to estimate and simulate multi-state semi-Markov models. The methods implemented are described in &lt;a href=&#34;https://www.tandfonline.com/doi/abs/10.1080/10485250701261913&#34;&gt;Barbu &amp;amp; Limnios (2008)&lt;/a&gt; and &lt;a href=&#34;https://www.tandfonline.com/doi/abs/10.1080/10485252.2011.555543&#34;&gt;Trevezas &amp;amp; Limnios (2011)&lt;/a&gt;. The &lt;a href=&#34;https://cran.r-project.org/web/packages/smmR/vignettes/Textile-Factory.html&#34;&gt;vignette&lt;/a&gt; contains an extended example.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;smmR.png&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=spotoroo&#34;&gt;spotoroo&lt;/a&gt; v0.1.1: Implements an algorithm to cluster satellite hot spot data spatially and temporally. See the &lt;a href=&#34;https://cran.r-project.org/web/packages/spotoroo/vignettes/Clustering-hot-spots.html&#34;&gt;vignette&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;spotoroo.png&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;h3 id=&#34;utilities&#34;&gt;Utilities&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=clock&#34;&gt;clock&lt;/a&gt; v0.2.0: Provides a comprehensive library for date-time manipulations using a new family of orthogonal date-time classes (duration, time points, zoned-times, and calendars) that partition responsibilities so that the complexities of time zones are only considered when they are really needed. There is a &lt;a href=&#34;Getting Started&#34;&gt;Getting Started&lt;/a&gt; guide, as well as vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/clock/vignettes/faq.html&#34;&gt;FAQ&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/clock/vignettes/recipes.html&#34;&gt;Examples and Recipies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=crosstable&#34;&gt;crosstable&lt;/a&gt; v0.2.1: Provides functions to create descriptive tables for continuous and categorical variables, apply summary statistics, and create reports using &lt;code&gt;rmarkdown&lt;/code&gt; or &lt;code&gt;officer&lt;/code&gt;. There is an &lt;a href=&#34;https://cran.r-project.org/web/packages/crosstable/vignettes/crosstable.html&#34;&gt;Introduction&lt;/a&gt;, and vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/crosstable/vignettes/crosstable-install.html&#34;&gt;Troubleshooting&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/crosstable/vignettes/crosstable-report.html&#34;&gt;Making Automatic Reports&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/crosstable/vignettes/crosstable-selection.html&#34;&gt;Selecting Variables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=pkgdepends&#34;&gt;pkgdepends&lt;/a&gt; v0.1.0: Provides functions to find recursive dependencies for R packages from various sources including CRAN, Bioconductor, and GitHub enabling users to obtain a consistent set of packages to install. See &lt;a href=&#34;https://cran.r-project.org/web/packages/pkgdepends/readme/README.html&#34;&gt;README&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=pkglite&#34;&gt;pkglite&lt;/a&gt; v0.1.1: Implements a tool, grammar, and standard to represent and exchange R package source code as text files. Converts one or more source packages to a text file and restores the package structures from the file. There are vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/pkglite/vignettes/filespec.html&#34;&gt;Generating File Specifications&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/pkglite/vignettes/format.html&#34;&gt;Representing Packages&lt;/a&gt;, and &lt;a href=&#34;https://cran.r-project.org/web/packages/pkglite/index.html&#34;&gt;Compact Package Representation&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&#34;visualization&#34;&gt;Visualization&lt;/h3&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=datplot&#34;&gt;datplot&lt;/a&gt; v1.0.0: Provides tools to process and prepare data for visualization and employs the concept of &lt;a href=&#34;https://www.jratcliffe.net/aoristic-analysis&#34;&gt;aoristic analysis&lt;/a&gt;. See &lt;a href=&#34;https://bit.ly/3svhbdV&#34;&gt;aorist&lt;/a&gt; and the vignettes &lt;a href=&#34;https://cran.r-project.org/web/packages/datplot/vignettes/data_preparation.html&#34;&gt;Data Preparation and Visualization&lt;/a&gt; and &lt;a href=&#34;https://cran.r-project.org/web/packages/datplot/vignettes/how-to.html&#34;&gt;Visualizing Chronological Distribution&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;datplot.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=ferrn&#34;&gt;ferrn&lt;/a&gt; v0.0.1: Implements diagnostic plots for optimization, with a focus on projection pursuit which show paths the optimizer takes in the high-dimensional space. See &lt;a href=&#34;https://cran.r-project.org/web/packages/ferrn/readme/README.html&#34;&gt;README&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;ferrn.gif&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=funcharts&#34;&gt;funcharts&lt;/a&gt; v1.0.0: Provides functional control charts for statistical process monitoring of functional data, using the methods of &lt;a href=&#34;https://onlinelibrary.wiley.com/doi/abs/10.1002/asmb.2507&#34;&gt;Capezza et al. (2020)&lt;/a&gt; and &lt;a href=&#34;https://www.tandfonline.com/doi/abs/10.1080/00401706.2020.1753581?journalCode=utch20&#34;&gt;Centofanti et al. (2020)&lt;/a&gt;. There are  vignettes on &lt;a href=&#34;https://cran.r-project.org/web/packages/funcharts/vignettes/capezza2020.html&#34;&gt;Capezza 2020&lt;/a&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/funcharts/vignettes/centofanti2020.html&#34;&gt;Centofanti 2020&lt;/a&gt; and on the &lt;a href=&#34;https://cran.r-project.org/web/packages/funcharts/vignettes/mfd.html&#34;&gt;mfd class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;funcharts.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=gghilbertstrings&#34;&gt;gghilbertstrings&lt;/a&gt; v0.3.3: Provides functions to plot Hilbert curves which are used to map one dimensional data into the 2D plane. A specific use case maps a character column in a data frame into 2D space allowing visually comparing long lists of URLs, words, genes or other data that has a fixed order and position. See &lt;a href=&#34;https://cran.r-project.org/web/packages/gghilbertstrings/readme/README.html&#34;&gt;README&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;gghilbertstrings.png&#34; height = &#34;300&#34; width=&#34;500&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cran.r-project.org/package=mapsf&#34;&gt;mapsf&lt;/a&gt; v0.1.1: Provides functions to create and integrate thematic maps including functions to design various cartographic representations such as proportional symbols, choropleth or typology maps. Look &lt;a href=&#34;https://riatelab.github.io/mapsf&#34;&gt;here&lt;/a&gt; for examples.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;mapsf.png&#34; height = &#34;400&#34; width=&#34;400&#34;&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; I have used phrases like &lt;em&gt;By my count&lt;/em&gt; and &lt;em&gt;stuck to CRAN&lt;/em&gt; in the past, but I do not believe that I have explained what I mean. For some time now, but I believe more frequently in recent months, packages will appear as new on CRAN, only to be removed within a relatively short period of time for failing to resolve check problems. If you happen to know about these packages and search for them by name on CRAN you will receive the message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Package XXXX was removed from the CRAN repository.
Formerly available versions can be obtained from the archive.
Archived on 2021-04-17 as check problems remained after update.
A summary of the most recent check results can be obtained from the check results archive.
Please use the canonical form &lt;a href=&#34;https://CRAN.R-project.org/package=XXXX&#34;&gt;https://CRAN.R-project.org/package=XXXX&lt;/a&gt; to link to this page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I did not include the ten packages that were identified as being new for March when I created my list of March packages on April 10, 2021, but were removed by the time I finalized my list for this post a week later, in my total count of new CRAN packages. So, there is some instability with the notion of counting new packages in a given month.&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2021/04/22/march-2021-top-40-new-cran-packages/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Modern Rule-Based Models</title>
      <link>https://rviews.rstudio.com/2020/05/21/modern-rule-based-models/</link>
      <pubDate>Thu, 21 May 2020 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2020/05/21/modern-rule-based-models/</guid>
      <description>
        
&lt;script src=&#34;/rmarkdown-libs/header-attrs/header-attrs.js&#34;&gt;&lt;/script&gt;


&lt;div id=&#34;modern-rule-based-models&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Modern Rule-Based Models&lt;/h2&gt;
&lt;p&gt;Machine learning models come in many shapes and sizes. While deep learning models currently have the lion’s share of coverage, there are many other classes of models that are effective across many different problem domains. This post gives a short summary of several &lt;em&gt;rule-based models&lt;/em&gt; that are closely related to tree-based models (but are less widely known).&lt;/p&gt;
&lt;p&gt;While this post is focused on explaining on how these models work, it coincides with the release of the &lt;code&gt;rules&lt;/code&gt; package, a tidymodels package that provides a user interface to these models. A &lt;a href=&#34;https://www.tidyverse.org/blog/2020/05/rules-0-0-1/&#34;&gt;companion post&lt;/a&gt; at the tidyverse blog describes the usage of the package.&lt;/p&gt;
&lt;p&gt;To start, let’s discuss the concept of rules more generally.&lt;/p&gt;
&lt;div id=&#34;what-is-a-rule&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;What is a rule?&lt;/h3&gt;
&lt;p&gt;Rules in machine learning have been around for a long time (Quinlan, 1979). The focus of this article is using rules for traditional supervised learning (as opposed to &lt;a href=&#34;https://en.wikipedia.org/wiki/Association_rule_learning&#34;&gt;&lt;em&gt;association rule&lt;/em&gt;&lt;/a&gt; mining). In the context of feature engineering, a rule is a &lt;strong&gt;conditional logical statement&lt;/strong&gt;. It can be attached to some sort of predicted value too, such as&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;if (chance_of_rain &amp;gt; 0.75) {
  umbrella &amp;lt;- &amp;quot;yes&amp;quot;
} else {
  umbrella &amp;lt;- &amp;quot;no&amp;quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are various ways to create rules from data. The most popular method is to create a tree-based model and then “flatten” the model structure into a set of rules. This is called a “separate and conquer” approach.&lt;/p&gt;
&lt;p&gt;To demonstrate this, let’s use a data set with housing prices from Sacramento CA. Two predictors with a large number of levels are removed to make the rule output more readable:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;for (pkg in c(&amp;#39;dplyr&amp;#39;, &amp;#39;modeldata&amp;#39;, &amp;#39;rpart&amp;#39;)) {
  if (!requireNamespace(pkg)) {
    install.packages(pkg)
  }
}

library(dplyr)

data(Sacramento, package = &amp;quot;modeldata&amp;quot;)

Sacramento &amp;lt;-
  Sacramento %&amp;gt;% 
  mutate(price = log10(price)) %&amp;gt;% 
  select(-zip, -city)

str(Sacramento)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## tibble [932 × 7] (S3: tbl_df/tbl/data.frame)
##  $ beds     : int [1:932] 2 3 2 2 2 3 3 3 2 3 ...
##  $ baths    : num [1:932] 1 1 1 1 1 1 2 1 2 2 ...
##  $ sqft     : int [1:932] 836 1167 796 852 797 1122 1104 1177 941 1146 ...
##  $ type     : Factor w/ 3 levels &amp;quot;Condo&amp;quot;,&amp;quot;Multi_Family&amp;quot;,..: 3 3 3 3 3 1 3 3 1 3 ...
##  $ price    : num [1:932] 4.77 4.83 4.84 4.84 4.91 ...
##  $ latitude : num [1:932] 38.6 38.5 38.6 38.6 38.5 ...
##  $ longitude: num [1:932] -121 -121 -121 -121 -121 ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Consider a basic CART model created using &lt;code&gt;rpart&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(rpart)

rpart(price ~ ., data = Sacramento) &lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;## n= 932 
## 
## node), split, n, deviance, yval
##       * denotes terminal node
## 
##  1) root 932 48.5000 5.334  
##    2) sqft&amp;lt; 1594 535 18.7200 5.211  
##      4) sqft&amp;lt; 1170 235  8.0440 5.107  
##        8) sqft&amp;lt; 934.5 71  2.2760 5.002 *
##        9) sqft&amp;gt;=934.5 164  4.6500 5.152 *
##      5) sqft&amp;gt;=1170 300  6.1390 5.292  
##       10) longitude&amp;lt; -121.3 243  4.8850 5.270 *
##       11) longitude&amp;gt;=-121.3 57  0.6081 5.388 *
##    3) sqft&amp;gt;=1594 397 10.6000 5.501  
##      6) sqft&amp;lt; 2317 245  4.7420 5.432  
##       12) longitude&amp;lt; -121.3 205  3.3190 5.407 *
##       13) longitude&amp;gt;=-121.3 40  0.6179 5.562 *
##      7) sqft&amp;gt;=2317 152  2.8450 5.611  
##       14) longitude&amp;lt; -121.2 110  1.6790 5.570 *
##       15) longitude&amp;gt;=-121.2 42  0.4864 5.720 *&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The splits in this particular tree involve the same two predictors. These paths to the terminal nodes are comprised of a set of &lt;code&gt;if-then&lt;/code&gt; rules. Consider the path to the eighth terminal node:&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;if (sqft &amp;lt; 1594 &amp;amp; sqft &amp;lt; 1169.5 &amp;amp; sqft &amp;lt; 934.5) then pred = 5.001820&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is easy to &lt;em&gt;prune&lt;/em&gt; this rule down to just &lt;code&gt;sqft &amp;lt; 934.5&lt;/code&gt;. In all, the rules generated from this model would be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##  price                                              
##    5.0 when sqft &amp;lt;   935                            
##    5.2 when sqft is  935 to 1170                    
##    5.3 when sqft is 1170 to 1594 &amp;amp; longitude &amp;lt;  -121
##    5.4 when sqft is 1170 to 1594 &amp;amp; longitude &amp;gt;= -121
##    5.4 when sqft is 1594 to 2317 &amp;amp; longitude &amp;lt;  -121
##    5.6 when sqft is 1594 to 2317 &amp;amp; longitude &amp;gt;= -121
##    5.6 when sqft &amp;gt;=         2317 &amp;amp; longitude &amp;lt;  -121
##    5.7 when sqft &amp;gt;=         2317 &amp;amp; longitude &amp;gt;= -121&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What are some more modern models that can use or generate rules? We’ll walk through three (which are all included in the &lt;code&gt;rules&lt;/code&gt; package).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&#34;c5.0-rules&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;C5.0 rules&lt;/h2&gt;
&lt;p&gt;The C4.5 algorithm (Quinlan, 1993b) was an early tree-based model that was released not long after the more well known CART model. One cool aspect of this model is that it could generate a classification tree &lt;em&gt;or&lt;/em&gt; a set of rules. These rules are derived from the original tree much in the same way that was shown above for &lt;code&gt;rpart&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Over the years, the author (Ross Quinlan) kept evolving the next generation of the model called &lt;a href=&#34;https://www.rulequest.com/see5-unix.html&#34;&gt;C5.0&lt;/a&gt;. About 10 years ago, he open-sourced that model and the C50 R package was born. Like its predecessor, C5.0 could be used for trees or for rules. There are a variety of advances to this model (detailed in Kuhn and Johnson (2013)), but the most significant was the inclusion of boosting. In effect, you could create an ensemble of classification rules. Rather than approaching the problem via the more modern stochastic gradient boosting paradigm, it is more similar to the classical AdaBoost methodology.&lt;/p&gt;
&lt;p&gt;Since C5.0 is classification only, an example of a single rule set for the iris data is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Rule 1: (50, lift 2.9)
    Petal.Length &amp;lt;= 1.9
    -&amp;gt;  class setosa  [0.981]

Rule 2: (48/1, lift 2.9)
    Petal.Length &amp;gt; 1.9
    Petal.Length &amp;lt;= 4.9
    Petal.Width &amp;lt;= 1.7
    -&amp;gt;  class versicolor  [0.960]

Rule 3: (46/1, lift 2.9)
    Petal.Width &amp;gt; 1.7
    -&amp;gt;  class virginica  [0.958]

Rule 4: (46/2, lift 2.8)
    Petal.Length &amp;gt; 4.9
    -&amp;gt;  class virginica  [0.938]

Default class: setosa&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If used on the iris data, a boosting ensemble with 50 constituent models used a total of 276 rules and each iteration averaged 5.52 rules per rule set.&lt;/p&gt;
&lt;p&gt;The primary method of controlling the complexity of each rule set is to adjust the minimum number of data points required to make additional splits within a node. The default for this parameter is two data points. If this value was increased to 20, the mean number of rules was reduced from 5.52 rules to 3.2 rules.&lt;/p&gt;
&lt;p&gt;When tuning, the other main tuning parameter is the number of boosting iterations. The C50 R package enables &lt;a href=&#34;https://tune.tidymodels.org/articles/extras/optimizations.html&#34;&gt;sub-model predictions&lt;/a&gt; across boosting iterations; tuning this parameter over many values is not very computation expensive.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;cubist&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Cubist&lt;/h2&gt;
&lt;p&gt;The aforementioned Ross Quinlan also developed &lt;em&gt;model trees&lt;/em&gt; (Quinlan, 1992). These are regression tree-based models that contain &lt;em&gt;linear regression models&lt;/em&gt; in the terminal nodes. This model was called M5 and, much like C5.0, there was a rule-based analog. Most tree-based models, especially ensembles of trees, tend to produce models that underfit in the tails (much like regression to the mean). Model trees do not suffer from this issue since their terminal regression models could make predictions across the whole range of the outcome data.&lt;/p&gt;
&lt;p&gt;After an initial set of papers in the 1990’s, Quinlan didn’t publish much on the methodology as he evolved it. The modern version of model rules was called &lt;a href=&#34;https://www.rulequest.com/cubist-unix.html&#34;&gt;&lt;em&gt;Cubist&lt;/em&gt;&lt;/a&gt;. There were a number of small technical differences between Cubist and M5 rules (enumerated in Kuhn and Johnson (2013)) but the main improvements were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An ensemble method for predictions called &lt;em&gt;committees&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A nearest-neighbor adjustment that occurs after the model predictions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’ll summarize these approaches in-turn.&lt;/p&gt;
&lt;p&gt;Committee ensembles are similar to boosting. In boosting, a set of models are created sequentially. For the current iteration of boosting, the model is created using case weights that are defined by the results of previous models. In committees, case weights are not changed. Instead the outcome values are modified for each iteration of committees. For example, if a sample is under-predicted previously, its outcome value is changed to be larger so that the model will be pulled upward in an effort to stop under-predicting.&lt;/p&gt;
&lt;p&gt;In committees, the first model uses the original outcome value &lt;span class=&#34;math inline&#34;&gt;\(y\)&lt;/span&gt;. On further iterations, a modified value &lt;span class=&#34;math inline&#34;&gt;\(y^*\)&lt;/span&gt; is used instead. For iteration &lt;span class=&#34;math inline&#34;&gt;\(m\)&lt;/span&gt;, the model uses this adjustment formula:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math display&#34;&gt;\[
y^*_{(m)} = y - (\widehat{y}_{(m-1)} - y)
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As a demonstration, the plot below shows how the pseudo-outcome changes over iterations. The observed price of the house in question is 5.3. On the first iteration, the model under-predicts (show as the solid black dot). The vertical line represents the residual from this model. On the next iteration, the open blue circle shows the value of &lt;span class=&#34;math inline&#34;&gt;\(y^*_{(2)}\)&lt;/span&gt;. On the second iteration, the prediction for this data point becomes &lt;em&gt;worse&lt;/em&gt; but, as iterations proceed, the residuals generally become smaller.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-05-15-rule-based-models/index_files/figure-html/committee-plot-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Ensemble predictions are made by averaging over the committee model predictions. This concludes in the &lt;em&gt;model-based&lt;/em&gt; prediction made by Cubist.&lt;/p&gt;
&lt;p&gt;After the model prediction, there is an option to conduct a post-model nearest-neighbor adjustment (Quinlan, 1993a). When predicting a new data point, the &lt;em&gt;K&lt;/em&gt;-nearest neighbors are found in the training set (along with their original predictions). If the training set predictions for the neighbors are denoted as &lt;span class=&#34;math inline&#34;&gt;\(t\)&lt;/span&gt;, the adjustment is:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;math display&#34;&gt;\[
\widehat{y}_{adj} = \frac{1}{K}\sum_{\ell=1}^K w_\ell \left[t_\ell  + \left(\widehat{y} - \widehat{t}_\ell \right)\right]
\]&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;where the weights &lt;span class=&#34;math inline&#34;&gt;\(w_\ell\)&lt;/span&gt; are based on the inverse distances (so that far points contribute less to the adjustment). The adjustment is large when the difference between the original and new predictions is large. As the difference between the predictions of the new sample and its closest neighbor increases, the adjustment becomes larger.&lt;/p&gt;
&lt;p&gt;Suppose our model used only square footage and longitude as predictors. The training data are shown below with a new prediction point represented as a large red square. The 6-closest neighbors in the training set are shown as red circles. The size of the circle represents the magnitude of the weight. This shows that the nearby points influence the adjustment more than distant points.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-05-15-rule-based-models/index_files/figure-html/nn-plot-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;This adjustment generally improves performance of the model. Interestingly, there is often a pattern when tuning where the 1-nearest neighbor model does much worse than using no adjustment but two or more neighbors do a much better job. One idea is that the use of a single neighbor is likely overfitting to the training set (as would occur with a more traditional K-NN model).&lt;/p&gt;
&lt;p&gt;Using both of these techniques, Cubist tends to produce &lt;em&gt;very&lt;/em&gt; competitive in terms of performance. The two primary parameters are the number of committee members and the number of nearest neighbors to use in the adjustment. The Cubist package can use the same fitted model to make predictions across the number of neighbors, so there is little computational cost when tuning this parameter.&lt;/p&gt;
&lt;p&gt;For the Sacramento data, a single model (e.g. one committee) consisted of 6 rules, each with its own linear regression model. For example, the first four rules are:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##   Rule 1: [198 cases, mean 5.187264, range 4.681241 to 5.759668, est err 0.131632]
## 
##     if
##  sqft &amp;lt;= 1712
##  latitude &amp;gt; 38.46639
##  latitude &amp;lt;= 38.61377
##  longitude &amp;gt; -121.5035
##     then
##  outcome = -48.838393 + 0.000376 sqft + 1.39 latitude
## 
##   Rule 2: [254 cases, mean 5.220439, range 4.477121 to 5.923762, est err 0.105572]
## 
##     if
##  sqft &amp;lt;= 1712
##  latitude &amp;gt; 38.61377
##  longitude &amp;gt; -121.5035
##  longitude &amp;lt;= -121.0504
##     then
##  outcome = 93.155414 + 0.000431 sqft + 0.78 longitude + 0.16 latitude
## 
##   Rule 3: [90 cases, mean 5.273133, range 4.851258 to 5.580444, est err 0.078920]
## 
##     if
##  sqft &amp;lt;= 1712
##  latitude &amp;lt;= 38.46639
##     then
##  outcome = 15.750124 + 0.000344 sqft + 0.09 longitude - 0.005 beds
##            + 0.005 baths
## 
##   Rule 4: [35 cases, mean 5.340909, range 5.018076 to 5.616476, est err 0.086056]
## 
##     if
##  sqft &amp;lt;= 1712
##  longitude &amp;lt;= -121.5035
##     then
##  outcome = 4.865655 + 0.000357 sqft&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;New samples being predicted will fall into one or more rule conditions and the final prediction is the average of all of the corresponding linear model predictions.&lt;/p&gt;
&lt;p&gt;If the model is run for 50 committees, a total of 271 rules were used across the committees with the average of 5.42 rules per committee member.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;rulefit&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;RuleFit&lt;/h2&gt;
&lt;p&gt;RuleFit models (Friedman and Popescu, 2008) are fairly simple in concept: use a tree ensemble to create a large set of rules, use the rules as binary predictors, then fit a regularized model that only includes the most important rule features.&lt;/p&gt;
&lt;p&gt;For example, if a boosted tree were used to generate rules, each path through each tree would generate a conditional statement that can be used to define a model predictor (as was shown above for &lt;code&gt;rpart&lt;/code&gt;). If an &lt;code&gt;xgboost&lt;/code&gt; model with 100 boosting iterations with a limit of three splits were used on the Sacramento data, an initial set of 609 rules were generated. Some examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqft &amp;gt;= 1594&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;longitude &amp;lt; -121.2 &amp;amp; latitude &amp;gt;= 38.73 &amp;amp; latitude &amp;gt;= 38.86&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;longitude &amp;gt;= -121.2 &amp;amp; longitude &amp;lt; -121 &amp;amp; baths &amp;lt; 2.75&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;longitude &amp;lt; -121.3 &amp;amp; sqft &amp;gt;= 1246 &amp;amp; type == &amp;quot;Multi_Family&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clearly, the rules show some redundancy; there tends to be a significant amount of similarity in the rules.&lt;/p&gt;
&lt;p&gt;These predictors are added to a regularized regression model (e.g. linear or logistic) that will conduct feature selection to remove unhelpful or redundant rules. Based on how much the model is penalized, the user can choose the number of rules that are contained in the final model. For example, depending on the penalty, the final rule set for the Sacramento data can be as large as hundreds or as small as a handful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-05-15-rule-based-models/index_files/figure-html/num-rules-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We can generate glmnet variable importance scores and then parameterize the importance in terms of the original predictors (instead of the rules). For example: for a penalty value of &lt;span class=&#34;math inline&#34;&gt;\(\lambda\)&lt;/span&gt; = 0.005:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-05-15-rule-based-models/index_files/figure-html/xrf-preds-small-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;RuleFit has many tuning parameters; it inherits them from the boosting model as well as one from &lt;code&gt;glmnet&lt;/code&gt; (the amount of lasso regularization). Fortunately, multiple predictions can be made across the lasso penalty using the same model object.&lt;/p&gt;
&lt;p&gt;All-in-all, RuleFit is a neat and powerful method for using rules as features. It is interesting to contrast this model and Cubist:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cubist creates rules as data subsets then estimates a linear regression models within each.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RuleFit creates rules as predictors then fits one (generalized) linear model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&#34;acknowledgments&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;Acknowledgments&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.rulequest.com/Personal/&#34;&gt;Ross Quinlan&lt;/a&gt; has been supportive of our efforts to publish the inner workings of C5.0 and Cubist. I’d like to thank him for his help and all of the excellent work he has done over the years.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;references&#34; class=&#34;section level2&#34;&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;p&gt;Friedman JH; Popescu BE (2008) “&lt;a href=&#34;https://scholar.google.com/scholar?hl=en&amp;amp;as_sdt=0%2C7&amp;amp;q=%22Predictive+learning+via+rule+ensembles%22&amp;amp;btnG=&#34;&gt;Predictive learning via rule ensembles&lt;/a&gt;.” _Annals of Applied Statistic_s, pp. 916-954.&lt;/p&gt;
&lt;p&gt;Kuhn M; Johnson K (2013) &lt;em&gt;&lt;a href=&#34;https://scholar.google.com/scholar?hl=en&amp;amp;as_sdt=0%2C7&amp;amp;q=%22Applied+Predictive+Modeling%22+author%3Akuhn&amp;amp;btnG=&#34;&gt;Applied Predictive Modeling&lt;/a&gt;&lt;/em&gt;, Springer. New York.&lt;/p&gt;
&lt;p&gt;Quinlan R (1979). “Discovering rules by induction from large collections of examples.” &lt;em&gt;Expert Systems in the Micro Electronics Age&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Quinlan R (1992). “&lt;a href=&#34;https://scholar.google.com/scholar?hl=en&amp;amp;as_sdt=0%2C7&amp;amp;q=%22Learning+with+continuous+classes%22&amp;amp;btnG=&#34;&gt;Learning with continuous classes&lt;/a&gt;.” &lt;em&gt;Proceedings of the 5th Australian Joint Conference On Artificial Intelligence&lt;/em&gt;, pp. 343-348.&lt;/p&gt;
&lt;p&gt;Quinlan R (1993a). “&lt;a href=&#34;https://scholar.google.com/scholar?hl=en&amp;amp;as_sdt=0%2C7&amp;amp;q=%22Combining+instance-based+and+model-based+learning%22&amp;amp;btnG=&#34;&gt;Combining instance-based and model-based learning&lt;/a&gt;.” &lt;em&gt;Proceedings of the Tenth International Conference on Machine Learning&lt;/em&gt;, pp. 236-243.&lt;/p&gt;
&lt;p&gt;Quinlan R (1993b). &lt;em&gt;&lt;a href=&#34;https://scholar.google.com/scholar?hl=en&amp;amp;as_sdt=0%2C7&amp;amp;q=%22C4.5%3A+Programs+for+Machine+Learning%22&amp;amp;btnG=&#34;&gt;C4.5: Programs for Machine Learning&lt;/a&gt;&lt;/em&gt;. Morgan Kaufmann Publishers.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2020/05/21/modern-rule-based-models/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Comparing Machine Learning Algorithms for Predicting Clothing Classes: Part 4</title>
      <link>https://rviews.rstudio.com/2020/03/24/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-4/</link>
      <pubDate>Tue, 24 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2020/03/24/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-4/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;Florianne Verkroost is a Ph.D. candidate at Nuffield College at the University of Oxford. She has a passion for data science and a background in mathematics and econometrics. She applies her interdisciplinary knowledge to computationally address societal problems of inequality.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is the fourth and final post in a series devoted to comparing different machine learning methods for predicting clothing categories from images using the Fashion MNIST data by Zalando. In the &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;first post&lt;/a&gt;, we prepared the data for analysis and built a Python deep learning neural network model to predict the clothing categories of the Fashion MNIST data. In &lt;a href=&#34;https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/&#34;&gt;Part 2&lt;/a&gt;, we used principal components analysis (PCA) to compress the clothing image data down from 784 to just 17 pixels. In &lt;a href=&#34;https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/&#34;&gt;Part 3&lt;/a&gt; we saw that gradient-boosted trees and random forests achieve relatively high accuracy on dimensionality-reduced data, although not as high as the neural network. In this post, we will fit a support vector machine, compare the findings from all models we have built and discuss the results. The R code for this post can be found on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;Github&lt;/a&gt; repository.&lt;/p&gt;
&lt;div id=&#34;support-vector-machine&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Support Vector Machine&lt;/h1&gt;
&lt;p&gt;Support vector machines (SVMs) provide another method for classifying the clothing categories in the Fashion MNIST data. To better understand what SVMs entail, we’ll have to go through some more complex explanations –mainly summarizing &lt;a href=&#34;http://faculty.marshall.usc.edu/gareth-james/ISL/&#34;&gt;James et. al. (2013)&lt;/a&gt;– so please bear with me! The figure below might help you in understanding the different classifiers I will discuss in the next sections (figures taken from &lt;a href=&#34;https://slideplayer.com/slide/3266197/&#34;&gt;here&lt;/a&gt;, &lt;a href=&#34;https://www.datasciencecentral.com/profiles/blogs/implementing-a-soft-margin-kernelized-support-vector-machine&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;https://www.exlservice.com/optimizing-healthcare-analytics-by-choosing-the-right-predictive-model&#34;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;classifiers.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;For an &lt;span class=&#34;math inline&#34;&gt;\(n \times p\)&lt;/span&gt; data matrix and binary outcome variable &lt;span class=&#34;math inline&#34;&gt;\(y_i \in \{-1, 1\}\)&lt;/span&gt;, a hyperplane is a flat affine subspace of dimension &lt;span class=&#34;math inline&#34;&gt;\(p - 1\)&lt;/span&gt; that divides the &lt;span class=&#34;math inline&#34;&gt;\(p\)&lt;/span&gt;-dimensional space into two halves, defined by &lt;span class=&#34;math inline&#34;&gt;\(\beta_0 + \beta_1 X_1 + \dots + \beta_p X_p\)&lt;/span&gt;. An observation in the test data is assigned an outcome class depending on which side of the perfectly separating hyperplane it lies, assuming that such a hyperplane exists. Cutoff &lt;span class=&#34;math inline&#34;&gt;\(t\)&lt;/span&gt; for an observation’s score &lt;span class=&#34;math inline&#34;&gt;\(\hat{f}(X) = \hat{\beta}_1 X_1 + \hat{\beta}_2 X_2 + \dots + \hat{\beta}_p X_p\)&lt;/span&gt; determines which class it will be assigned to. The further an observation is located from the hyperplane at zero, the more confident the classifier is about the class assignment. If existent, an infinite number of separating hyperplanes can be constructed. A good option in this case would be to use the maximal margin classifier (MMC), which maximizes the margin around the midline of the widest strip that can be inserted between the two outcome classes.&lt;/p&gt;
&lt;p&gt;If a perfectly separating hyperplane does not exist, “almost separating” hyperplanes can be used by means of the support vector classifier (SVC). The SVC extends the MMC as it does not require classes to be separable by a linear boundary by including slack variables &lt;span class=&#34;math inline&#34;&gt;\(\epsilon_i\)&lt;/span&gt; that allow some observations to be on the incorrect side of the margin or hyperplane. The extent to which incorrect placements are done is determined by tuning parameter cost &lt;span class=&#34;math inline&#34;&gt;\(C \geq \sum_{i=1}^{n} \epsilon_i\)&lt;/span&gt;, which thereby controls the bias-variance trade-off. The SVC is preferable over the MMC as it is more confident in class assignments due to the larger margins and ensures greater robustness as merely observations on the margin or violating the margin affect the hyperplane (James et al., 2013).&lt;/p&gt;
&lt;p&gt;Both MMCs and SVCs assume a linear boundary between the two classes of the outcome variable. Non-linearity can be addressed by enlarging the feature space using functions of predictors. Support vector machines combine SVCs with non-linear (e.g. radial, polynomial or sigmoid) Kernels &lt;span class=&#34;math inline&#34;&gt;\(K(x_i, x_{i&amp;#39;})\)&lt;/span&gt; to achieve efficient computations. Kernels are generalizations of inner products that quantify the similarity of two observations (James et al., 2013). Usually, the radial Kernel is selected for non-linear models as it provides a good default Kernel in the absence of prior knowledge of invariances regarding translations. The radial Kernel is defined as &lt;span class=&#34;math inline&#34;&gt;\(K(x_i, x_{i&amp;#39;})= \exp{(-\sigma \sum_{j=1}^{p} (x_{ij} - x_{i&amp;#39;j})^2)}\)&lt;/span&gt;, where &lt;span class=&#34;math inline&#34;&gt;\(\sigma\)&lt;/span&gt; is a positive constant that makes the fit more non-linear as it increases. Tuning &lt;span class=&#34;math inline&#34;&gt;\(C\)&lt;/span&gt; and &lt;span class=&#34;math inline&#34;&gt;\(\sigma\)&lt;/span&gt; is necessary to find the optimal trade-off between reducing the number of training errors and making the decision boundary more irregular (by increasing C). As SVMs only require the computation of &lt;span class=&#34;math inline&#34;&gt;\(\bigl(\begin{smallmatrix} n\\ 2 \end{smallmatrix}\bigr)\)&lt;/span&gt; Kernels for all distinct observation pairs, they greatly improve efficiency.&lt;/p&gt;
&lt;p&gt;As aforementioned, the parameters that need to be tuned are cost &lt;code&gt;C&lt;/code&gt; and, in the case of a radial Kernel, non-linearity constant &lt;code&gt;sigma&lt;/code&gt;. Let’s start by tuning these parameters using a random search algorithm, again making use of the &lt;code&gt;caret&lt;/code&gt; framework. We set the controls to perform 5-fold cross-validation and we use the &lt;code&gt;multiClassSummary()&lt;/code&gt; function from the &lt;code&gt;MLmetrics&lt;/code&gt; library to perform multi-class classification. We specify a radial Kernel, use accuracy as the performance metric&lt;a href=&#34;#fn1&#34; class=&#34;footnote-ref&#34; id=&#34;fnref1&#34;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; and let the algorithm perform a random search for the cost parameter &lt;code&gt;C&lt;/code&gt; over &lt;code&gt;pca.dims&lt;/code&gt; (=17) random values. Note that the random search algorithm only searches for values of &lt;code&gt;C&lt;/code&gt; while keeping a constant value for &lt;code&gt;sigma&lt;/code&gt;. Also, contrary to previous calls to &lt;code&gt;trainControl()&lt;/code&gt;, we now set &lt;code&gt;classProbs = FALSE&lt;/code&gt; because the base package used for estimating SVMs in &lt;code&gt;caret&lt;/code&gt;, &lt;code&gt;kernlab&lt;/code&gt;, leads to lower accuracies when specifying &lt;code&gt;classProbs = TRUE&lt;/code&gt; due to using a secondary regression model (also check &lt;a href=&#34;https://github.com/topepo/caret/issues/386&#34;&gt;this link for the Github issue&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;We begin with training the support vector machine using the PCA reduced training and test data sets &lt;code&gt;train.images.pca&lt;/code&gt; and &lt;code&gt;test.images.pca&lt;/code&gt; constructed in &lt;a href=&#34;https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/&#34;&gt;Part 2&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(MLmetrics)
svm_control = trainControl(method = &amp;quot;repeatedcv&amp;quot;,   
                            number = 5,  
                            repeats = 5, 
                            classProbs = FALSE,
                            allowParallel = TRUE, 
                            summaryFunction = multiClassSummary,
                           savePredictions = TRUE)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1234)
svm_rand_radial = train(label ~ ., 
                data = cbind(train.images.pca, label = train.classes),
                method = &amp;quot;svmRadial&amp;quot;, 
                trControl = svm_control, 
                tuneLength = pca.dims,
                metric = &amp;quot;Accuracy&amp;quot;)
svm_rand_radial$results[, c(&amp;quot;sigma&amp;quot;, &amp;quot;C&amp;quot;, &amp;#39;Accuracy&amp;#39;)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_rand_radial_print_accuracy.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We can check the model performance on both the training and test sets by means of different metrics using a custom function, &lt;code&gt;model_performance&lt;/code&gt;, which can be found in &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;this code on my Github&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mp.svm.rand.radial = model_performance(svm_rand_radial, train.images.pca, test.images.pca, 
                                       train.classes, test.classes, &amp;quot;svm_random_radial&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_rand_radial_mp.png&#34; height = &#34;1000&#34; width=&#34;1000&#34;&gt;&lt;/p&gt;
&lt;p&gt;The results show that the model is achieving relatively high accuracies of 88% and 87% on the training and test sets respectively, selecting &lt;code&gt;sigma = 0.040&lt;/code&gt; and &lt;code&gt;C = 32&lt;/code&gt; as the optimal parameters. Let’s have a look at which clothing categories are best and worst predicted by visualizing the confusion matrix. First, let’s compute the predictions for the training data. We need to use the out-of-bag predictions contained in the model object (&lt;code&gt;svm_rand_radial$pred&lt;/code&gt;) rather than the manually computed in-sample (non-out-of-bag) predictions for the training data computed using the &lt;code&gt;predict()&lt;/code&gt; function. Object &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; contains the predictions for all tuning parameter values specified by the user. However, we only need those predictions belonging to the optimal tuning parameter values. Therefore, we subset &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; to only contain those predictions and observations in indices &lt;code&gt;rows&lt;/code&gt;. Note that we convert &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; to a &lt;code&gt;data.table&lt;/code&gt; object to find these indices as computations on &lt;code&gt;data.table&lt;/code&gt; objects are much faster for large data (e.g. &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; has 4.5 million rows).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(data.table)
pred_dt = as.data.table(svm_rand_radial$pred[, names(svm_rand_radial$bestTune)]) 
names(pred_dt) = names(svm_rand_radial$bestTune)
index_list = lapply(1:ncol(svm_rand_radial$bestTune), function(x, DT, tune_opt){
  return(which(DT[, Reduce(`&amp;amp;`, lapply(.SD, `==`, tune_opt[, x])), .SDcols = names(tune_opt)[x]]))
}, pred_dt, svm_rand_radial$bestTune)
rows = Reduce(intersect, index_list)
pred_train = svm_rand_radial$pred$pred[rows]
trainY = svm_rand_radial$pred$obs[rows]
conf = table(pred_train, trainY)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we reshape the confusion matrix into a data frame with three columns: one for the true categories (&lt;code&gt;trainY&lt;/code&gt;), one for the predicted categories (&lt;code&gt;pred_train&lt;/code&gt;), and one for the proportion of correct predictions for the true category (&lt;code&gt;Freq&lt;/code&gt;). We plot this as a tile plot with a blue color scale where lighter values indicate larger proportions of matches between a particular combination of true and predicted categories, and darker values indicate a small proportion of matches between them. Note that we use the custom plotting theme &lt;code&gt;my_theme()&lt;/code&gt; as defined in the &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-2/&#34;&gt;second blog post of this series&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;conf = data.frame(conf / rowSums(conf))
ggplot() + 
  geom_tile(data = conf, aes(x = trainY, y = pred_train, fill = Freq)) + 
  labs(x = &amp;quot;Actual&amp;quot;, y = &amp;quot;Predicted&amp;quot;, fill = &amp;quot;Proportion&amp;quot;) +
  my_theme() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 
   scale_fill_continuous(breaks = seq(0, 1, 0.25)) +
  coord_fixed()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_radial_conf_plot.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We observe from this plot that most of the classes are predicted accurately as the light blue (high percentages of correct predictions) are on the diagonal of the tile plot. We can also observe that the categories that are most often mixed up include shirts, tops, pullovers and coats, which makes sense because these are all mostly upper body clothing parts having similar shapes. The model predicts trousers, bags, boots and sneakers well, given that these rows and columns are particularly dark except for the diagonal element. These results are in agreement with those from the random forest and gradient-boosted trees from &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-3/&#34;&gt;the previous blog post of this series&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, we repeat the above process for fitting a support vector machine but instead of a random search for the optimal parameters, we perform a grid search. As such, we can prespecify values to evaluate the model at, not only for &lt;code&gt;C&lt;/code&gt; but also for &lt;code&gt;sigma&lt;/code&gt;. We define the grid values in &lt;code&gt;grid_radial&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;grid_radial = expand.grid(sigma = c(.01, 0.04, 0.1), C = c(0.01, 10, 32, 70, 150))
set.seed(1234)
svm_grid_radial = train(label ~ ., 
                              data = cbind(train.images.pca, label = train.classes),
                              method = &amp;quot;svmRadial&amp;quot;, 
                              trControl = svm_control, 
                              tuneGrid = grid_radial,
                              metric = &amp;quot;Accuracy&amp;quot;)
svm_grid_radial$results[, c(&amp;quot;sigma&amp;quot;, &amp;quot;C&amp;quot;, &amp;#39;Accuracy&amp;#39;)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_grid_radial_print_accuracy.png&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mp.svm.grid.radial = model_performance(svm_grid_radial, train.images.pca, test.images.pca, 
                                       train.classes, test.classes, &amp;quot;svm_grid_radial&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_grid_radial_mp.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The grid search selects the same optimal parameter values as the random search (&lt;code&gt;C=32&lt;/code&gt; and &lt;code&gt;sigma = 0.040&lt;/code&gt;), therefore also resulting in 88% and 87% training and test accuracies. To get an idea on how &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;sigma&lt;/code&gt; influence the training set accuracy, we plot the cross-validation accuracy as a function of &lt;code&gt;C&lt;/code&gt;, with separate lines for each value of &lt;code&gt;sigma&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot() + 
  my_theme() +
  geom_line(data = svm_grid_radial$results, aes(x = C, y = Accuracy, color = factor(sigma))) +
  geom_point(data = svm_grid_radial$results, aes(x = C, y = Accuracy, color = factor(sigma))) +
  labs(x = &amp;quot;Cost&amp;quot;, y = &amp;quot;Cross-Validation Accuracy&amp;quot;, color = &amp;quot;Sigma&amp;quot;) +
  ggtitle(&amp;#39;Relationship between cross-validation accuracy and values of cost and sigma&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_grid_radial_cost_sigma.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The plot shows that the green line (&lt;code&gt;sigma = 0.04&lt;/code&gt;) has the highest cross-validation accuracy for all values of &lt;code&gt;C&lt;/code&gt; except for smaller values of &lt;code&gt;C&lt;/code&gt; such as 0.01 and 10. Although the accuracy at &lt;code&gt;C=10&lt;/code&gt; and &lt;code&gt;sigma = 0.1&lt;/code&gt; (blue line) comes close, the highest overall accuracy achieved is for &lt;code&gt;C=32&lt;/code&gt; and &lt;code&gt;sigma=32&lt;/code&gt; (green line).&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;wrapping-up&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Wrapping Up&lt;/h1&gt;
&lt;p&gt;To compare the models we have estimated throughout this series of blog posts, we can look at the resampled accuracies of the models. We can do this in our case because we set the same seed of 1234 before training each model.&lt;a href=&#34;#fn2&#34; class=&#34;footnote-ref&#34; id=&#34;fnref2&#34;&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; Essentially, resampling is an important tool to validate our models, and to what extent they are generalizeable onto data they have not been trained on. We used five repeats of five-fold cross-validation, which means that the training data was divided into five random subsets, and that throughout five iterations (“folds”) the model was trained on four of these subsets and tested on the remaining subset (changing with every fold), and that this whole process has been repeated five times. The goal of these repetitions of k-fold cross-validation is to reduce the bias in the estimator, given that the folds in non-repeated cross-validation are not independent (as data used for training at one fold is used for testing at another fold). As we performed five repeats of five-fold cross-validation, we can essentially obtain 5*5=25 accuracies per model. Let’s compare these resampled accuracies visually by means of a boxplot. First, we create a list of all models estimated, including the random forests, gradient-boosted trees and support vector machines. We then compute the resampled accuracies using the &lt;code&gt;resamples()&lt;/code&gt; function from the &lt;code&gt;caret&lt;/code&gt; package. From the resulting object, &lt;code&gt;resamp&lt;/code&gt;, we only keep the columns containing the resample unit (e.g. &lt;code&gt;Fold1.Rep1&lt;/code&gt;) and the five columns containing the accuracies for each of the five models. We melt this into a long format and from the result, &lt;code&gt;plotdf&lt;/code&gt;, we remove the &lt;code&gt;~Accuracy&lt;/code&gt; part from the strings in column &lt;code&gt;Model&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reshape2)
model_list = list(rf_rand, rf_grid, xgb_tune, svm_rand_radial, svm_grid_radial)
names(model_list) = c(paste0(&amp;#39;Random forest &amp;#39;, c(&amp;quot;(random &amp;quot;, &amp;quot;(grid &amp;quot;), &amp;quot;search)&amp;quot;), &amp;quot;Gradient-boosted trees&amp;quot;, 
                      paste0(&amp;#39;Support vector machine &amp;#39;, c(&amp;quot;(random &amp;quot;, &amp;quot;(grid &amp;quot;), &amp;quot;search)&amp;quot;))
resamp = resamples(model_list)
accuracy_variables = names(resamp$values)[grepl(&amp;quot;Accuracy&amp;quot;, names(resamp$values))]
plotdf = melt(resamp$values[, c(&amp;#39;Resample&amp;#39;, accuracy_variables)], 
              id = &amp;quot;Resample&amp;quot;, value.name = &amp;quot;Accuracy&amp;quot;, variable.name = &amp;quot;Model&amp;quot;)
plotdf$Model = gsub(&amp;quot;~.*&amp;quot;,&amp;quot;&amp;quot;, plotdf$Model)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we create a boxplot with the estimated models on the x-axis and the accuracy on the y-axis.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot() +
  geom_boxplot(data = plotdf, aes(x = Model, y = Accuracy, color = Model)) +
  ggtitle(&amp;#39;Resampled accuracy for machine learning models estimated&amp;#39;) + 
  my_theme() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  labs(x = NULL, color = NULL) +
  guides(color = FALSE)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;resampled_accuracy_models_color.png&#34; height = &#34;700&#34; width=&#34;900&#34;&gt;&lt;/p&gt;
&lt;p&gt;We observe from these box plots that the support vector machines perform best, followed by the gradient-boosted trees and the random forests. Let’s also take a look at the other performance metrics from all models we have looked at.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mp.df = rbind(mp.rf.rand, mp.rf.grid, mp.xgb, mp.svm.rand.radial, mp.svm.grid.radial, mp.svm.grid.linear)
mp.df[order(mp.df$accuracy_test, decreasing = TRUE), ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;combine_mp_models.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;After taking measures to reduce overfitting, the convolutional neural network from &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;the first blog post of this series&lt;/a&gt; achieved training and test set accuracies of 89.4% and 88.8% respectively. The random and grid search for the best value of &lt;code&gt;mtry&lt;/code&gt; in the random forests resulted in the selection of &lt;code&gt;mtry=5&lt;/code&gt;. The grid search performed better on the training set than the random search on the basis of all metrics except recall (i.e. sensitivity), and better on the test set on all metrics except precision (i.e. positive predictive value). The test set accuracies achieved by the random search and grid search were 84.7% and 84.8% respectively. The gradient-boosted decision trees performed slightly better than the random forests on all metrics and achieved a test set accuracy of 85.5%. Both tree-based models more often misclassified pullovers, shirts and coats, while correctly classifying trousers, boots, bags and sneakers. The random forests and gradient-boosted trees are however outperformed by the support vector machine with radial Kernel specification with tuning parameter values of &lt;code&gt;C=32&lt;/code&gt; and &lt;code&gt;sigma=0.040&lt;/code&gt;: this model achieved 86.9% test set accuracy upon a random search for the best parameters. The grid search resulted in slightly worse test set performance, but better training set performance in terms of all metrics except accuracy. Nonetheless, none of the models estimated beats the convolutional neural network from &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;the first blog post of this series&lt;/a&gt;, neither in performance nor computational time and feasibility. However, the differences in test set performance are only small: the convolutional neural network achieved 88.8% test set accuracy, compared to 86.9% test set accuracy achieved by the support vector machine with radial Kernel. This shows that we do not always need to resort to deep learning to obtain high accuracies, but that we can also perform image classification to a reasonable standard using basic machine learning models with dimensionality-reduced data.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;footnotes&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn1&#34;&gt;&lt;p&gt;Just as a side note, accuracy may not be a good model performance metric in some cases. As the Fashion MNIST data has balanced categories (i.e. each category has the same number of observations), accuracy can be a good measure of model performance. However, in the case of unbalanced data, accuracy may be a misleading metric (&lt;a href=&#34;https://towardsdatascience.com/accuracy-paradox-897a69e2dd9b&#34;&gt;“accuracy paradox”&lt;/a&gt;). Imagine for example that in a binary classification problem of 100 instances, there are 99 observations of class 0 and 1 observation of class 1. If the predictions are 1 for each observation, the model performs with 99% accuracy. As this may be misleading, recall and precision are often used instead. Have a look at &lt;a href=&#34;https://towardsdatascience.com/whats-the-deal-with-accuracy-precision-recall-and-f1-f5d8b4db1021&#34;&gt;this blog post&lt;/a&gt; if you are unsure what these performance metrics entail.&lt;a href=&#34;#fnref1&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&#34;fn2&#34;&gt;&lt;p&gt;Note that in order to compare the resampled accuracies of different models, they need to have been trained with the same seed, and they need to have the same training method and control settings as specified in the &lt;code&gt;trainControl()&lt;/code&gt; function. In our case, the method used is &lt;code&gt;repeatedcv&lt;/code&gt;, and so all models should have been trained with five repeats (&lt;code&gt;repeats = 5&lt;/code&gt;) of five-fold cross-validation (&lt;code&gt;number = 5&lt;/code&gt;). Note that the gradient-boosted model in the &lt;a href=&#34;https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/&#34;&gt;previous post of this series&lt;/a&gt; was trained with non-repeated five-fold cross-validation (&lt;code&gt;method = &amp;quot;cv&amp;quot;&lt;/code&gt;). In order to compare this model with the random forests and support vector machines, the method in &lt;code&gt;trainControl()&lt;/code&gt; should be changed to &lt;code&gt;method = &amp;quot;repeatedcv&amp;quot;&lt;/code&gt; and the number of repeats should be five: &lt;code&gt;repeats = 5&lt;/code&gt;. This should be the same for all models trained in order to compute resampled accuracies.&lt;a href=&#34;#fnref2&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2020/03/24/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-4/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Comparing Machine Learning Algorithms for Predicting Clothing Classes: Part 4</title>
      <link>https://rviews.rstudio.com/2020/03/24/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-4/</link>
      <pubDate>Tue, 24 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2020/03/24/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-4/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;Florianne Verkroost is a Ph.D. candidate at Nuffield College at the University of Oxford. She has a passion for data science and a background in mathematics and econometrics. She applies her interdisciplinary knowledge to computationally address societal problems of inequality.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is the fourth and final post in a series devoted to comparing different machine learning methods for predicting clothing categories from images using the Fashion MNIST data by Zalando. In the &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;first post&lt;/a&gt;, we prepared the data for analysis and built a Python deep learning neural network model to predict the clothing categories of the Fashion MNIST data. In &lt;a href=&#34;https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/&#34;&gt;Part 2&lt;/a&gt;, we used principal components analysis (PCA) to compress the clothing image data down from 784 to just 17 pixels. In &lt;a href=&#34;https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/&#34;&gt;Part 3&lt;/a&gt; we saw that gradient-boosted trees and random forests achieve relatively high accuracy on dimensionality-reduced data, although not as high as the neural network. In this post, we will fit a support vector machine, compare the findings from all models we have built and discuss the results. The R code for this post can be found on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;Github&lt;/a&gt; repository.&lt;/p&gt;
&lt;div id=&#34;support-vector-machine&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Support Vector Machine&lt;/h1&gt;
&lt;p&gt;Support vector machines (SVMs) provide another method for classifying the clothing categories in the Fashion MNIST data. To better understand what SVMs entail, we’ll have to go through some more complex explanations –mainly summarizing &lt;a href=&#34;http://faculty.marshall.usc.edu/gareth-james/ISL/&#34;&gt;James et. al. (2013)&lt;/a&gt;– so please bear with me! The figure below might help you in understanding the different classifiers I will discuss in the next sections (figures taken from &lt;a href=&#34;https://slideplayer.com/slide/3266197/&#34;&gt;here&lt;/a&gt;, &lt;a href=&#34;https://www.datasciencecentral.com/profiles/blogs/implementing-a-soft-margin-kernelized-support-vector-machine&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;https://www.exlservice.com/optimizing-healthcare-analytics-by-choosing-the-right-predictive-model&#34;&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;classifiers.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;For an &lt;span class=&#34;math inline&#34;&gt;\(n \times p\)&lt;/span&gt; data matrix and binary outcome variable &lt;span class=&#34;math inline&#34;&gt;\(y_i \in \{-1, 1\}\)&lt;/span&gt;, a hyperplane is a flat affine subspace of dimension &lt;span class=&#34;math inline&#34;&gt;\(p - 1\)&lt;/span&gt; that divides the &lt;span class=&#34;math inline&#34;&gt;\(p\)&lt;/span&gt;-dimensional space into two halves, defined by &lt;span class=&#34;math inline&#34;&gt;\(\beta_0 + \beta_1 X_1 + \dots + \beta_p X_p\)&lt;/span&gt;. An observation in the test data is assigned an outcome class depending on which side of the perfectly separating hyperplane it lies, assuming that such a hyperplane exists. Cutoff &lt;span class=&#34;math inline&#34;&gt;\(t\)&lt;/span&gt; for an observation’s score &lt;span class=&#34;math inline&#34;&gt;\(\hat{f}(X) = \hat{\beta}_1 X_1 + \hat{\beta}_2 X_2 + \dots + \hat{\beta}_p X_p\)&lt;/span&gt; determines which class it will be assigned to. The further an observation is located from the hyperplane at zero, the more confident the classifier is about the class assignment. If existent, an infinite number of separating hyperplanes can be constructed. A good option in this case would be to use the maximal margin classifier (MMC), which maximizes the margin around the midline of the widest strip that can be inserted between the two outcome classes.&lt;/p&gt;
&lt;p&gt;If a perfectly separating hyperplane does not exist, “almost separating” hyperplanes can be used by means of the support vector classifier (SVC). The SVC extends the MMC as it does not require classes to be separable by a linear boundary by including slack variables &lt;span class=&#34;math inline&#34;&gt;\(\epsilon_i\)&lt;/span&gt; that allow some observations to be on the incorrect side of the margin or hyperplane. The extent to which incorrect placements are done is determined by tuning parameter cost &lt;span class=&#34;math inline&#34;&gt;\(C \geq \sum_{i=1}^{n} \epsilon_i\)&lt;/span&gt;, which thereby controls the bias-variance trade-off. The SVC is preferable over the MMC as it is more confident in class assignments due to the larger margins and ensures greater robustness as merely observations on the margin or violating the margin affect the hyperplane (James et al., 2013).&lt;/p&gt;
&lt;p&gt;Both MMCs and SVCs assume a linear boundary between the two classes of the outcome variable. Non-linearity can be addressed by enlarging the feature space using functions of predictors. Support vector machines combine SVCs with non-linear (e.g. radial, polynomial or sigmoid) Kernels &lt;span class=&#34;math inline&#34;&gt;\(K(x_i, x_{i&amp;#39;})\)&lt;/span&gt; to achieve efficient computations. Kernels are generalizations of inner products that quantify the similarity of two observations (James et al., 2013). Usually, the radial Kernel is selected for non-linear models as it provides a good default Kernel in the absence of prior knowledge of invariances regarding translations. The radial Kernel is defined as &lt;span class=&#34;math inline&#34;&gt;\(K(x_i, x_{i&amp;#39;})= \exp{(-\sigma \sum_{j=1}^{p} (x_{ij} - x_{i&amp;#39;j})^2)}\)&lt;/span&gt;, where &lt;span class=&#34;math inline&#34;&gt;\(\sigma\)&lt;/span&gt; is a positive constant that makes the fit more non-linear as it increases. Tuning &lt;span class=&#34;math inline&#34;&gt;\(C\)&lt;/span&gt; and &lt;span class=&#34;math inline&#34;&gt;\(\sigma\)&lt;/span&gt; is necessary to find the optimal trade-off between reducing the number of training errors and making the decision boundary more irregular (by increasing C). As SVMs only require the computation of &lt;span class=&#34;math inline&#34;&gt;\(\bigl(\begin{smallmatrix} n\\ 2 \end{smallmatrix}\bigr)\)&lt;/span&gt; Kernels for all distinct observation pairs, they greatly improve efficiency.&lt;/p&gt;
&lt;p&gt;As aforementioned, the parameters that need to be tuned are cost &lt;code&gt;C&lt;/code&gt; and, in the case of a radial Kernel, non-linearity constant &lt;code&gt;sigma&lt;/code&gt;. Let’s start by tuning these parameters using a random search algorithm, again making use of the &lt;code&gt;caret&lt;/code&gt; framework. We set the controls to perform 5-fold cross-validation and we use the &lt;code&gt;multiClassSummary()&lt;/code&gt; function from the &lt;code&gt;MLmetrics&lt;/code&gt; library to perform multi-class classification. We specify a radial Kernel, use accuracy as the performance metric&lt;a href=&#34;#fn1&#34; class=&#34;footnote-ref&#34; id=&#34;fnref1&#34;&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; and let the algorithm perform a random search for the cost parameter &lt;code&gt;C&lt;/code&gt; over &lt;code&gt;pca.dims&lt;/code&gt; (=17) random values. Note that the random search algorithm only searches for values of &lt;code&gt;C&lt;/code&gt; while keeping a constant value for &lt;code&gt;sigma&lt;/code&gt;. Also, contrary to previous calls to &lt;code&gt;trainControl()&lt;/code&gt;, we now set &lt;code&gt;classProbs = FALSE&lt;/code&gt; because the base package used for estimating SVMs in &lt;code&gt;caret&lt;/code&gt;, &lt;code&gt;kernlab&lt;/code&gt;, leads to lower accuracies when specifying &lt;code&gt;classProbs = TRUE&lt;/code&gt; due to using a secondary regression model (also check &lt;a href=&#34;https://github.com/topepo/caret/issues/386&#34;&gt;this link for the Github issue&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;We begin with training the support vector machine using the PCA reduced training and test data sets &lt;code&gt;train.images.pca&lt;/code&gt; and &lt;code&gt;test.images.pca&lt;/code&gt; constructed in &lt;a href=&#34;https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/&#34;&gt;Part 2&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(MLmetrics)
svm_control = trainControl(method = &amp;quot;repeatedcv&amp;quot;,   
                            number = 5,  
                            repeats = 5, 
                            classProbs = FALSE,
                            allowParallel = TRUE, 
                            summaryFunction = multiClassSummary,
                           savePredictions = TRUE)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;set.seed(1234)
svm_rand_radial = train(label ~ ., 
                data = cbind(train.images.pca, label = train.classes),
                method = &amp;quot;svmRadial&amp;quot;, 
                trControl = svm_control, 
                tuneLength = pca.dims,
                metric = &amp;quot;Accuracy&amp;quot;)
svm_rand_radial$results[, c(&amp;quot;sigma&amp;quot;, &amp;quot;C&amp;quot;, &amp;#39;Accuracy&amp;#39;)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_rand_radial_print_accuracy.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We can check the model performance on both the training and test sets by means of different metrics using a custom function, &lt;code&gt;model_performance&lt;/code&gt;, which can be found in &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;this code on my Github&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mp.svm.rand.radial = model_performance(svm_rand_radial, train.images.pca, test.images.pca, 
                                       train.classes, test.classes, &amp;quot;svm_random_radial&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_rand_radial_mp.png&#34; height = &#34;1000&#34; width=&#34;1000&#34;&gt;&lt;/p&gt;
&lt;p&gt;The results show that the model is achieving relatively high accuracies of 88% and 87% on the training and test sets respectively, selecting &lt;code&gt;sigma = 0.040&lt;/code&gt; and &lt;code&gt;C = 32&lt;/code&gt; as the optimal parameters. Let’s have a look at which clothing categories are best and worst predicted by visualizing the confusion matrix. First, let’s compute the predictions for the training data. We need to use the out-of-bag predictions contained in the model object (&lt;code&gt;svm_rand_radial$pred&lt;/code&gt;) rather than the manually computed in-sample (non-out-of-bag) predictions for the training data computed using the &lt;code&gt;predict()&lt;/code&gt; function. Object &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; contains the predictions for all tuning parameter values specified by the user. However, we only need those predictions belonging to the optimal tuning parameter values. Therefore, we subset &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; to only contain those predictions and observations in indices &lt;code&gt;rows&lt;/code&gt;. Note that we convert &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; to a &lt;code&gt;data.table&lt;/code&gt; object to find these indices as computations on &lt;code&gt;data.table&lt;/code&gt; objects are much faster for large data (e.g. &lt;code&gt;svm_rand_radial$pred&lt;/code&gt; has 4.5 million rows).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(data.table)
pred_dt = as.data.table(svm_rand_radial$pred[, names(svm_rand_radial$bestTune)]) 
names(pred_dt) = names(svm_rand_radial$bestTune)
index_list = lapply(1:ncol(svm_rand_radial$bestTune), function(x, DT, tune_opt){
  return(which(DT[, Reduce(`&amp;amp;`, lapply(.SD, `==`, tune_opt[, x])), .SDcols = names(tune_opt)[x]]))
}, pred_dt, svm_rand_radial$bestTune)
rows = Reduce(intersect, index_list)
pred_train = svm_rand_radial$pred$pred[rows]
trainY = svm_rand_radial$pred$obs[rows]
conf = table(pred_train, trainY)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we reshape the confusion matrix into a data frame with three columns: one for the true categories (&lt;code&gt;trainY&lt;/code&gt;), one for the predicted categories (&lt;code&gt;pred_train&lt;/code&gt;), and one for the proportion of correct predictions for the true category (&lt;code&gt;Freq&lt;/code&gt;). We plot this as a tile plot with a blue color scale where lighter values indicate larger proportions of matches between a particular combination of true and predicted categories, and darker values indicate a small proportion of matches between them. Note that we use the custom plotting theme &lt;code&gt;my_theme()&lt;/code&gt; as defined in the &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-2/&#34;&gt;second blog post of this series&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;conf = data.frame(conf / rowSums(conf))
ggplot() + 
  geom_tile(data = conf, aes(x = trainY, y = pred_train, fill = Freq)) + 
  labs(x = &amp;quot;Actual&amp;quot;, y = &amp;quot;Predicted&amp;quot;, fill = &amp;quot;Proportion&amp;quot;) +
  my_theme() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) + 
   scale_fill_continuous(breaks = seq(0, 1, 0.25)) +
  coord_fixed()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_radial_conf_plot.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We observe from this plot that most of the classes are predicted accurately as the light blue (high percentages of correct predictions) are on the diagonal of the tile plot. We can also observe that the categories that are most often mixed up include shirts, tops, pullovers and coats, which makes sense because these are all mostly upper body clothing parts having similar shapes. The model predicts trousers, bags, boots and sneakers well, given that these rows and columns are particularly dark except for the diagonal element. These results are in agreement with those from the random forest and gradient-boosted trees from &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-3/&#34;&gt;the previous blog post of this series&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, we repeat the above process for fitting a support vector machine but instead of a random search for the optimal parameters, we perform a grid search. As such, we can prespecify values to evaluate the model at, not only for &lt;code&gt;C&lt;/code&gt; but also for &lt;code&gt;sigma&lt;/code&gt;. We define the grid values in &lt;code&gt;grid_radial&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;grid_radial = expand.grid(sigma = c(.01, 0.04, 0.1), C = c(0.01, 10, 32, 70, 150))
set.seed(1234)
svm_grid_radial = train(label ~ ., 
                              data = cbind(train.images.pca, label = train.classes),
                              method = &amp;quot;svmRadial&amp;quot;, 
                              trControl = svm_control, 
                              tuneGrid = grid_radial,
                              metric = &amp;quot;Accuracy&amp;quot;)
svm_grid_radial$results[, c(&amp;quot;sigma&amp;quot;, &amp;quot;C&amp;quot;, &amp;#39;Accuracy&amp;#39;)]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_grid_radial_print_accuracy.png&#34; /&gt;&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mp.svm.grid.radial = model_performance(svm_grid_radial, train.images.pca, test.images.pca, 
                                       train.classes, test.classes, &amp;quot;svm_grid_radial&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_grid_radial_mp.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The grid search selects the same optimal parameter values as the random search (&lt;code&gt;C=32&lt;/code&gt; and &lt;code&gt;sigma = 0.040&lt;/code&gt;), therefore also resulting in 88% and 87% training and test accuracies. To get an idea on how &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;sigma&lt;/code&gt; influence the training set accuracy, we plot the cross-validation accuracy as a function of &lt;code&gt;C&lt;/code&gt;, with separate lines for each value of &lt;code&gt;sigma&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot() + 
  my_theme() +
  geom_line(data = svm_grid_radial$results, aes(x = C, y = Accuracy, color = factor(sigma))) +
  geom_point(data = svm_grid_radial$results, aes(x = C, y = Accuracy, color = factor(sigma))) +
  labs(x = &amp;quot;Cost&amp;quot;, y = &amp;quot;Cross-Validation Accuracy&amp;quot;, color = &amp;quot;Sigma&amp;quot;) +
  ggtitle(&amp;#39;Relationship between cross-validation accuracy and values of cost and sigma&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;svm_grid_radial_cost_sigma.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;The plot shows that the green line (&lt;code&gt;sigma = 0.04&lt;/code&gt;) has the highest cross-validation accuracy for all values of &lt;code&gt;C&lt;/code&gt; except for smaller values of &lt;code&gt;C&lt;/code&gt; such as 0.01 and 10. Although the accuracy at &lt;code&gt;C=10&lt;/code&gt; and &lt;code&gt;sigma = 0.1&lt;/code&gt; (blue line) comes close, the highest overall accuracy achieved is for &lt;code&gt;C=32&lt;/code&gt; and &lt;code&gt;sigma=32&lt;/code&gt; (green line).&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;wrapping-up&#34; class=&#34;section level1&#34;&gt;
&lt;h1&gt;Wrapping Up&lt;/h1&gt;
&lt;p&gt;To compare the models we have estimated throughout this series of blog posts, we can look at the resampled accuracies of the models. We can do this in our case because we set the same seed of 1234 before training each model.&lt;a href=&#34;#fn2&#34; class=&#34;footnote-ref&#34; id=&#34;fnref2&#34;&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/a&gt; Essentially, resampling is an important tool to validate our models, and to what extent they are generalizeable onto data they have not been trained on. We used five repeats of five-fold cross-validation, which means that the training data was divided into five random subsets, and that throughout five iterations (“folds”) the model was trained on four of these subsets and tested on the remaining subset (changing with every fold), and that this whole process has been repeated five times. The goal of these repetitions of k-fold cross-validation is to reduce the bias in the estimator, given that the folds in non-repeated cross-validation are not independent (as data used for training at one fold is used for testing at another fold). As we performed five repeats of five-fold cross-validation, we can essentially obtain 5*5=25 accuracies per model. Let’s compare these resampled accuracies visually by means of a boxplot. First, we create a list of all models estimated, including the random forests, gradient-boosted trees and support vector machines. We then compute the resampled accuracies using the &lt;code&gt;resamples()&lt;/code&gt; function from the &lt;code&gt;caret&lt;/code&gt; package. From the resulting object, &lt;code&gt;resamp&lt;/code&gt;, we only keep the columns containing the resample unit (e.g. &lt;code&gt;Fold1.Rep1&lt;/code&gt;) and the five columns containing the accuracies for each of the five models. We melt this into a long format and from the result, &lt;code&gt;plotdf&lt;/code&gt;, we remove the &lt;code&gt;~Accuracy&lt;/code&gt; part from the strings in column &lt;code&gt;Model&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(reshape2)
model_list = list(rf_rand, rf_grid, xgb_tune, svm_rand_radial, svm_grid_radial)
names(model_list) = c(paste0(&amp;#39;Random forest &amp;#39;, c(&amp;quot;(random &amp;quot;, &amp;quot;(grid &amp;quot;), &amp;quot;search)&amp;quot;), &amp;quot;Gradient-boosted trees&amp;quot;, 
                      paste0(&amp;#39;Support vector machine &amp;#39;, c(&amp;quot;(random &amp;quot;, &amp;quot;(grid &amp;quot;), &amp;quot;search)&amp;quot;))
resamp = resamples(model_list)
accuracy_variables = names(resamp$values)[grepl(&amp;quot;Accuracy&amp;quot;, names(resamp$values))]
plotdf = melt(resamp$values[, c(&amp;#39;Resample&amp;#39;, accuracy_variables)], 
              id = &amp;quot;Resample&amp;quot;, value.name = &amp;quot;Accuracy&amp;quot;, variable.name = &amp;quot;Model&amp;quot;)
plotdf$Model = gsub(&amp;quot;~.*&amp;quot;,&amp;quot;&amp;quot;, plotdf$Model)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we create a boxplot with the estimated models on the x-axis and the accuracy on the y-axis.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot() +
  geom_boxplot(data = plotdf, aes(x = Model, y = Accuracy, color = Model)) +
  ggtitle(&amp;#39;Resampled accuracy for machine learning models estimated&amp;#39;) + 
  my_theme() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  labs(x = NULL, color = NULL) +
  guides(color = FALSE)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;resampled_accuracy_models_color.png&#34; height = &#34;700&#34; width=&#34;900&#34;&gt;&lt;/p&gt;
&lt;p&gt;We observe from these box plots that the support vector machines perform best, followed by the gradient-boosted trees and the random forests. Let’s also take a look at the other performance metrics from all models we have looked at.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;mp.df = rbind(mp.rf.rand, mp.rf.grid, mp.xgb, mp.svm.rand.radial, mp.svm.grid.radial, mp.svm.grid.linear)
mp.df[order(mp.df$accuracy_test, decreasing = TRUE), ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;combine_mp_models.png&#34; /&gt;&lt;/p&gt;
&lt;p&gt;After taking measures to reduce overfitting, the convolutional neural network from &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;the first blog post of this series&lt;/a&gt; achieved training and test set accuracies of 89.4% and 88.8% respectively. The random and grid search for the best value of &lt;code&gt;mtry&lt;/code&gt; in the random forests resulted in the selection of &lt;code&gt;mtry=5&lt;/code&gt;. The grid search performed better on the training set than the random search on the basis of all metrics except recall (i.e. sensitivity), and better on the test set on all metrics except precision (i.e. positive predictive value). The test set accuracies achieved by the random search and grid search were 84.7% and 84.8% respectively. The gradient-boosted decision trees performed slightly better than the random forests on all metrics and achieved a test set accuracy of 85.5%. Both tree-based models more often misclassified pullovers, shirts and coats, while correctly classifying trousers, boots, bags and sneakers. The random forests and gradient-boosted trees are however outperformed by the support vector machine with radial Kernel specification with tuning parameter values of &lt;code&gt;C=32&lt;/code&gt; and &lt;code&gt;sigma=0.040&lt;/code&gt;: this model achieved 86.9% test set accuracy upon a random search for the best parameters. The grid search resulted in slightly worse test set performance, but better training set performance in terms of all metrics except accuracy. Nonetheless, none of the models estimated beats the convolutional neural network from &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;the first blog post of this series&lt;/a&gt;, neither in performance nor computational time and feasibility. However, the differences in test set performance are only small: the convolutional neural network achieved 88.8% test set accuracy, compared to 86.9% test set accuracy achieved by the support vector machine with radial Kernel. This shows that we do not always need to resort to deep learning to obtain high accuracies, but that we can also perform image classification to a reasonable standard using basic machine learning models with dimensionality-reduced data.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;footnotes&#34;&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li id=&#34;fn1&#34;&gt;&lt;p&gt;Just as a side note, accuracy may not be a good model performance metric in some cases. As the Fashion MNIST data has balanced categories (i.e. each category has the same number of observations), accuracy can be a good measure of model performance. However, in the case of unbalanced data, accuracy may be a misleading metric (&lt;a href=&#34;https://towardsdatascience.com/accuracy-paradox-897a69e2dd9b&#34;&gt;“accuracy paradox”&lt;/a&gt;). Imagine for example that in a binary classification problem of 100 instances, there are 99 observations of class 0 and 1 observation of class 1. If the predictions are 1 for each observation, the model performs with 99% accuracy. As this may be misleading, recall and precision are often used instead. Have a look at &lt;a href=&#34;https://towardsdatascience.com/whats-the-deal-with-accuracy-precision-recall-and-f1-f5d8b4db1021&#34;&gt;this blog post&lt;/a&gt; if you are unsure what these performance metrics entail.&lt;a href=&#34;#fnref1&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li id=&#34;fn2&#34;&gt;&lt;p&gt;Note that in order to compare the resampled accuracies of different models, they need to have been trained with the same seed, and they need to have the same training method and control settings as specified in the &lt;code&gt;trainControl()&lt;/code&gt; function. In our case, the method used is &lt;code&gt;repeatedcv&lt;/code&gt;, and so all models should have been trained with five repeats (&lt;code&gt;repeats = 5&lt;/code&gt;) of five-fold cross-validation (&lt;code&gt;number = 5&lt;/code&gt;). Note that the gradient-boosted model in the &lt;a href=&#34;https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/&#34;&gt;previous post of this series&lt;/a&gt; was trained with non-repeated five-fold cross-validation (&lt;code&gt;method = &amp;quot;cv&amp;quot;&lt;/code&gt;). In order to compare this model with the random forests and support vector machines, the method in &lt;code&gt;trainControl()&lt;/code&gt; should be changed to &lt;code&gt;method = &amp;quot;repeatedcv&amp;quot;&lt;/code&gt; and the number of repeats should be five: &lt;code&gt;repeats = 5&lt;/code&gt;. This should be the same for all models trained in order to compute resampled accuracies.&lt;a href=&#34;#fnref2&#34; class=&#34;footnote-back&#34;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2020/03/24/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-4/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Comparing Machine Learning Algorithms for Predicting Clothing Classes: Part 3</title>
      <link>https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/</link>
      <pubDate>Tue, 10 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/</guid>
      <description>
        

&lt;p&gt;&lt;em&gt;Florianne Verkroost is a Ph.D. candidate at Nuffield College at the University of Oxford. She has a passion for data science and a background in mathematics and econometrics. She applies her interdisciplinary knowledge to computationally address societal problems of inequality.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the third post in a series devoted to comparing different machine learning methods for predicting clothing categories from images using the Fashion MNIST data by Zalando. In the &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;first post&lt;/a&gt; of this series, we prepared the data for analysis and used my &amp;ldquo;go-to&amp;rdquo; Python deep learning neural network model to predict the clothing categories of the Fashion MNIST data. In &lt;a href=&#34;https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/&#34;&gt;Part 2&lt;/a&gt;, we used principal components analysis (PCA) to compress the clothing image data down from 784 to just 17 pixels. In this post, we pick up where we left off in Part 2 and use the two data sets &lt;code&gt;train.data.pca&lt;/code&gt; and &lt;code&gt;test.data.pca&lt;/code&gt; to build and compare random forest and gradient-boosted models. The R code for this post can be found on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;Github&lt;/a&gt; repository.&lt;/p&gt;

&lt;h3 id=&#34;tree-based-methods&#34;&gt;Tree-Based Methods&lt;/h3&gt;

&lt;p&gt;Tree-based methods stratify or segment the predictor space into a number of simple regions using a set of decision rules that can be summarized in a decision tree. The focus here will be on classification trees, as the Fashion MNIST outcome variable is categorical with ten classes. Because single trees have a relatively low level of predictive accuracy compared to other classification approaches, I will not show you how to fit a single tree in this blog post, but you can find the code for this (as well as tree pruning) on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ensemble methods improve predictive accuracy and decrease variance by aggregating many single decision trees. Here, I show both random forests and gradient-boosted trees as ensemble methods because the former are easier to implement as they are more robust to overfitting and require less tuning, while the latter generally outperform other tree-based methods in terms of prediction accuracy. The models are estimated in supervised mode here as labeled data are available and the goal is to predict classes. For a more formal explanation of the tree-based methods, I refer you to &lt;a href=&#34;http://faculty.marshall.usc.edu/gareth-james/ISL/&#34;&gt;James et al. (2013)&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&#34;random-forest&#34;&gt;Random Forest&lt;/h3&gt;

&lt;p&gt;Random forests use bootstrap aggregating to reduce the variance of the outcomes. In the first step, bootstrapping (sampling with replacement) is used to create &lt;code&gt;B&lt;/code&gt; training sets from the population with the same size as the original training set. Hereafter, a separate tree for each of these training sets is built. Trees are grown using recursive binary splitting on the training data until a node reaches some minimum number of observations. The idea is that the tree should go from impure (equal mixing of classes) to pure (each leaf corresponds to one class exactly). The splits are determined such that they decrease variance, error and impurity. Random forests decorrelate the trees by considering only &lt;code&gt;m&lt;/code&gt; of all &lt;code&gt;p&lt;/code&gt; predictors as split candidates, whereby often &lt;code&gt;m = sqrt(p)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Classification trees predict that each observation belongs to the most commonly occurring class (i.e. majority vote) of training observations in the region to which it belongs. The classification error rate is the fraction of the number of misclassified observations and the total number of classified observations. The Gini index and cross-entropy measures determine the level of impurity in order to decide on the best split at each node. In the final step, the average of the classification prediction results of all &lt;code&gt;B&lt;/code&gt; trees is computed from the majority vote. The accuracy is computed as the out-of-bag (OOB) error and/or the test set error.&lt;/p&gt;

&lt;p&gt;As each bootstrap samples from the training set with replacement, about &lt;sup&gt;2&lt;/sup&gt;&amp;frasl;&lt;sub&gt;3&lt;/sub&gt; of the observations are not sampled and some are sampled multiple times. In the case of &lt;code&gt;B&lt;/code&gt; trees in the forest, each observation is left out of approximately &lt;code&gt;B&lt;/code&gt;/ 3 trees. The non-sampled observations are used as test set and the &lt;code&gt;B&lt;/code&gt;/ 3 trees are used for out-of-sample predictions. In random forests, pruning is not needed as potential over-fitting is (partially) mitigated by the usage of bootstrapped samples and multiple decorrelated random trees.&lt;/p&gt;

&lt;p&gt;We start by tuning the number of variables that are randomly sampled as candidates at each split,&lt;code&gt;mtry&lt;/code&gt;. We make use of the &lt;code&gt;caret&lt;/code&gt; framework, which makes it easy to train and evaluate a large number of different types of models. For random forests, we have the &lt;code&gt;repeatedcv&lt;/code&gt; method perform five-fold cross-validation with five repetitions. For now, we build a random forest containing 200 trees because previous analyses with these data showed that the error does not decrease substantially when the number of trees is larger than 200, while a larger number of trees does require more computational power. We will see later on that 200 trees is indeed sufficient for this analysis. We let the algorithm determine what the best model is based on the accuracy metric, and we ask the algorithm to run the model for &lt;code&gt;pca.dims&lt;/code&gt; (= 17) different values of &lt;code&gt;mtry&lt;/code&gt;. We first specify the controls in &lt;code&gt;rf_rand_control&lt;/code&gt;: we perform 5-fold cross-validation with 5 repeats (&lt;code&gt;method = &amp;quot;cv&amp;quot;&lt;/code&gt;, &lt;code&gt;number = 5&lt;/code&gt; and &lt;code&gt;repeats = 5&lt;/code&gt;), allow parallel computation (&lt;code&gt;allowParallel = TRUE&lt;/code&gt;) and save the predicted values (&lt;code&gt;savePredictions = TRUE&lt;/code&gt;).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;library(caret)
rf_rand_control = trainControl(method = &amp;quot;repeatedcv&amp;quot;, 
                             search = &amp;quot;random&amp;quot;, 
                             number = 5, 
                             repeats = 5, 
                             allowParallel = TRUE, 
                             savePredictions = TRUE)
set.seed(1234)
rf_rand = train(x = train.images.pca, 
                 y = train.data.pca$label,
                 method = &amp;quot;rf&amp;quot;, 
                 ntree = 200,
                 metric = &amp;quot;Accuracy&amp;quot;, 
                 trControl = rf_rand_control, 
                 tuneLength = pca.dims) 
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;print(rf_rand)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;rf_rand_print.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;We can check the model performance on both the training and test sets by means of different metrics using a custom function, &lt;code&gt;model_performance&lt;/code&gt;, which can be found on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;mp.rf.rand = model_performance(rf_rand, train.images.pca, test.images.pca, 
                               train.data.pca$label, test.data.pca$label, &amp;quot;random_forest_random&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;rf_rand_mp.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;We can also use the &lt;code&gt;caret&lt;/code&gt; framework to perform a grid search with pre-specified values for &lt;code&gt;mtry&lt;/code&gt; rather than a random search as above.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;rf_grid_control = trainControl(method = &amp;quot;repeatedcv&amp;quot;, 
                             search = &amp;quot;grid&amp;quot;, 
                             number = 5, 
                             repeats = 5, 
                             allowParallel = TRUE, 
                             savePredictions = TRUE)
set.seed(1234)
rf_grid = train(x = train.images.pca, 
                 y = train.data.pca$label,
                 method = &amp;quot;rf&amp;quot;, 
                 ntree = 200,
                 metric = &amp;quot;Accuracy&amp;quot;, 
                 trControl = rf_grid_control,
                 tuneGrid = expand.grid(.mtry = c(1:pca.dims)))
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;plot(rf_grid)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;rf_grid_plot.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;mp.rf.grid = model_performance(rf_grid, train.images.pca, test.images.pca, 
                               train.data.pca$label, test.data.pca$label, &amp;quot;random_forest_grid&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;rf_grid_mp.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;As shown by the results, the random search selects &lt;code&gt;mtry=4&lt;/code&gt; as the optimal parameter, resulting in 85% training and test set accuracies. The grid search selects &lt;code&gt;mtry=5&lt;/code&gt; and achieves similar accuracies for both values of 4 and 5 for &lt;code&gt;mtry&lt;/code&gt;. We can see from the results that according to &lt;code&gt;rf_rand&lt;/code&gt;, &lt;code&gt;mtry&lt;/code&gt; values of 4 and 5 lead to very similar results, which also goes for &lt;code&gt;mtry&lt;/code&gt; values of 5 and 6 for &lt;code&gt;rf_grid&lt;/code&gt;. Although the results of &lt;code&gt;rf_rand&lt;/code&gt; and &lt;code&gt;rf_grid&lt;/code&gt; are very similar, we choose the best model on the basis of accuracy and save this in &lt;code&gt;rf_best&lt;/code&gt;. For this model, we&amp;rsquo;ll look at the relationship between the error and random forest size as well as the receiver operating characteristic (ROC) curves for every class. Let&amp;rsquo;s start by subtracting the best performing model from &lt;code&gt;rf_rand&lt;/code&gt; and &lt;code&gt;rf_grid&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;rf_models = list(rf_rand$finalModel, rf_grid$finalModel)
rf_accs = unlist(lapply(rf_models, function(x){ sum(diag(x$confusion)) / sum(x$confusion) }))
rf_best = rf_models[[which.max(rf_accs)]]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, we plot the relationship between the size of the random forest and the error using the &lt;code&gt;plot()&lt;/code&gt; function from the &lt;code&gt;randomForest&lt;/code&gt; package.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;library(randomForest)
plot(rf_best, main = &amp;quot;Relation between error and random forest size&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;rf_error_trees.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;We observe from this plot that the error does not decrease anymore for any of the classes after about 100 trees, and so we can conclude that our forest size of 200 is sufficient. We can also use the &lt;code&gt;varImpPlot()&lt;/code&gt; function from the &lt;code&gt;randomForest&lt;/code&gt; package to plot the importance for each variable. I will not show that here because it&amp;rsquo;s not as meaningful given that our variables are principal components of the actual pixels, but it&amp;rsquo;s good to keep in mind when extending these analyses to other data.&lt;/p&gt;

&lt;p&gt;Finally, we plot the ROC curves for every class.  On the x-axis of an ROC plot, we usually have the false positive rate (false positive / (true negative + false positive)) and on the y-axis the true positive rate (true positive / (true positive + false negative)). Essentially, the ROC plot helps us to compare the performance of our model with respect to predicting different classes. The area underneath each curve is the proportion of correct classifications for that particular class. Therefore, the further the curve is &amp;ldquo;drawn&amp;rdquo; towards the top left from the 45 degrees line, the better the classification for that class. We first need to obtain the data for the ROC curve for every class (or clothing category) in our data, which we bind together by rows, including a label for the classes.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;library(ROCR)
library(plyr)
pred_roc = predict(rf_best, test.images.pca, type = &amp;quot;prob&amp;quot;)
classes = unique(test.data.pca$label)
classes = classes[order(classes)]
plot_list = list()
for (i in 1:length(classes)) { 
  actual = ifelse(test.data.pca$label == classes[i], 1, 0)
  pred = prediction(pred_roc[, i], actual)
  perf = performance(pred, &amp;quot;tpr&amp;quot;, &amp;quot;fpr&amp;quot;)
  plot_list[[i]] = data.frame(matrix(NA, nrow = length(perf@x.values[[1]]), ncol = 2))
  plot_list[[i]][&#39;x&#39;] = perf@x.values[[1]]
  plot_list[[i]][&#39;y&#39;] = perf@y.values[[1]]
}
plotdf = rbind.fill(plot_list)
plotdf[&amp;quot;Class&amp;quot;] = rep(cloth_cats, unlist(lapply(plot_list, nrow)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, we plot the ROC curves for every class. Note that we use the custom plotting theme &lt;code&gt;my_theme()&lt;/code&gt; as defined in the &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-2/&#34;&gt;the second blog post of this series&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;ggplot() +
  geom_line(data = plotdf, aes(x = x, y = y, color = Class)) + 
  labs(x = &amp;quot;False positive rate&amp;quot;, y = &amp;quot;True negative rate&amp;quot;, color = &amp;quot;Class&amp;quot;) +
  ggtitle(&amp;quot;ROC curve per class&amp;quot;) + 
  theme(legend.position = c(0.85, 0.35)) +
  coord_fixed() + 
  my_theme()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;rf_roc.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;We observe from the ROC curves that shirts and pullovers are most often misclassified, whereas trousers, bags, boots and sneakers are most often correctly classified. A possible explanation for this could be that shirts and pullovers can be very similar in shape to other categories, such as tops, coats and dresses; whereas bags, trousers, boots and sneakers are more dissimilar to other categories in the data.&lt;/p&gt;

&lt;h2 id=&#34;gradient-boosted-trees&#34;&gt;Gradient-Boosted Trees&lt;/h2&gt;

&lt;p&gt;While in random forests each tree is fully grown and trained independently with a random sample of data, in boosting every newly built tree incorporates the error from the previously built tree. That is, the trees are grown sequentially on an adapted version of the initial data, which does not require bootstrap sampling. Because of this, boosted trees are usually smaller and more shallow than the trees in random forests, improving the tree where it does not work well enough yet. Boosting is often said to outperform random forests, which is mainly because the approach learns slowly. This learning rate can be controlled by the shrinkage parameter, which we&amp;rsquo;ll tune later.&lt;/p&gt;

&lt;p&gt;In boosting, it&amp;rsquo;s important to tune the parameters well and play around with different values of the parameters, which can easily be done using the &lt;code&gt;caret&lt;/code&gt; framework. These parameters include the learning rate, &lt;code&gt;eta&lt;/code&gt;, the minimal required loss reduction to further partition on a leaf node of the tree, &lt;code&gt;gamma&lt;/code&gt;, the maximal depth of a tree &lt;code&gt;max_depth&lt;/code&gt;, the number of trees in the forest, &lt;code&gt;nrounds&lt;/code&gt;, the minimum number of observations in the trees&amp;rsquo; nodes, &lt;code&gt;min_child_weight&lt;/code&gt;, the fraction of the training set observations randomly selected to grow trees, &lt;code&gt;subsample&lt;/code&gt;, and the proportion of independent variables to use for each tree, &lt;code&gt;colsample_bytree&lt;/code&gt;. An overview of all parameters can be found &lt;a href=&#34;https://xgboost.readthedocs.io/en/latest/parameter.html#parameters-for-tree-booster&#34;&gt;here&lt;/a&gt;. Again, we use the &lt;code&gt;caret&lt;/code&gt; framework to tune our boosting model.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;xgb_control = trainControl(
  method = &amp;quot;cv&amp;quot;,
  number = 5,
  classProbs = TRUE,
  allowParallel = TRUE,
  savePredictions = TRUE
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, we define the possible combinations of the tuning parameters in the form of a grid, named &lt;code&gt;xgb_grid&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;xgb_grid = expand.grid(
  nrounds = c(50, 100), 
  max_depth = seq(5, 15, 5),
  eta = c(0.002, 0.02, 0.2),
  gamma = c(0.1, 0.5, 1.0), 
  colsample_bytree = 1, 
  min_child_weight = c(1, 2, 3),
  subsample = c(0.5, 0.75, 1)
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We set the seed and then train the model onto the transformed principal components of the training data using &lt;code&gt;xgb_control&lt;/code&gt; and &lt;code&gt;xgb_grid&lt;/code&gt; as specified earlier. Note that because of the relatively large number of tuning parameters, and thus the larger number of possible combinations of these parameters (&lt;code&gt;nrow(xgb_grid) = 486&lt;/code&gt;), this may take quite a long time to run.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;set.seed(1234)
xgb_tune = train(x = train.images.pca, 
                 y = train.classes,
                 method = &amp;quot;xgbTree&amp;quot;,
                 trControl = xgb_control,
                 tuneGrid = xgb_grid
)
xgb_tune
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Note that the output of xgb_tune has been truncated for this post.)
&lt;img src=&#34;xgb_tune_print_1.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;xgb_tune_print_4.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s have a look at the tuning parameters resulting in the highest accuracy, and the model performance overall.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;xgb_tune$results[which.max(xgb_tune$results$Accuracy), ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;xgb_highest_accuracy.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;mp.xgb = model_performance(xgb_tune, train.images.pca, test.images.pca, 
                           train.classes, test.classes, &amp;quot;xgboost&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;xgb_mp.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;The optimal combination of tuning parameter values resulted in 86.2% training and 85.5% testing accuracies. Although there may be some slight overfitting going on, the model performs a bit better than the random forest, as was expected. Let&amp;rsquo;s have a look at the confusion matrix for the test set predictions to observe what clothing categories are mostly correctly or wrongly classified.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r,&#34;&gt;table(pred = predict(xgb_tune, test.images.pca),
      true = test.classes)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;xgb_confusion.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;

&lt;p&gt;As we saw with the random forests, pullovers, shirts and coats are most often mixed up, while trousers, boots, bags and sneakers are most often correctly classified.&lt;/p&gt;

&lt;p&gt;In the next and final post of this series, we will use the PCA reduced data again, but this time to estimate and assess support vector machines. Will these models be able to achieve similar results on the reduced data as neural networks on the full data? Let&amp;rsquo;s see!&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2020/03/10/comparing-machine-learning-algorithms-for-predicting-clothing-classes-part-3/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>Comparing Machine Learning Algorithms for Predicting Clothing Classes: Part 2</title>
      <link>https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/</link>
      <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/</guid>
      <description>
        


&lt;p&gt;&lt;em&gt;Florianne Verkroost is a Ph.D. candidate at Nuffield College at the University of Oxford. She has a passion for data science and a background in mathematics and econometrics. She applies her interdisciplinary knowledge to computationally address societal problems of inequality.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is the second post in a series devoted to comparing different machine and deep learning methods to predict clothing categories from images using the Fashion MNIST data by Zalando. In &lt;a href=&#34;https://rviews.rstudio.com/2019/11/11/a-comparison-of-methods-for-predicting-clothing-classes-using-the-fashion-mnist-dataset-in-rstudio-and-python-part-1/&#34;&gt;the first blog post of this series&lt;/a&gt;, we explored the data, prepared the data for analysis and learned how to predict the clothing categories of the Fashion MNIST data using my go-to model: an artificial neural network in Python. In this second blog post, we will perform dimension reduction on the data in order to make it feasible to run standard machine learning models (including tree-based methods and support vector machines) in the future. The R code for the first post can be found on my &lt;a href=&#34;https://github.com/fverkroost/RStudio-Blogs/blob/master/machine_learning_fashion_mnist_post234.R&#34;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(keras)
library(magrittr)
library(ggplot2)&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&#34;data-preparation&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Data Preparation&lt;/h3&gt;
&lt;p&gt;Let’s fetch the data again and prepare the training and test sets.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;install_keras()  
fashion_mnist = keras::dataset_fashion_mnist()
c(train.images, train.labels) %&amp;lt;-% fashion_mnist$train
c(test.images, test.labels) %&amp;lt;-% fashion_mnist$test&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we normalize the image data by dividing the pixel values by the maximum value of 255.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;train.images = data.frame(t(apply(train.images, 1, c))) / max(fashion_mnist$train$x)
test.images = data.frame(t(apply(test.images, 1, c))) / max(fashion_mnist$train$x)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we combine the training images &lt;code&gt;train.images&lt;/code&gt; and labels &lt;code&gt;train.labels&lt;/code&gt; as well as test images &lt;code&gt;test.images&lt;/code&gt; and labels &lt;code&gt;test.labels&lt;/code&gt; in separate data sets, &lt;code&gt;train.data&lt;/code&gt; and &lt;code&gt;test.data&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pixs = ncol(fashion_mnist$train$x)
names(train.images) = names(test.images) = paste0(&amp;#39;pixel&amp;#39;, 1:(pixs^2))
train.labels = data.frame(label = factor(train.labels))
test.labels = data.frame(label = factor(test.labels))
train.data = cbind(train.labels, train.images)
test.data = cbind(test.labels, test.images)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As &lt;code&gt;train.labels&lt;/code&gt; and &lt;code&gt;test.labels&lt;/code&gt; contain integer values for the clothing category (i.e. 0, 1, 2, etc.), we also create objects &lt;code&gt;train.classes&lt;/code&gt; and &lt;code&gt;test.classes&lt;/code&gt; that contain factor labels (i.e. Top, Trouser, Pullover etc.) for the clothing categories. We will need these for some of the machine learning models later on.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;cloth_cats = c(&amp;#39;Top&amp;#39;, &amp;#39;Trouser&amp;#39;, &amp;#39;Pullover&amp;#39;, &amp;#39;Dress&amp;#39;, &amp;#39;Coat&amp;#39;,  
                &amp;#39;Sandal&amp;#39;, &amp;#39;Shirt&amp;#39;, &amp;#39;Sneaker&amp;#39;, &amp;#39;Bag&amp;#39;, &amp;#39;Boot&amp;#39;)
train.classes = factor(cloth_cats[as.numeric(as.character(train.labels$label)) + 1])
test.classes = factor(cloth_cats[as.numeric(as.character(test.labels$label)) + 1])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div id=&#34;principal-components-analysis&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;Principal Components Analysis&lt;/h3&gt;
&lt;p&gt;Our training and test image data sets currently contain 784 pixels or variables. We may expect a large share of these pixels, especially those towards the boundaries of the images, to have relatively small variance, because most of the fashion items are centered in the images. In other words, there may be quite some redundant pixels in our data set. To check whether this is the case, let’s plot the average pixel value on a 28 by 28 grid. We first obtain the average pixel values and store these in &lt;code&gt;train.images.ave&lt;/code&gt;, after which we plot these values on the grid. We also define a custom plotting theme, &lt;code&gt;my_theme&lt;/code&gt;, to make sure all our figures have the same aesthetics. Note that in the plot created, a higher cell (pixel) value means that the average value of that pixel is higher, and thus that the pixel is darker on average (as a pixel value of 0 refers to white and a pixel value of 255 refers to black).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;train.images.ave = data.frame(pixel = apply(train.images, 2, mean), 
                               x = rep(1:pixs, each = pixs), 
                               y = rep(1:pixs, pixs))
my_theme = function () { 
  theme_bw() + 
    theme(axis.text = element_text(size = 14),
          axis.title = element_text(size = 14),
          strip.text = element_text(size = 14),
          panel.grid.major = element_blank(), 
          panel.grid.minor = element_blank(),
          panel.background = element_blank(), 
          legend.position = &amp;quot;bottom&amp;quot;,
          strip.background = element_rect(fill = &amp;#39;white&amp;#39;, colour = &amp;#39;white&amp;#39;))
}
ggplot() +
  geom_raster(data = train.images.ave, aes(x = x, y = y, fill = pixel)) +
  my_theme() +
  labs(x = NULL, y = NULL, fill = &amp;quot;Average scaled pixel value&amp;quot;) +
  ggtitle(&amp;#39;Average image in Fashion MNIST training data&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-02-24-predicting-clothing-classes-part-2/index_files/figure-html/unnamed-chunk-6-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;As we can see from the plot, there are many pixels with a low average value, meaning that they are white in most of the images in our training data. These pixels are mostly redundant, while they do contribute to computational costs and sparsity. Therefore, we might be better off reducing the dimensionality in our data to reduce redundancy, overfitting and computational cost. One method to do so is principal components analysis &lt;a href=&#34;https://www.nature.com/articles/nmeth.4346.pdf&#34;&gt;(PCA)&lt;/a&gt;. Essentially, PCA statistically reduces the dimensions of a set of correlated variables by transforming them into a smaller number of linearly uncorrelated variables. The resulting “principal components” are linear combinations of the original variables. The first principal component explains the largest part of the variance, followed by the second principal component and so forth. For a more extensive explanation of PCA, I refer you to James et al. (2013).&lt;/p&gt;
&lt;p&gt;Let’s have a look at how many variables can explain which part of the variance in our data. We compute the 784 by 784 covariance matrix of our training images using the &lt;code&gt;cov()&lt;/code&gt; function, after which we execute PCA on the covariance matrix using the &lt;code&gt;prcomp()&lt;/code&gt; function in the &lt;code&gt;stats&lt;/code&gt; library. Looking at the results, we observe that 50 principal components in our data explain 99.902% of the variance in the data. This can be nicely shown in a plot of the cumulative proportion of variance against component indices. Note that the component indices here are sorted by their ability to explain the variance in our data, and not based on their pixel position in the 28 by 28 image.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(stats)
cov.train = cov(train.images)                      
pca.train = prcomp(cov.train)                  
plotdf = data.frame(index = 1:(pixs^2), 
                    cumvar = summary(pca.train)$importance[&amp;quot;Cumulative Proportion&amp;quot;, ])
t(head(plotdf, 50)) &lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;##           PC1    PC2    PC3    PC4    PC5    PC6    PC7    PC8    PC9
## index  1.0000 2.0000 3.0000 4.0000 5.0000 6.0000 7.0000 8.0000 9.0000
## cumvar 0.6491 0.8679 0.9107 0.9421 0.9611 0.9759 0.9816 0.9862 0.9885
##           PC10    PC11    PC12    PC13    PC14    PC15   PC16    PC17
## index  10.0000 11.0000 12.0000 13.0000 14.0000 15.0000 16.000 17.0000
## cumvar  0.9906  0.9918  0.9928  0.9935  0.9941  0.9945  0.995  0.9954
##           PC18   PC19    PC20    PC21    PC22    PC23   PC24    PC25
## index  18.0000 19.000 20.0000 21.0000 22.0000 23.0000 24.000 25.0000
## cumvar  0.9957  0.996  0.9962  0.9965  0.9967  0.9969  0.997  0.9972
##           PC26    PC27    PC28    PC29    PC30   PC31   PC32    PC33
## index  26.0000 27.0000 28.0000 29.0000 30.0000 31.000 32.000 33.0000
## cumvar  0.9974  0.9975  0.9976  0.9978  0.9979  0.998  0.998  0.9981
##           PC34    PC35    PC36    PC37    PC38    PC39    PC40    PC41
## index  34.0000 35.0000 36.0000 37.0000 38.0000 39.0000 40.0000 41.0000
## cumvar  0.9982  0.9983  0.9984  0.9984  0.9985  0.9986  0.9986  0.9987
##           PC42    PC43    PC44    PC45    PC46    PC47   PC48   PC49
## index  42.0000 43.0000 44.0000 45.0000 46.0000 47.0000 48.000 49.000
## cumvar  0.9987  0.9988  0.9988  0.9989  0.9989  0.9989  0.999  0.999
##          PC50
## index  50.000
## cumvar  0.999&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;ggplot() + 
  geom_point(data = plotdf, aes(x = index, y = cumvar), color = &amp;quot;red&amp;quot;) +
  labs(x = &amp;quot;Index of primary component&amp;quot;, y = &amp;quot;Cumulative proportion of variance&amp;quot;) +
  my_theme() +
  theme(strip.background = element_rect(fill = &amp;#39;white&amp;#39;, colour = &amp;#39;black&amp;#39;))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2020-02-24-predicting-clothing-classes-part-2/index_files/figure-html/unnamed-chunk-7-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;
&lt;p&gt;We also observe that 99.5% of the variance is explained by only 17 principal components. As 99.5% is already a large share of the variance, and we want to reduce the number of pixels (variables) by as many as we can to reduce computation time for the models coming up, we choose to select these 17 components for further analysis. (Although this is unlikely to influence our results hugely, if you have more time I’d suggest you select the 50 components explaining 99.9% of the data, or execute the analyses on the full data set.)&lt;/p&gt;
&lt;p&gt;We also save the relevant part of the rotation matrix created by the &lt;code&gt;prcomp()&lt;/code&gt; function and stored in &lt;code&gt;pca.train&lt;/code&gt;, such that its dimensions become 784 by 17. We then multiply our training and test image data by this rotation matrix called &lt;code&gt;pca.rot&lt;/code&gt;. We further combine the transformed image data (&lt;code&gt;train.images.pca&lt;/code&gt; and &lt;code&gt;test.images.pca&lt;/code&gt;) with the integer labels for the clothing categories in &lt;code&gt;train.data.pca&lt;/code&gt; and &lt;code&gt;test.data.pca&lt;/code&gt;. We will use these reduced data in our further analyses to decrease computational time.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;pca.dims = which(plotdf$cumvar &amp;gt;= .995)[1]
pca.rot = pca.train$rotation[, 1:pca.dims]  
train.images.pca = data.frame(as.matrix(train.images) %*% pca.rot)
test.images.pca  = data.frame(as.matrix(test.images) %*% pca.rot)
train.data.pca = cbind(train.images.pca, label = factor(train.data$label))
test.data.pca = cbind(test.images.pca, label = factor(test.data$label))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the next post of this series, we will use the PCA reduced data to estimate and assess tree-based methods, including random forests and gradient-boosted trees.&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&#34;references&#34; class=&#34;section level3&#34;&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;James, G., Witten, D., Hastie, T., &amp;amp; Tibshirani, R. (2013). An introduction to statistical learning (Vol. 112, p. 18). New York: Springer.&lt;/p&gt;
&lt;/div&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2020/03/03/predicting-clothing-classes-part-2/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>tidyposterior&#39;s Bayesian Approach to Model Comparison</title>
      <link>https://rviews.rstudio.com/2019/12/16/bayesian-model-comparison/</link>
      <pubDate>Mon, 16 Dec 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/12/16/bayesian-model-comparison/</guid>
      <description>
        &lt;p&gt;A task common to many machine learning workflows is to compare the performance of several models with respect to some metric such as accuracy or area under the ROC curve. Standard practice is to try out several different algorithms on a training data set and see which works better. Unfortunately, all to often, after this work has been done, model selection comes down to &amp;ldquo;eyeballing&amp;rdquo; several different ROC curves. If you find eyeballing a little too informal, then take a look at the &lt;a href=&#34;https://cran.r-project.org/package=tidyposterior&#34;&gt;&lt;code&gt;tidyposterior&lt;/code&gt;&lt;/a&gt; package (part of the universe of &lt;a href=&#34;https://cran.r-project.org/web/packages/tidymodels/index.html&#34;&gt;&amp;lsquo;tidymodels`&lt;/a&gt;). The &lt;a href=&#34;https://cran.r-project.org/web/packages/tidyposterior/vignettes/Getting_Started.html&#34;&gt;&lt;em&gt;Getting Started&lt;/em&gt;&lt;/a&gt; vignette asks the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When looking at resampling results, are the differences between the models &amp;ldquo;real&amp;rdquo;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and lays out a modelling approach to answering it that is grounded in Bayesian theory. This means that all of the information about the differences between two models to be compared will be summed up in a posterior distribution for the differences that will make it possible to calculate probabilities entirely based on the particular data at hand, the information encoded in the various priors, and the effect size that has been chosen because it is considered large enough to make a practical difference. Given an effect size chosen up front to represent a practical difference, the &lt;code&gt;tidyposterior&lt;/code&gt; model will enable conclusions such as; &lt;em&gt;there is a probability of .65 that, for all practical purposes, the two distributions are different&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There is no magic here. &lt;code&gt;tidyposterior&lt;/code&gt; works through the mechanics of an Bayesian analysis with minimum input from the user (a situation that is possible because of the very constrained nature of comparison problem and the use of cross-validation statistics as input), but the result ultimately comes down to choosing an effect size that makes sense based on domain knowledge of the underlying experiment. For some studies, an effect of .05 might seem to be reasonable. In a situation where having an interpretable is worth more than a black-box prediction modelers may be willing to go higher. And, for studies where models may support life or death decisions something in the 2% range may be required.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Getting Started&lt;/em&gt; guide provides end-to-end road map on using the &lt;code&gt;tidyposterior&lt;/code&gt; functions to compare models, but it assumes quite a bit of background. Someone just getting started with Bayesian statistics or doing Bayesian statistics in R, might be more comfortable with a little more description of the landscape. For the rest of this post, I&amp;rsquo;ll be the tour guide and provide a little color commentary that you may find helpful as you walk through the vignette.&lt;/p&gt;

&lt;p&gt;The first thing to point out is that the vignette begins after most of the heavy lifting for a model comparison study has already been done, and the information required to compare models has been wrapped up in the data frame &lt;code&gt;precise_example&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As &lt;a href=&#34;https://tidymodels.github.io/tidyposterior/reference/precise_example.html&#34;&gt;website&lt;/a&gt; for &lt;code&gt;tidyposterior&lt;/code&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;precise_example&lt;/code&gt; contains the results of the classification analysis of a real data set using 10-fold CV. The holdout data sets contained thousands of examples and have precise performance estimates. Three models were fit to the original data and several performance metrics are included.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;precise_example[,-1]
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 10 x 28
##    id    glm_Accuracy glm_Kappa glm_ROC glm_Sens glm_Spec glm_PRAUC
##    &amp;lt;chr&amp;gt;        &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;    &amp;lt;dbl&amp;gt;    &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;
##  1 Fold…        0.722     0.328   0.798    0.729    0.720     0.489
##  2 Fold…        0.696     0.290   0.778    0.720    0.691     0.456
##  3 Fold…        0.701     0.297   0.790    0.723    0.696     0.486
##  4 Fold…        0.704     0.316   0.795    0.763    0.691     0.497
##  5 Fold…        0.721     0.324   0.797    0.722    0.721     0.481
##  6 Fold…        0.711     0.303   0.780    0.706    0.712     0.484
##  7 Fold…        0.702     0.305   0.790    0.739    0.694     0.485
##  8 Fold…        0.718     0.321   0.784    0.729    0.715     0.477
##  9 Fold…        0.720     0.328   0.795    0.739    0.715     0.491
## 10 Fold…        0.719     0.324   0.796    0.728    0.717     0.488
## # … with 21 more variables: glm_Precision &amp;lt;dbl&amp;gt;, glm_Recall &amp;lt;dbl&amp;gt;,
## #   glm_F &amp;lt;dbl&amp;gt;, knn_Accuracy &amp;lt;dbl&amp;gt;, knn_Kappa &amp;lt;dbl&amp;gt;, knn_ROC &amp;lt;dbl&amp;gt;,
## #   knn_Sens &amp;lt;dbl&amp;gt;, knn_Spec &amp;lt;dbl&amp;gt;, knn_PRAUC &amp;lt;dbl&amp;gt;, knn_Precision &amp;lt;dbl&amp;gt;,
## #   knn_Recall &amp;lt;dbl&amp;gt;, knn_F &amp;lt;dbl&amp;gt;, nnet_Accuracy &amp;lt;dbl&amp;gt;, nnet_Kappa &amp;lt;dbl&amp;gt;,
## #   nnet_ROC &amp;lt;dbl&amp;gt;, nnet_Sens &amp;lt;dbl&amp;gt;, nnet_Spec &amp;lt;dbl&amp;gt;, nnet_PRAUC &amp;lt;dbl&amp;gt;,
## #   nnet_Precision &amp;lt;dbl&amp;gt;, nnet_Recall &amp;lt;dbl&amp;gt;, nnet_F &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The various statistics for the different models have been  &lt;em&gt;matched&lt;/em&gt; by setting a random number seed and using the same random sampling process for each fold. This is the kind of thing that can easily be done with the &lt;a href=&#34;https://cran.r-project.org/package=rsample&#34;&gt;&lt;code&gt;rsample&lt;/code&gt;&lt;/a&gt; or &lt;a href=&#34;https://topepo.github.io/caret/&#34;&gt;&lt;code&gt;caret&lt;/code&gt;&lt;/a&gt; packages, but requires a bit of work otherwise.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Getting Started&lt;/em&gt; vignette focuses on &lt;code&gt;ROC&lt;/code&gt;, the area under the &lt;a href=&#34;https://rviews.rstudio.com/2019/01/17/roc-curves/&#34;&gt;ROC Curve&lt;/a&gt;. A little bit of preprocessing produces &amp;ldquo;wide&amp;rdquo; and &amp;ldquo;long&amp;rdquo; (or stacked) versions of the ROC statistics. Note that information on the performance statistics contained in the data set can be found on the &lt;a href=&#34;https://tidymodels.github.io/yardstick/reference/index.html&#34;&gt;&lt;code&gt;yardstick&lt;/code&gt; reference page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This first bit of code selects the ROC for the different statistics and creates a stacked version of the data set.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;rocs &amp;lt;- precise_example %&amp;gt;%
  select(id, contains(&amp;quot;ROC&amp;quot;)) %&amp;gt;%
  setNames(tolower(gsub(&amp;quot;_ROC$&amp;quot;, &amp;quot;&amp;quot;, names(.)))) 
rocs
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 10 x 4
##    id       glm   knn  nnet
##    &amp;lt;chr&amp;gt;  &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;
##  1 Fold01 0.798 0.753 0.843
##  2 Fold02 0.778 0.744 0.827
##  3 Fold03 0.790 0.743 0.846
##  4 Fold04 0.795 0.755 0.852
##  5 Fold05 0.797 0.760 0.838
##  6 Fold06 0.780 0.747 0.852
##  7 Fold07 0.790 0.757 0.833
##  8 Fold08 0.784 0.754 0.832
##  9 Fold09 0.795 0.764 0.846
## 10 Fold10 0.796 0.748 0.847
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;rocs_stacked &amp;lt;- gather.rset(rocs)
head(rocs_stacked)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;##       id model statistic
## 1 Fold01   glm    0.7981
## 2 Fold02   glm    0.7779
## 3 Fold03   glm    0.7901
## 4 Fold04   glm    0.7948
## 5 Fold05   glm    0.7972
## 6 Fold06   glm    0.7804
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first graph of the vignette plots the ROC cross-validation statistics for each fold. The fact that the lines are nearly all parallel indicates that there is probably a resampling effect, but it does look like there is a clear difference in the performance of the three models.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(rocs_stacked, aes(x = model, y = statistic, group = id, col = id)) + geom_line(alpha = .75) + theme(legend.position = &amp;quot;none&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-4-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;The following plot makes the performance differences even more apparent. There is very little overlap of the kernel density estimates of the three distributions.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(rocs_stacked, aes(x = statistic, col = model, fill = model)) + 
  geom_histogram(aes(y=..density..), alpha=0.5, position=&amp;quot;identity&amp;quot;, bins = 35)+
  geom_density(alpha=.2) + xlim(min = .73, max = .87) +
  geom_rug()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-5-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Quantifying these differences is the point of our exercise. The strategy will be to fit a Bayesian model that explicitly accounts for the fold effects, and then compare the differences in posterior distributions to calculate the probability that the various distributions are indeed different.&lt;/p&gt;

&lt;p&gt;Here, we fit the model with the &lt;code&gt;tidyposterior&lt;/code&gt; function &lt;code&gt;perf_mod()&lt;/code&gt;. Note that although the only parameter passed to &lt;code&gt;perf_mod()&lt;/code&gt; is to set the seed, other options can be specified. A great deal of attention has been put into setting the defaults. I will explain the model &lt;code&gt;perf_mod()&lt;/code&gt; executes below when we examine the resulting model object.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;roc_model &amp;lt;- perf_mod(rocs, seed = 2824)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I have suppressed the voluminous output generated by fitting the model, but if you run it yourself, you will see that there was a lot going on under the covers. Four independent Markov chains were initiated for the &lt;code&gt;stan&lt;/code&gt; Monte Carlo algorithm used to evaluate the model, each chain going through 1,000 warm up iterations and then another 1,000 to fit the model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;perf_mod()&lt;/code&gt;is based on the &lt;a href=&#34;https://cran.r-project.org/web/packages/rstanarm/vignettes/glmer.html&#34;&gt;&lt;code&gt;glmer()&lt;/code&gt; function&lt;/a&gt; from the &lt;a href=&#34;https://cran.r-project.org/package=rstanarm&#34;&gt;&lt;code&gt;rstanarm&lt;/code&gt; package&lt;/a&gt;. We can assess the &lt;code&gt;rstanarm&lt;/code&gt; model object which is returned as part of &lt;code&gt;perf_mod&lt;/code&gt; model object.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;summary(roc_model$stan)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## 
## Model Info:
##  function:     stan_glmer
##  family:       gaussian [identity]
##  formula:      statistic ~ model + (1 | id)
##  algorithm:    sampling
##  sample:       4000 (posterior sample size)
##  priors:       see help(&#39;prior_summary&#39;)
##  observations: 30
##  groups:       id (10)
## 
## Estimates:
##                                     mean   sd   10%   50%   90%
## (Intercept)                       0.8    0.0  0.8   0.8   0.8  
## modelknn                          0.0    0.0  0.0   0.0   0.0  
## modelnnet                         0.1    0.0  0.0   0.1   0.1  
## b[(Intercept) id:Fold01]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold02]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold03]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold04]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold05]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold06]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold07]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold08]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold09]          0.0    0.0  0.0   0.0   0.0  
## b[(Intercept) id:Fold10]          0.0    0.0  0.0   0.0   0.0  
## sigma                             0.0    0.0  0.0   0.0   0.0  
## Sigma[id:(Intercept),(Intercept)] 0.0    0.0  0.0   0.0   0.0  
## 
## Fit Diagnostics:
##            mean   sd   10%   50%   90%
## mean_PPD 0.8    0.0  0.8   0.8   0.8  
## 
## The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help(&#39;summary.stanreg&#39;)).
## 
## MCMC diagnostics
##                                   mcse Rhat n_eff
## (Intercept)                       0.0  1.0  2691 
## modelknn                          0.0  1.0  2977 
## modelnnet                         0.0  1.0  3314 
## b[(Intercept) id:Fold01]          0.0  1.0  3389 
## b[(Intercept) id:Fold02]          0.0  1.0  1162 
## b[(Intercept) id:Fold03]          0.0  1.0  3683 
## b[(Intercept) id:Fold04]          0.0  1.0  2016 
## b[(Intercept) id:Fold05]          0.0  1.0  2888 
## b[(Intercept) id:Fold06]          0.0  1.0  3557 
## b[(Intercept) id:Fold07]          0.0  1.0  3332 
## b[(Intercept) id:Fold08]          0.0  1.0  1952 
## b[(Intercept) id:Fold09]          0.0  1.0  1929 
## b[(Intercept) id:Fold10]          0.0  1.0  3219 
## sigma                             0.0  1.0  1391 
## Sigma[id:(Intercept),(Intercept)] 0.0  1.0  1386 
## mean_PPD                          0.0  1.0  4180 
## log-posterior                     0.2  1.0   688 
## 
## For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The default model constructed by &lt;code&gt;perf_mod()&lt;/code&gt; specified by the R formula &lt;code&gt;statistic ~ model + (1 | id)&lt;/code&gt; is a Bayesian, multi-level, varying intercept, linear regression model with an intercept, coefficients for the knn and nnet model effects, and an intercept for each of the cross-validation folds. This model might be written as:&lt;/p&gt;

&lt;p&gt;Y&lt;sub&gt;i&lt;/sub&gt; = &lt;code&gt;\(\alpha\)&lt;/code&gt; + &lt;code&gt;\(\beta\)&lt;/code&gt;X&lt;sub&gt;i&lt;/sub&gt; + b&lt;sub&gt;[i]&lt;/sub&gt;&lt;/p&gt;

&lt;p&gt;in the one of the common notations used by Bayesians. (For example, see &lt;a href=&#34;https://books.google.com/books?hl=en&amp;amp;lr=&amp;amp;id=c9xLKzZWoZ4C&amp;amp;oi=fnd&amp;amp;pg=PR17&amp;amp;dq=gelman+and+hill+hierarchical+models&amp;amp;ots=bbT8P1Ksmg&amp;amp;sig=tyBMOIoF2pcSavdePWLUuUDgoiM#v=onepage&amp;amp;q=varying%20intercept%20model&amp;amp;f=false]&#34;&gt;Gelman and Hill (2007)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rstanarm&lt;/code&gt; allows R users to build a wide range of Bayesian regression models using the &lt;a href=&#34;https://mc-stan.org/&#34;&gt;&lt;code&gt;stan&lt;/code&gt; engine&lt;/a&gt; without having to explicitly program in &lt;code&gt;stan&lt;/code&gt;. That&amp;rsquo;s the good news. The bad news is that R&amp;rsquo;s &lt;a href=&#34;https://stat.ethz.ch/R-manual/R-devel/library/stats/html/formula.html&#34;&gt;formula interface&lt;/a&gt; takes some getting used to. A good source for learning the how to interpret formulas for the type of model we are considering here is the &lt;a href=&#34;https://cran.r-project.org/web/packages/lme4/vignettes/lmer.pdf&#34;&gt;vignette&lt;/a&gt; for the &lt;a href=&#34;https://cran.r-project.org/package=lme4&#34;&gt;&lt;code&gt;lme4&lt;/code&gt; package&lt;/a&gt; which implements Frequentist analog of this sort of model. (For an in depth discussion of the pros and cons of R formula interface see the Max Kuhn&amp;rsquo;s two posts on the subject: &lt;a href=&#34;https://rviews.rstudio.com/2017/02/01/the-r-formula-method-the-good-parts/&#34;&gt;pros here&lt;/a&gt; and &lt;a href=&#34;https://rviews.rstudio.com/2017/03/01/the-r-formula-method-the-bad-parts/&#34;&gt;cons here &lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can examine the prior default prior distributions that were selected for this model as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;rstanarm::prior_summary(roc_model$stan)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## Priors for model &#39;roc_model$stan&#39; 
## ------
## Intercept (after predictors centered)
##   Specified prior:
##     ~ normal(location = 0, scale = 10)
##   Adjusted prior:
##     ~ normal(location = 0, scale = 0.38)
## 
## Coefficients
##   Specified prior:
##     ~ normal(location = [0,0], scale = [2.5,2.5])
##   Adjusted prior:
##     ~ normal(location = [0,0], scale = [0.094,0.094])
## 
## Auxiliary (sigma)
##   Specified prior:
##     ~ exponential(rate = 1)
##   Adjusted prior:
##     ~ exponential(rate = 26)
## 
## Covariance
##  ~ decov(reg. = 1, conc. = 1, shape = 1, scale = 1)
## ------
## See help(&#39;prior_summary.stanreg&#39;) for more details
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, that the model has been fit, we use the  &lt;code&gt;tidyposterior&lt;/code&gt; function &lt;code&gt;tidy()&lt;/code&gt; to extract the posterior samples from the &lt;code&gt;perf_mod()&lt;/code&gt; model object, examine the first few values and plot the posterior distributions for the three models being compared. (Note that you may also find the &lt;a href=&#34;https://cran.r-project.org/package=tidybayes&#34;&gt;&lt;code&gt;tidybayes&lt;/code&gt;&lt;/a&gt; package helpful in examining draws from the posterior distribution.)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;roc_post &amp;lt;- tidy(roc_model)
glimpse(roc_post)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## Observations: 12,000
## Variables: 2
## $ model     &amp;lt;chr&amp;gt; &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;…
## $ posterior &amp;lt;dbl&amp;gt; 0.7934, 0.7905, 0.7919, 0.7891, 0.7889, 0.7887, 0.7867…
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Also, even though this is a relatively simple ANOVA type model, some model checking ought to be done to determine whether the model is good enough to be interpreted. This kind of checking can be done interactively with the &lt;code&gt;shinystan&lt;/code&gt; application that can be launched with the command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;launch_shinystan(roc_model$stan)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The following screen capture from &lt;code&gt;shinystan&lt;/code&gt; shows the diagnostics for the intercept for the seventh fold of the cross-validation. Note that the mixing (variability from high to low) in the top left graph which shows a trace of the Markov chain for this parameter looks quite good.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;shinystan.png&#34; height = &#34;400&#34; width=&#34;600&#34;&gt;&lt;/p&gt;

&lt;p&gt;The vignette shows violin plots for each of the three models, but in order to make the comparison with the data plot above, I plot the distributions with histograms, kernel density, and rug plots.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;roc_post2 &amp;lt;- as.data.frame(roc_post) # get object of class data.frame
ggplot(roc_post2, aes(x = posterior, col = model, fill = model)) + 
  geom_histogram(aes(y=..density..), alpha=0.5, position=&amp;quot;identity&amp;quot;, bins = 100)+
  geom_density(alpha=.2) + xlim(min = .74, max = .86) +
  geom_rug()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-11-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Notice that the distribution all look like pretty narrow, normal distributions. And although it sure looks like there is quite a bit of difference between the distributions of ROC values for the glm and nnet models, we can quantify the difference by setting up a &lt;em&gt;contrast&lt;/em&gt; computing the posterior difference in RMSE for the two models as &lt;code&gt;nnet&lt;/code&gt; - &lt;code&gt;glm&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;glm_v_nnet &amp;lt;- contrast_models(roc_model, &amp;quot;nnet&amp;quot;, &amp;quot;glm&amp;quot;)
head(glm_v_nnet)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;##   difference model_1 model_2
## 1    0.04669    nnet     glm
## 2    0.04897    nnet     glm
## 3    0.05226    nnet     glm
## 4    0.05251    nnet     glm
## 5    0.05364    nnet     glm
## 6    0.05583    nnet     glm
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ideally, before beginning a modeling effort, one would have some idea about how different the models have to be in order to make any practical difference. Let&amp;rsquo;s suppose that there needs to be at least a 5% increase in ROC area between the two distributions for there to be a practical difference. We can compute this probability by setting the &lt;code&gt;size&lt;/code&gt; parameter to the &lt;code&gt;summary()&lt;/code&gt; function to 0.05.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;summary(glm_v_nnet, size = 0.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 1 x 9
##   contrast probability   mean  lower  upper  size pract_neg pract_equiv
##   &amp;lt;chr&amp;gt;          &amp;lt;dbl&amp;gt;  &amp;lt;dbl&amp;gt;  &amp;lt;dbl&amp;gt;  &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;       &amp;lt;dbl&amp;gt;
## 1 nnet vs…           1 0.0511 0.0459 0.0563  0.05         0       0.351
## # … with 1 more variable: pract_pos &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The column labeled &lt;em&gt;probability&lt;/em&gt; indicates that all of the density of the difference is positive. &lt;em&gt;mean&lt;/em&gt;, &lt;em&gt;lower&lt;/em&gt; and &lt;em&gt;upper&lt;/em&gt; give the mean of the posterior difference distribution, and the lower and upper bounds plotted. &lt;em&gt;pract_neg&lt;/em&gt; and &lt;em&gt;prac_pos&lt;/em&gt; report the result of a calculation that uses the &lt;em&gt;Highest Density Interval&lt;/em&gt; &lt;a href=&#34;https://easystats.github.io/bayestestR/reference/hdi.html&#34;&gt;HDI&lt;/a&gt;, the narrowest interval containing some prespecified percentage of the probability density curve, to determine the &lt;em&gt;Region of Practical Equivalence&lt;/em&gt;, &lt;a href=&#34;https://cran.r-project.org/web/packages/bayestestR/vignettes/region_of_practical_equivalence.html&#34;&gt;ROPE&lt;/a&gt;. &lt;em&gt;pract_neg&lt;/em&gt; indicates  that none of the density is below the interval of meaningful difference and &lt;em&gt;prac_pos&lt;/em&gt; indicates that 65% of the density is above the area of practical equivalence. The latter can be interpreted as saying that the probability that the two distributions are different for all practicable purposes is .65. Likewise, &lt;em&gt;pract_equiv&lt;/em&gt; means that there is a 35% chance that the two distributions are practically equivalent.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(glm_v_nnet, size = .05)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-14-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;contrast_models()&lt;/code&gt; function is also flexible enough to perform multiple simultaneous contrasts. Here, we set up to contrast both the glm model against the nnet model and the knn model at the 5% level&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;models_contrast &amp;lt;- contrast_models(roc_model, list(&amp;quot;knn&amp;quot;,&amp;quot;nnet&amp;quot;), list(&amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;))
summary(models_contrast, size=.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 2 x 9
##   contrast probability    mean   lower   upper  size pract_neg pract_equiv
##   &amp;lt;chr&amp;gt;          &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;       &amp;lt;dbl&amp;gt;
## 1 knn vs …           0 -0.0377 -0.0430 -0.0325  0.05   0.00025       1.000
## 2 nnet vs…           1  0.0511  0.0459  0.0563  0.05   0             0.351
## # … with 1 more variable: pract_pos &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that first row of the table above and the following plot both indicate that there is no practical difference between the glm and knn models at the 5% level.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(models_contrast, size = .05)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-16-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Because the vignette also mentions &lt;a href=&#34;https://developers.google.com/machine-learning/crash-course/classification/accuracy&#34;&gt;accuracy&lt;/a&gt;, the fraction of correct predictions, we briefly repeat the above analysis using accuracy as the performance measure.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;acc &amp;lt;- precise_example %&amp;gt;%
  select(id, contains(&amp;quot;Accuracy&amp;quot;)) %&amp;gt;%
  setNames(tolower(gsub(&amp;quot;_Accuracy$&amp;quot;, &amp;quot;&amp;quot;, names(.)))) 
acc_model &amp;lt;- perf_mod(acc,seed = 2824)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The posterior distributions for accuracy are tightly clustered and indicate that the models yield different levels of performance for this measure.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;acc_post &amp;lt;- as.data.frame(tidy(acc_model)) # get object of class data.frame
ggplot(roc_post2, aes(x = posterior, col = model, fill = model)) + 
  geom_histogram(aes(y=..density..), alpha=0.5, position=&amp;quot;identity&amp;quot;, bins = 50) +
  geom_density(alpha=.2) + xlim(min = .74, max = .86) +
  geom_rug()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-18-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;And, the &lt;code&gt;contrast_models()&lt;/code&gt; function that confirms the practical difference.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;acc_models_contrast &amp;lt;- contrast_models(acc_model, list(&amp;quot;knn&amp;quot;,&amp;quot;nnet&amp;quot;), list(&amp;quot;glm&amp;quot;, &amp;quot;glm&amp;quot;))
summary(acc_models_contrast, size=.05)
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;## # A tibble: 2 x 9
##   contrast probability    mean   lower   upper  size pract_neg pract_equiv
##   &amp;lt;chr&amp;gt;          &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt;   &amp;lt;dbl&amp;gt; &amp;lt;dbl&amp;gt;     &amp;lt;dbl&amp;gt;       &amp;lt;dbl&amp;gt;
## 1 knn vs …           0 -0.239  -0.250  -0.229   0.05         1     0      
## 2 nnet vs…           1  0.0687  0.0583  0.0792  0.05         0     0.00175
## # … with 1 more variable: pract_pos &amp;lt;dbl&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-r&#34;&gt;ggplot(acc_models_contrast, size = .05)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&#34;/post/2019-12-02-bayesian-model-comparison/index_files/figure-html/unnamed-chunk-20-1.png&#34; width=&#34;672&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Note that the nnet model comes out on top with respect to both ROC and accuracy, but there appears to be a practical differences in practical differences. A modeler who is primarily interested in accuracy as the performance metric would likely be more confident than a modeler who has selected ROC as the performance metric. This  example should serve as a warning not to engage in &lt;em&gt;performance measure hacking&lt;/em&gt;. Choose your performance measure before you compute anything.&lt;/p&gt;

&lt;p&gt;For additional reading, look here for a &lt;a href=&#34;https://static1.squarespace.com/static/51156277e4b0b8b2ffe11c00/t/5aec85718a922d93cc33bf56/1525450098182/Comparing+Models+Using+Resampling+and+Bayesian+Methods.pdf&#34;&gt;short presentation&lt;/a&gt; by Max Kuhn on the key &lt;code&gt;tidyposterior&lt;/code&gt; ideas, and see the the following papers for an in-depth look at the underlying theory:
&lt;a href=&#34;http://www.jmlr.org/papers/volume18/16-305/16-305.pdf&#34;&gt;Benavoli et al. (2017)&lt;/a&gt; and
&lt;a href=&#34;https://link.springer.com/content/pdf/10.3758%2Fs13423-016-1221-4.pdf&#34;&gt;Kruschke and Liddell (2017)&lt;/a&gt;. For Bayesian analysis in general, and more on the ROPE concept see: Kruschke  (2014). &lt;a href=&#34;https://www.elsevier.com/books/doing-bayesian-data-analysis/kruschke/978-0-12-405888-0&#34;&gt;Doing Bayesian Data Analysis, Second Edition: A Tutorial with R, JAGS, and Stan (2 edition)&lt;/a&gt;.&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/12/16/bayesian-model-comparison/&#39;;&lt;/script&gt;
      </description>
    </item>
    
    <item>
      <title>ROC Curves</title>
      <link>https://rviews.rstudio.com/2019/01/17/roc-curves/</link>
      <pubDate>Thu, 17 Jan 2019 00:00:00 +0000</pubDate>
      
      <guid>https://rviews.rstudio.com/2019/01/17/roc-curves/</guid>
      <description>
        


&lt;p&gt;I have been thinking about writing a short post on R resources for working with (&lt;a href=&#34;https://en.wikipedia.org/wiki/Receiver_operating_characteristic&#34;&gt;ROC&lt;/a&gt;) curves, but first I thought it would be nice to review the basics. In contrast to the usual (usual for data scientists anyway) machine learning point of view, I’ll frame the topic closer to its historical origins as a portrait of practical decision theory.&lt;/p&gt;
&lt;p&gt;ROC curves were invented during WWII to help radar operators decide whether the signal they were getting indicated the presence of an enemy aircraft or was just noise. (&lt;a href=&#34;https://web.stanford.edu/~yesavage/ROC%20Slides%20OHara.ppt&#34;&gt;O’Hara et al.&lt;/a&gt; specifically refer to the Battle of Britain, but I haven’t been able to track that down.)&lt;/p&gt;
&lt;p&gt;I am relying comes from James Egan’s classic text &lt;a href=&#34;https://amzn.to/2FgC3BH&#34;&gt;&lt;em&gt;signal Detection Theory and ROC Analysis&lt;/em&gt;&lt;/a&gt;) for the basic setup of the problem. It goes something like this: suppose there is an observed quantity (maybe the amplitude of the radar blip), X, that could indicate either the presence of a meaningful signal (e.g. from a &lt;a href=&#34;https://en.wikipedia.org/wiki/Messerschmitt_Bf_109&#34;&gt;Messerschmitt&lt;/a&gt;) embedded in noise, or just noise alone (geese). When viewing X in some small interval of time, we would like to establish a threshold or cutoff value, c, such that if X &amp;gt; c we will we can be pretty sure we are observing a signal and not just noise. The situation is illustrated in the little animation below.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;library(tidyverse)
library(gganimate)  #for animation
library(magick)     # to put animations sicde by side&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We model the noise alone as random draws from a N(0,1) distribution, signal plus noise as draws from N(s_mean, S_sd), and we compute two conditional distributions. The probability of a “Hit” or P(X &amp;gt; c | a signal is present) and the probability of a “False Alarm”, P(X &amp;gt; c | noise only).&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;s_mean &amp;lt;- 2  # signal mean
s_sd &amp;lt;- 1.1   # signal standard deviation

x &amp;lt;- seq(-5,5,by=0.01) # range of signal
signal &amp;lt;- rnorm(100000,s_mean,s_sd)
noise &amp;lt;- rnorm(100000,0,1)

PX_n &amp;lt;- 1 - pnorm(x, mean = 0, sd = 1) # P(X &amp;gt; c | noise only) = False alarm rate
PX_sn &amp;lt;- 1 - pnorm(x, mean = s_mean, sd = s_sd) # P(X &amp;gt; c | signal plus noise) = Hit rate&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We plot these two distributions in the left panel of the animation for different values of the cutoff threshold threshold.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;threshold &amp;lt;- data.frame(val = seq(from = .5, to = s_mean, by = .2))

dist &amp;lt;- 
  data.frame(signal = signal, noise = noise) %&amp;gt;% 
  gather(data, value) %&amp;gt;% 
  ggplot(aes(x = value, fill = data)) +
  geom_density(trim = TRUE, alpha = .5) +
  ggtitle(&amp;quot;Conditional Distributions&amp;quot;) +
  xlab(&amp;quot;observed signal&amp;quot;)  + 
  scale_fill_manual(values = c(&amp;quot;pink&amp;quot;, &amp;quot;blue&amp;quot;))

p1 &amp;lt;- dist + geom_vline(data = threshold, xintercept = threshold$val, color = &amp;quot;red&amp;quot;) +
            transition_manual(threshold$val)
p1 &amp;lt;- animate(p1)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, we plot the ROC curve for our detection system in the right panel. Each point in this plot corresponds to one of the cutoff thresholds in the left panel.&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;df2 &amp;lt;- data.frame(x, PX_n, PX_sn)
roc &amp;lt;- ggplot(df2) +
  xlab(&amp;quot;P(X | n)&amp;quot;) + ylab(&amp;quot;P(X | sn)&amp;quot;) +
  geom_line(aes(PX_n, PX_sn)) +
  geom_abline(slope = 1) +
  ggtitle(&amp;quot;ROC Curve&amp;quot;) + 
  coord_equal()

q1 &amp;lt;- roc +
        geom_point(data = threshold, aes(1-pnorm(val),
                          1- pnorm(val, mean = s_mean, sd = s_sd)), 
                          color = &amp;quot;red&amp;quot;) +
                          transition_manual(val)

q1 &amp;lt;- animate(q1)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(The slick trick of getting these two animation panels to line up in the same frame is due to a helper function from Thomas Pedersen and Patrick Touche that can be found &lt;a href=&#34;https://github.com/thomasp85/gganimate/issues/226&#34;&gt;here&lt;/a&gt;)&lt;/p&gt;
&lt;pre class=&#34;r&#34;&gt;&lt;code&gt;combine_gifs(p1,q1)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;/post/2019-01-06-roc-curves_files/figure-html/unnamed-chunk-6-1.gif&#34; /&gt;&lt;!-- --&gt;&lt;/p&gt;
&lt;p&gt;Notice that as the cutoff line moves further to the right, giving the decision maker a better chance of making a correct decision, the corresponding point moves down the ROC curve towards a lower Hit rate. This illustrates the fundamental tradefoff between hit rate and false alarm rate in the underlying decision problem. For any given problem, a decision algorithm or classifier will live on some ROC curve in false alarm / hit rate space. Improving the hit rate usually come at the cost of increasing the probability of more false alarms.&lt;/p&gt;
&lt;p&gt;The simulation code also lets you vary s_mean, the mean of the signal, Setting this to a large value (maybe 5), will sufficiently separate the signal from the noise, and you will get the kind of perfect looking ROC curve you may be accustomed to seeing produced by your best classification models.&lt;/p&gt;
&lt;p&gt;The usual practice in machine learning applications is to compute the area under the ROC curve, &lt;a href=&#34;https://en.wikipedia.org/wiki/Receiver_operating_characteristic#Area_under_the_curve&#34;&gt;AUC&lt;/a&gt;. This has become the “gold standard” for evaluating classifiers. Given a choice between different classification algorithms, data scientists routinely select the classifier with the highest AUC. The intuition behind this is compelling: given that the ROC is always a monotone increasing, concave downward curve, the best possible curve will have an inflection point in the upper left hand corner and an AUC approaching one (All of the area in ROC space).&lt;/p&gt;
&lt;p&gt;Unfortunately, the automatic calculation and model selection of the AUC discourages analysis of how the properties and weaknesses of ROC curves may pertain to the problem at hand. Keeping sight of the decision theory point of view may help to protect against the spell of mechanistic thinking encouraged by powerful algorithms. Although, automatically selecting a classifier based on the value of the AUC may make good sense most of the time, things can go wrong. For example, it is not uncommon for analysts to interpret AUC as a measure of the accuracy of the classifier. But, the AUC is not a measure of accuracy as a little thought about the decision problem would make clear. The irony here is that there was a time, not too long ago, when people thought it was necessary to argue that the AUC is a better measure than accuracy for evaluating machine learning algorithms. For example, have a look at the &lt;a href=&#34;https://www.cse.ust.hk/nevinZhangGroup/readings/yi/Bradley_PR97.pdf&#34;&gt;1997 paper&lt;/a&gt; by Andrew Bradley where he concludes that &lt;em&gt;“…AUC be used in preference to overall accuracy for ‘single number’ evaluation of machine learning algorithms”.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What does the AUC measure? For the binary classification problem of our simple signal processing example, a little calculus will show that the AUC is the probability that a randomly drawn interval with a signal present will produce a higher X value than a signal interval containing noise alone. See &lt;a href=&#34;https://link.springer.com/content/pdf/10.1007%2Fs10994-009-5119-5.pdf&#34;&gt;&lt;em&gt;Hand (2009)&lt;/em&gt;&lt;/a&gt;, and the very informative &lt;a href=&#34;https://stats.stackexchange.com/questions/180638/how-to-derive-the-probabilistic-interpretation-of-the-auc&#34;&gt;&lt;em&gt;StackExchange&lt;/em&gt;&lt;/a&gt; discussion for the math.&lt;/p&gt;
&lt;p&gt;Also note, that in the paper just cited, Hand examines some of the deficiencies of the AUC. His discussion provides an additional incentive for keeping the decision theory tradeoff in mind when working with ROC curves. Hand concludes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;…it [AUC] is fundamentally incoherent in terms of misclassification costs: the AUC uses different misclassification cost distributions for different classifiers. This means that using the AUC is equivalent to using different metrics to evaluate different classification rules.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and goes on to propose the &lt;strong&gt;H measure&lt;/strong&gt; for ranking classifiers. (See the R package &lt;a href=&#34;https://cran.r-project.org/package=hmeasure&#34;&gt;hmeasure&lt;/a&gt;) Following up on this will have to be an investigation for another day.&lt;/p&gt;
&lt;p&gt;Our discussion in this post has taken us part way along just one path through the enormous literature on ROC curves which could not be totally explored in a hundred posts. I will just mention that not long after its inception, ROC analysis was used to establish a conceptual framework for problems relating to sensation and perception in the field of psychophysics (&lt;a href=&#34;https://psych.nyu.edu/pelli/pubs/pelli1995methods.pdf&#34;&gt;&lt;em&gt;Pelli and Farell (1995)&lt;/em&gt;&lt;/a&gt;) and thereafter applied to decision problems in Medical Diagnostics, (&lt;a href=&#34;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3755824/#B26&#34;&gt;&lt;em&gt;Hajian-Tilaki (2013)&lt;/em&gt;&lt;/a&gt;), National Intelligence (&lt;a href=&#34;https://www.nap.edu/read/13062/chapter/7&#34;&gt;&lt;em&gt;McCelland (2011)&lt;/em&gt;&lt;/a&gt;) and just about any field that collects data to support decision making.&lt;/p&gt;
&lt;p&gt;If you are interested in delving deeper into ROC curves, the references in papers mentioned above may help to guide further exploration.&lt;/p&gt;

        &lt;script&gt;window.location.href=&#39;https://rviews.rstudio.com/2019/01/17/roc-curves/&#39;;&lt;/script&gt;
      </description>
    </item>
    
  </channel>
</rss>
