<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Andreas De Ruiter's BI blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/andreasderuiter/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/feed.xml</link>
    <description>Best practices on designing business intelligence solutions using SSAS, SSIS and other Microsoft BI tools</description>
    <lastBuildDate>Wed, 24 Feb 2016 09:37:40 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Creating a security-enabled AngularJS application with ASP.NET MVC the easy way (tutorial)</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/creating-a-security-enabled-angularjs-application-with-asp-net-mvc-the-easy-way-tutorial</link>
      <pubDate>Wed, 06 Jan 2016 05:26:42 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2016/01/06/creating-a-security-enabled-angularjs-application-with-asp-net-mvc-the-easy-way-tutorial/</guid>
      <description><![CDATA[In recent years Single Page Applications (SPAs) have become popular and there are plenty JavaScript...]]></description>
      <content:encoded><![CDATA[<p>In recent years Single Page Applications (SPAs) have become popular and there are plenty JavaScript frameworks which make it relatively easy to develop them. Angular, which is developed Google, seems to be the one with the most momentum. </p>  <p>In this post I’ll explain how you can use AngularJS together with ASP.NET to create Angular apps together with ASP.NET Web APIs. For this post I used the Community Editions of Visual Studio 2015 with MVC 4 and .NET Framework 4.6. I used the latest production version of Angular 1. I also used TypeScript and Bootstrap. There are many great tutorials on the web for each of these technologies. Together Bootstrap, Angular and TypeScript (the ‘BAT’ stack) complement each other to create a great client-side stack for building modern web applications.</p>  <p>The role of ASP.NET is to host our server-side logic. There are two important parts to this: Web APIs are used to expose REST endpoints which are backed by .NET code. Additionally we’ll leverage ASP.NET’s rich identity framework to implement security. ASP.NET’s identity framework supports many powerful features including social login, two-factor authentication and active directory support.</p>  <p>This tutorial assumes you’re already familiar with the basics of ASP.NET, AngularJS and Bootstrap. If you want to learn more about any of these topics. there are lots of other good tutorials on the net.</p>  <p>At the end of this tutorial you’ll have a skeleton Angular application backed with a simple Web API and security enabled so that users of the app will be presented a login experience. You can find the source code for this project on GitHub: <a title="https://github.com/andreasderuiter/bat-mvc" href="https://github.com/andreasderuiter/bat-mvc">https://github.com/andreasderuiter/bat-mvc</a>.</p>  <h3></h3>  <h3></h3>  <h3></h3>  <h3>Something to understand before we get started</h3>  <p>In this tutorial we will <em>not</em> implement a login page within the Angular app itself. Instead we’ll create the boilerplate for an AngularJS app which will <em>integrate with </em>the existing login pages which are part of the standard MVC project template. </p>  <p>At first, this approach may seem non-optimal because the whole point of a SPA application is to embed all functionality in a single page. In my perspective however, the goal is to create a fluid and responsive user experience and SPA is just a means to an end. The fact that a user will temporarily be redirected to a login page, which will add 1 second of delay once per user session, is a small price to pay for the benefits we get from using this approach: </p>  <ul>   <li>The solution is very simple and easy to understand</li>    <li>No extra code means no bugs and minimal cost of maintenance</li>    <li>The solution fully supports all of the features of MVCs authentication system</li>    <li>If you have a traditional non-SPA web application which you want to migrate to Angular, this offers a smoother migration path than rewriting all of this in Angular at once.</li> </ul>  <p>Nonetheless, if you wish to implement authentication within your Angular app, here are some resources: </p>  <ul>   <li><a title="http://www.codeproject.com/Articles/806029/Getting-started-with-AngularJS-and-ASP-NET-MVC-Par" href="http://www.codeproject.com/Articles/806029/Getting-started-with-AngularJS-and-ASP-NET-MVC-Par">http://www.codeproject.com/Articles/806029/Getting-started-with-AngularJS-and-ASP-NET-MVC-Par</a>. This tutorial shows how you can implement authentication in an&#160; easy to understand way.</li>    <li><a title="https://bitbucket.org/david.antaramian/so-21662778-spa-authentication-example/overview" href="https://bitbucket.org/david.antaramian/so-21662778-spa-authentication-example/overview">https://bitbucket.org/david.antaramian/so-21662778-spa-authentication-example/overview</a>. Another tutorial.</li>    <li><a title="https://github.com/JustMaier/MVC5-SPA-Angular" href="https://github.com/JustMaier/MVC5-SPA-Angular">https://github.com/JustMaier/MVC5-SPA-Angular</a>. This is a component which you can include as a script library in your app. This library works with MVC5 (not MVC4 which we’re using in this tutorial) </li> </ul>  <p>If you’re not sure which way to go, my advice would be to follow this tutorial first so you quickly have a working solution and can then focus on the actual core of your app. There’s nothing that will prohibit you from adding authentication plumbing directly into your Angular code at a later point in time.</p>  <h3>Step 1: Setting up the initial project</h3>  <p>In Visual Studio, create a new ASP.NET MVC4 application with MVC forms and Web API enabled, and configured for Individual Users Accounts. </p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4861.image_thumb33_40B1ADA9.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4861.image_5F00_thumb33_5F00_40B1ADA9.png"><img title="image_thumb33" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image_thumb33" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/1212.image_thumb33_thumb_2C2EEEDC.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/1212.image_5F00_thumb33_5F00_thumb_5F00_2C2EEEDC.png" width="617" height="484" /></a></p>  <p>Test the default app to ensure it runs fine, and the login link in the top right of the page works. If there’s no login link in the upper right corner of the home page, you probably selected Web API instead of MVC as the project type. In that case, go back and recreate the project.</p>  <p>Since we want to keep things simple for now, we'll only use nuget as our package manager and rely on the standard build process. We won’t use bower, npm, gulp, grant or any of the other modern web development tools.</p>  <p>Next we'll use the nuget Package Manager Console (Tools menu) to get the latest stable versions of the following components by entering the following commands:</p>  <p><strong>Install-Package jquery      <br />Install-Package bootstrap       <br />Install-Package angularjs       <br />Install-Package jquery.TypeScript.DefinitelyTyped       <br />Install-Package angularjs.TypeScript.DefinitelyTyped       <br />Install-Package bootstrap.TypeScript.DefinitelyTyped</strong></p>  <p>In my case, the following versions were installed: jquery.2.1.4; bootstrap.3.3.6; angularjs.1.4.8; jquery.TypeScript.DefinitelyTyped 2.3.3; angularjs.TypeScript.DefinitelyTyped.4.4.0; bootstrap.TypeScript.DefinitelyTyped 0.1.7.</p>  <p>In addition to jquery, AngularJS and BootStrap, we loaded the API definitions for those JavaScript libraries which enables Visual Studio to provide IntelliSense as we start building our Angular app using TypeScript.</p>  <p>Now make sure you have the latest stable version of the following tools installed (via Extensions and Updates in the Visual Studio Tools menu).</p>  <ul>   <li><strong>TypeScript for Visual Studio</strong>. At this time the most recent stable version is 1.7.6. </li>    <li><strong>SideWaffle Template Pack.</strong> This will install TypeScript item templates for various Angular components, such as directives and controllers, which we will user later during this tutorial. </li> </ul>  <h3>Step 2 - creating &quot;Hello World&quot;</h3>  <p>The next step is to create a simple Hello World app using AngularJS and TypeScript. To keep things as simple as possible, we’ll simply use the About page (/Home/About) to host our Angular app.&#160; </p>  <p>Create a folder called &quot;App&quot; under the Scripts folder. This is where we'll put the app code.</p>  <p>The HTML page will need to load all the required script files, including the libraries we installed through nuget and all the scripts which will eventually belong to our SPA app. This is a pain if you do it manually, so we'll automate this by adjusting BundleConfig.cs in the App_Start folder by adding the following lines.</p>  <p>bundles.Add(new ScriptBundle(&quot;~/bundles/angular&quot;).Include(&quot;~/Scripts/angular.js&quot;));   <br />bundles.Add(new ScriptBundle(&quot;~/bundles/app&quot;).IncludeDirectory(&quot;~/Scripts/App&quot;, &quot;*.js&quot;, true));</p>  <p>On GitHub: <a href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/App_Start/BundleConfig.cs">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/App_Start/BundleConfig.cs</a></p>  <p>If you wish to use additional Angular components, such as for routing and animations, you can include these in the first bundle statement. The second statement takes all the .js files under our App directory and includes them as well. Note that we'll be writing the code in TypeScript files which have a .ts extension, and TypeScript will transpile them into .js files in the same folder. </p>  <p>Next we'll adjust the _Layout.cshtml in the Views/Shared folder to ensure our bundles are actually loaded in the HTML page. By doing this in the _Layout.cshtml file, any view/page in our MVC project will be able to contain Angular apps. </p>  <p>Add the following lines in the bottom of _Layout.cshtml where the other bundles are also loaded:</p>  <p>@Scripts.Render(&quot;~/bundles/angular&quot;)   <br />@Scripts.Render(&quot;~/bundles/app&quot;)</p>  <p>On GitHub: <a href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Views/Shared/_Layout.cshtml">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Views/Shared/_Layout.cshtml</a></p>  <p>Now we're ready to create the SPA app itself! First, open About.cshtml in the Views/Home folder and remove everything in it and replace it as follows:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5488.image_5D7BC173.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5488.image_5F00_5D7BC173.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5611.image_thumb_46BC79EA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5611.image_5F00_thumb_5F00_46BC79EA.png" width="203" height="86" /></a></p>  <p>On GitHub: <a href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Views/Home/About.cshtml">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Views/Home/About.cshtml</a></p>  <p>Now we'll create the AngularJS app itself, which initially consists of an Angular module file and an Angular Directive which implements the <strong>&lt;my-app&gt;</strong> tag.</p>  <p>In the Visual Studio solution explorer, right-click the Scripts/App folder and select Add Item. Create a new file called <strong>app.ts </strong>using the AngularJs TypeScript Module template under the TypeScript/SideWaffle section. </p>  <p>The default code loads ngAnimate and ngRoute, but we're not using those for now and we'll comment them out to avoid errors. (If you want to use these modules later, you'll need to first install them through the package manager, then update the BundleConfig.cs file to ensure they are loaded on the page and finally add the modules back in app.ts.)</p>  <p>On GitHub: <a href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/app.ts">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/app.ts</a></p>  <p>Next we add a file called MyAppDirective.ts to the Scripts/App folder, using the AngularJs TypeScript Directive item template. We'll make two modifications: add a template line for the code we wish to generate, and on the last line we'll rename the directive to &quot;myApp&quot;, which is how we will wire it up to the &quot;&lt;my-app&gt;&quot; tag in the HTML file we edited before.</p>  <p>The file should look like this: <a href="https://gist.github.com/andreasderuiter/05c11f9f13b82a012683#file-myapp1-ts">myapp.ts</a>. Later in this tutorial we’ll make more changes to the file.</p>  <p>Now run the app to test that it works. When you navigate to the About page you should see this:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7215.image_thumb1_3B26BCAB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7215.image_5F00_thumb1_5F00_3B26BCAB.png"><img title="image_thumb1" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image_thumb1" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8206.image_thumb1_thumb_2F90FF6C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8206.image_5F00_thumb1_5F00_thumb_5F00_2F90FF6C.png" width="644" height="212" /></a></p>  <h3>Step 3: adding a Web API</h3>  <p>In the Visual Studio solution explorer, add a folder under the Controllers folder called &quot;api&quot;. This is where our API controllers will live. Right-click the folder to add an item and add a file called <strong>SampleController.cs</strong> using the Web API Controller Class (v2.1) template.</p>  <p>The default code already has a sample Get method which returns an array of two string values “value1” and “value2”. This will suffice and we won’t make any further changes to the Web API: <a title="https://gist.github.com/andreasderuiter/1542af70d626f2593b46" href="https://gist.github.com/andreasderuiter/1542af70d626f2593b46">SampleController.cs</a></p>  <p>Check that the controller works by starting the app and then navigate to the <a href="http://localhost:%3cport%3e/api/sample">http://localhost:&lt;port&gt;/api/sample</a> (keep the port number which is already used for your app). If you're using Internet Explorer, you'll be prompted to download a file. If you open Chrome and navigate to the same URL, you'll see the following:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/1055.image_55F2E2B7.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/1055.image_5F00_55F2E2B7.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/2352.image_thumb_13EF0427.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/2352.image_5F00_thumb_5F00_13EF0427.png" width="644" height="156" /></a></p>  <p>So now we know our new Web API is working!</p>  <h3>Step 4: letting our Angular app consume the Web API</h3>  <p>Create an HTML file in the Scripts/App folder called <strong>my-app.html </strong>with the following contents:</p>  <p>On GitHub: <a title="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/my-app.html" href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/my-app.html">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/my-app.html</a></p>  <p>Create a new file under /Scripts/App called MyAppController which you create using the &quot;AngularJs TypeScript Controller using 'Controller As' &quot; item template, and adjust the code as follows: <a title="https://gist.github.com/andreasderuiter/12711103380d6e0a5c69#file-myappcontroller1-cs" href="https://gist.github.com/andreasderuiter/12711103380d6e0a5c69#file-myappcontroller1-cs">MyAppController.cs</a>.</p>  <p>The controller class has a method called GetValues() which retrieves the data from the Web API REST service and stores it in a variable called “values”. The GetValues() method is called in the constructor, so the data will be loaded as soon as the page loads. The values variable is bound to the HTML template we added before so that the data from the web service is shown on the page.</p>  <p>Now we’ll adjust the <strong>MyAppDirective.ts</strong> file which we created in step 2 by copying the contents of the following file on GitHub:</p>  <p>On GitHub: <a title="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/MyAppDirective.ts" href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/MyAppDirective.ts">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/MyAppDirective.ts</a></p>  <p>The MyAppDirective.ts file contains all the plumbing to link the my-app.html template with the controller and map it to the &lt;my-app&gt; tag.</p>  <p>Now run the app and click the About link to verify that the app works:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8103.image_7A1ACDF7.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8103.image_5F00_7A1ACDF7.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7776.image_thumb_75381A3B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7776.image_5F00_thumb_5F00_75381A3B.png" width="644" height="234" /></a></p>  <p>Adjust the URL on line 21 of MyAppController.ts file to test what happens if the call fails:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8037.image_308B7FFA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8037.image_5F00_308B7FFA.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5811.image_thumb_52E31573.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5811.image_5F00_thumb_5F00_52E31573.png" width="644" height="226" /></a></p>  <p>As you see the Angular app returns an error message in red when it fails retrieving the data from the Web API.</p>  <p>Change the URL back to the original value.</p>  <h3>Step 5: Securing the Web API</h3>  <p>One way to secure the app is by decorating the About() method in the HomeController class with the [Authorize] attribute. This method would prevent the About page to be loaded unless the user is logged in. Keep in mind that you also need to secure the Web API controllers otherwise a hacker could still call the Web API directly.</p>  <p>In our app we'll leave the About() method unsecured, but we'll secure the Web API so that users can't access the data, even if they can initially load the About page. Doing this is very simple: just add the [Authorize] attribute to the Web API controller class or method which needs to be secured:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/1538.image_7164173C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/1538.image_5F00_7164173C.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4442.image_thumb_5AA4CFB3.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4442.image_5F00_thumb_5F00_5AA4CFB3.png" width="377" height="170" /></a></p>  <p>On GitHub: <a title="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Controllers/api/SampleController.cs" href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Controllers/api/SampleController.cs">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Controllers/api/SampleController.cs</a></p>  <p>We just secured the entire Web API controller class, so let's see what happens when you run the app:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4857.image_443AFA3F.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4857.image_5F00_443AFA3F.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6507.image_thumb_2D7BB2B6.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6507.image_5F00_thumb_5F00_2D7BB2B6.png" width="644" height="229" /></a></p>  <p>The good news is that we don’t see the data. Security works!</p>  <p>The bad news is that we don't get redirected to the Login page. Even worse, we don't get an error message, which is strange because we built the app such that it displays an error message whenever it fails to get data from the web service. At the very least, we would expect an error message like “Access denied”.</p>  <p>The reason for this behavior is that by default ASP.NET transparently returns the login page instead of a HTTP 401 status code when a user is not authenticated. For a traditional web application which directly renders the HTML it receives from the server this is fine, but in our case the REST service is the one getting back the HTML and it only knows how to parse a JSON response, therefore we don’t see anything.</p>  <p>Fortunately we can modify this default behavior in ASP.NET by removing a single line of code in the Startup.Auth.cs file in the App_Start folder. </p>  <p>Find the following line (probably at line 28)…</p>  <pre><p>                LoginPath = new PathString(&quot;/Account/Login&quot;), </p></pre>

<p>… and remove it or comment it out. </p>

<p>On GitHub: <a title="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/App_Start/Startup.Auth.cs" href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/App_Start/Startup.Auth.cs">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/App_Start/Startup.Auth.cs</a></p>

<p>Now, run our Web application to see what happens when we navigate to the About page:</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/1411.image_01CAE8BA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/1411.image_5F00_01CAE8BA.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6471.image_thumb_5D395B35.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6471.image_5F00_thumb_5F00_5D395B35.png" width="644" height="222" /></a></p>

<p>Yay! We now have an error message!</p>

<h3>Step 6: Redirecting to the ASP.NET Login page</h3>

<p>Adjust the catch part of getValues() in MyAppController.ts to check for a 401 status code and redirect to the login page:</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4540.image_4F0F1274.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4540.image_5F00_4F0F1274.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/1258.image_thumb_2A7D84F0.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/1258.image_5F00_thumb_5F00_2A7D84F0.png" width="580" height="266" /></a></p>

<p>On GitHub: <a title="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/MyAppController.ts" href="https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/MyAppController.ts">https://github.com/andreasderuiter/bat-mvc/blob/master/bat-mvc/Scripts/App/MyAppController.ts</a></p>

<p>Note how we also pass the return URL so that the login page knows where to return back to after the user successfully logged in. You’ll need to add this code to every REST call you make to the Web API in your app (in this boilerplate app this, this is the only place).</p>

<p>That's it! Now, when the Angular app gets a 401 message back, it will redirect the user to the /Account/Login page. Once the user logged in, he or she is redirected back to the About page and subsequent Web API calls return data for as long as the session lasts.</p>

<p>If you implement routing in your Angular application, you can even pass a deep link as returnurl so that the login page will redirect back to a certain “location” in your Angular app.</p>

<h3>Conclusion</h3>

<p>As you see, ASP.NET works very nicely together with Angular apps. Enabling the solution to leverage all the powerful security functionality in ASP.NET just requires a couple of simple tweaks. I hope you found this useful!</p>]]></content:encoded>
    </item>
    <item>
      <title>Transforming data in a data warehouse through SQL views</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/transforming-data-in-a-data-warehouse-through-sql-views</link>
      <pubDate>Mon, 20 Apr 2015 10:18:52 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/04/20/transforming-data-in-a-data-warehouse-through-sql-views/</guid>
      <description><![CDATA[This post is an add on to the another post titled Designing an ETL process with SSIS: two approaches...]]></description>
      <content:encoded><![CDATA[<p>This post is an add on to the another post titled <b><a href="http://blogs.msdn.com/b/andreasderuiter/archive/2012/12/05/designing-an-etl-process-with-ssis-two-approaches-to-extracting-and-transforming-data.aspx#10608606">Designing an ETL process with SSIS: two approaches to extracting and transforming data</a></b> where Phill Devey responded with the following question:</p>  <blockquote>   <p><strong><em>With regard to your statement &quot; With staging tables, transformations are implemented as database views&quot;&#160; Are you suggesting that your Dimension and Fact &quot;tables&quot; will be views or do you mean the query that populates the Dimension and Fact tables will be based on a view?</em></strong></p> </blockquote>  <p>That’s a good question, and let me explain what I’ve found most practical.</p>  <p>Initially, when I create a new dimension or fact table, I simply create it as a view. Doing so allows me to quickly develop, test and debug the transformation in SSMS. If performance is not an issue, I might even deploy it this way into production.</p>  <p>In practice, once the transformation seems to work well, what I usually do is rename the view and create a stored procedure which creates a table using the original view. Because the new table has the same name as the original view, nothing breaks. Here’s what the stored procedure might look like:</p>  <div id="codeSnippetWrapper" style="font-size: 8pt; overflow: auto; cursor: text; border-top: silver 1px solid; font-family: &#39;Courier New&#39;, courier, monospace; border-right: silver 1px solid; width: 97.5%; border-bottom: silver 1px solid; padding-bottom: 4px; direction: ltr; text-align: left; padding-top: 4px; padding-left: 4px; border-left: silver 1px solid; margin: 20px 0px 10px; line-height: 12pt; padding-right: 4px; max-height: 200px; background-color: #f4f4f4">   <div id="codeSnippet" style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4">     <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum1" style="color: #606060">   1:</span> <span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">PROCEDURE</span> [dbo].[sp_AccountFact]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum2" style="color: #606060">   2:</span> <span style="color: #0000ff">AS</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum3" style="color: #606060">   3:</span> <span style="color: #0000ff">BEGIN</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum4" style="color: #606060">   4:</span>     <span style="color: #0000ff">SET</span> NOCOUNT <span style="color: #0000ff">ON</span>;</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum5" style="color: #606060">   5:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum6" style="color: #606060">   6:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum7" style="color: #606060">   7:</span>     <span style="color: #0000ff">SELECT</span> * </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum8" style="color: #606060">   8:</span>     <span style="color: #0000ff">INTO</span> #MyTempTable</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum9" style="color: #606060">   9:</span>     <span style="color: #0000ff">FROM</span> dbo.[Account Fact <span style="color: #0000ff">View</span>]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum10" style="color: #606060">  10:</span>     </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum11" style="color: #606060">  11:</span>     <span style="color: #0000ff">BEGIN</span> <span style="color: #0000ff">TRANSACTION</span> MY_TRANS</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum12" style="color: #606060">  12:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum13" style="color: #606060">  13:</span>     <span style="color: #0000ff">IF</span>  <span style="color: #0000ff">EXISTS</span> (</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum14" style="color: #606060">  14:</span>             <span style="color: #0000ff">SELECT</span> [name]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum15" style="color: #606060">  15:</span>             <span style="color: #0000ff">FROM</span> [sys].[tables]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum16" style="color: #606060">  16:</span>             <span style="color: #0000ff">WHERE</span> name=<span style="color: #006080">'Account Fact'</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum17" style="color: #606060">  17:</span>     )</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum18" style="color: #606060">  18:</span>     <span style="color: #0000ff">BEGIN</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum19" style="color: #606060">  19:</span>         <span style="color: #0000ff">DROP</span> <span style="color: #0000ff">TABLE</span> tabular.[Account Fact]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum20" style="color: #606060">  20:</span>     <span style="color: #0000ff">END</span>;</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum21" style="color: #606060">  21:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum22" style="color: #606060">  22:</span>     <span style="color: #0000ff">SELECT</span> * </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum23" style="color: #606060">  23:</span>     <span style="color: #0000ff">INTO</span> tabular.[Account Fact]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum24" style="color: #606060">  24:</span>     <span style="color: #0000ff">FROM</span> #MyTempTable</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum25" style="color: #606060">  25:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum26" style="color: #606060">  26:</span>     <span style="color: #0000ff">COMMIT</span> <span style="color: #0000ff">TRANSACTION</span> MY_TRANS </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum27" style="color: #606060">  27:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum28" style="color: #606060">  28:</span>     <span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">UNIQUE</span> <span style="color: #0000ff">NONCLUSTERED</span> <span style="color: #0000ff">INDEX</span> [Account <span style="color: #0000ff">key</span>] <span style="color: #0000ff">ON</span> [tabular].[Account Fact]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum29" style="color: #606060">  29:</span>     (</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum30" style="color: #606060">  30:</span>         [Account <span style="color: #0000ff">key</span>] <span style="color: #0000ff">ASC</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum31" style="color: #606060">  31:</span>     )<span style="color: #0000ff">WITH</span> (PAD_INDEX = <span style="color: #0000ff">OFF</span>, STATISTICS_NORECOMPUTE = <span style="color: #0000ff">OFF</span>, SORT_IN_TEMPDB = <span style="color: #0000ff">OFF</span>, IGNORE_DUP_KEY = <span style="color: #0000ff">OFF</span>, DROP_EXISTING = <span style="color: #0000ff">OFF</span>, ONLINE = <span style="color: #0000ff">OFF</span>, ALLOW_ROW_LOCKS = <span style="color: #0000ff">ON</span>, ALLOW_PAGE_LOCKS = <span style="color: #0000ff">ON</span>) <span style="color: #0000ff">ON</span> [<span style="color: #0000ff">PRIMARY</span>]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum32" style="color: #606060">  32:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum33" style="color: #606060">  33:</span>     <span style="color: #0000ff">CREATE</span> COLUMNSTORE <span style="color: #0000ff">INDEX</span> [NonClusteredColumnStoreIndex-20130801-145157]</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum34" style="color: #606060">  34:</span>     <span style="color: #0000ff">ON</span> [tabular].[Account Fact](</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum35" style="color: #606060">  35:</span>         <span style="color: #008000">-- Add columns here</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum36" style="color: #606060">  36:</span>     );</pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum37" style="color: #606060">  37:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; font-size: 8pt; overflow: visible; border-left-style: none; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum38" style="color: #606060">  38:</span> END</pre>
<!--CRLF--></div>
</div>

<p>In this example, the stored procedure <strong>[dbo].[sp_AccountFact]</strong> does the heavy lifting of recreating the [<strong>tabular].[Account Fact] </strong>table. You would call this stored procedure during the transform phase of your ETL process.</p>

<p>The view with the transformation logic (which was previously called <strong>[tabular].[Account Fact]</strong>) is now called <strong>[dbo].[Account Fact View]</strong>. The content of this view is first copied into a temporary table using the SELECT INTO statement. On line 11 we start a transaction so that the entire update is atomic. In lines 13-20 the existing table is deleted if it exists, and then in line 22 the table is recreated from the temporary table we created before, again using the SELECT INTO statement. Then the transaction is committed. Finally we recreate the indexes. Note that the second index in this example is a column store index which greatly improves the performance of most ad hoc queries.</p>

<p>The transaction ensures that clients performing queries on the data warehouse get valid results while the ETL is running. However, those clients might get blocked until the transaction completes. Therefore it’s important to minimize the duration of the transaction. This is why we first copy the view into a temporary table before the transaction starts. We also recreate the indexes outside the transaction for this reason.</p>

<p>Another design decision is whether to drop the table and recreate it – as I do here – or to just truncate the table. If we would just truncate the table, the stored procedure would become even simpler because we don’t need to check if the table already exists before dropping/truncating it, and we don't need to recreate the indexes. On the other hand, a benefit of using the drop-and-recreate method is that we only need to maintain the table’s schema in one place, i.e. in the view. In the truncate scenario we would need to update the schema of the table every time we modified the view. A second benefit is that it simplifies the usage of column store indexes. In SQL Server 2012, you cannot modify the contents of a table which has a column store index. (I believe this constraint no longer exists in SQL Server 2014, but I have not yet verified that myself).</p>

<p>Note: another good practice is to use schemas. In this data warehouse, the dimensional tables are in a schema called “tabular”. I also typically create a separate schema for each source system and its staging tables. The dbo schema has “internal” objects which are important for the ETL but are not visible to normal users. Normal users of the data warehouse only need read access to the tabular schema to perform the OLAP queries of their desire.</p>]]></content:encoded>
    </item>
    <item>
      <title>How to rename data columns in Azure ML programmatically</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/how-to-rename-data-columns-in-azure-ml-programmatically</link>
      <pubDate>Tue, 10 Mar 2015 20:16:02 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/03/10/how-to-rename-data-columns-in-azure-ml-programmatically/</guid>
      <description><![CDATA[In Azure ML you usually rename columns using the Metadata Editor component. This works well in...]]></description>
      <content:encoded><![CDATA[<p>In Azure ML you usually rename columns using the Metadata Editor component. This works well in common scenarios, but I ran into a situation where this was extremely impractical and I needed a way to rename the columns in bulk. I found the following way to do that:</p>  <p>First, add an Execute R Script item to the model.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4263.image_5CCA52A4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4263.image_5F00_5CCA52A4.png"><img title="image" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7801.image_thumb_1574FCB2.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7801.image_5F00_thumb_5F00_1574FCB2.png" width="244" height="79" /></a> </p>  <p>Then, adjust the code as follows:</p>  <div id="codeSnippetWrapper" style="overflow: auto; cursor: text; font-size: 8pt; border-top: silver 1px solid; font-family: &#39;Courier New&#39;, courier, monospace; border-right: silver 1px solid; width: 97.5%; border-bottom: silver 1px solid; padding-bottom: 4px; direction: ltr; text-align: left; padding-top: 4px; padding-left: 4px; margin: 20px 0px 10px; border-left: silver 1px solid; line-height: 12pt; padding-right: 4px; max-height: 200px; background-color: #f4f4f4">   <div id="codeSnippet" style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4">     <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum1" style="color: #606060">   1:</span> <span style="color: #008000"># Map 1-based optional input ports to variables</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum2" style="color: #606060">   2:</span> dataset1 &lt;- maml.mapInputPort(1) <span style="color: #008000"># class: data.frame</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum3" style="color: #606060">   3:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum4" style="color: #606060">   4:</span> colnames(dataset1) &lt;- gsub(<span style="color: #006080">&quot;Scored Probabilities for Class &quot;</span>,<span style="color: #006080">&quot;&quot;</span>,colnames(dataset1))</pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum5" style="color: #606060">   5:</span> colnames(dataset1) &lt;- gsub(<span style="color: #006080">&quot;\&quot;&quot;</span>,<span style="color: #006080">&quot;&quot;</span>,colnames(dataset1))</pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum6" style="color: #606060">   6:</span>&#160; </pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white"><span id="lnum7" style="color: #606060">   7:</span> <span style="color: #008000"># Select data.frame to be sent to the output Dataset port</span></pre>
<!--CRLF-->

    <pre style="border-top-style: none; overflow: visible; font-size: 8pt; font-family: &#39;Courier New&#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4"><span id="lnum8" style="color: #606060">   8:</span> maml.mapOutputPort(<span style="color: #006080">&quot;dataset1&quot;</span>);</pre>
<!--CRLF--></div>
</div>

<p></p>

<p>The trick is to use the gsub function in R to replace a text with another text, and use that on the column names. In the example above I did two replacements, but normally you would need to do only one.</p>

<p>The reason I wanted to do this is that my experiment has a multi-class classification model with a few thousand classes, which results in a column to be added to the data for each of the classes while it is scored. This is exactly what I needed, however, Azure ML inconveniently names each of the added columns <strong>Scored Probabilities for Class “<em>&lt;class name&gt;</em>”</strong>. The most annoying thing about this is that the column name include the double quotes, and the SQL transformation module which I added downstream choked on that. Hence I needed to rename a couple thousand columns. In the code above, the second gsub call removes the double-quote characters. The result of this code is that each scored column is simply named after the class.</p>

<p>There is however a potential issue with this. Even though the script doesn’t do much, the entire data set needs to be loaded into the dataset1 variable, and later the same data is passed back to the Azure ML runtime. When the dataset is large, this can take a lot of time.</p>]]></content:encoded>
    </item>
    <item>
      <title>Azure ML now supports Python scripts</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/azure-ml-now-supports-python-scripts</link>
      <pubDate>Mon, 16 Feb 2015 06:07:00 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/02/16/azure-ml-now-supports-python-scripts/</guid>
      <description><![CDATA[Update 2/19/2015: Microsoft has now officially announced general availability of Azure Machine...]]></description>
      <content:encoded><![CDATA[<p><em>Update 2/19/2015: Microsoft has now officially announced general availability of Azure Machine Learning and the fact that Python is now supported. You can find the announcement <a title="here" href="http://blogs.technet.com/b/machinelearning/archive/2015/02/18/announcing-the-general-availability-of-azure-machine-learning.aspx" target="_blank">here</a>.</em></p>
<p>As I was experimenting with Azure ML, I noticed a module called &ldquo;Execute Python Script&rdquo;.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6443.image_3F482E2C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6443.image_5F00_3F482E2C.png"><img style="display: inline; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7875.image_thumb_5CBD0FE9.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7875.image_5F00_thumb_5F00_5CBD0FE9.png" alt="image" width="202" height="244" border="0" /></a></p>
<p>I am pretty sure I haven&rsquo;t seen this before and I also couldn't find any official announcement about it from Microsoft. (I also noticed a new &ldquo;Deprecated&rdquo; section, and under &ldquo;Data Input and Output&rdquo; I saw an &ldquo;Enter Data&rdquo; module which I can&rsquo;t remember seeing before).</p>
<p>Anyway, this means you can now easily add Python scripts to Azure ML which is great because Python and R are the two most commonly used languages for machine learning in the industry, and both can now be used in the cloud with Azure ML.</p>
<p>Like with the Execute R Script module, the Python module accepts three inputs, two of which are for data sets.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8473.image_2C299231.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8473.image_5F00_2C299231.png"><img style="display: inline; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6266.image_thumb_072BD1B8.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6266.image_5F00_thumb_5F00_072BD1B8.png" alt="image" width="244" height="134" border="0" /></a></p>
<p>The version of Python which Azure ML currently uses seems to be 2.7.7 and not the more recent 3.4. This is not necessarily a bad thing because versions 2 and 3 of Python are incompatible and not all of the libraries are available for Python 3. (I did not find a setting in Azure ML which allows you to select the version of Python you wish to use.)</p>
<p>The Python script runs in a sandbox which has 93 packages pre-installed, which I listed below. Pre-installed packages include scikit-learn &ndash; which is a set of machine learning algorithms - and&nbsp; scikit-image, which includes powerful routines for image processing.</p>
<p>You can also use other packages via a zip file which you provide to the third input. The comments in the Python template script in Azure ML say:</p>
<blockquote>
<p>If a zip file is connected to the third input port is connected, it is unzipped under ".\Script Bundle". This directory is added&nbsp; to sys.path. Therefore, if your zip file contains a Python file mymodule.py you can import it using: <br />import mymodule</p>
</blockquote>
<p>I have not tested the ability to load other packages with Python this way. When I tested the equivalent functionality for R a while ago, it did not work very smooth. Keep in mind that Azure ML is still in preview and some rough edges are to be expected.</p>
<p>Below is the list of packages currently pre-installed with Azure ML:</p>
<p>&nbsp;</p>
<p>'argcomplete==0.6.7'</p>
<p>'astropy==0.3.2'</p>
<p>'atom==0.3.7'</p>
<p>'backports.ssl-match-hostname==3.4.0.2'</p>
<p>'beautifulsoup4==4.3.1'</p>
<p>'binstar==0.5.3'</p>
<p>'bitarray==0.8.1'</p>
<p>'blaze==0.5'</p>
<p>'blz==0.6.2'</p>
<p>'bokeh==0.4.4'</p>
<p>'boto==2.28.0'</p>
<p>'casuarius==1.1'</p>
<p>'cdecimal==2.3'</p>
<p>'chaco==4.4.1'</p>
<p>'colorama==0.2.7'</p>
<p>'conda-build==1.3.5'</p>
<p>'conda==3.5.5'</p>
<p>'configobj==5.0.5'</p>
<p>'cubes==0.10.2'</p>
<p>'cython==0.20.1'</p>
<p>'datashape==0.2'</p>
<p>'docutils==0.11'</p>
<p>'enable==4.3.0'</p>
<p>'enaml==0.9.1'</p>
<p>'flask==0.10.1'</p>
<p>'future==0.12.1'</p>
<p>'gevent-websocket==0.9.3'</p>
<p>'gevent==1.0.1'</p>
<p>'greenlet==0.4.2'</p>
<p>'grin==1.2.1'</p>
<p>'h5py==2.3.0'</p>
<p>'ipython==2.1.0'</p>
<p>'itsdangerous==0.24'</p>
<p>'jdcal==1.0'</p>
<p>'jinja2==2.7.2'</p>
<p>'kiwisolver==0.1.2'</p>
<p>'llvmpy==0.12.6'</p>
<p>'lxml==3.3.5'</p>
<p>'markupsafe==0.18'</p>
<p>'matplotlib==1.3.1'</p>
<p>'menuinst==1.0.3'</p>
<p>'mock==1.0.1'</p>
<p>'multipledispatch==0.4.3'</p>
<p>'networkx==1.8.1'</p>
<p>'nltk==2.0.4'</p>
<p>'nose==1.3.3'</p>
<p>'numba==0.13.2'</p>
<p>'numexpr==2.3.1'</p>
<p>'numpy==1.8.1'</p>
<p>'openpyxl==1.8.5'</p>
<p>'pandas==0.14.0'</p>
<p>'patsy==0.2.1'</p>
<p>'pep8==1.5.6'</p>
<p>'pil==1.1.7'</p>
<p>'ply==3.4'</p>
<p>'psutil==2.1.1'</p>
<p>'py==1.4.20'</p>
<p>'pycosat==0.6.1'</p>
<p>'pycparser==2.10'</p>
<p>'pycrypto==2.6.1'</p>
<p>'pyface==4.4.0'</p>
<p>'pyflakes==0.8.1'</p>
<p>'pygments==1.6'</p>
<p>'pyparsing==2.0.1'</p>
<p>'pyreadline==2.0'</p>
<p>'pytest==2.5.2'</p>
<p>'python-dateutil==1.5'</p>
<p>'pytz==2014.3'</p>
<p>'pywin32==218.4'</p>
<p>'pyyaml==3.11'</p>
<p>'pyzmq==14.3.1'</p>
<p>'requests==2.3.0'</p>
<p>'rope==0.9.4'</p>
<p>'runipy==0.1.0'</p>
<p>'scikit-image==0.10.0'</p>
<p>'scikit-learn==0.14.1'</p>
<p>'scipy==0.14.0'</p>
<p>'six==1.6.1'</p>
<p>'sphinx==1.2.2'</p>
<p>'spyder==2.3.0rc'</p>
<p>'sqlalchemy==0.9.4'</p>
<p>'statsmodels==0.5.0'</p>
<p>'sympy==0.7.5'</p>
<p>'tables==3.1.1'</p>
<p>'tornado==3.2.1'</p>
<p>'traits==4.4.0'</p>
<p>'traitsui==4.4.0'</p>
<p>'ujson==1.33'</p>
<p>'werkzeug==0.9.6'</p>
<p>'wsgiref==0.1.2'</p>
<p>'xlrd==0.9.3'</p>
<p>'xlsxwriter==0.5.5'</p>
<p>'xlwings==0.1.0'</p>
<p>'xlwt==0.7.5'</p>]]></content:encoded>
    </item>
    <item>
      <title>Setting the threshold of a binary learning model in Azure ML</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/setting-the-threshold-of-a-binary-learning-model-in-azure-ml</link>
      <pubDate>Tue, 10 Feb 2015 03:31:04 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/02/10/setting-the-threshold-of-a-binary-learning-model-in-azure-ml/</guid>
      <description><![CDATA[This is the last of three articles about performance measures and graphs for binary learning models...]]></description>
      <content:encoded><![CDATA[<p>This is the last of three articles about performance measures and graphs for binary learning models in Azure ML. Binary learning models are models which just predict one of two outcomes: positive or negative. These models are very well suited to drive decisions, such as whether to administer a patient a certain drug or to include a lead in a targeted marketing campaign.</p>  <p>This final article will cover the threshold setting, and how to find the optimal value for it. As you will learn, this requires a good understanding of error cost, that is, the cost of inaccurate predictions.</p>  <p>The first article laid the foundation by covering several statistical measures: accuracy, precision, recall and F1 score. These measures require a solid understanding of the two types of prediction errors which we also covered: false positives and false negatives. </p>  <p>Part 1: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/09/performance-measures-in-azure-ml-accuracy-precision-recall-and-f1-score.aspx">Performance measures in Azure ML: Accuracy, Precision, Recall and F1 Score</a></p>  <p>The second article discussed the ROC curve and the related AUC measure. We also looked at another graph in Azure ML called the Precision/Recall curve.</p>  <p>Part 2: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/using-roc-plots-and-the-auc-measure-in-azure-ml.aspx">Using ROC plots and the AUC measure in Azure ML</a></p>  <p>If you have not yet done so, I encourage reading the other articles before first.</p>  <p>At the time of this writing, Azure ML is still in preview and most of the documentation you find on the internet appears to be written by data scientists for data scientists. This blog is focused on people who want to learn Azure ML, but do not have a PhD in data science.</p>  <h1>Getting started</h1>  <p>When you build a machine learning solution, you’ll normally first try and compare multiple models. As we discussed in part 2, you'd normally use the AUC measure to determine the best performing model. Once you made your selection, you can remove all the other models and optimize the selected one. (In case you want to re-evaluate the models in the future with a new data set, it might be better to just create a new experiment).</p>  <p>In binary models you can influence the behavior using the threshold slider. If you’re not sure how to do this and what the effect is of changing the threshold, please go back and review parts 1 and 2 of this series.</p>  <p>The threshold lets you change the balance of two types of prediction errors: false positives and false negatives. The cost associated with one type of error can be much higher than the cost of the other. If that’s the case, it makes sense to err on the side of safety which means you will accept a (much) higher number of cheap errors to reduce the number of expensive errors.</p>  <p>Therefore, in order to determine the optimal value for threshold, you first need to determine the cost of false positives and false negatives. I’ll use an example to explain this and provide a spreadsheet which helps you do the calculation.</p>  <h1>An example</h1>  <p>We’ll use the same data set as in the previous posts, but instead of predicting if someone is above 50 years old, we’ll act as if this is data from a quality test in a factory. The data is from a test device which tests whether a given product is thought to be faulty (positive) or not (negative). The testing data has been labeled with an additional piece of data: whether the product was in fact defective after further inspection. This allows us to train the model and to validate its performance. Based on the labeled data, we know that about 24% of the devices are in fact defective. </p>  <p>Now you need to determine the cost of false positives and false negatives. This will require some research and if possible you may want to include an accountant from your company to do this. Keep in mind that you need to include opportunity cost and that some of the cost, such as customer dissatisfaction and reputation damage, is difficult to quantify.</p>  <p>False Positive: these are devices which were tested as defective, when in fact they are not. This type of error leads to rework, and let’s assume that the cost associated with this unnecessary re-work is $10 per wrong prediction.</p>  <p>False Negative: these are defective devices which were shipped to customers because the defects were not detected. The cost of these types of errors are much higher due to extra handling, customer dissatisfaction and reputation damage. Let’s assume that the cost of this type of error is $100 per wrong prediction.</p>  <p>Based on the cost per error and the frequency that each type of error occurs, we can calculate the average error cost per prediction. The optimal threshold is the one with the lowest average error cost per prediction. The math is fairly straightforward, and the following spreadsheet shows the average error cost per prediction in the right-most column and the related threshold setting in the left-most column. The optimal threshold is 0.10 which has a cost of $2.70.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/0726.image4_77A002D0.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/0726.image4_5F00_77A002D0.png"><img title="image" style="display: inline" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4401.image4_thumb_1E0476CD.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4401.image4_5F00_thumb_5F00_1E0476CD.png" width="640" height="343" /></a> </p>  <p>The table shows various performance measures for different threshold settings. There are a lot of things we can conclude from this table:</p>  <ul>   <li>The default threshold of 0.5 would have resulted in an error cost of $6.50 per prediction, much higher than the optimal cost of $2.70.</li>    <li>Precision and recall are – when used independently – useless to determine the right threshold setting because they are conflicting forces. If you only focus on one of them, you’ll end up with a threshold of either 0 or 1.</li>    <li>The optimal accuracy is obtained with a threshold value of 0.55. The accuracy at that point is 90.0%. As we concluded in <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/09/performance-measures-in-azure-ml-accuracy-precision-recall-and-f1-score.aspx">part 1</a>, accuracy is not a very good measure when the class distribution is uneven, and it does not take the error cost into account. This table confirms that. If we would use this threshold, we’d incur 2.7x too much cost.</li>    <li>The F1 Score is the weighted average between Precision and Recall. If the cost of false positives and false negatives were roughly equal, this would be a pretty good measure. However, that’s not the case. If we would have used the F1 score to determine the optimal threshold, we’d incur almost 2x to much cost.</li> </ul>  <p>Here is how the costs vary by threshold, displayed in a graph:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6013.image13_7AD70C1A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6013.image13_5F00_7AD70C1A.png"><img title="image" style="display: inline" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7444.image13_thumb_3A37505C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7444.image13_5F00_thumb_5F00_3A37505C.png" width="640" height="402" /></a></p>  <p>As you can see, the optimal threshold is where the grey line reaches its lowest point.</p>  <h1></h1>  <h1>Sample spreadsheet</h1>  <p>You can download the spreadsheet which was used to generate the table and chart here:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/1207.UAC%20and%20ROC%20Aalyses.xlsx" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11/1207.UAC-and-ROC-Aalyses.xlsx"><img border="0" alt="1207.UAC and ROC Aalyses.xlsx" src="http://blogs.msdn.com/utility/filethumbnails/xlsx-Small.gif" align="absMiddle" /></a> <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/1207.UAC%20and%20ROC%20Aalyses.xlsx" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11/1207.UAC-and-ROC-Aalyses.xlsx">1207.UAC and ROC Aalyses.xlsx</a></p>  <p>To use this spreadsheet and analyze data from a different data set, you need to replace the data in the Data worksheet:</p>  <ul>   <li>Column A (Actual) has the actual (labeled) value for each observation. In the spreadsheet the cells in this column contain either “Defective” or “Not defective”, but this could be anything as long as there are only two different values.</li>    <li>Column B (Scored Probabilities) has the scores of the scored data set. Every score is a real number between 0 and 1.</li>    <li>Column C (Pos) and D (Neg) have a simple formula which set to value to 0 or 1, depending on the value in column A. You’ll probably need to adjust these formulas to reflect the data in column A.</li> </ul>  <p>When you want to load data from another data set, it’s important that this data is already scored. Here’s how you would download the scored data for the sample experiment “Sample 5: Train, Test, Evaluate for Binary Classification: Adult Dataset”.</p>  <ul>   <li>Log into your Azure ML workspace. </li>    <li>In the Experiments screen, click on the Samples link in the top of the screen and then click on the sample called “Sample 5: Train, Test, Evaluate for Binary Classification: Adult Dataset” .</li>    <li>Click the Save As button in the bottom of the screen and save the experiment under a different name. This will enable us to edit this sample.</li>    <li>Add a “Convert to CSV” item to the workspace and connect its input to the output of the Score Model item which you wish to analyze (see screenshot below)</li>    <li>Run the model</li>    <li>After the model has completed running, right-click the output of the Convert to CSV item and select Download.</li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6811.image_129364E3.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6811.image_5F00_129364E3.png"><img title="image" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6354.image_thumb_497049DA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6354.image_5F00_thumb_5F00_497049DA.png" width="644" height="425" /></a> </p>  <h1>Putting it together</h1>  <p>This was the last of a series of three articles on using performance measures and plots in Azure ML. We discovered that for model selection, the ROC plot and AUC measure are the best and easiest to use. Once the best model has been selected, you can optimize its performance by adjusting the threshold, as we discussed in this last article.</p>  <p>I hope you found this useful. Please leave comments or send me email if you have any further suggestions.</p>  <ul></ul>]]></content:encoded>
    </item>
    <item>
      <title>Performance measures in Azure ML: Accuracy, Precision, Recall and F1 Score.</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/performance-measures-in-azure-ml-accuracy-precision-recall-and-f1-score</link>
      <pubDate>Mon, 09 Feb 2015 22:52:52 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/02/09/performance-measures-in-azure-ml-accuracy-precision-recall-and-f1-score/</guid>
      <description><![CDATA[This is the first of three articles about performance measures and graphs for binary learning models...]]></description>
      <content:encoded><![CDATA[<p>This is the first of three articles about performance measures and graphs for binary learning models in Azure ML. Binary learning models are models which just predict one of two outcomes: positive or negative. These models are very well suited to drive decisions, such as whether to administer a patient a certain drug or to include a lead in a targeted marketing campaign.</p>
<p>This first article lays the foundation by covering several statistical measures: accuracy, precision, recall and F1 score, These measures require a solid understanding of the two types of prediction errors which we will also cover: false positives and false negatives.</p>
<p>In the second article we&rsquo;ll discuss the ROC curve and the related AUC measure. We&rsquo;ll also look at another graph in Azure ML called the Precision/Recall curve.</p>
<p>Part 2: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/using-roc-plots-and-the-auc-measure-in-azure-ml.aspx">Using ROC plots and the AUC measure in Azure ML</a></p>
<p>The final article will cover the threshold setting, and how to find the optimal value for it. As you will learn, this requires a good understanding of the cost of inaccurate predictions.</p>
<p>Part 3: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/setting-the-threshold-of-a-binary-learning-model-in-azure-ml.aspx">Setting the threshold of a binary learning model in Azure ML</a></p>
<p>At the time of this writing, Azure ML is still in preview and most of the documentation you find on the internet appears to be written by data scientists for data scientists. This blog is focused on people who want to learn Azure ML, but do not have a PhD in data science.</p>
<h1>Getting started</h1>
<p>When you build experiments in Azure ML, you need to score the used learning models and evaluate their performance to understand how well they perform and which model actually works best. Ideally you would want just one number which tells you how good a model is, but Azure ML provides 9 metrics plus a couple of charts. If you&rsquo;re new to Azure ML and you&rsquo;re not already a data scientist, this can be a bit intimidating.</p>
<p>You can usually rely on the AUC measure to select which model works best for a given data set. We will cover this further in the second article of this series. After you select a model. you will need to find the optimal threshold value for it, which, as we will see in the third article, you <em>cannot </em>do using the AUC measure. This first article will set the foundation with regard to statistical measures in Azure ML and set you up for the other articles.</p>
<p>The reason Azure ML provides multiple performance measures is that data sets differ in two key ways:</p>
<ul>
<li>The distribution of positives and negatives, that is, the ratio between positive values and negative values in the data. Data scientists call this the <strong>class distribution</strong>. More often than not the class distribution of positive and negative values is not 50/50.</li>
</ul>
<ul>
<li>The <strong>cost </strong>of wrong observations. When data scientists say &ldquo;observation&rdquo;, they mean the predicted value. It is important to realize there are two types of errors &ndash; false positives and false negatives - which often have a different associated cost. For example, the cost of not administering a drug to someone who needs it might be different than the cost of administering the drug to someone who does not need it.</li>
</ul>
<p>Binary models just predict one of two values. The meaning of these values could be anything, depending on the data. For example, whether a tested device is defective, if a person is over 50 years old, or whether a certain stock will increase in value. To generalize this, statisticians and data scientists use the terms &ldquo;positive&rdquo; and &ldquo;negative&rdquo;, rather than true/false or 0/1 as a software developer would do. We will follow this convention.</p>
<p>The decision of which value to assign the &ldquo;positive&rdquo; label is not arbitrary. You should assign &ldquo;positive&rdquo; to the value that drives your decision. For example, if you are going to run a marketing campaign targeted towards people over 50 years old, and your model will predict if someone is over 50 years old, you should assign &ldquo;&gt;=50 years&rdquo; as the positive value. Or, if you build a model to detect defective devices so you can rework them before shipping to the customer. you should define &ldquo;is defective&rdquo; as the positive value. If you don't do this right, many statistical measures, including precision and recall, will make little sense.</p>
<h1>&nbsp;</h1>
<h1>Accessing performance metrics in Azure ML</h1>
<p>Azure ML has a large number of sample models. One you can use to follow along in called &ldquo;Sample 5: Train, Test, Evaluate for Binary Classification: Adult Dataset&rdquo;.</p>
<p>To view the performance metrics, right-click on the output port of the Evaluate Model item and select Visualize.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/3632.image_3C1E148F.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/3632.image_5F00_3C1E148F.png"><img style="display: inline; border-width: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8372.image_thumb_6BDBBD0E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8372.image_5F00_thumb_5F00_6BDBBD0E.png" alt="image" width="240" height="192" border="0" /></a></p>
<p>After you click the Visualize button, you&rsquo;ll initially see the ROC plot. Scroll down to see the performance metrics:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6747.image3_26C2EFD8.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6747.image3_5F00_26C2EFD8.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8764.image3_thumb_519DE49B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8764.image3_5F00_thumb_5F00_519DE49B.png" alt="image" width="640" height="103" /></a></p>
<p>Above, all measures except AUC can be calculated based on the left-most four numbers. I created a spreadsheet which allows you to experiment with these numbers. You can also use the spreadsheet to review the formulas for each of the metrics. Here&rsquo;s what the spreadsheet looks like:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5732.image7_7389471F.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5732.image7_5F00_7389471F.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5001.image7_thumb_2A662C17.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5001.image7_5F00_thumb_5F00_2A662C17.png" alt="image" width="640" height="340" /></a></p>
<p>You can download the spreadsheet here: <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/5141.Binary%20Model%20Performance%20Metric%20Calculator.xlsx" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11/5141.Binary-Model-Performance-Metric-Calculator.xlsx"><img src="http://blogs.msdn.com/utility/filethumbnails/xlsx-Small.gif" alt="5141.Binary Model Performance Metric Calculator.xlsx" align="absMiddle" border="0" /></a> <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/5141.Binary%20Model%20Performance%20Metric%20Calculator.xlsx" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11/5141.Binary-Model-Performance-Metric-Calculator.xlsx">5141.Binary Model Performance Metric Calculator.xlsx</a></p>
<h1>Understanding false positives and false negatives</h1>
<p>The performance of a trained model is determined by how good the observations (predictions) reflect the actual events. When you train a (supervised) model, you need a &ldquo;labeled&rdquo; data set which includes the actual values which you&rsquo;re trying to predict. You set aside a subset of the labeled data so that, after you trained the model, you can evaluate the model against labeled data which was not used to train the model. By comparing the predicted values against the labeled ones, you end up with four bins:</p>
<table style="width: 400px;" border="0" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<td valign="top" width="133">&nbsp;</td>
<td valign="top" width="133">Event = positive</td>
<td valign="top" width="133">Event = negative</td>
</tr>
<tr>
<td valign="top" width="133">Prediction = positive</td>
<td valign="top" width="133"><strong><span style="color: #008000;">True Positive</span></strong></td>
<td valign="top" width="133"><strong><span style="color: #ff0000;">False Positive</span></strong></td>
</tr>
<tr>
<td valign="top" width="133">Prediction = negative</td>
<td valign="top" width="133"><strong><span style="color: #ff0000;">False Negative</span></strong></td>
<td valign="top" width="133"><strong><span style="color: #008000;">True Negative</span></strong></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>True positives and true negatives are the observations which were correctly predicted, and therefore shown in green.</p>
<p>The terms &ldquo;false positive&rdquo; and &ldquo;false negative&rdquo; can be confusing. False negatives are observations where the actual event was <span style="text-decoration: underline;">positive</span>. The way to think about it is that the terms refer to the observations and not the actual events. So if the term starts with &ldquo;false&rdquo;, the actual value is the opposite of the word that follows it.</p>
<h1>&nbsp;</h1>
<h2>Accuracy</h2>
<p>Accuracy is perhaps the most intuitive performance measure. It is simply the ratio of correctly predicted observations.</p>
<p>In the screenshot you saw before, the two green cells are the correctly predicted observations: 2894+11750=14,644. The total number of events is 16,281, hence the accuracy is 14,644/16,281=0.899, or approximately 90%.</p>
<p>Using accuracy is only good for symmetric data sets where the class distribution is 50/50 and the cost of false positives and false negatives are roughly the same. It can be attractive at first because it&rsquo;s intuitively easy to understand, however. you should not rely on it too much because most data sets are far from symmetric.</p>
<p>Example: you are building a model which predicts whether a device is defective. The class distribution is such that 6 in 1000 devices is truly defective (positive). A model which simply returns &ldquo;negative&rdquo; &ndash; i.e. not defective &ndash; all the time gets it right 99.4% of the time and therefore has an accuracy of 0.994, when in fact it never correctly identifies a defective device!</p>
<h1>Precision</h1>
<p>Precision looks at the ratio of correct positive observations.</p>
<p>The formula is True Positives / (True Positives + False Positives).</p>
<p>Note that the denominator is the count of all positive predictions, including positive observations of events which were in fact negative.</p>
<p>Extending the example we used before to explain the accuracy, let's assume we improved the model and it now predicts 8 out of 1000 devices being faulty. If only 5 of those 8 are truly defective the precision is 5/8 = 0.625.</p>
<h1>Recall</h1>
<p>Recall is also known as <strong>sensitivity </strong>or <strong>true positive rate</strong>. It&rsquo;s the ratio of correctly predicted positive events.</p>
<p>Recall is calculated as True Positives / (True Positives + False Negatives).</p>
<p>Note that the denominator is the count of all positive events, regardless whether they were correctly predicted by the model.</p>
<p>Using the same example as before, we already knew that 6 out of 1000 devices are truly defective. The model correctly predicted 5 of them. (It also predicted 3 incorrectly, but for the recall measure that&rsquo;s not important). The recall therefore is 5/6 = 0.833.</p>
<h1>Comparing Precision and Recall</h1>
<p>To better understand the difference between precision and recall, here is a screenshot with all the stats:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/0333.image_2E730A9A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/0333.image_5F00_2E730A9A.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/0116.image_thumb_3213B628.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/0116.image_5F00_thumb_5F00_3213B628.png" alt="image" width="640" height="315" /></a></p>
<p>Both precision and recall work well if there&rsquo;s an uneven class distribution as is often case. They both focus on the performance of positives rather than negatives, which is why it&rsquo;s important to correctly assign the &ldquo;positive&rdquo; predicate to the value of most interrest.</p>
<p>The precision measure shows what percentage of positive predictions where correct, whereas recall measures what percentage of positive events were correctly predicted. To put it in a different way: precision is a measure of how good predictions are with regard to false positives, whereas recall is measures how good the predictions are with regard to false negatives. Whichever type of error is more important &ndash; or costs more - is the one that should receive most attention.</p>
<p>As we will further explore in the third article, you can influence precision and recall by changing the threshold of the model. Based on the cost of false positives and false negatives, you can calculate the optimal threshold value.</p>
<h1>&nbsp;</h1>
<h1>F1 Score</h1>
<p>The F1 Score is the weighted average of Precision and Recall. Therefore, this score takes both false positives and false negatives into account. Intuitively it is not as easy to understand as accuracy, but F1 is usually more useful than accuracy, especially if you have an uneven class distribution. It works best if false positives and false negatives have similar cost. If the cost of false positives and false negatives are very different, it&rsquo;s better to look at both Precision and Recall.</p>
<p>The formula for F1 Score is 2*(Recall * Precision)/(Recall + Precision)</p>
<h1>Putting it together</h1>
<p>The table below shows you which of the measures described in this article probably best to focus on, depending on the class distribution and the cost of false positives and false negatives:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4606.image_7FCDC5CA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4606.image_5F00_7FCDC5CA.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7762.image_thumb_4CFDBF56.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7762.image_5F00_thumb_5F00_4CFDBF56.png" alt="image" width="404" height="114" /></a></p>
<p>In addition to the measures mentioned in this post, you might encounter others in literature such as &ldquo;negative recall&rdquo;. With the basic understanding of this article you should be able to figure out what they mean and when they are to be used. If you correctly assign the value that is of most interest as &ldquo;positive&rdquo;, you usually won't need those other measures anyway.</p>
<p>I hope you found this useful. Please leave comments or send me an email if you think I missed something important or if you have any other questions or feedback about this topic.</p>
<p>Continue reading:</p>
<p>Part 2: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/using-roc-plots-and-the-auc-measure-in-azure-ml.aspx">Using ROC plots and the AUC measure in Azure ML</a></p>
<p>Part 3: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/setting-the-threshold-of-a-binary-learning-model-in-azure-ml.aspx">Setting the threshold of a binary learning model in Azure ML</a></p>
<div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:0090fc72-db91-4455-9518-194732ef7e2f" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">Technorati Tags: <a href="http://technorati.com/tags/Azure+ML" rel="tag">Azure ML</a></div>]]></content:encoded>
    </item>
    <item>
      <title>Using ROC plots and the AUC measure in Azure ML</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/using-roc-plots-and-the-auc-measure-in-azure-ml</link>
      <pubDate>Mon, 09 Feb 2015 22:50:00 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/02/09/using-roc-plots-and-the-auc-measure-in-azure-ml/</guid>
      <description><![CDATA[This is the second of three articles about performance measures and graphs for binary learning...]]></description>
      <content:encoded><![CDATA[<p>This is the second of three articles about performance measures and graphs for binary learning models in Azure ML. Binary learning models are models which just predict one of two outcomes: positive or negative. These models are very well suited to drive decisions, such as whether to administer a patient a certain drug or to include a lead in a targeted marketing campaign.</p>
<p>The first article laid the foundation by covering several statistical measures: accuracy, precision, recall and F1 score. These measures require a solid understanding of the two types of prediction errors which we also covered: false positives and false negatives. If you have not yet done so, I encourage reading that article first:</p>
<p>Part 1: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/09/performance-measures-in-azure-ml-accuracy-precision-recall-and-f1-score.aspx">Performance measures in Azure ML: Accuracy, Precision, Recall and F1 Score</a></p>
<p>In this second article we&rsquo;ll discuss the ROC curve and the related AUC measure. We&rsquo;ll also look at another graph in Azure ML called the Precision/Recall curve.</p>
<p>The final article will cover the threshold setting, and how to find the optimal value for it. As you will learn, this requires a good understanding of the cost of inaccurate predictions.</p>
<p>Part 3: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/setting-the-threshold-of-a-binary-learning-model-in-azure-ml.aspx">Setting the threshold of a binary learning model in Azure ML</a></p>
<p>At the time of this writing, Azure ML is still in preview and most of the documentation you find on the internet appears to be written by data scientists for data scientists. This blog is focused on people who want to learn Azure ML, but do not have a PhD in data science.</p>
<h1>Getting started</h1>
<p>If you&rsquo;re new to Azure ML, you&rsquo;ll probably soon wonder what the term &ldquo;AUC&rdquo; means and how to read ROC plots. Both of these are used as a way to measure how well a binary learning model is performing. You can use this to compare multiple learning models and decide which one works best for a given data set.</p>
<p>This is what a ROC plot in Azure ML looks like:</p>
<p><br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8054.image_6FDE9768.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8054.image_5F00_6FDE9768.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6170.image_thumb_48A6DEE4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6170.image_5F00_thumb_5F00_48A6DEE4.png" alt="image" width="640" height="470" /></a></p>
<p>If you want to follow along yourself, you can get to the screen above as follows:</p>
<ul>
<li>Log into your Azure ML workspace.</li>
<li>In the Experiments screen, click on the Samples link in the top of the screen and then click on the sample called &ldquo;Sample 5: Train, Test, Evaluate for Binary Classification: Adult Dataset&rdquo; .</li>
<li>In the design window, right-click on the output of the Evaluate item and select Visualize.</li>
</ul>
<h1>&nbsp;</h1>
<p>Before we dig deeper into the ROC plot, we need to take a closer look at the threshold setting.</p>
<h1>Threshold and scoring values</h1>
<p>When you scroll down in the screen with the ROC plot, you&rsquo;ll see something like this:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4456.image_50BE0B39.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4456.image_5F00_50BE0B39.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5670.image_thumb_7552296E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5670.image_5F00_thumb_5F00_7552296E.png" alt="image" width="640" height="91" /></a></p>
<p>The statistics you see here are for the blue curve which represents the left input of the Evaluate item in the experiment. Notice that when you drag the Threshold slider, all the numbers, except for AUC, change.</p>
<p>To understand why this is the case, you need to know that the learning model doesn&rsquo;t just predict positive or negative as an outcome, but it generates a score which is a real number between 0 and 1, and then uses the threshold setting to decide if the prediction is a &ldquo;positive&rdquo; or a &ldquo;negative&rdquo;. By default, the threshold is set to 0.5 which means that all scoring values above that value are interpreted as positive, and everything below as negative. Therefore, the threshold impacts the predictions which the model makes and thus the related statistics.</p>
<p>While 0.5 is a good default to start with, you will eventually want to tweak the threshold to get the best results. This is the topic of the third article in this series.</p>
<h1>&nbsp;</h1>
<h1>The ROC plot</h1>
<p>In the ROC plot which was shown before, two learning models are compared. The blue curve represents the model which feeds the left input of the Evaluate item, the red one represents the right input pin. (In this context performance does not relate to the speed but to how well the model predicts the desired outcome).</p>
<p>The blue and red lines in the plot connect the lower-left corner with the upper-right corner of the graph. Each position on the lines represents how the model performs in two different dimensions for a certain threshold setting. Unfortunately the ROC plot does not clearly show which point on the lines represents the current threshold value (perhaps the Azure ML Dev team can note this as a feature request:-), however the lower-left corner (0,0) represents a threshold of&nbsp;1 whereas the upper-right corner (1,1) represents a threshold of 0. The two dimensions are the false positive rate on the horizontal axis and the true positive rate on the vertical axis.</p>
<p>In <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/09/performance-measures-in-azure-ml-accuracy-precision-recall-and-f1-score.aspx">part 1</a> I explained how there are two different types of errors, false positives and false negatives. The two dimensions in the graph basically reflect this.</p>
<p>By changing the threshold, you decrease the frequency of one type of error at the expense of increasing the other type. The position on the curve where the slope is 1 is where they are in balance such that, if you would move the threshold slider a little bit, the number of one type of errors would increase by the same percentage as the other would decrease..</p>
<p>Usually when you see two models compared in a graph as the one above, the lines only intersect in the corners. The line which comes closest to the upper-left corner &ndash; in this case the blue line - provides the best predictions. For every threshold value it scores better than the other model for both false positives and false negatives. The further a curve is to the middle, the worse is its performance. Theoretically the worst possible model is one which produces random predictions with the same distribution as the class distribution. Such a model has a ROC curve which approximates a straight diagonal line from (0,0) to (1,1).</p>
<p>You might wonder if curves can also occur <em>under</em> the diagonal line. The answer is that if the ROC plot for a learning model would consistently perform under the diagonal, you could simply use the opposite value of what the model predicts as your actual prediction, and the result would then perform better than a random prediction.</p>
<p>The ROC plot was invented in WWII for the analyses for radar signals (see the article about <a href="http://en.wikipedia.org/wiki/Receiver_operating_characteristic">Receiver operating characteristic</a> on Wikipedia) and is commonly used by data scientists. However, I don't find it extremely insightful because the way I think of it, the ROC plot is about striking the right balance between the two types of errors. While the horizontal axis represents the ratio of false positives, the vertical axis shows the ratio of true positives, which only <em>indirectly</em> implies the ratio of false negatives. In other words, on the horizontal axis a higher value implies worse performance, while on the vertical axis a higher value implies a better performance.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4861.image_57077278.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4861.image_5F00_57077278.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/8688.image_thumb_3AF9443E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/8688.image_5F00_thumb_5F00_3AF9443E.png" alt="image" width="640" height="434" /></a></p>
<h1>Precision/Recall plot</h1>
<p>Azure ML also includes another graph called for the precision/recall, which essentially displays the same information as the ROC plot, but in a more intuitive way.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/7870.image_687CF4B2.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/7870.image_5F00_687CF4B2.png"><img style="display: inline;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/1588.image_thumb_1E8173C0.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/1588.image_5F00_thumb_5F00_1E8173C0.png" alt="image" width="640" height="425" /></a></p>
<p>To understand how this graph relates to false positives and false negatives, you need to remember that Precision and Recall are calculated as follows:</p>
<p><strong>Recall </strong>= True Positives / (True Positives + False Negatives)</p>
<p><strong>Precision </strong>= True Positives / (True Positives + False Positives)</p>
<p>In this curve, the &ldquo;sweet spot&rdquo; for the ideal model is in the upper right corner.</p>
<h1>AUC</h1>
<p>AUC stands for &ldquo;area under curve&rdquo;, and as it's name implies, it refers to the amount of area under the ROC curve, which theoretically is a value between 0 and 1. As I explained, the worst possible curve in practice is a diagonal line, hence the AUC should never be lower than 0.5 (for large data sets) .</p>
<p>Using the AUC metric you can quickly compare multiple learning models. Remember that the ROC curves of two models usually don&rsquo;t cross each other, hence when comparing two models, the one with a higher AUC will be the better one regardless of the threshold setting. Compared to the statistical measures of accuracy, precision, recall and F1 score, AUC&rsquo;s independence of threshold makes it uniquely qualified for model selection.</p>
<p>On the other hand, unlike accuracy, precision, recall and F1 score, AUC does not tell us what performance to expect from the model for a given threshold setting, nor can it be used to determine the optimal value for threshold. In that regard it doesn't take away the need for the other statistical measures.</p>
<h1>Putting it together</h1>
<p>The ROC plot and the AUC are very useful for comparing and selecting the best machine learning model for a given data set. A model with an AUC score near 1, and where the ROC curve comes close to the upper left corner, has a very good performance. A model with a score near 0.5 will have a curve near to the diagonal and its performance is hardly better than a random predictor.</p>
<p>After selecting the best model, the next step will be to configure the threshold and the other model configuration settings. The Precision/Recall plot can be helpful for understanding the trade-off between false positives and false negatives. In the third article we&rsquo;ll look closer at the threshold value and learn now to calculate the optimal value for it.</p>
<p>Continue reading:</p>
<p>Part 3: <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/02/10/setting-the-threshold-of-a-binary-learning-model-in-azure-ml.aspx">Setting the threshold of a binary learning model in Azure ML</a></p>
<div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:b918dcc2-bcc7-492c-8782-0b1a6f2b5e08" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">Technorati Tags: <a href="http://technorati.com/tags/Azure+ML" rel="tag">Azure ML</a></div>]]></content:encoded>
    </item>
    <item>
      <title>Troubleshooting “Error 1000: RPackage library exception: Failed to convert RObject to DataSet” when running R scripts in Azure ML</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/troubleshooting-error-1000-rpackage-library-exception-failed-to-convert-robject-to-dataset-when-running-r-scripts-in-azure-ml</link>
      <pubDate>Tue, 03 Feb 2015 04:26:18 GMT</pubDate>
      <dc:creator><![CDATA[aaaab]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/02/03/troubleshooting-error-1000-rpackage-library-exception-failed-to-convert-robject-to-dataset-when-running-r-scripts-in-azure-ml/</guid>
      <description><![CDATA[I’ve recently started experimenting with Azure ML and while I find this technology – which currently...]]></description>
      <content:encoded><![CDATA[<p>I’ve recently started experimenting with Azure ML and while I find this technology – which currently is still in preview – amazing, it’s also clear that the standard modules don’t natively support a lot of scenarios. Fortunately Azure ML lets you embed R scripts as part of experiments. This way you can overcome most limitations. In fact, R is so powerful that one way of looking at Azure ML is as a way to easily deploy an R solution in the cloud!</p>  <p>As I started experimenting with R scripts, I frequently encountered scripts which ran well outside of Azure ML – in RStudio – but failed inside Azure ML with the following error:</p>  <p><strong>Error 1000: RPackage library exception: Failed to convert RObject to DataSet</strong></p>  <p>As an Azure ML and R-newbie it took me some time&#160; to get to the bottom of this.&#160; The error occurs when you pass data back from R back to Azure ML. If you run into this error, try commenting out the line which calls the maml.mapOutputPort function, which is usually the last line in the R script. Now run the experiment again. If the error no longer occurs, this confirms that our suspicion. Of course, without calling maml.mapOutputPort you won’t get any data back to Azure ML so you need to add that line back and fix the actual problem.</p>  <p>I encountered at least three situations which can cause this error:</p>  <ol>   <li>Limited date/time support in Azure ML </li>    <li>Duplicate column names </li>    <li>You have a bug in your code which does not surface in RStudio </li> </ol>  <p>I’ll describe them in more detail below</p>  <h2>Limited date/time support in Azure ML</h2>  <p>Because Azure ML does not&#160; natively support learning models to deal time series data, this is an important scenario where R is very useful. For example, if you want to build a model which predicts the gold price based on historic gold prices, you need to resort to R which has powerful time series analyses and forecasting capabilities. R has several classes which deal with dates and times, including Date, POSIXct and POSIXlt. One of the problems according to <a href="http://www.mikelanzetta.com/2015/01/data-cleaning-with-azureml-and-r-dates/">Mike Lanzetta</a> is that Azure ML does not yet support parsing of POSIXlt values in the result set. I have not yet been able to verify this myself, but I guess it's a good idea to stay away from POSIXlt dates and use Date and POSIXct instead.</p>  <p>Another problem is that date and time values don’t always seem to convert well between different types of variables. I could only reproduce this with date/time values which were passed to R through an input port in Azure ML. Therefore I suspect there may be an issue in the way Azure ML initializes date/time values going into the R script component. </p>  <p>For example, let’s assume that your data contains two columns, one called <strong>Date </strong>which is a DateTime field and gets passed as vector of POSIXct into the script, and the other called <strong>Value </strong>which is vector of numeric values (see <a href="http://blogs.msdn.com/b/andreasderuiter/archive/2015/01/28/loading-daily-gold-prices-into-azure-ml.aspx">Loading daily gold prices into Azure ML</a> for a quick way to implement this data source).</p>  <p>Let’s say we want to perform time series analyses using the zoo class.&#160; We’ll leave away all the code that does actual time analyses and just focus on the conversions. The R script is shown below:</p> <script src="https://gist.github.com/andreasderuiter/b035ba3b3716efd8dd1c.js"></script>  <p>When we create the zoo time series variable called <strong>z </strong>in line 5, we provide the prices and the dates. The dates variable is a vector of POSIXct. In line 6 we load the dates from the <strong>z </strong>variable into a new vector called <strong>t</strong>, using the time() function. You’d expect <strong>t</strong> to be a vector of POSIXct since that was the type of the original dates vector which we used before, and when you run the code in RStudio, this is the case. However, when running this code in Azure ML, <strong>t</strong> becomes a vector of numeric. Somehow the time() function no longer sees this data as POSIXct and instead returns the numeric which represents the number of seconds since 1/1/1970.</p>  <p><em>Note: this sample does not generate the Error 1000 error message, but it does lead to a state where this could easily happen if you change the code a bit. However I no longer have the version of the code which generated that error message.</em></p>  <p>The work-around I found was to add two extra lines of code after line 3. The updated script below now also works in Azure ML:</p> <script src="https://gist.github.com/andreasderuiter/262a94a9da0cdd4d9827.js"></script>  <p>Know a better solution, let me know!</p>  <h2>Duplicate column names </h2>  <p>Another case I encountered which caused Error 1000 was when multiple columns have the same name. It’s easy to understand that Azure ML does not like duplicate column names and throws an error. </p>  <h2>You have a bug in your code which does not surface in RStudio</h2>  <p>Consider the following R script:</p> <script src="https://gist.github.com/andreasderuiter/e0b462c88a46d6c8705f.js"></script>  <p>This R script does not require any input data. It creates a vector called test and then two variables called v1 and v2 which will hold the classes of all elements in the vector. Then it concatenates v1 and v2 using the rbind() function and returns that to the output port. If you run this in RStudio (except for the last line) and then inspect the data.set variable, all looks as expected and this is the result:</p>  <pre>        X1      X2      X3
v1 numeric numeric numeric
v2 numeric numeric numeric</pre>

<p>However, when you run this in Azure ML, you’ll get “Error 1000: RPackage library exception: Failed to convert RObject to DataSet”.</p>

<p>The code has a bug. The rbind() function is intended to concatenate vector and matrix variables, but the output of the lapply() function is a list variable which can hold heterogeneous objects. Azure ML does not like heterogeneous objects because you can’t map the values to columns with a specific type. (I would have liked R to throw an error when I used rbind(), I’m not sure why it didn't do so) </p>

<p>One solution is to cast the output of lapply() to a text string, as follows:</p>
<script src="https://gist.github.com/andreasderuiter/6d8135b4ebed19792a58.js"></script>

<p>Now it also works in Azure ML!</p>

<div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:41b84ce2-b06a-49bc-9486-42d6cb0d04b6" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">Technorati Tags: <a href="http://technorati.com/tags/Azure+ML" rel="tag">Azure ML</a>,<a href="http://technorati.com/tags/R" rel="tag">R</a></div>]]></content:encoded>
    </item>
    <item>
      <title>Loading daily gold prices into Azure ML</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/loading-daily-gold-prices-into-azure-ml</link>
      <pubDate>Wed, 28 Jan 2015 05:22:00 GMT</pubDate>
      <dc:creator><![CDATA[aaaab]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2015/01/28/loading-daily-gold-prices-into-azure-ml/</guid>
      <description><![CDATA[Technorati Tags: Azure ML
Want to load daily gold prices into Azure ML? Here&rsquo;s one way to do...]]></description>
      <content:encoded><![CDATA[<div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:00af2010-0b4b-4705-b9b3-998097dfd67b" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">Technorati Tags: <a href="http://technorati.com/tags/Azure+ML" rel="tag">Azure ML</a></div>
<p>Want to load daily gold prices into Azure ML? Here&rsquo;s one way to do that:</p>
<ul>
<li>Create a new experiment, or open a new one</li>
<li>Add a &ldquo;reader&rdquo; object onto the canvas</li>
<li>Select the Reader object and set the following properties:
<ul>
<li>Data source = Web URL via HTTP</li>
<li>URL = <a href="https://www.quandl.com/api/v1/datasets/BUNDESBANK/BBK01_WT5511.csv">https://www.quandl.com/api/v1/datasets/BUNDESBANK/BBK01_WT5511.csv</a></li>
<li>Data format = CSV, and check &ldquo;CSV or TSV has header row&rdquo;</li>
</ul>
</li>
</ul>
<p>The properties for the Reader object now looks like this:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/4743.image_2C62B2D2.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/4743.image_5F00_2C62B2D2.png"><img style="display: inline; border-width: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/0044.image_thumb_2A28BAC7.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/0044.image_5F00_thumb_5F00_2A28BAC7.png" alt="image" width="642" height="257" border="0" /></a></p>
<p>&nbsp;</p>
<p>Now run your experiment, and after it completes, check the results by clicking Visualize on the output port:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/0385.clip_image002_59E66346.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/0385.clip_5F00_image002_5F00_59E66346.jpg"><img style="margin-left: 0px; display: inline; margin-right: 0px; border-width: 0px;" title="clip_image002" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/6366.clip_image002_thumb_06FB5015.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/6366.clip_5F00_image002_5F00_thumb_5F00_06FB5015.jpg" alt="clip_image002" width="531" height="482" border="0" /></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Tracking changes to tables in your data warehouse using snapshot-based versioning</title>
      <link>https://docs.microsoft.com/archive/blogs/andreasderuiter/tracking-changes-to-tables-in-your-data-warehouse-using-snapshot-based-versioning</link>
      <pubDate>Tue, 19 Mar 2013 07:20:38 GMT</pubDate>
      <dc:creator><![CDATA[Andreas de Ruiter (Microsoft)]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/andreasderuiter/2013/03/19/tracking-changes-to-tables-in-your-data-warehouse-using-snapshot-based-versioning/</guid>
      <description><![CDATA[Today I will explain a way to track historic changes in a data warehouse and share a little program...]]></description>
      <content:encoded><![CDATA[<p>Today I will explain a way to track historic changes in a data warehouse and share a little program I wrote that will help you do this in minutes. It’s based on a method I’ve use many times and which is generic enough to cover many different scenarios. I’ll use an example to clarify how this works. In this example, I have a data warehouse database called MyDW, and this database contains a table called ImportTable which I’ll assume to be a table which is imported from another database by the data warehouse’s ETL process. For the purpose of this example I’ve kept thus table very simple:</p>  <div id="codeSnippetWrapper">   <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">CREATE</span> <span style="color: rgb(0, 0, 255);">TABLE</span> [dbo].[ImportTable]( <br />    [ID] [<span style="color: rgb(0, 0, 255);">int</span>] <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [Name] [nvarchar](100) <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [Country] [nvarchar](100) <span style="color: rgb(0, 0, 255);">NULL</span> <br /> ) <span style="color: rgb(0, 0, 255);">ON</span> [<span style="color: rgb(0, 0, 255);">PRIMARY</span>]</pre>

  <br />It’s important to understand that the ETL process periodically refreshes this table by fetching fresh data from an external database. Records might be added, removed or changed. The ETL process might simply update it by truncating the existing table and reloading it entirely. The ID field is the primary key which is imported from the external database (for this example we don’t add our own ID in the data warehouse).</div>

<p>I called this method of tracking history “snapshot-based versioning”. It does <em>not </em>rely on database triggers or on CDC. While those features might be useful for other scenarios, I’ve found them too limiting for data warehouse scenarios. For example, these features don’t work well if your ETL process refreshes the data by truncating the existing table and then reloading it.</p>

<p>Snapshot-based versioning instead relies on TSQL’s <a href="http://technet.microsoft.com/en-us/library/bb510625.aspx">MERGE</a> statement. The MERGE statement is supported in SQL Server 2008 or higher. A limitation is that you cannot track changes to fields of type image, ntext and text. This is usually not a blocker because in real-life scenarios most tables have a “modified” field that is updated whenever a change to the record is changed.</p>

<p>The idea is to have a second table called H_ImportTable which has the same fields as the original ImportTable. This second table, which I’ll refer to as the “history table”, has a versioned copy of the data in H_ImportTable. In other words, it works like a log which appends a row for any change to ImportTable. Obviously, if the table in the external database has changed multiple times between two ETL runs, this will appear only as one change in H_ImportTable, hence the name “snapshot-based versioning”.</p>

<p>You can create H_ImportTable as follows:</p>

<div id="codeSnippetWrapper">
  <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);"><span style="color: rgb(0, 0, 255);">CREATE</span> <span style="color: rgb(0, 0, 255);">TABLE</span> [dbo].[H_ImportTable] ( <br />    [_ACTION] [<span style="color: rgb(0, 0, 255);">char</span>](1) <span style="color: rgb(0, 0, 255);">NOT</span> <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [_KEY] [<span style="color: rgb(0, 0, 255);">int</span>] <span style="color: rgb(0, 0, 255);">IDENTITY</span>(1,1) <span style="color: rgb(0, 0, 255);">NOT</span> <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [_EFF_FROM] [datetime] <span style="color: rgb(0, 0, 255);">NOT</span> <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [_EFF_TO] [datetime] <span style="color: rgb(0, 0, 255);">NOT</span> <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [ID] [<span style="color: rgb(0, 0, 255);">int</span>] <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [Name] [nvarchar](100) <span style="color: rgb(0, 0, 255);">NULL</span>, <br />    [Country] [nvarchar](100) <span style="color: rgb(0, 0, 255);">NULL</span> <br /> )</pre>

  <br />As you see there are four additional fields in this table:</div>

<ul>
  <li>_ACTION indicates whether a row has been added (‘A’), changed (‘C’), deleted (‘D’) or undeleted (‘U’).</li>

  <li>_KEY is a unique row identifier. This field is only useful in specific scenarios</li>

  <li>_EFF_FROM indicates the date/time from which this row is effective</li>

  <li>_EFF_TO indicates the date/time until which this row is active. In case the row is effective now, this field is set to 9999-12-31.</li>
</ul>

<p>After refreshing ImportTable, the ETL process should update H_ImportTable using the following SQL statement:</p>

<div id="codeSnippetWrapper" style="margin: 20px 0px 10px; padding: 4px; border: 1px solid silver; width: 97.45%; height: 310px; text-align: left; line-height: 12pt; overflow: auto; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; cursor: text; direction: ltr; max-height: 200px; background-color: rgb(244, 244, 244);">
  <pre id="codeSnippet" style="margin: 0em; padding: 0px; width: 100%; height: 331px; text-align: left; color: black; line-height: 12pt; overflow: visible; font-family: &quot;Courier New&quot;, courier, monospace; font-size: 8pt; direction: ltr; background-color: rgb(244, 244, 244);">INSERT <span style="color: rgb(0, 0, 255);">INTO</span> <u>[dbo].[H_ImportTable]</u> <br /> <span style="color: rgb(0, 0, 255);">SELECT</span> _ACTION, SYSDATETIME() <span style="color: rgb(0, 0, 255);">AS</span> _EFF_FROM, <span style="color: rgb(0, 96, 128);">'9999-12-31'</span> <span style="color: rgb(0, 0, 255);">AS</span> _EFF_TO,<u>[ID],[Name],[Country]</u> <br /> <span style="color: rgb(0, 0, 255);">FROM</span> <br /> ( <br />  MERGE <u>[dbo].[H_ImportTable]</u> <span style="color: rgb(0, 0, 255);">AS</span> T <br />  <span style="color: rgb(0, 0, 255);">USING</span> <u>[dbo].[ImportTable]</u> <span style="color: rgb(0, 0, 255);">AS</span> S <br />  <span style="color: rgb(0, 0, 255);">ON</span> (S.<u>[ID]</u>=T.<u>[ID]</u> <span style="color: rgb(0, 0, 255);">AND</span> T._EFF_TO=<span style="color: rgb(0, 96, 128);">'9999-12-31'</span>) <br />  <span style="color: rgb(0, 0, 255);">WHEN</span> <span style="color: rgb(0, 0, 255);">NOT</span> MATCHED <span style="color: rgb(0, 0, 255);">BY</span> TARGET <span style="color: rgb(0, 0, 255);">THEN</span> <br />    INSERT(_ACTION,_EFF_FROM,_EFF_TO,<u>[ID],[Name],[Country]</u>) <br />    <span style="color: rgb(0, 0, 255);">VALUES</span>(<span style="color: rgb(0, 96, 128);">'A'</span>,SYSDATETIME(),<span style="color: rgb(0, 96, 128);">'9999-12-31'</span>,<u>[S].[ID],[S].[Name],[S].[Country]</u>) <br />  <span style="color: rgb(0, 0, 255);">WHEN</span> MATCHED <span style="color: rgb(0, 0, 255);">AND</span> T._EFF_TO=<span style="color: rgb(0, 96, 128);">'9999-12-31'</span> <span style="color: rgb(0, 0, 255);">AND</span> (T._ACTION=<span style="color: rgb(0, 96, 128);">'D'</span> <span style="color: rgb(0, 0, 255);">OR</span> (<u>[S].[Name]&lt;&gt;[T].[Name] <span style="color: rgb(0, 0, 255);">OR</span> [S].[Country]&lt;&gt;[T].[Country])</u>) <span style="color: rgb(0, 0, 255);">THEN</span> <br />    <span style="color: rgb(0, 0, 255);">UPDATE</span> <span style="color: rgb(0, 0, 255);">SET</span> T._EFF_TO=SYSDATETIME() <br />  <span style="color: rgb(0, 0, 255);">WHEN</span> <span style="color: rgb(0, 0, 255);">NOT</span> MATCHED <span style="color: rgb(0, 0, 255);">BY</span> SOURCE <span style="color: rgb(0, 0, 255);">AND</span> T._EFF_TO=<span style="color: rgb(0, 96, 128);">'9999-12-31'</span> <span style="color: rgb(0, 0, 255);">AND</span> T._ACTION&lt;&gt;<span style="color: rgb(0, 96, 128);">'D'</span> <span style="color: rgb(0, 0, 255);">THEN</span> <br />    <span style="color: rgb(0, 0, 255);">UPDATE</span> <span style="color: rgb(0, 0, 255);">SET</span> T._EFF_TO=SYSDATETIME() <br />  <span style="color: rgb(0, 0, 255);">OUTPUT</span> $<span style="color: rgb(0, 0, 255);">Action</span> Action_Out <br />         ,<span style="color: rgb(0, 0, 255);">CASE</span> <span style="color: rgb(0, 0, 255);">WHEN</span> S.<u>[ID]</u> <span style="color: rgb(0, 0, 255);">IS</span> <span style="color: rgb(0, 0, 255);">NULL</span> <span style="color: rgb(0, 0, 255);">THEN</span> <span style="color: rgb(0, 96, 128);">'D'</span> <span style="color: rgb(0, 0, 255);">WHEN</span> Inserted._ACTION=<span style="color: rgb(0, 96, 128);">'D'</span> <span style="color: rgb(0, 0, 255);">THEN</span> <span style="color: rgb(0, 96, 128);">'U'</span> <span style="color: rgb(0, 0, 255);">ELSE</span> <span style="color: rgb(0, 96, 128);">'C'</span> <span style="color: rgb(0, 0, 255);">END</span> <span style="color: rgb(0, 0, 255);">AS</span> _ACTION <br />         ,ISNULL(S.<u>[ID]</u>,Deleted.<u>[ID]</u>) <span style="color: rgb(0, 0, 255);">AS</span> <u>ID,[S].[Name],[S].[Country]</u> <br /> ) <span style="color: rgb(0, 0, 255);">AS</span> MERGE_OUT <br /> <span style="color: rgb(0, 0, 255);">WHERE</span> MERGE_OUT.Action_Out = <span style="color: rgb(0, 96, 128);">'UPDATE'</span> <br /></pre>

  <br /></div>

<div>&#160;&#160;&#160; <br />The statement above does all of the heavy lifting to update the history table H_ImportTable. Because MERGE is just a single SQL statement, it is very efficient compared to using multiple SELECT, INSERT and UPDATE statements to accomplish the same. The statement makes the assumption that the ID field is unique in the source table and you’re advised to create a unique index on that field in ImportTable. Obviously, in the history table H_ImportTable this field will no longer be unique. </div>

<div>&#160;</div>

<div>For performance you’ll want to create the following indexes on the history table H_ImprotTable: ID, _ACTION, _EFF_FROM and _EFF_TO.</div>

<p>You can adjust this SQL code to work with the tables in your data warehouse. To do so, adjust all the parts that are underlined. If you need to do this for many tables, making these changes manually is laborious and error-prone, This is why I wrote the program “Table Merge Scripts” which you can <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/2055.Table%20Merge%20Scripts.zip" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11/2055.Table-Merge-Scripts.zip">download here</a>. This program generates the SQL code based on an existing table in the database. You can see the UI below:</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/3022.image_75C89363.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/3022.image_5F00_75C89363.png"><img title="image" style="border: 0px currentcolor; display: inline;" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/57/11/metablogapi/5670.image_thumb_6D64BE0C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-57-11-metablogapi/5670.image_5F00_thumb_5F00_6D64BE0C.png" width="466" height="345" /></a> </p>

<p>First, edit the connection string by entering the correct server name and database name. Then click Connect.</p>

<p>Next select the table and the field that is the primary key.</p>

<p>The first button in the bottom half of the screen copies the SQL code to create the history table to the clipboard. The new table will have the same name as the selected source table, prefixed with “H_”. The code also creates indexes for _ACTION, _EFF_FROM, _EFF_TO, _KEY and the field which you designated as the primary key in the source table.</p>

<p>The next button copies the SQL code to update the history table to the clipboard. This consists of a MERGE command similar to the example shown above.</p>

<p>The final button copies the same SQL code as the second table, but wrapped in a stored procedure. </p>

<p>Good luck!</p>]]></content:encoded>
    </item>
  </channel>
</rss>