<p></p>
<p><a href="https://twitter.com/drsimonj">@drsimonj</a> here to introduce my latest tidy-modelling package for R, “<a href="https://github.com/drsimonj/twidlr">twidlr</a>”. twidlr wraps model and predict functions you already know and love with a consistent data.frame-based API!</p>
<p>All models wrapped by twidlr can be fit to data and used to make predictions as follows:</p>
<pre><code>library(twidlr)

fit &lt;- model(data, formula, ...)
predict(fit, data, ...)
</code></pre>
<ul><li> <code>data</code> is a data.frame (or object that can be corced to one) and is <strong>required</strong></li>
<li> <code>formula</code> describes the model to be fit</li>
</ul><h2><a href="https://drsimonj.svbtle.com/#the-motivation_2"> </a>The motivation</h2>
<p>The APIs of model and predict functions in R are inconsistent and messy.</p>
<p>Some models like linear regression want a formula and data.frame:</p>
<pre><code>lm(hp ~ ., mtcars)
</code></pre>
<p>Models like gradient-boosted decision trees want vectors and matrices:</p>
<pre><code>library(xgboost)

y &lt;- mtcars$hp
x &lt;- as.matrix(mtcars[names(mtcars) != &quot;hp&quot;])

xgboost(x, y, nrounds = 5)
</code></pre>
<p>Models like generalized linear models want you to work. For example, to create interactions and dummy-coded variables:</p>
<pre><code>library(glmnet)

y &lt;- iris$Petal.Length
x &lt;- model.matrix(Petal.Length ~ Sepal.Width * Sepal.Length + Species, iris)

glmnet(x, y)
</code></pre>
<p>Some models like k-means don’t have a corresponding predict function:</p>
<pre><code>fit &lt;- kmeans(iris[1:120,-5], centers = 3)
predict(fit, iris[121:150,])

## Error in UseMethod(&quot;predict&quot;) : 
##   no applicable method for 'predict' applied to an object of class &quot;kmeans&quot;
</code></pre>
<p>Some predict functions are impure and return unexpected results. For example, linear discriminant analysis:</p>
<pre><code>library(MASS)

d &lt;- iris
fit &lt;- lda(Species ~ ., d)

table(predict(fit)$class)
#> 
#>     setosa versicolor  virginica 
#>         50         49         51

d &lt;- d[1:10,]

table(predict(fit)$class)
#> 
#>     setosa versicolor  virginica 
#>         10          0          0
</code></pre>
<h2><a href="https://drsimonj.svbtle.com/#twidlr_2"> </a>~ twidlr</h2>
<p>twidlr helps to solve these problems by wrapping model and predict functions you already know and love with a consistent data.frame-based API!</p>
<p>Load twidlr and your favourite models can be fit to a data.frame with a formula and any additional arguments! To demonstrate, compare API to above:</p>
<pre><code>library(twidlr)

lm(mtcars, hp ~ .)
xgboost(mtcars, hp ~ ., nrounds = 5)
glmnet(iris, Petal.Length ~ Sepal.Width * Sepal.Length + Species)
</code></pre>
<p>What’s more, predictions can be made with all fitted models via <code>predict</code> and a data.frame. This even works for models that don’t traditionally have a <code>predict</code> method:</p>
<pre><code>library(twidlr)

fit &lt;- kmeans(iris[1:140,-5], centers = 3)
predict(fit, iris[141:150,])
#>  [1] 3 3 3 3 3 3 3 3 3 3
</code></pre>
<h2><a href="https://drsimonj.svbtle.com/#bonus-example_2"> </a>Bonus example</h2>
<p>Although useful in itself, a consistent data.frame-based API expands the capabilities of other tidy and data.frame-based packages like the <a href="http://tidyverse.org/">tidyverse packages</a> and <a href="https://github.com/drsimonj/pipelearner">pipelearner</a>.</p>
<p>For the motivated, this demonstrates how to fit multiple models and compare their RMSE on new data. It’s streamlined because purrr’s map functions can exploit the consistent API for each model and <code>predict</code>.</p>
<pre><code>library(twidlr)
library(purrr)

train &lt;- cars[ 1:40, ]
test  &lt;- cars[41:50, ]
f &lt;- c(&quot;lm&quot;, &quot;randomForest&quot;, &quot;rpart&quot;)

# Fit each model to training data and compute RMSE on test data
rmse &lt;- invoke_map(f, data = train, formula = speed ~ dist) %>%
  map(predict, data = test) %>% 
  map_dbl(~ sqrt(mean((. - test$speed)^2)))

setNames(rmse, f)
#>           lm randomForest        rpart 
#>     3.832426     6.129539     6.034932
</code></pre>
<p>If you can’t see the value, try doing this without twidlr.</p>
<h2><a href="https://drsimonj.svbtle.com/#take-home-messages_2"> </a>Take home messages</h2>
<p>twidlr attempts to brings the follwing to modelling in R:</p>
<ul><li> a consistent and tidy model APIs</li>
<li> pure and available predict functions</li>
<li> the power of formula operators</li>
<li> tidyverse philosophy (eg keep piping)</li>
</ul><p>But twidlr is new, and needs your help to grow. So if your favourite model isn’t listed <a href="https://github.com/drsimonj/twidlr">here</a>, fork <a href="https://github.com/drsimonj/twidlr">twidlr on GitHub</a> and add it to help improve modelling in R! Advice for contributing can be found <a href="https://github.com/drsimonj/twidlr/blob/master/CONTRIBUTING.md">here</a>.</p>
<p>Thanks already to <a href="https://github.com/joranE">Joran Elias</a> and <a href="https://github.com/Lingtax">Mathew Ling</a> for their contributions!</p>
<h2><a href="https://drsimonj.svbtle.com/#sign-off_2"> </a>Sign off</h2>
<p>Thanks for reading and I hope this was useful for you.</p>
<p>For updates of recent blog posts, follow <a href="https://twitter.com/drsimonj">@drsimonj</a> on Twitter, or email me at <a href="mailto:drsimonjackson@gmail.com">drsimonjackson@gmail.com</a> to get in touch.</p>
<p>If you’d like the code that produced this blog, check out the <a href="https://github.com/drsimonj/blogR">blogR GitHub repository</a>.</p>