<p><div><div><div><div data-width="100" data-show-faces="false" data-send="true" data-layout="button_count" data-height="21" data-href="https://www.r-bloggers.com/versioning-r-model-objects-in-sql-server/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://itsalocke.com/versioning-r-model-objects-in-sql-server/"> R – Locke Data</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<div></div>
<h1>High-level info</h1>
<p>If you build a model and never update it you’re missing a trick. Behaviours change so your model will tend to perform worse over time. You’ve got to regularly refresh it, whether that’s adjusting the existing model to fit the latest data (recalibration) or building a whole new model (retraining), but this means you’ve got new versions of your model that you have to handle. You need to think about your methodology for versioning R model objects, ideally before you lose any versions.</p>
<p>You could store models with ye olde YYYYMMDD style of versioning but that means regularly changing your code to use the latest model version. I’m too lazy for that!</p>
<p>If we’re storing our R model objects in SQL Server then we can utilise another SQL Server capability, <a target="_blank" rel="nofollow" href="https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables">temporal tables</a>, to take the pain out of versioning and make it super simple.</p>
<p>Temporal tables will track changes automatically so you would overwrite the previous model with the new one and it would keep a copy of the old one automagically in a history table. You get to always use the latest version via the main table but you can then write temporal queries to extract any version of the model that’s ever been implemented. Super neat!</p>
<p>For some of you, if you’re not interested in the technical details you can drop off now with the knowledge that you can store your models in a non-destructive but easy to use way in SQL Server if you need to.</p>
<p>If you want to see how it’s done, read on!</p>
<h1>The technical info</h1>
<p>Below is a working example of how to do versioning R model objects in SQL Server:</p>
<ul>
<li>define a versioned model table</li>
<li>write a model into the model table</li>
<li>create a new model and overwrite the old</li>
<li>create a prediction using the latest model on the fly</li>
</ul>
<blockquote><p>
Note this doesn’t tell you what changed, just that something did change. To identify model changes you will need to load up the models and compare their coefficients or other attributes to identify what exactly changed.
</p>
</blockquote>
<h2>SQL objects</h2>
<h3>A temporal table</h3>
<p>A normal table for storing a model might look like</p>
<pre><code>CREATE TABLE [companyModels]    (  
  [id] int NOT NULL PRIMARY KEY CLUSTERED   
, [name] varchar(200) NOT NULL      
, [modelObj] varbinary(max)    
, CONSTRAINT unique_modelname UNIQUE ([name]))
</code></pre>
<p>If we’re turning it into a temporal table we need to add some extra columns, but we won’t worry about these extra columns day-to-day.</p>
<pre><code>CREATE TABLE [companyModels]    (  
  [id] int NOT NULL PRIMARY KEY CLUSTERED   
, [name] varchar(200) NOT NULL      
, [modelObj] varbinary(max)    
, [ValidFrom] datetime2 (2) GENERATED ALWAYS AS ROW START  
, [ValidTo] datetime2 (2) GENERATED ALWAYS AS ROW END  
, PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)  
, CONSTRAINT unique_modelname UNIQUE ([name]))
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.companyModelsHistory));  
</code></pre>
<h3>A stored procedure</h3>
<p>As we have the ID and Valid* columns in our table, we can’t use RODBCs simple table functions and the ID column doesn’t play nicely with RevoScaleR’s <code>rxWriteObject()</code> function as that wants to insert a NULL. It’s not all bad though because we can get around it by using a stored procedure.</p>
<p>This stored procedure will perform an INSERT if it can’t find a model by the given name, and will perform an UPDATE if it does find a match.</p>
<pre><code>CREATE PROCEDURE modelUpsert
@modelname  varchar(200) , 
@modelobj varbinary(max) 
AS
WITH MySource as (
    select @modelname as modelName, @modelobj as modelObj
)
MERGE companymodels AS MyTarget
USING MySource
     ON MySource.modelname = MyTarget.modelname
WHEN MATCHED THEN UPDATE SET 
    modelObj = MySource.modelObj
WHEN NOT MATCHED THEN INSERT
    (
        modelname, 
        modelObj
    )
    VALUES (
        MySource.modelname, 
        MySource.modelObj
    );
</code></pre>
<h2>R</h2>
<h3>Build a model</h3>
<p>We need a model to save!</p>
<p>To be able to store the model in the database we need to use the <code>serialize()</code> function to turn it into some raw character thingies and then combine them together with <code>paste0()</code> so they go in the same row.</p>
<pre><code>myModelV1&lt;-lm(column1~column2, someData)
preppedDF&lt;-data.frame(modelname=&quot;myModel&quot;,
                      modelobj=paste0(serialize(myModelV1,NULL)
                                      ,collapse = &quot;&quot;)
                      )
</code></pre>
<h3>Call the stored procedure</h3>
<p>We need <a target="_blank" rel="nofollow" href="http://itsalocke.com/versioning-r-model-objects-in-sql-server/">RODBC</a> and <a target="_blank" rel="nofollow" href="http://itsalocke.com/versioning-r-model-objects-in-sql-server/">RODBCext</a> for executing our stored procedure in our database.</p>
<pre><code>library(RODBC)
library(RODBCext)

dbstring&lt;-'Driver={ODBC Driver 13 for SQL Server};Server=XXX;Database=XXX;Uid=XXX;Pwd=XXX'
dbconn&lt;-RODBC::odbcDriverConnect(dbstring)

RODBCext::sqlExecute(dbconn, &quot;exec modelUpsert @modelname=? , @modelobj=?&quot;,
                      data = preppedDF)
</code></pre>
<p>This will now have our model in our database table.</p>
<pre><code>RODBC::sqlQuery(dbconn, &quot;select * from companymodels&quot;)
# 1 row
RODBC::sqlQuery(dbconn, &quot;select * from companymodelshistory&quot;)
# 0 row
</code></pre>
<p>You should get one row in our main table and no rows in ouor history table.</p>
<h3>Rinse and repeat</h3>
<p>If we make a change to our model and then push the new model with the same name, we’ll still get one row in our core table, but now we’ll get a row in our history table that contains our v1 model.</p>
<pre><code>myModelV2&lt;-lm(column1~column2, someData[-1,])
preppedDF&lt;-data.frame(modelname=&quot;myModel&quot;,
                      modelobj=paste0(serialize(myModelV2,NULL)
                                      ,collapse = &quot;&quot;)
)
RODBCext::sqlExecute(dbconn, &quot;exec modelUpsert @modelname=? , @modelobj=?&quot;,
                     data = preppedDF)

RODBC::sqlQuery(dbconn, &quot;select * from companymodels&quot;)
# 1 row
RODBC::sqlQuery(dbconn, &quot;select * from companymodelshistory&quot;)
# 1 row
</code></pre>
<h2>Using our model in SQL</h2>
<p>If we want to use our model for predictions in SQL, we can now retrieve it from the table along with some input data and get back our input data plus a prediction.</p>
<pre><code>DECLARE @mymodel VARBINARY(MAX)=(SELECT modelobj 
                FROM companymodels 
                WHERE modelname='mymodel'
                );
EXEC sp_execute_external_script
@language = N'R',  
@script = N'
OutputDataSet&lt;-cbind(InputDataSet,
    predict(unserialize(as.raw(model)), InputDataSet)
    )
',
@input_data_1 = N'SELECT 42 as column2',  
@params = N'@model varbinary(max)',  
@model =  @mymodel 
</code></pre>
<p>The post <a target="_blank" rel="nofollow" href="http://itsalocke.com/versioning-r-model-objects-in-sql-server/">Versioning R model objects in SQL Server</a> appeared first on <a target="_blank" rel="nofollow" href="http://itsalocke.com/">Locke Data</a>. Locke Data are a data science consultancy aimed at helping organisations get ready and get started with data science.</p>
<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://itsalocke.com/versioning-r-model-objects-in-sql-server/"> R – Locke Data</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" rel="nofollow" href="https://www.r-project.org/">R</a> news and <a title="R tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/tutorial">tutorials</a> on topics such as: <a title="Data science" rel="nofollow" href="https://www.r-bloggers.com/search/data%20science">Data science</a>, <a title="Big Data" rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data">Big Data, <a title="R jobs" rel="nofollow" href="https://www.r-users.com/">R jobs</a>, visualization (<a title="ggplot and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2">ggplot2</a>, <a title="Boxplots using lattice and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/boxplot">Boxplots</a>, <a title="Maps and gis" rel="nofollow" href="https://www.r-bloggers.com/search/map">maps</a>, <a title="Animation in R" rel="nofollow" href="https://www.r-bloggers.com/search/animation">animation</a>), programming (<a title="RStudio IDE for R" rel="nofollow" href="https://www.r-bloggers.com/search/RStudio">RStudio</a>, <a title="Sweave and literate programming" rel="nofollow" href="https://www.r-bloggers.com/search/sweave">Sweave</a>, <a title="LaTeX in R" rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX">LaTeX</a>, <a title="SQL and databases" rel="nofollow" href="https://www.r-bloggers.com/search/SQL">SQL</a>, <a title="Eclipse IDE for R" rel="nofollow" href="https://www.r-bloggers.com/search/eclipse">Eclipse</a>, <a title="git and github, Version Control System" rel="nofollow" href="https://www.r-bloggers.com/search/git">git</a>, <a title="Large data in R using Hadoop" rel="nofollow" href="https://www.r-bloggers.com/search/hadoop">hadoop</a>, <a title="Web Scraping of google, facebook, yahoo, twitter and more using R" rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping">Web Scraping</a>) statistics (<a title="Regressions and ANOVA analysis tutorials" rel="nofollow" href="https://www.r-bloggers.com/search/regression">regression</a>, <a title="principal component analysis tutorial" rel="nofollow" href="https://www.r-bloggers.com/search/PCA">PCA</a>, <a title="Time series" rel="nofollow" href="https://www.r-bloggers.com/search/time+series">time series</a>, <a title="finance trading" rel="nofollow" href="https://www.r-bloggers.com/search/trading">trading</a>) and more...
<img height="1" alt="" width="1" src="http://feeds.feedburner.com/~r/RBloggers/~4/rHlel9qCqp8"></a></div>