<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: James MacIvor</title>
    <description>The latest articles on Forem by James MacIvor (@robotoptimist).</description>
    <link>https://forem.com/robotoptimist</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18319%2F86701240-8111-4439-bf5f-152954a46017.jpeg</url>
      <title>Forem: James MacIvor</title>
      <link>https://forem.com/robotoptimist</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/robotoptimist"/>
    <language>en</language>
    <item>
      <title>For The Fear Of Fouling Up</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Fri, 22 Jan 2021 18:35:45 +0000</pubDate>
      <link>https://forem.com/robotoptimist/for-the-fear-of-fouling-up-52ei</link>
      <guid>https://forem.com/robotoptimist/for-the-fear-of-fouling-up-52ei</guid>
      <description>&lt;h1&gt;
  
  
  For The Fear Of Fouling Up
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GtSogEt5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/heroes/Fear_of_Fouling_Up_eqshjl" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GtSogEt5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/heroes/Fear_of_Fouling_Up_eqshjl" alt="A key board with the title text 'Fear of Fouling Up' and the subtext 'how fear stymied my open source enthusiasm'"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Forewarned
&lt;/h2&gt;

&lt;p&gt;This article starts out talking about .NET culture and patterns and swiftly transforms into a discussion on my personal experience contributing to OSS. But if you want to engage with me on the former topic please reach out to me on &lt;a href="https://twitter.com/macivortech"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/jamesmacivor/"&gt;LinkedIn&lt;/a&gt;. I'd love to hear from you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Obligatory Patterns
&lt;/h2&gt;

&lt;p&gt;I was reading Twitter and saw an interesting series of conversations unfold with David Fowler. David Fowler is a domain famous developer and is an architect at Microsoft. He authored the SignalR project which is the most popular method of enabling web sockets in a .NET application. He is also well known for his involvement in the community where he frequently shares what he's learning about and offers resources to fellow developers are also curious and learning. He will occasionally post a strong opinion and is not averse to a little bit of controversy for the benefit of the community at large.&lt;/p&gt;

&lt;p&gt;This particular twitter thread was about how you don't need the full enterprise implementation for every little project. He wrote that not every tiny project benefits from dependency injection and repository patterns and so on. This tweet squarely fell into the "controversy" category.&lt;/p&gt;

&lt;p&gt;He encountered a lot of resistance from .NET developers, but I deeply agreed with what he wrote. Those patterns help on larger projects, but sometimes they're useless bloat and can be discarded for smaller projects. Fowler argued that we should be thoughtful about where and why we use those patterns. While some felt that they would always end up using the patterns (so why not just implement them right away) I recalled instances where I didn't want to deal with the overhead. I've certainly worked on projects with patterns that did not make sense or were implemented poorly. Some patterns are better off not used than implemented poorly.&lt;/p&gt;

&lt;p&gt;You can get a taste of the conversation from the following tweet from February 2020.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ohyag5Fe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1320061631339126786/gHKfzYft_normal.jpg" alt="David Fowler 🇧🇧 profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        David Fowler 🇧🇧
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @davidfowl
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Your .net code does not need 15 projects with a class per command and query. You don’t need an interface for everything either. It’s amazing to read extremely simple samples using .net vs node vs go. It seems our ecosystem is attracted to complexity... &lt;a href="https://twitter.com/hashtag/dotnet"&gt;#dotnet&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      04:13 AM - 08 Feb 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1225996008544460800" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1225996008544460800" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1225996008544460800" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;Fowler's initial 2019 tweet was about his backend &lt;a href="https://github.com/davidfowl/Todos"&gt;Todo app implementations&lt;/a&gt; where developers criticized the repo for lacking some of the common patterns found in many .NET projects.&lt;/p&gt;

&lt;p&gt;The exchange generated an entertaining and informative back and forth. As I read Fowler's tweets and started to feel like I understood his point of view I felt an affinity for his approach. I even agreed to his stance on testing in that many unit tests do not add value and once again we should exercise good judgement. A blanket requirement for test coverage makes sense for some development teams but sometimes it tramples the question that should be asked: "Do these tests make sense?"&lt;/p&gt;

&lt;p&gt;Reading his tweets made me think about all the times I &lt;em&gt;didn't&lt;/em&gt; reach for .NET Core for a personal project or proof of concept. Why not? Because it seemed like a lot of overhead. I didn't want my internal code reviewer berating me for not using the patterns necessary for a normal project. I didn't want to show it to another .NET developer and immediately feel like I have to apologize for not having those things - even though in the context of the project it does not make sense! So I'll just write it in Node.js or something because the expectation for fully thought out patterns are not as prevalent. I can focus on the functionality I want to explore and not deal with the overhead of a repository pattern or whatever.&lt;/p&gt;

&lt;p&gt;Hearing David Fowler sound out a few of those same thoughts made me realize that all of this expectation in .NET isn't necessary. I felt I had permission to also dissent against the use of over-architected patterns.&lt;/p&gt;

&lt;p&gt;So, when I saw in late December that Fowler had created a &lt;a href="https://github.com/featherhttp/framework"&gt;new project called FeatherHttp&lt;/a&gt; I was stoked. It appeared to me that this project was a manifestation of his thoughts around .NET bloat. I jumped on it and quickly started buzzing around the threads. I wanted to help write the documentation. I wanted to be a part of this effort. Also, here was a .NET open source project in the initial stages. I felt I could contribute. I was also very excited to work with David Fowler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Involved
&lt;/h2&gt;

&lt;p&gt;Have you ever worked with someone who you admired so much that it actively caused you anxiety? I have.&lt;/p&gt;

&lt;p&gt;I mean, not at first. At first I was 100% excitement. "Here's a starter template for the docs! Why are we doing this? Let me write the docs with an angle in mind for people who are not .NET. Also, who are we targeting? Should expect .NET familiarity?" So many questions. Buzz buzz buzz.&lt;/p&gt;

&lt;p&gt;I create a basic statically generated site to host the docs. I am brimming with enthusiasm as I start trying to convert the examples into docs and show my worth. Things move quickly. I initially think to use Netlify but somebody says "lets use GitHub pages instead" so I reconfigure everything for that. This is actually kind of hard. GitHub pages requires a branch dedicated to the static pages. The static site generator needs to create the files and then GitHub Actions has to pick up that output and commit it to the github-pages branch. It's a solved problem, but just the idea of a dedicated branch only for the static pages seemed pretty different and novel to me.&lt;/p&gt;

&lt;p&gt;Anyway, there are plenty of tutorials and guides to explain how to do that. I was pretty happy once I had a build system figured out and had submitted the PR to David.&lt;/p&gt;

&lt;p&gt;I remember feeling so proud. "This is going great."&lt;/p&gt;

&lt;h2&gt;
  
  
  Fear
&lt;/h2&gt;

&lt;p&gt;After that I saw there was an opportunity to help out with the GitHub actions to package up the dotnet template into a NuGet package. I had done none of this before and I was excited for the opportunity to learn. I had a fairly bare bones description in the issue but it cited another project that did something similar and I could crib off of that.&lt;/p&gt;

&lt;p&gt;However, there were challenges. The FeatherHttp project used Nerdbank Git Versioning, but the example project did not. Also, the example project used a PowerShell script to set a couple of things up, but along the way David let me know that he's prefer not to use a PowerShell script unless absolutely necessary.&lt;/p&gt;

&lt;p&gt;I iterated a lot. I made faulty assumptions and was misled by GitHub issues on other projects. I worked on these GitHub actions every night for a couple of weeks. I submitted several versions of it as a PR and found I was frequently missing the mark.&lt;/p&gt;

&lt;p&gt;In fact, it's kind of fun to look back at my &lt;a href="https://github.com/RobotOptimist/FeatherHttp/commits/ci-changes"&gt;commit history&lt;/a&gt; for that and just... marvel at the struggle.&lt;/p&gt;

&lt;p&gt;But with each breakthrough in understanding something else was happening. I allowed my insecurity to eat away at me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why can't I just &lt;em&gt;do&lt;/em&gt; this?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;David seemed to have the exact idea of what he wanted, and I felt like I was discovering that in jibs and jabs along the way.&lt;/p&gt;

&lt;p&gt;I remember how I felt proud when David finally approved the PR and my code merged. I followed it closely to make sure it built properly and the GitHub Actions behaved exactly as I had tested. It worked!&lt;/p&gt;

&lt;p&gt;But then, David found an issue and had to fix a few things. I felt like I had failed and missed the mark.&lt;/p&gt;

&lt;p&gt;After that, when my confidence should have been soaring I felt disappointed instead. What I wanted was to talk to David and hear him actually talk about the project. Communicating over GitHub issues and PR comments felt like a poor substitute.&lt;/p&gt;

&lt;p&gt;I puttered around with the docs site for a little while longer, but my anxiety continued its patter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why So Serious?
&lt;/h2&gt;

&lt;p&gt;David Fowler, among the many software architects and coders that I'm aware of, is a strong leader endowed with uncommon skill, like an Achilles of code or something. Working with him, I believed, would be a step along the way to becoming &lt;em&gt;like&lt;/em&gt; him. Where, now I am a strong coder for creating business solutions, I could become like David who is a strong programmer and architect creating tools for other developers to use.&lt;/p&gt;

&lt;p&gt;So, first error was allowing David to become larger than life in my own head. The project became overshadowed by my inability to communicate well with David. Also, I felt the eyes of his other followers on me. By becoming part of this journey I opened myself up to scrutiny and criticism. Even though none was forthcoming from other developers, my inner voice provided plenty! I was unprepared for such feelings. I imagined a group of fellow software developers who were unfeeling and unkind, like a host of StackOverflow curmudgeons prepared to descend upon me.&lt;/p&gt;

&lt;p&gt;All of this while constructing some docs pages and a few GitHub actions. How can the voices of my insecurity be so loud?&lt;/p&gt;

&lt;p&gt;If you have ever suffered from anxiety you know there is a threshold that, when crossed, the anxiety cannot be calmed. No one can say anything to assuage it. There is nothing that can be done except to either walk away if you can, or to soldier on and shove the unpleasant emotions to the side.&lt;/p&gt;

&lt;p&gt;For me, time and emotional distance will eventually lessen the anxiety. In fact, I've come to recognize that when I heap too much expectation on an experience or outcome I will almost surely self-sabotage. I find that keeping calm and resisting excitement is the key to preventing my fears and insecurities from overwhelming me in the face of a big opportunity.&lt;/p&gt;

&lt;p&gt;Ultimately, this time, it was important to walk away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remembering Strength
&lt;/h2&gt;

&lt;p&gt;I could think of no way to rid myself of this weakness and so I decided to do the coder equivalent of "hitting the gym." I doubled down on my projects at work and trained hard to be a better technical lead and developer.&lt;/p&gt;

&lt;p&gt;This is not the first time my confidence has betrayed me. I know the cause of it and I know how to recover. The fact is that I'm pretty good at what I do. I occupy a great spot in my local community of developers. I can provide a leg up to less experienced developers, and I can be a steady pillar on a project. These fears rarely surface when I'm in somewhere "doing the work."&lt;/p&gt;

&lt;p&gt;And therein is the answer for me, to practice my craft within a team, either leading or following. I can shine so long as I'm connected to a community of people. I am an extrovert in a field favoring introverts. It's no wonder that OSS is difficult for me as it's lonely hours spent in code away from the life giving connections. Others in OSS seek it out for that same quality. However, fellowship within work is the top coinage that can be offered to me, and I am eternally greedy for it.&lt;/p&gt;

&lt;p&gt;Remembering this, I could reflect accurately on my experiences. What I wanted was to speak to David to hear him talk directly about his thoughts and goals and what he believed was right. That is the true work. The code is a reflection of those ideas. Lacking a supply of that, I'm left to create my own meaning based on what I can see written already. And herein I come to my second error.&lt;/p&gt;

&lt;p&gt;OSS is a conversation occurring over a long time. Patience is key.&lt;/p&gt;

&lt;p&gt;I know this because months later I contributed to a different project, rewriting a section of their docs. I'm tremendously proud of that contribution but it was made with completely different motivations. There was a task I wanted to do that was not accurately covered in the docs. This annoyed me and I decided no one else needed to suffer after me. I wrote up the change and put it in a PR. I felt completely confident in my motivation and my understanding of the project goals. I imagined a shared bond with a bunch of other similarly irritated developers. It all sailed through without a single problem and I felt rewarded. It was the equivalent of saying a single sentence in a very long conversation involving many other developers. This is as it should be. The developers with the vision for the project drive not just the technology itself, but the entire conversation. This conversation happens at a pace with which they are comfortable.&lt;/p&gt;

&lt;p&gt;Now I can see that the fire of enthusiasm I brought into the FeatherHttp project would have better served as a slow burn, ready to be stoked to flames when the time was right.&lt;/p&gt;

&lt;p&gt;Further I can see that big personalities don't matter. The code matters. The problem we seek to solve matters. Yes, even your local yokel tech lead can be awed by the fame and technical acumen of others. But the developer versions of the heroes from the Iliad do not walk among us. They don't have Twitter accounts. They are not real. All are normal people, some knowing more than others but still people just the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onward
&lt;/h2&gt;

&lt;p&gt;Why did I write this? What did I hope to gain by revealing a weakness, no matter if I did learn from it? One thing I hope happens is that others will find their own motivation to contribute to open source. Where I did it because I believed in the project and sought a connection with a person I admired, others will do it because they want to create a better experience for the next developer. Or maybe because they want to learn something new. There are no wrong reasons to contribute.&lt;/p&gt;

&lt;p&gt;Another reason I wrote this is to better understand myself. Sometimes the act of putting a lesson to words defines it and brings it into clarity. This is, after all, a story of me versus myself. These internal struggles happen to all of us and it's good to reveal that. I want you to see that I struggle sometimes. I don't want to forget the lessons learned either.&lt;/p&gt;

&lt;p&gt;Also this internal conflict represents a huge reason why people don't want to contribute to OSS. Why wrestle with OSS when I can write code within my team and receive the validation of having done a good job, or the gentle criticism from people who will look me in the eye when they say it.&lt;/p&gt;

&lt;p&gt;I jumped at shadows and imaginary criticisms and missed the point. You needn't suffer this. Learn from my errors and engage with a vibrant community, but learn to be patient as well - with yourself most of all.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>The Road To Developer Jobs</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 28 Dec 2020 21:59:25 +0000</pubDate>
      <link>https://forem.com/robotoptimist/the-road-to-developer-jobs-2kko</link>
      <guid>https://forem.com/robotoptimist/the-road-to-developer-jobs-2kko</guid>
      <description>&lt;h1&gt;
  
  
  The Road To Developer Jobs
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--deohPGAK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/heroes/career-guy-up-the-steps_f1sjta" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--deohPGAK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/heroes/career-guy-up-the-steps_f1sjta" alt="A suit clad person climbing steps with a case in hand."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's hard out there.&lt;/p&gt;

&lt;p&gt;I think nothing has shown me that more than following and reading the heart breaking updates from recent boot camp grads. They don't know it, but I've seen them. I've read their portfolios. I've read their posts on LinkedIn, which are alternating in bravery and despair. I feel for them.&lt;/p&gt;

&lt;p&gt;I'm a lucky one. I taught myself to code while working an office job. I never used a boot camp, but I thought about it. I was personally invited to join one by the founder of The Odin Project. He had just started his Viking School and was looking at promising students in his free online course. I was tempted! He seemed like he could help me achieve my dream of being a developer.&lt;/p&gt;

&lt;p&gt;Ultimately, the finances didn't make sense to me so I passed. Wow, how different it would be had I not. At that time boot camps were popping up everywhere but the demand for new developers was nowhere near sated. I would probably be happily writing on Ruby or JavaScript projects somewhere. Instead, I went down the path of .NET and got an enterprise job. It was pretty good for a while.&lt;/p&gt;

&lt;p&gt;The point is that it was harder to acquire the base skills, but easier to get a job.&lt;/p&gt;

&lt;p&gt;Now, it's different. Coding has become a symbol of upward mobility and people have flocked to the craft. This has naturally made the junior level jobs a little more scarce.&lt;/p&gt;

&lt;p&gt;So what to do? I've been thinking about that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Easiest Way
&lt;/h2&gt;

&lt;p&gt;Lets start with the easiest possible way to get a job. If you come from an office environment then the best thing is to network back into that company. Get in touch with people you know and try to find a way to reach the ears of a hiring manager in an IT department. Even if you're not in an office environment, say you come from a warehouse or a factory - there are still office staff there and you should definitely network in that direction.&lt;/p&gt;

&lt;p&gt;You are now a triple threat to secure any open coding position. Not only do you have that base knowledge of coding (hopefully in the stack your company uses), but you also have knowledge about how the business operates and you're already accepted into the culture of the company. From a hiring manager's perspective this could be a slam dunk. It's way easier to bring in someone who kind of already knows their way around than someone who's completely fresh. You immediately put yourself on par with an experienced junior developer or even a mid-level.&lt;/p&gt;

&lt;p&gt;If you were wise in your previous position and made friends and had a decent network then that makes you even more valuable. With a little time you can be someone who knows where to get that requirement clarified, or steers the a coworker ( or even the team! ) around a critical detail that only someone experienced in business operations would know. This is particularly true if you work in a field with complicated processes, like insurance or finance. You are made of gold if you've already got insight into existing systems and processes.&lt;/p&gt;

&lt;p&gt;Of course that's not for everyone. Also, many people turn to code to flee their old job, not to return and conquer it. In fact, even though I label this as "easy" few people lay the ground work necessary to make this a reality. It does require a fair amount of networking. It does require you not hate your old employer. It is easier for people with office backgrounds. These differences add up to a big advantage. However, if this is you then you should definitely try this route.&lt;/p&gt;

&lt;p&gt;Use every advantage at your disposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Traditional Way
&lt;/h2&gt;

&lt;p&gt;It's harder, but the boot camps are geared around this. This is the path of job fairs and competitive interviewing. If you are a super personable confident outgoing person who excelled in boot camp then you'll tie this up. No problem!&lt;/p&gt;

&lt;p&gt;However, if you have self-doubt and uncertainty and occasionally self-sabotage or if you didn't do so great in boot camp (just well enough). Then you'll struggle here. But you can still pull it through by following the advice of your instructors and going all in studying and working on boosting your confidence.&lt;/p&gt;

&lt;p&gt;The profiles of companies that target boot camp graduates vary. Some are Startups seeking inexpensive but promising junior developers. Some are enterprise companies trying to modernize their legacy applications and compete with more nimble companies. Some are companies afflicted with huge amounts of attrition and are trying to find more developers to maintain and build their applications while not increasing pay or improving working conditions. (You'll know who these companies are by the uneasy rumors that precede them. Also checkout Glassdoor!) And lastly, some seek talent from a wide array of places and will gladly take a boot camp graduate or a CS graduate based on who they think will do the job well and, of equal importance, who they personally want to work alongside.&lt;/p&gt;

&lt;p&gt;Second career or military veteran boot camp students are appealing. Many of them have already worked in professional environments. They know how to dress, how to speak, and how to deal with adverse conditions. This something your average college grad will spend the next 2-3 years learning. Not only that, but boot camp students are humble. They've been on the sweaty side of employment negotiations almost every time. Part of the fun of hiring them is watching them grow as engineers and begin to understand their value to companies.&lt;/p&gt;

&lt;p&gt;People can find themselves hovering along the traditional route for a long time. Especially candidates who seek 100% remote work or are in a locale already saturated with top talent. It's harder for people who don't easily fit in with others or who feel different. Folks who have struggled with the third rail of software development ("soft" skills) will find this much more difficult. They have to work three times as hard because they need to acquire and master the base coding skill and also acquire some of the important people skills.&lt;/p&gt;

&lt;p&gt;So what do you do if the easy way isn't so easy and the traditional way has you circling the runway for an indefinite amount of time?&lt;/p&gt;

&lt;h2&gt;
  
  
  Another Way
&lt;/h2&gt;

&lt;p&gt;If a month or two goes by and you can't find a position then it's reasonable to start thinking of alternatives.&lt;/p&gt;

&lt;p&gt;I like people who are self-starters who will find a different game with rules that favor them. I value this skill and I'm not the only one.&lt;/p&gt;

&lt;p&gt;Consider that keeping a portfolio of pretty but otherwise not useful projects can only get you so far. Why not try for real experience on your own terms?&lt;/p&gt;

&lt;p&gt;I suggest looking around on Upwork or similar sites for a contract you think you can do for a price that suits you. Yes, I am suggesting going for real work. I know you're not experienced. But what if you tried anyway? What could you learn?&lt;/p&gt;

&lt;p&gt;Also, what the hell, if nothing else is working what will you lose if you fail?&lt;/p&gt;

&lt;p&gt;Upwork might be too intimidating at first, but what about churches? What about professional groups or charities? If you're already doing portfolio work then why not do work that's meaningful and for a good cause. Yes it's free or low paid work, but what's your portfolio paying? Also, you might ask if you could include the work you do as part of your portfolio. If you do good work you might ask them for references too.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But listen, and this is important, do this work because you want to do it, because you believe in their cause or you like them or whatever. Never allow anyone to brow beat you into doing free work. If you get an offer for free work and your emotional reaction is "meh". Pass. It's not worth it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You know what, if put in the time to do something cool for a cause or people you like - you might as well form your own LLC. (Congrats you're hired!)&lt;/p&gt;

&lt;p&gt;Also, those people you just did good work for have a friend at BigCo who needs a new developer on their team. Can they introduce you?&lt;/p&gt;

&lt;p&gt;This is how it works. It makes it super easy to get hired by a bigger company if you already have successful projects and you have experience talking to business people. People who do this successfully are leaders in our industry. They are sought after. It's not easy and it's not for everyone. From what I've seen, people who try this and fail are still miles ahead of people who go into interviews with only a portfolio and hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patience, Practice, and People
&lt;/h2&gt;

&lt;p&gt;Ultimately though, patience, skill, and your network will be your friends in this initial stage as well as throughout your career.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yes, get into those interviews and put your best face forward. &lt;/li&gt;
&lt;li&gt;Yes, practice at the craft and build up to showcase your skill.&lt;/li&gt;
&lt;li&gt;Yes, build up your network on LinkedIn and elsewhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That third point is the most important. And what do I really mean? I mean you should be trying to schedule chats with people who interest you. The goal from these chats is not to get an interview, but to learn. Ask about their business. Ask about what they like in their job. Ask about the culture. Listen to their responses and build follow up questions. Be respectful of their time and try not to keep them longer than 15-30 minutes.&lt;/p&gt;

&lt;p&gt;You're not going to be good at this right away. You'll make mistakes and feel foolish from time to time. You'll make gaffes. Embrace that awkwardness! I try to do this three times a week even being fully employed. If you are not otherwise employed then you might try to do this three times a day. Don't exhaust yourself. If it feels like you're going through the motions then take the day off. Being genuine and authentic is important. If you're tired it makes it a lot harder!&lt;/p&gt;

&lt;p&gt;I think it's reasonable to ask, what's the point of all that? The point is to build relationships with people. You'll want to start conversations and contribute thoughts toward any problems they might have. People who get good at this are super valuable and can get jobs anywhere.&lt;/p&gt;

&lt;p&gt;If starting conversations up with strangers is not your thing then that's completely okay. This worked for me, but I know that we're all different. In a more general sense my advice is to find a way to sharpen your people skills. If you're not naturally a people person it will be hard, but it's incredibly important.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Right Job Matters
&lt;/h2&gt;

&lt;p&gt;Indeed, when you're first getting started it can seem like &lt;em&gt;any&lt;/em&gt; job sounds great. I believe this. Getting that first year or so of experience is critical for setting up the rest of your career. But if you have options, I recommend consulting.&lt;/p&gt;

&lt;p&gt;Working for a small homegrown consulting firm like HMB transformed me. I've been exposed to amazing and talented people. I've worked on interesting and engaging projects. I've been exposed to a wide variety of company cultures and discovered what works best for me. I've been challenged to become a better communicator, a better engineer, and a better person. I've even been challenged to be a leader. All of this and without the chilling bureaucracy of a larger company.&lt;/p&gt;

&lt;p&gt;It means a lot when what you do matters to the success of the company in a tangible way.&lt;/p&gt;

&lt;p&gt;Moreover, working with smaller companies and startups you are encouraged to steer your own success. Your success is their success and vice versa. It helps when your mission is more than providing value to faceless shareholders. Instead the mission of the business is something you shape with your own hands. It's something you can believe in. That matters to me.&lt;/p&gt;

&lt;p&gt;But there are downsides as well. The benefits might not be as good as a big company. Large enterprise companies are stable. Changes are incremental at those places. Shakeups of an entire company do not occur over the course of one or two weeks, for example. There is value in that. Even though big enterprise employment is not my favorite (although I do enjoy working on a contract basis for such companies), I do recognize that such companies provide safety and stability for their employees. There is nothing wrong with seeking safety and stability!&lt;/p&gt;

&lt;p&gt;There is another downside to big national and multinational companies to watch out for though. I mention it here briefly as I intend to expand on this in a future post. Big companies will disappoint you. It's slow, over the course of years at times. The rewards will never seem to be quite right. There will always be a little more red tape, an extra caveat, a "gotcha" that always works against you. Be cautious.&lt;/p&gt;

&lt;p&gt;No matter where you go one thing will be true. It is up to you to seize control of your future. Do not let time and fate dictate your path. Listen to advice and learn from your mistakes but also invest time into finding the &lt;em&gt;right&lt;/em&gt; advice and learning the &lt;em&gt;right&lt;/em&gt; lessons. Many people will seek to influence you. Some will have agendas that include your best interest and some will not. And if this is your second career and you already know all of this then please forgive this gentle reminder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;We are a community of engineers and we want to encourage and promote each other. I want you to feel encouraged and hopeful. I want you to succeed.&lt;/p&gt;

&lt;p&gt;Most of all, I am glad to welcome you into the community of developers.&lt;/p&gt;

&lt;p&gt;Someday, when you've become established, I want you to remember this time and help a newbie out. Do something, an introduction, encouragement, mentorship - something.&lt;/p&gt;

&lt;p&gt;All of those senior engineers, the people who don't quite seem to hear you. As they are so shall you be.&lt;/p&gt;

&lt;p&gt;Remember where you came from and who helped you. Remember to help the people who come after you. This is the way.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>gethired</category>
      <category>newbie</category>
      <category>bootcamp</category>
    </item>
    <item>
      <title>ML.NET and Python Polynomial Linear Regression</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 28 Dec 2020 21:57:17 +0000</pubDate>
      <link>https://forem.com/robotoptimist/ml-net-and-python-polynomial-linear-regression-9</link>
      <guid>https://forem.com/robotoptimist/ml-net-and-python-polynomial-linear-regression-9</guid>
      <description>&lt;h1&gt;
  
  
  ML.NET and Python Polynomial Linear Regression
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fheroes%2Frobotmlnet-yes_tfryby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fheroes%2Frobotmlnet-yes_tfryby.png" alt="The ML.NET logo with a robot face next to it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the fourth in a series talking about differences implementing machine learning tasks in ML.NET and Python&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Here are the previous articles if you'd like to peruse them&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/take-a-look-at-mlnet" rel="noopener noreferrer"&gt;Take A Look A ML.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/mlnet-v-python-simple" rel="noopener noreferrer"&gt;ML.NET and Python Simple Linear Regression&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/mlnet-v-python-multiple" rel="noopener noreferrer"&gt;ML.NET and Python Multiple Linear Regression&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Polynomial Linear Regression?
&lt;/h2&gt;

&lt;p&gt;Polynomial linear regression is a bit of a strange customer. Where before our regressions could be consistently described as lines, a polynomial regression is a curve. A polynomial regression is appropriate when there is exponential growth in the dependent variable.&lt;/p&gt;

&lt;p&gt;So something like this simple example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Independent&lt;/th&gt;
&lt;th&gt;Dependent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Our data looks the same as simple linear regression at first, but upon noticing exponential growth in the dependent variable we need a different equation. A linear regression line will not be accurate. In fact, observing a regression line from a typical simple regression may be how we can identify the need for a polynomial regression.&lt;/p&gt;

&lt;p&gt;On data such as this we would need to determine the correct exponent for growth and then apply it to the independent variable.&lt;/p&gt;

&lt;p&gt;In order to perform the regression we need to expand out the independent variable by expanding this equation out for each possible result of 0 - {possible exponent value}. In practice such a transformation would change the above example into something like this.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Independent1&lt;/th&gt;
&lt;th&gt;Independent2&lt;/th&gt;
&lt;th&gt;Independent3&lt;/th&gt;
&lt;th&gt;Dependent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The reason we still call this linear regression despite the fact the growth curve is exponential is because the independent variables are still expressed linearly. Basically when we see &lt;code&gt;1, 2, 3, 4 ...&lt;/code&gt; and can say&lt;/p&gt;

&lt;p&gt;"Ok, this variable is growing in a linear way."&lt;/p&gt;

&lt;p&gt;Then, even though our dependent variable is growing exponentially we still consider this a linear regression.&lt;/p&gt;

&lt;p&gt;Polynomial regression is commonly used for tasks such as predicting disease spread in a population.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data
&lt;/h2&gt;

&lt;h3 id="thank-you-super-data-science-team"&gt;
&lt;span&gt;&lt;/span&gt;Thank you Super Data Science Team&lt;/h3&gt;

&lt;p&gt;This dataset and the Python information comes from &lt;a href="https://www.linkedin.com/in/keremenko/" rel="nofollow noopener noreferrer"&gt;Kirill Eremenko&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/hadelin-de-ponteves-1425ba5b/" rel="nofollow noopener noreferrer"&gt;Hadelin de Ponteves&lt;/a&gt; along with the &lt;a href="https://www.superdatascience.com/" rel="nofollow noopener noreferrer"&gt;Super Data Science Team.&lt;/a&gt;. They provide an excellent quality &lt;a href="https://www.udemy.com/course/machinelearning/learn/lecture/19229340" rel="nofollow noopener noreferrer"&gt;data science course&lt;/a&gt; on Udemy. The data is reused here with permission.&lt;/p&gt;

&lt;p&gt;Here we have a list of 10 positions within a company and their corresponding salary. You can see also that each position has been assigned a level which grows linearly relative to the salaries which grow exponentially. It really pays to move up!&lt;/p&gt;

&lt;h2&gt;
  
  
  Polynomial Linear Regression in Python
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/RobotOptimist/cc82e87e7d2104e58711b7c846a9e220/HEAD?filepath=polynomial-regression.ipynb" rel="noopener noreferrer"&gt;Try it yourself on Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I like about the Python implementation is how consistent the steps are. There is very little variation between regression tasks and yet we are still able to get great results. This task has some small variations in terms of the transformation we'll be doing and how we load in the data.&lt;/p&gt;

&lt;p&gt;First as usual, we import the libraries we'll need. We need &lt;code&gt;pandas&lt;/code&gt; to load in the data from our file and &lt;code&gt;numpy&lt;/code&gt; as it's a dependency of &lt;code&gt;pandas&lt;/code&gt;. Finally we need &lt;code&gt;maplotlib.pyplot&lt;/code&gt; to chart and visualize our data and results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now unlike previously we are not loading all of the columns, instead we're using Python array syntax to leave out the text column from the data. We can do that because having the &lt;code&gt;Level&lt;/code&gt; column already does all of the work the text column would do. In fact, if the &lt;code&gt;Level&lt;/code&gt; column did not already exist we would transform &lt;code&gt;Position&lt;/code&gt; into something similar. If you recall discussing transformations from &lt;a href="https://www.macivortech.com/blog/mlnet-v-python-multiple" rel="noopener noreferrer"&gt;multiple linear regression article&lt;/a&gt; then you might remember &lt;code&gt;OneHotEncoder&lt;/code&gt; which is a function to transform text columns into non-weighted vectors for the trainer to use. In this case though, transforming &lt;code&gt;Position&lt;/code&gt; into keys makes sense since there is an order inherent to the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Position_Salaries.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="c1"&gt;#leaving out the Position column by reading in the data starting at index 1 as opposed to 0 
&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="c1"&gt;#we read in only the last column, Salary for our dependent variable
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we'll want to transform the &lt;code&gt;Level&lt;/code&gt; column so we can model can have a proper curve representing exponential growth. Once we do that we can use good old &lt;code&gt;LinearRegression&lt;/code&gt; trainer to take care of the rest.&lt;/p&gt;

&lt;p&gt;Python has a class called &lt;code&gt;PolynomialFeatures&lt;/code&gt; that takes care of this transformation for us. Python makes this exceptionally easy as we will soon appreciate. Notice that the &lt;code&gt;PolynomialFeatures&lt;/code&gt; class takes a constructor argument called degree. Degree will be the exponent we expect the dependent variable to grow by. In this case we choose four, but you can experiment for yourself with different exponents to see how that changes the curve.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.preprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PolynomialFeatures&lt;/span&gt;
&lt;span class="n"&gt;poly_reg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PolynomialFeatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;degree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X_poly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;poly_reg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;lin_reg_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LinearRegression&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;lin_reg_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_poly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that transform out of the way we can go ahead and model the data and do a prediction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;red&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lin_reg_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poly_reg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit_transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;blue&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Polynomial Regression&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Position level&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Salary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fpolynomial-regression-python_oltlxa" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fpolynomial-regression-python_oltlxa" alt="A scatter chart with a regression line curving up exponentially."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's all so easy in Python. The tools are very consistent and I find it easy to find examples with a simple search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polynomial Linear Regression in ML.NET
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/RobotOptimist/1b7d5fd7bd386e03cb83335a043176e9/HEAD?filepath=mlnet_polynomial_regression.ipynb" rel="noopener noreferrer"&gt;Try it yourself on Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was a difficult road. Polynomial Linear Regression does not have a built in transform in ML.NET so I needed to devise a custom one. This led to many hours of reading through documentation and simple trial and error to find the correct way to do this.&lt;/p&gt;

&lt;p&gt;I did eventually figure it out and I'm glad to share my findings with you. There are gaps in the ML.NET documentation that made this harder than it really needed to be. Once I found the solution it became suddenly simple to do this.&lt;/p&gt;

&lt;p&gt;Let us first throw out the mlnet command line tool. For this task it could not really provide us with anything useful. Unlike with multiple linear regression it could not even get us close.&lt;/p&gt;

&lt;p&gt;The generated code also required significant alterations in order to reach our goal, which is a similar result to the Python process.&lt;/p&gt;

&lt;p&gt;This is not to say others have not struggled with this and perhaps found their own way.&lt;/p&gt;

&lt;p&gt;I'm reminded of this &lt;a href="https://github.com/dotnet/machinelearning-samples/issues/639" rel="noopener noreferrer"&gt;GitHub issue.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or this &lt;a href="https://stackoverflow.com/questions/49976719/write-polynomialfeatures-function-from-scikit-learn-for-f/49996800#49996800" rel="noopener noreferrer"&gt;StackOverflow question.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So lets get started. We do start from the usual place of setting up our using statements and reading in the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Composition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.ML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.ML.Data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.ML.Trainers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.ML.Transforms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;XPlot.Plotly&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am once again using XPlot to visualize our data.&lt;/p&gt;

&lt;p&gt;Here are our proposed input and output classes as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelInput&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ColumnName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Position"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;LoadColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ColumnName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Level"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;LoadColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ColumnName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;LoadColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelOutput&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Score&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And lets read in that data!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MLContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;IDataView&lt;/span&gt; &lt;span class="n"&gt;trainingDataView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadFromTextFile&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"./Position_Salaries.csv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;hasHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;separatorChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allowQuoting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allowSparse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next steps require a lot more thought and alteration from what we've done previously.&lt;/p&gt;

&lt;p&gt;The first thing to think about is our transformation. We need to define a custom transformation, but how can we do that?&lt;/p&gt;

&lt;p&gt;There are two main ways, one requires a contract be defined on the transformation so that a pipeline can be saved to a file. I don't intend to save my pipeline to a file in any case, but nevertheless I chose to pursue that approach as I believed it would provide anyone attempting to learn from this blog greater flexibility in what they are attempting to do.&lt;/p&gt;

&lt;p&gt;We will need to define an output class for our transform. We will also need to pay more attention to the attributes assigned to the class. In particular, as we will be expanding a single vector into a multitude of vectors we will need to define an array of floats in order to capture this change.&lt;/p&gt;

&lt;p&gt;If your intent with a transformation is to change a single property into multiple properties then an array is the simplest way to do this. However, if you have specific and consistent transformations in mind then you may also define new properties on your &lt;code&gt;TransformOutput&lt;/code&gt; class. We also can now make some guesses at how the &lt;code&gt;OneHotEncoding&lt;/code&gt; transform works under the hood. (If you remember that discussion from the &lt;a href="https://www.macivortech.com/blog/mlnet-v-python-multiple#a-small-discussion-on-onehotencoding-in-mlnet" rel="noopener noreferrer"&gt;previous entry in this series&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The caveat is that we must know the length of the array so we can put the proper value in the attribute. &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.ml.data.vectortypeattribute?view=ml-dotnet" rel="noopener noreferrer"&gt;Per the documentation&lt;/a&gt;, leaving the argument off of the Vector attribute should allow us to put in an array of unknown length. In practice this does not work and an error will be thrown upon attempting to fit the data.&lt;/p&gt;

&lt;p&gt;For polynomial transforms the array size will be the exponent value plus one. So, if I have an exponent of four, than the array size will be five.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TransformOutput&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;VectorType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Features&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt; 

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create our custom transform. I'd like it to receive the same inputs and produce the same outputs as the python transform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;CustomMappingFactoryAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PolynomialFeatures&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CustomMappingFactory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TransformOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_degree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PolynomialFeatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;degree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_degree&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;degree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TransformOutput&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_degree&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Salary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TransformOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetMapping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this implementation is good enough for our trivial example, but if you start dealing with extremely large numbers you'll need to likely use a better implementation. The requirements are that the transformation must be thread safe and the transformation must not have side effects. So no altering of anything aside from the output parameter.&lt;/p&gt;

&lt;p&gt;I tested my custom transform just to make sure it was good to go. Note that the VectorType array length requirement does not matter until we reach the &lt;code&gt;Fit&lt;/code&gt; method of the pipeline onto the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;polyFeaturesTest&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PolynomialFeatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//array length not required to match VectorType... yet&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testInputs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Level&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testOutputs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testInputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ti&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TransformOutput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;polyFeaturesTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ti&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;testOutput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testOutputs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fmlnet-polynomial-transform-test_hlklt1" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fmlnet-polynomial-transform-test_hlklt1" alt="A table showing the results of the custom transform."&gt;&lt;/a&gt; &amp;lt;!----&amp;gt;&lt;/p&gt;

&lt;p&gt;Now we can create the pipeline. Since we transformed the &lt;code&gt;Level&lt;/code&gt; column and &lt;code&gt;Level&lt;/code&gt; is a tokenized version of &lt;code&gt;Position&lt;/code&gt; I chose to drop both &lt;code&gt;Level&lt;/code&gt; and &lt;code&gt;Position&lt;/code&gt;. I only need the transformed feature to proceed with the regression training.&lt;/p&gt;

&lt;p&gt;For trainer, neither &lt;code&gt;SDCA&lt;/code&gt; or &lt;code&gt;FastTree&lt;/code&gt; do a great job here. In fact, both produce some hilarious results. Here is SDCA for example. (If you do use &lt;code&gt;SDCA&lt;/code&gt; don't forget to set &lt;code&gt;maximumNumberOfIterations&lt;/code&gt; as a parameter for the trainer, otherwise you'll be waiting around for fine minutes.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fsdca_polynomial_fail_hjfzcy" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fsdca_polynomial_fail_hjfzcy" alt="A chart where the SDCA regression curve goes off into the negative trillions."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;So I keep coming back to the &lt;code&gt;Ols&lt;/code&gt; trainer. It works super well with very little configuration! The only problem with it is that it requires a dependency not naturally available on Linux. It's really a shame because it's the closest we get to the easy functionality of the python sklearn &lt;code&gt;LinearRegression&lt;/code&gt; trainer.&lt;/p&gt;

&lt;p&gt;Lucky for you, I do show how to install that dependency on Linux &lt;a href="https://www.macivortech.com/blog/how-to-run-dotnet-on-binder" rel="noopener noreferrer"&gt;while discussing how to get up and running using binder.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyway, here we are training with &lt;code&gt;Ols&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;polyFeatures&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PolynomialFeatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataProcessPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomMapping&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TransformOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;polyFeatures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetMapping&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;contractName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"PolynomialFeatures"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DropColumns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Position"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Level"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concatenate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;

 &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Regression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Trainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ols&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;featureColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;labelColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataProcessPipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then training the data is once again easy in terms of lines of code, but if there is an issue in your pipeline this is where it will manifest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transformedDataView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transformedData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TransformOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;transformedDataView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reuseRowObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transformedData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For fun, I'm also now showing the result of our transformation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fols-train-polynomial_fxvq5m" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fols-train-polynomial_fxvq5m" alt="A table showing transformed data."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And at last we have the results of all of this - it's nearly identical to the python result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fols-result-polynomial_djy2aim" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmacivortech%2Fimage%2Fupload%2Ff_auto%2Cq_auto%2Fv1%2Fscreen-shots%2Fols-result-polynomial_djy2aim" alt="A chart showing a near perfect polynomial regression curve."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;Whew. What a ride. I mean, ML.NET really had me running to figure stuff out!&lt;/p&gt;

&lt;p&gt;But there is value in that. I understand regression in ML.NET a lot better than I did. I read through a lot of their transformer implementation while researching the custom transformer.&lt;/p&gt;

&lt;p&gt;I have questions for the ML.NET maintainers. For example, why not re-implement the sklearn transformers and trainers? They took a way different path that requires a lot more work to use this library. I'll be seeking out an ML.NET engineer to interview to answer these and other questions. We'll see if any of them feel inclined to talk to me. I respect the work they've done here. It does feel like they did not write this to compete with python, or to even do the same things as Python. I can't find any information to corroborate this, but it seems like they had different goals in mind. I'd like perspective on that.&lt;/p&gt;

&lt;p&gt;I am not discouraged though! If anything, this series has inspired me to keep learning deeply about these different implementations. ML.NET is young enough that it has a lot of catching up to do. Don't forget that the scikit-learn (sklearn) repository has been in active development since 2007 while ML.NET was started in 2018.&lt;/p&gt;

&lt;p&gt;I've invited a guest to co-write the next article with me. He's a Java developer and so for the first time we'll be attempting to compare implementations between .NET, Python and Java. We'll be discussing which machine learning task will be the most interesting to compare. If you have an opinion on that then you should definitely let me know via the contact me form.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit: We're going to do it on NLP!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Either way, eventually I'll be talking about Support Vector Regression! So look for that as well.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>dotnet</category>
      <category>python</category>
    </item>
    <item>
      <title>ML.NET and Python Multiple Linear Regression</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 07 Dec 2020 15:11:07 +0000</pubDate>
      <link>https://forem.com/robotoptimist/ml-net-and-python-multiple-linear-regression-1gpp</link>
      <guid>https://forem.com/robotoptimist/ml-net-and-python-multiple-linear-regression-1gpp</guid>
      <description>&lt;h1&gt;
  
  
  ML.NET and Python Multiple Linear Regression
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TVBkCEbc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/heroes/robotmlnet-yes_tfryby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TVBkCEbc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/heroes/robotmlnet-yes_tfryby.png" alt="The ML.NET logo with a robot face next to it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the third in a series talking about the differences in implementation between ML.NET and Python for machine learning tasks. Don't worry though, you should be able to jump right in to read this post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, here are the previous posts if you'd like to take a look.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/take-a-look-at-mlnet"&gt;Take A Look A ML.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/mlnet-v-python-simple"&gt;ML.NET and Python Simple Linear Regression&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to install ML.NET and setup a .NET notebook environment then I explain how to do that in these posts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/take-a-look-at-mlnet"&gt;Take A Look A ML.NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/dotnet-interactive-notebooks"&gt;.NET Interactive Notebooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/how-to-run-dotnet-on-binder"&gt;Run .NET on Binder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.macivortech.com/blog/get-set-up-with-dotnet-and-jupyter-notebooks"&gt;.NET and Jupyter Notebooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Multiple Linear Regression
&lt;/h2&gt;

&lt;p&gt;It is the same thing as simple linear regression but with many more variables.&lt;/p&gt;

&lt;p&gt;Also, data for a successful linear regression must adhere to some core assumptions.&lt;/p&gt;

&lt;p&gt;I list these assumptions out in another &lt;a href="https://www.macivortech.com/blog/assumptions-of-linear-regression"&gt;article&lt;/a&gt;. However, understanding these will not be necessary for the ML.NET vs Python implementation comparison. Our data can be assumed to meet the requirements &lt;em&gt;this time&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data
&lt;/h2&gt;

&lt;h3 id="thank-you-super-data-science-team"&gt;
&lt;span&gt;&lt;/span&gt;Thank you Super Data Science Team&lt;/h3&gt;
&lt;p&gt;This dataset and the Python information comes from &lt;a href="https://www.linkedin.com/in/keremenko/" rel="nofollow noopener noreferrer"&gt;Kirill Eremenko&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/hadelin-de-ponteves-1425ba5b/" rel="nofollow noopener noreferrer"&gt;Hadelin de Ponteves&lt;/a&gt; along with the &lt;a href="https://www.superdatascience.com/" rel="nofollow noopener noreferrer"&gt;Super Data Science Team.&lt;/a&gt;. They provide an excellent quality &lt;a href="https://www.udemy.com/course/machinelearning/learn/lecture/19229340" rel="nofollow noopener noreferrer"&gt;data science course&lt;/a&gt; on Udemy. It is reused here with permission.&lt;/p&gt;

&lt;p&gt;This data represents a bunch of different startups. Our goal is to predict the profit of a startup.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We can see as independent variables we have R&amp;amp;D Spend, Administration, Marketing Spend, and the State in which the startup operates. The dependent variable is the Profit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Linear Regression in Python
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/RobotOptimist/7003f85731e1fa3916c521729581eff8/HEAD?filepath=multiple_linear_regression.ipynb"&gt;Try it for yourself on Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As usual, we start by importing our libraries and reading in the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

dataset = pd.read_csv('50_Startups.csv')
X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, -1].values
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, however, we need to do a data transformation. The State column cannot be represented as a string. Instead we're going to use methods imported from the sklearn library to transform it into three columns, a column for each State. The method that does the majority of the work here is &lt;code&gt;OneHotEncoder&lt;/code&gt;. Now we can represent this data numerically.&lt;/p&gt;

&lt;p&gt;You might wonder why a single column with values of 1, 2, and 3 are not used. The answer is that the ML algorithm will then seek to order those values even though there is no real ordering of States in that way. Making each State it's own column eliminates that bias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
ct = ColumnTransformer(transformers=[('encoder', OneHotEncoder(), [3])], remainder='passthrough')
X = np.array(ct.fit_transform(X))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NqvY_1V8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/multiple_regression_transformed_data_hvytif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NqvY_1V8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/multiple_regression_transformed_data_hvytif" alt="The transformed data resulting from the OneHotEncoder function."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Aside from that small change the steps are now almost identical to simple linear regression.&lt;/p&gt;

&lt;p&gt;We now split into training and test sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We train the model using the exact same function as we used for simple linear regression. The &lt;code&gt;LinearRegression&lt;/code&gt; function is capable of training models for simple and multiple regression.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit(X_train, y_train)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can predict the results as normal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y_pred = regressor.predict(X_test)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets see what the results were. Rather than chart the data we will transform it into two columns. The first column will be the predicted profits and the second will be actual profits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;np.set_printoptions(precision=2)
print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QDyrcnyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/python-multiple-regression_zttgcs" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QDyrcnyu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/python-multiple-regression_zttgcs" alt="An image depicting the predicted and actual data side by side."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the predictions are pretty good while others are just sort of okay. Nevertheless, given the small sample this is a pretty good result for a multiple linear regression model. It's nice that there are few extra steps to train this model in spite of the fact that the data is much more complex relative to simple linear regression.&lt;/p&gt;

&lt;p&gt;In fact, lets calculate the RSquared value as this will be relevant when we look at ML.NET. The RSquared value is a metric we can use to calculate the accuracy of a trainer algorithm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sklearn.metrics import r2_score
print(r2_score(y_test, y_pred))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I6eyZeeP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/python-multiple-regression-r2score_dsyro3" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I6eyZeeP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/python-multiple-regression-r2score_dsyro3" alt="An image depicting generated RSquared score for the model output."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The score for this regression is 0.9347068473282515, which is quite good given that we have a small data set. The goal for an RSquared score should be to get as close to 1 as possible. Scores at .5 or below means the model is more or less guessing for the prediction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Linear Regression in ML.NET
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/RobotOptimist/1adeb410287b0dde2a8bbfd77d38b228/HEAD?filepath=multiple_linear_regression.ipynb"&gt;Try it for yourself on Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right away we start to see some major differences. First off, the algorithm detection experiment from the command-line tool shows some less than encouraging results for the proposed model.&lt;/p&gt;

&lt;p&gt;We can get this output by running the command-line tool against our dataset with this command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mlnet regression --dataset .\50_Startups.csv --label-col Profit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can read more about the command-line tool &lt;a href="https://www.macivortech.com/blog/take-a-look-at-mlnet"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_dAa6Ll5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/regression-commandline-output_n1uacy" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_dAa6Ll5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/regression-commandline-output_n1uacy" alt="Truly terrible metrics shown for the trainer algorithms in ML.NET"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The columns are not labeled at this stage of the output, but they are AlgorithmName, RSquared, Absolute-loss, Squared-loss, RMS-loss, Duration, Iteration. You can read about how to interpret these results from &lt;a href="https://docs.microsoft.com/en-us/dotnet/machine-learning/resources/metrics#evaluation-metrics-for-regression-and-recommendation"&gt;Microsoft's guide.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But here we are getting negative values for our RSquared values and our other metrics are equally bad. So already things are not equivalent to Python. However, the command-line experiment still did narrow down to the top two training algorithms used for regression.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SdcaRegression&lt;/li&gt;
&lt;li&gt;FastTreeRegression&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also, recall that in Python we used OneHotEncoder to transform the State column such that it could be more easily used by the training algorithm. At this stage, it's unclear if ML.NET has done the same transformation - although judging by the analysis output it would appear it has not.&lt;/p&gt;

&lt;p&gt;Lets run a quick and dirty test to see if that column is the cause of our bad data by deleting the state column and running the command-line test again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--36ieyoT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/regression-commandline-output-nostate_ft08dt" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--36ieyoT9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/regression-commandline-output-nostate_ft08dt" alt="I cannot fool you, the metrics shown for these trainers are just awful."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see an improvement in that the negative numbers are smaller - but still not the kind of output we would like.&lt;/p&gt;

&lt;p&gt;This indicates that the dataset is either too small for the mlnet command-line tool to create or that there is some other issue with the data that causes the mlnet command-line experiment to be no better than a guess as it regards making predictions based on this data.&lt;/p&gt;

&lt;p&gt;But lets forgive the command-line tool and move on to see if the actual implementation does better. After all, we took our RSquared score for Python after we had completed a full implementation. I'd like to see parity with they Python sklearn &lt;code&gt;LinearRegression&lt;/code&gt; trainer function.&lt;/p&gt;

&lt;p&gt;Some of these steps are exactly the same as simple linear regression covered in the previous blog post.&lt;/p&gt;

&lt;p&gt;First, we download some NuGet packages and set up our using statements. You'll notice I'm pulling in the NuGet package for the FastTree regression as well - that's because I want to experiment between regression trainers once we establish our test and training datasets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ML.NET Nuget packages installation
#r "nuget:Microsoft.ML,1.5.0"
#r "nuget:Microsoft.ML.Mkl.Components,1.5.0"
#r "nuget:Microsoft.ML.FastTree,1.5.0"
//Install XPlot package
#r "nuget:XPlot.Plotly,2.0.0"    
using Microsoft.ML;    
using Microsoft.ML.Data;
using Microsoft.ML.Trainers.FastTree;
using XPlot.Plotly;
using System;
using System.Linq;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, we set up our classes for input and output. The &lt;code&gt;Score&lt;/code&gt; property for the output is the predicted profit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ModelInput
{
    [ColumnName("R&amp;amp;D Spend"), LoadColumn(0)]
    public float R_D_Spend { get; set; }

    [ColumnName("Administration"), LoadColumn(1)]
    public float Administration { get; set; }

    [ColumnName("Marketing Spend"), LoadColumn(2)]
    public float Marketing_Spend { get; set; }

    [ColumnName("State"), LoadColumn(3)]
    public string State { get; set; }

    [ColumnName("Profit"), LoadColumn(4)]
    public float Profit { get; set; }

}

public class ModelOutput
{
    public float Score { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third, we create the MLContext instance and we read in the &lt;code&gt;.csv&lt;/code&gt; file to create our test and training sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var mlContext = new MLContext(seed: 1);

IDataView trainingDataView = mlContext.Data.LoadFromTextFile&amp;lt;ModelInput&amp;gt;(
    path: @"./50_Startups.csv",
    hasHeader: true,
    separatorChar: ',',
    allowQuoting: true,
    allowSparse: false);

var split = mlContext.Data.TrainTestSplit(trainingDataView, testFraction: 0.2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These first three steps don't change much aside from the properties in the models. But now we start to see some interesting differences.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var dataProcessPipeline = mlContext.Transforms.Categorical.OneHotEncoding(new[] { new InputOutputColumnPair("State", "State") })
                                      .Append(mlContext.Transforms.Concatenate("Features", new[] { "State", "R&amp;amp;D Spend", "Administration", "Marketing Spend" }));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! Just as Python, we're using a tool called &lt;code&gt;OneHotEncoding&lt;/code&gt;. Also, just as Python it transform the encoded columns into multiple columns. However, unlike Python it's more difficult to inspect the data to see what it did.&lt;/p&gt;

&lt;h3 id="a-small-discussion-on-onehotencoding-in-mlnet"&gt;
&lt;span&gt;&lt;/span&gt;A Small Discussion on OneHotEncoding in ML.NET&lt;/h3&gt;
&lt;p&gt;Microsoft does provide a way inspect the transformed data which they go over in their &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.ml.categoricalcatalog.onehotencoding?view=ml-dotnet" rel="nofollow noopener noreferrer"&gt;discussion of the OneHotEncoding function.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The short of it is you must transform your model back into a DataView using a transform method. Then you can use the MLContext to convert the IDataView into an IEnumerable and inspect it as you would any list.&lt;/p&gt;
&lt;p&gt;Here is the useful example from the Microsoft documentation. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;var&lt;/span&gt;&lt;/span&gt; mlContext &lt;span&gt;=&lt;/span&gt; &lt;span&gt;new&lt;/span&gt; &lt;span&gt;MLContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;// Create a small dataset as an IEnumerable.&lt;/span&gt;
&lt;span&gt;&lt;span&gt;var&lt;/span&gt;&lt;/span&gt; samples &lt;span&gt;=&lt;/span&gt; &lt;span&gt;new&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
&lt;span&gt;{&lt;/span&gt;
    &lt;span&gt;new&lt;/span&gt; &lt;span&gt;DataPoint&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;Education &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"0-5yrs"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; ZipCode &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"98005"&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;new&lt;/span&gt; &lt;span&gt;DataPoint&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;Education &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"0-5yrs"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; ZipCode &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"98052"&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;new&lt;/span&gt; &lt;span&gt;DataPoint&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;Education &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"6-11yrs"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; ZipCode &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"98005"&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;new&lt;/span&gt; &lt;span&gt;DataPoint&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;Education &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"6-11yrs"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; ZipCode &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"98052"&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
    &lt;span&gt;new&lt;/span&gt; &lt;span&gt;DataPoint&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;Education &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"11-15yrs"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; ZipCode &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"98005"&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;
&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;// Convert training data to IDataView.&lt;/span&gt;
&lt;span&gt;IDataView&lt;/span&gt; data &lt;span&gt;=&lt;/span&gt; mlContext&lt;span&gt;.&lt;/span&gt;Data&lt;span&gt;.&lt;/span&gt;&lt;span&gt;LoadFromEnumerable&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;samples&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;// Multi column example: A pipeline for one hot encoding two columns&lt;/span&gt;
&lt;span&gt;// 'Education' and 'ZipCode'.&lt;/span&gt;
&lt;span&gt;&lt;span&gt;var&lt;/span&gt;&lt;/span&gt; multiColumnKeyPipeline &lt;span&gt;=&lt;/span&gt;
    mlContext&lt;span&gt;.&lt;/span&gt;Transforms&lt;span&gt;.&lt;/span&gt;Categorical&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OneHotEncoding&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;
        &lt;span&gt;new&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;
        &lt;span&gt;{&lt;/span&gt;
            &lt;span&gt;new&lt;/span&gt; &lt;span&gt;InputOutputColumnPair&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"Education"&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
            &lt;span&gt;new&lt;/span&gt; &lt;span&gt;InputOutputColumnPair&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"ZipCode"&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
        &lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;// Fit and Transform data.&lt;/span&gt;
&lt;span&gt;IDataView&lt;/span&gt; transformedData &lt;span&gt;=&lt;/span&gt;
    multiColumnKeyPipeline&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Fit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;data&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Transform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;data&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;&lt;span&gt;var&lt;/span&gt;&lt;/span&gt; convertedData &lt;span&gt;=&lt;/span&gt;
    mlContext&lt;span&gt;.&lt;/span&gt;Data&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;span&gt;CreateEnumerable&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;TransformedData&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;transformedData&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

Console&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;
    &lt;span&gt;"One Hot Encoding of two columns 'Education' and 'ZipCode'."&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;// One Hot Encoding of two columns 'Education' and 'ZipCode'.&lt;/span&gt;

&lt;span&gt;foreach&lt;/span&gt; &lt;span&gt;(&lt;/span&gt;&lt;span&gt;TransformedData&lt;/span&gt; item &lt;span&gt;in&lt;/span&gt; convertedData&lt;span&gt;)&lt;/span&gt;
    Console&lt;span&gt;.&lt;/span&gt;&lt;span&gt;WriteLine&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;"{0}\t\t\t{1}"&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;string&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;" "&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; item&lt;span&gt;.&lt;/span&gt;Education&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;
        &lt;span&gt;string&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;" "&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; item&lt;span&gt;.&lt;/span&gt;ZipCode&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;

&lt;span&gt;// 1 0 0 1 0&lt;/span&gt;
&lt;span&gt;// 1 0 0 0 1&lt;/span&gt;
&lt;span&gt;// 0 1 0 1 0&lt;/span&gt;
&lt;span&gt;// 0 1 0 0 1&lt;/span&gt;
&lt;span&gt;// 0 0 1 1 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's a lot more work to view this data in C#, but there are also few reasons you'd want to inspect this data in a production scenario.&lt;/p&gt;
&lt;p&gt;It's also good they include an example like ZipCode. Thinking about how this &lt;code&gt;OneHotEncoding&lt;/code&gt; works and ZipCodes - you might imagine a column with hundreds of unique ZipCodes. Would this mean that &lt;code&gt;OneHotEncoding&lt;/code&gt; generates hundreds of columns, one for each ZipCode?&lt;/p&gt;
&lt;p&gt;Yes. It would. However, you can configure the transform to generate a key encoded column instead, which would encode the data into a single column (and therefore take on the drawbacks of that transform, a potentially less accurate model). &lt;/p&gt;
&lt;p&gt;In the case of ZipCode, when there are hundreds of unique entries, why would you wish to include this data? Might you be better off without it? Or could you transform it into something less granular such as State, Province, County, Shire or what have you. &lt;/p&gt;
&lt;p&gt;ML.NET and Python both provide wonderful tooling for handling data such as this, but ultimately it's still up to us to make reasonable decisions about each data point.&lt;/p&gt;

&lt;p&gt;Let's get to training our model!&lt;/p&gt;

&lt;p&gt;I ran the mlnet command-line tool multiple times and ultimately the FastTree regression trainer consistently had the least worst RSquared Score. It reached an RSquared Score in the -800s. Abysmal, but still better than before. In the generated project, we see this is how the regression trainer is added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var trainer = mlContext.Regression.Trainers.FastTree(new FastTreeRegressionTrainer.Options() { NumberOfLeaves = 2, MinimumExampleCountPerLeaf = 10, NumberOfTrees = 100, LearningRate = 0.06297909f, Shrinkage = 0.0986204f, LabelColumnName = "Profit", FeatureColumnName = "Features" });

var trainingPipeline = dataProcessPipeline.Append(trainer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then train our model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var trainingPipeline = dataProcessPipeline.Append(trainer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we can perform an evaluation of the trainer. What's the RSquared score now? Keep in mind, RSquared is not the only metric we can evaluate - but it's still a really good one for determining accuracy.&lt;/p&gt;

&lt;p&gt;So lets take a look. Here's the code to generate an analysis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Console.WriteLine("=============== Cross-validating to get model's accuracy metrics ===============");
var crossValidationResults = mlContext.Regression.CrossValidate(split.TrainSet, trainingPipeline, numberOfFolds: 5, labelColumnName: "Profit");
var L1 = crossValidationResults.Select(r =&amp;gt; r.Metrics.MeanAbsoluteError);
var L2 = crossValidationResults.Select(r =&amp;gt; r.Metrics.MeanSquaredError);
var RMS = crossValidationResults.Select(r =&amp;gt; r.Metrics.RootMeanSquaredError);
var lossFunction = crossValidationResults.Select(r =&amp;gt; r.Metrics.LossFunction);
var R2 = crossValidationResults.Select(r =&amp;gt; r.Metrics.RSquared);

Console.WriteLine($" *************************************************************************************************************");
Console.WriteLine($"* Metrics for Regression model ");
Console.WriteLine($"*------------------------------------------------------------------------------------------------------------");
Console.WriteLine($"* Average L1 Loss: {L1.Average():0.###} ");
Console.WriteLine($"* Average L2 Loss: {L2.Average():0.###} ");
Console.WriteLine($"* Average RMS: {RMS.Average():0.###} ");
Console.WriteLine($"* Average Loss Function: {lossFunction.Average():0.###} ");
Console.WriteLine($"* Average R-squared: {R2.Average():0.###} ");
Console.WriteLine($" *************************************************************************************************************");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JYJJmBFe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/mlnet-least-bad-scores_hxseja" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JYJJmBFe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/mlnet-least-bad-scores_hxseja" alt="Shows metrics for the FastTree trainer that are awful, but not as awful as the commandline report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These metrics are still truly awful. Should you actually make predictions on the test set you would see it really is no better than a wild guess at this point. It's not even close to the actual profit.&lt;/p&gt;

&lt;p&gt;However, we see something encouraging. The actual implementation does improve the score. RSquared is now a mere -2.853.&lt;/p&gt;

&lt;p&gt;It seems at a certain point we cannot trust our mlnet command-line experiments.&lt;/p&gt;

&lt;p&gt;After some manual experimentation with some of the regression trainers I was able to generate this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var dataProcessPipeline = mlContext.Transforms.Categorical.OneHotEncoding(new[] { new InputOutputColumnPair("State", "State") })
                                      .Append(mlContext.Transforms.Concatenate("Features", new[] { "State", "R&amp;amp;D Spend", "Administration", "Marketing Spend" }));

var trainer = mlContext.Regression.Trainers.Ols(labelColumnName: "Profit", featureColumnName: "Features");
var trainingPipeline = dataProcessPipeline.Append(trainer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's right, good old &lt;code&gt;Ols&lt;/code&gt; regression trainer is back again. It did great for the simple linear regression and it continues to perform well enough for the multiple linear regression example.&lt;/p&gt;

&lt;p&gt;It evaluated to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OWjoMw8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/mlnet-pretty-much-okay-r2_wozbso" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OWjoMw8v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/mlnet-pretty-much-okay-r2_wozbso" alt="Shows metrics of the Ols trainer that are pretty much okay."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have an RSquared score of 0.909! This is more or less okay I guess! It's still not as good as the RSquared of the &lt;code&gt;LinearRegression&lt;/code&gt; algorithm from sklearn, but we're much closer.&lt;/p&gt;

&lt;p&gt;Now if you compare the predicted profit to the actual profit like so -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var predEngine = mlContext.Model.CreatePredictionEngine&amp;lt;ModelInput, ModelOutput&amp;gt;(model);
var testSet = mlContext.Data.CreateEnumerable&amp;lt;ModelInput&amp;gt;(split.TestSet, reuseRowObject:false);
foreach (var ts in testSet) 
{
    var testInput = new ModelInput() 
    {
        State = ts.State,
        Marketing_Spend = ts.Marketing_Spend,
        R_D_Spend = ts.R_D_Spend,
        Administration = ts.Administration,
        Profit = 0.0F
    };
    var prediction = predEngine.Predict(testInput).Score;
    var actual = ts.Profit;
    Console.WriteLine($"Prediction: {prediction}, Actual: {actual}");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see this -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GKQOJKbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/mlnet-side-by-side-profits_s15hne" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GKQOJKbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/macivortech/image/upload/f_auto%2Cq_auto/v1/screen-shots/mlnet-side-by-side-profits_s15hne" alt="Shows predicted and actual profits side by side"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Not bad! Pretty close in some instances! This is a pretty fair prediction of potential profits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;It would be easy, and perhaps accurate, to believe that Python is the better tool for this task.&lt;/p&gt;

&lt;p&gt;This is misleading though because the Python steps come from a Udemy course. The data and process were tailored to work well with that ecosystem.&lt;/p&gt;

&lt;p&gt;What are we really seeing here then?&lt;/p&gt;

&lt;p&gt;ML.NET could still use this data and create predictions that were comparable to the Python predictions. Moreover, the tooling with ML.NET set me on a path where I could figure out for myself which training algorithm to use. The command-line tool did not hand me the answers right away, as it did with simple linear regression - but it set me on a path to figure it out.&lt;/p&gt;

&lt;p&gt;Still, the sheer simplicity and ease of use from Python cannot be refuted. In Python's sklearn, we are not hunting for a linear regression training algorithm, there is only one: &lt;code&gt;LinearRegression()&lt;/code&gt;. The parameters for that function are documented and intuitive.&lt;/p&gt;

&lt;p&gt;With ML.NET, I'm reminded of the game Dwarf Fortress. Dwarf Fortress is well known for it's steep learning curve, but many players find the process of learning fun and rewarding. The value is there, hidden under trial and error. Success is found after a series of hilarious trials and failures.&lt;/p&gt;

&lt;p&gt;Also with ML.NET, we also come away with a deeper understanding of what's going on under the hood. As we continue to use it we see opportunities to tweak and change the process to produce better results.&lt;/p&gt;

&lt;p&gt;The Python libraries hand us success first and allow us to learn deeply later.&lt;/p&gt;

&lt;p&gt;From a business perspective, Python is the clear favorite, at least in the short term. The business value will be successful predictions from a process created as cheaply as possible. However, in the long term a business would benefit from type safety and the resulting easier maintenance.&lt;/p&gt;

&lt;p&gt;We'll see how ML.NET performs next to Python in my next post which will be looking at the Polynomial regression.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>python</category>
      <category>dotnet</category>
      <category>mlnet</category>
    </item>
    <item>
      <title>VSCode .NET Interactive Notebooks</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 30 Nov 2020 14:18:06 +0000</pubDate>
      <link>https://forem.com/robotoptimist/vscode-net-interactive-notebooks-21g5</link>
      <guid>https://forem.com/robotoptimist/vscode-net-interactive-notebooks-21g5</guid>
      <description>&lt;h1&gt;
  
  
  VSCode .NET Interactive Notebooks
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" alt="The ML.NET logo with a robot face next to it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Jupyter Notebooks Falls Short
&lt;/h2&gt;

&lt;p&gt;Do you love intellisense? I do.&lt;/p&gt;

&lt;p&gt;Which is why working in Jupyter Notebooks can be so frustrating. Part of the benefits of a strongly typed language is to enable tooling that can make us more efficient developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  VS Code Extension
&lt;/h2&gt;

&lt;p&gt;Well, lucky for us the folks who develop .NET Interactive love and support us. They have created an extension for VSCode called &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode"&gt;.NET Interactive Notebooks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now before you go and download this extension be aware that it is in &lt;em&gt;preview&lt;/em&gt; and only will work with &lt;a href="https://code.visualstudio.com/insiders/"&gt;Visual Studio Code Insiders&lt;/a&gt;. This is just a fancy way of saying that it's in active development and stuff will be broken.&lt;/p&gt;

&lt;p&gt;That said, lets take a look at how well it works at the moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup Experience
&lt;/h3&gt;

&lt;p&gt;In order to get Visual Studio Code to set everything up properly you'll need to attempt to open a .ipynb file.&lt;/p&gt;

&lt;p&gt;You can use the one from my gist if you don't have another one handy:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You'll be prompted to install the Python extension. Also, if you had the .ipynb file open when Python installed then you may need to close and reopen it. VSCode will also handle installing .NET Interactive in the background. In fact, VSCode is doing so much for me automatically it was a pretty nice experience to get setup.&lt;/p&gt;

&lt;p&gt;Once everything is setup you should see the notebook render properly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mOVHRUb0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ipynb_in_vscode.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mOVHRUb0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ipynb_in_vscode.png" alt="A screenshot of an interactive notebook."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;So, that's a fairly friendlier experience than Jupyter Notebooks - but do I get my intellisense?!&lt;/p&gt;

&lt;p&gt;The answer is... I do!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4M-mYCtr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/intellisense_yay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4M-mYCtr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/intellisense_yay.png" alt="A screenshot of an interactive notebook."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;So I know how &lt;em&gt;I'll&lt;/em&gt; be writing my .NET notebooks from now on!&lt;/p&gt;

&lt;p&gt;But if you still prefer Jupyter Notebooks with .NET then &lt;a href="https://www.macivortech.com/blog/get-set-up-with-dotnet-and-jupyter-notebooks"&gt;here is the guide to get set up with that too.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>dotnet</category>
      <category>vscode</category>
      <category>notebooks</category>
    </item>
    <item>
      <title>Share Your Dotnet Notebooks on Binder</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 23 Nov 2020 17:32:24 +0000</pubDate>
      <link>https://forem.com/robotoptimist/share-your-dotnet-notebooks-on-binder-274e</link>
      <guid>https://forem.com/robotoptimist/share-your-dotnet-notebooks-on-binder-274e</guid>
      <description>&lt;h1&gt;
  
  
  Share Your .NET Notebooks on Binder
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" alt="The ML.NET logo with a robot face next to it."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Binder?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/"&gt;Binder&lt;/a&gt; is a free online service that allows you to run your jupyter notebooks with other people. It's similar to &lt;a href="https://colab.research.google.com"&gt;Google Colab&lt;/a&gt; but does not require a Google login. In fact, all it requires is a public repository with a &lt;code&gt;.ipynb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It's a wonderful service and supports many languages right out of the box. To spin up a Python notebook you only need a simple &lt;code&gt;requirements.txt&lt;/code&gt; file. If you intend to use R or Julia then an &lt;code&gt;environment.yml&lt;/code&gt; file will do. You can follow Binder's guides to get this up and running.&lt;/p&gt;

&lt;p&gt;Setup can be a little more difficult in .NET.&lt;/p&gt;

&lt;p&gt;So, I'm happy to provide you with this base docker image: &lt;a href="https://hub.docker.com/layers/127236981/jmacivor/dotnet-binder/0.1.1/images/sha256-095b5f0245b905d1e9fcce399510198fd98f5b3445d362126453cf3ac526f908?context=explore&amp;amp;tab=layers"&gt;jmacivor/dotnet-binder:0.1.1&lt;/a&gt; which will provide you with the .NET SDK as well as everything necessary to work on Binder. You can then copy in your notebooks and related files and run them easily.&lt;/p&gt;

&lt;p&gt;If you are new to Docker you can set up a file similar to what I've created below. Again, you just need something like this in the root of the repository containing your &lt;code&gt;*.ipynb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Binder allows you to create shared notebooks from all kinds of repositories, notably from gists and github. You can setup a new github or gist. As long as you have a Dockerfile where you copy in a notebook then you're good to go!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM jmacivor/dotnet-binder:0.1.1

ARG NB_USER=jovyan
ARG NB_UID=1000

USER $NB_USER

ENV HOME=/home/$NB_USER

WORKDIR $HOME

COPY ml_net_simple_regression.ipynb $HOME/ml_net_simple_regression.ipynb
COPY Salary_Data.csv $HOME/Salary_Data.csv

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checkout the &lt;a href="https://gist.github.com/RobotOptimist/1bfd719dc621af45a0e633ffa7ecb9ec"&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you would like to design and test your notebook locally then follow &lt;a href="https://www.macivortech.com/blog/get-set-up-with-dotnet-and-jupyter-notebooks"&gt;my guide for getting setup with Jupyter Notebooks.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took a bit of effort to create the base image used in the above Dockerfile. If you're curious about what all went into it then check it out below. There is definitely room for improvement.&lt;/p&gt;

&lt;p&gt;This base image would benefit from the following changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Switch to dotnet alpine SDK in order to reduce the size of the overall image.&lt;/li&gt;
&lt;li&gt;Find a way to download and cache the nuget packages for ML.NET so that users are not forced to download them from the notebook.&lt;/li&gt;
&lt;li&gt;Upgrade the .NET base image to .NET 5 as soon as the dotnet-interactive tool is ready for .NET 5. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the &lt;a href="https://gist.github.com/RobotOptimist/818873bd61e03a3c934d79d7612e4107"&gt;gist&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM mcr.microsoft.com/dotnet/core/sdk:3.1-focal

USER root
RUN cd /tmp
# now get the key:
RUN wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB
# now install that key
RUN apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB
# now remove the public key file exit the root shell
RUN rm GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB

# we have to get this because the linux mlnet nuget expects a dependency that only ships with windows.
# so we add the public key as shown above and apt-get install intel-mkl-64bit-2020.0.088 
RUN sh -c 'echo deb https://apt.repos.intel.com/mkl all main &amp;gt; /etc/apt/sources.list.d/intel-mkl.list'

RUN apt-get update \
    &amp;amp;&amp;amp; apt-get -y upgrade \
    &amp;amp;&amp;amp; apt-get -y install python3 python3-pip python3-dev ipython3 intel-mkl-64bit-2020.0-088

RUN find /opt -name "libiomp5.so"
RUN ldconfig /opt/intel/compilers_and_libraries_2020.0.166/linux/compiler/lib/intel64_lin/

RUN pip3 install --no-cache notebook

ARG NB_USER=jovyan
ARG NB_UID=1000
RUN useradd -m -s /bin/bash -N -u $NB_UID $NB_USER

USER $NB_USER

ENV HOME=/home/$NB_USER

WORKDIR $HOME

ENV PATH="${PATH}:$HOME/.dotnet/tools/"

RUN dotnet tool install -g --add-source "https://dotnet.myget.org/F/dotnet-try/api/v3/index.json" dotnet-interactive

RUN dotnet-interactive jupyter install

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, dotnet-interactive has their own &lt;a href="https://github.com/dotnet/interactive/blob/main/Dockerfile"&gt;Dockerfile for Binder.&lt;/a&gt; They go a completely different direction where they start from a base image made for Binder and then install the .NET dependencies and .NET from there. I did try this approach, but found a bunch of problems along the way. Still, you might enjoy trying it this way. The time to start up the Binder server appears to be about the same regardless of which approach you use.&lt;/p&gt;

</description>
      <category>notebooks</category>
      <category>dotnet</category>
      <category>jupyternotebooks</category>
      <category>binder</category>
    </item>
    <item>
      <title>ML.NET and Python Simple Regression</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 23 Nov 2020 16:27:49 +0000</pubDate>
      <link>https://forem.com/robotoptimist/ml-net-and-python-simple-regression-k0</link>
      <guid>https://forem.com/robotoptimist/ml-net-and-python-simple-regression-k0</guid>
      <description>&lt;h1&gt;
  
  
  ML.NET and Python Simple Regression
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" alt="The ML.NET logo with a robot face next to it."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Why even bother with ML.NET
&lt;/h2&gt;

&lt;p&gt;ML.NET has been around for a few years now, and yet the community excitement has been muted. Don't get me wrong, it definitely exists - but on the whole the sentiment is that this has been solved in other languages so why bother with ML.NET.&lt;/p&gt;

&lt;p&gt;This is valid, but it ignores the many companies where the outcomes that can be achieved from machine learning are a goal, but they don't necessarily want to start creating and maintaining a bunch of Python projects. Companies that have invested in the .NET stack should look to ML.NET for as an alternative - but how close is it?&lt;/p&gt;

&lt;p&gt;I'll be examining that in a series of blogs.&lt;/p&gt;

&lt;p&gt;First things first, get set up with Jupyter Notebooks and the .NET runtime. &lt;a href="https://www.macivortech.com/blog/get-set-up-with-dotnet-and-jupyter-notebooks"&gt;Setup Jupyter Notebooks and .NET&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you're all set up, lets take a look at the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data
&lt;/h2&gt;

&lt;h3 id="thank-you"&gt;
&lt;span&gt;&lt;/span&gt;Thank you&lt;/h3&gt;
&lt;p&gt;First this dataset and the Python information comes from is from &lt;a href="https://www.linkedin.com/in/keremenko/" rel="nofollow noopener noreferrer"&gt;Kirill Eremenko&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/hadelin-de-ponteves-1425ba5b/" rel="nofollow noopener noreferrer"&gt;Hadelin de Ponteves&lt;/a&gt; along with the &lt;a href="https://www.superdatascience.com/" rel="nofollow noopener noreferrer"&gt;Super Data Science Team.&lt;/a&gt;. They provide an excellent quality &lt;a href="https://www.udemy.com/course/machinelearning/learn/lecture/19229340" rel="nofollow noopener noreferrer"&gt;data science course&lt;/a&gt; on Udemy.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="///data_files/Salary_Data.csv"&gt;data:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This data has two columns, years of experience and salary. The data suggests that there is a relationship between these two things - so we should be able to do a simple regression to predict a salary based on the years of experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Regression in Python
&lt;/h2&gt;

&lt;p&gt;First - how do we do this in Python?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/RobotOptimist/4186084f5fbd698c7ba5358e74d5a30e/master?filepath=simple_linear_regression.ipynb"&gt;Try it for yourself on Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First we need to import the necessary files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pandas will read in the data from the csv file while matplotlib will do the work of creating some graphs for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Salary_Data.csv'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is wonderful simplicity. The dataset is split into x and y axes using simple array syntax.&lt;/p&gt;

&lt;p&gt;Next we import a function from sklearn in order to split the dataset into train and test sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we train the model. Again we use sklearn and import the simply named LinearRegression class in order to train the regressor model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sklearn.linear_model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LinearRegression&lt;/span&gt;
&lt;span class="n"&gt;regressor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LinearRegression&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;regressor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can predict the test set results and store them into a variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;y_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regressor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can use the matplotlib library to visualize the results from our training set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regressor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Salary vs Experience (Training set)'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Years of Experience'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Salary'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3SC8F4tL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/simple_regression_training_visualization.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3SC8F4tL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/simple_regression_training_visualization.png" alt="A chart showing a scatter plot of the training data and a regression line."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And finally, as we have the test prediction results we can now put those into a visualization and compare them to the actual data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'red'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;regressor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Salary vs Experience (Test set)'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xlabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Years of Experience'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ylabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Salary'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F65a0rYl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/simple_regression_testing_visualization.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F65a0rYl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/simple_regression_testing_visualization.png" alt="A chart showing a scatter plot of the training data and a regression line."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Regression in ML.NET
&lt;/h2&gt;

&lt;p&gt;And now how do we do the same simple regression in ML.NET?&lt;/p&gt;

&lt;p&gt;You will notice some major differences here. ML.NET throws us a little further into the deep end. There are tools and support, but it's still a steeper learning curve than Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mybinder.org/v2/gist/RobotOptimist/1bfd719dc621af45a0e633ffa7ecb9ec/master?filepath=ml_net_simple_regression.ipynb"&gt;Try it for yourself on Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First things first, you have to install the nuget packages and set up the using statements. From a practical standpoint this is almost identical to Python. Jupyter Notebook has a special way to get a nuget package to download in a code block. All that has to be done is setting up a &lt;code&gt;#r&lt;/code&gt; followed by &lt;code&gt;nuget:&lt;/code&gt; and then the full name of the nuget package followed by a comma and the version desired. See below to see an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ML.NET Nuget packages installation&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s"&gt;"nuget:Microsoft.ML,1.5.0"&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s"&gt;"nuget:Microsoft.ML.Mkl.Components,1.5.0"&lt;/span&gt;
&lt;span class="c1"&gt;//Install XPlot package&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="s"&gt;"nuget:XPlot.Plotly,2.0.0"&lt;/span&gt;    
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.ML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.ML.Data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;XPlot.Plotly&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works just fine when running from a local Jupyter Notebook, but on Binder the nuget download has to be attempted twice and always works on the second time.&lt;/p&gt;

&lt;p&gt;Now, unlike Python where we could immediately pull in the data we have to setup some classes. As C# is a strongly typed language we will have classes to model the data we'll be working with. This is a bit more effort for setup, but placed me squarely in my comfort zone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelInput&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ColumnName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YearsExperience"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;LoadColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;YearsExperience&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ColumnName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;LoadColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Salary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelOutput&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;    
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;Score&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've defined our classes we can move forward with loading in the data. ML.NET uses a class called MLContext in order to load in data and perform operations on it. This class is a sort of combination of pandas and sklearn from Python. MLContext has a property called Data that can load the data from the csv file. It can also split the data up for us into training and test sets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;IDataView&lt;/span&gt; &lt;span class="n"&gt;trainingDataView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadFromTextFile&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                                            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"./Salary_Data.csv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                            &lt;span class="n"&gt;hasHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                            &lt;span class="n"&gt;separatorChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sc"&gt;','&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                            &lt;span class="n"&gt;allowQuoting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                            &lt;span class="n"&gt;allowSparse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrainTestSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testFraction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//split.TrainSet&lt;/span&gt;
&lt;span class="c1"&gt;//split.TestSet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Python has the matplotlib library which is a wonderful and versatile charting library. .NET has several libraries that can do similar things but require different contexts and setup. Luckily, &lt;a href="https://fslab.org/XPlot/plotly.html"&gt;FsLab&lt;/a&gt; has created something that works very well on Jupyter Notebooks called XPlot.&lt;/p&gt;

&lt;p&gt;Here we see how to model the training data and create a scatter plot. Matplotlib can also create a regression line with only a few arguments but XPlot requires a bit more setup as we will see later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;years&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrainSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetColumn&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"YearsExperience"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrainSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetColumn&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;yearsChart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scatter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;   
   &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;years&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"markers"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;yearsChart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Years Vs Salary"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;yearsChart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mVCHrgrL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ml_net_simple_regression_scatter_plot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mVCHrgrL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ml_net_simple_regression_scatter_plot.png" alt="A chart showing a scatter plot of the training data."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now at last we can create the pipeline which will allow us to train the model. A big difference here is the algorithm used. Where Python had the aptly named LinearRegression() class to handle this, we have a different experience with ML.NET. We are confronted with a bunch of confusingly named choices. For this, I chose to use the mlnet command line tool to analyze the dataset and and select the appropriate algorithm for me. This time it chose the &lt;code&gt;Ols()&lt;/code&gt; algorithm. The command line tool does print comparitive metrics for each algorithm tested so you can view for yourself why it selected whichever algorithm it did.&lt;/p&gt;

&lt;p&gt;I go over the command line tool a bit more in &lt;a href="https://dev.to/blog/take-a-look-at-mlnet"&gt;this post.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, unlike Python, you still have to define features even for this simple case in ML.NET.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataProcessPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concatenate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"YearsExperience"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
                          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NormalizeMinMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// Set the training algorithm &lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Regression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Trainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ols&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;labelColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;featureColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Build training pipeline&lt;/span&gt;
&lt;span class="n"&gt;IEstimator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITransformer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataProcessPipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can train our model. Python and ML.NET make this step almost comically easy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ITransformer&lt;/span&gt; &lt;span class="n"&gt;mlModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrainSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also now evaluate our pipeline. ML.NET has a method that will iterate over the validations a set amount of times (&lt;code&gt;numberOfFolds&lt;/code&gt;) and then display the averaged results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;crossValidationResults&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Regression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CrossValidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numberOfFolds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;labelColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;L1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crossValidationResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MeanAbsoluteError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;L2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crossValidationResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MeanSquaredError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;RMS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crossValidationResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RootMeanSquaredError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;lossFunction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crossValidationResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LossFunction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;R2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crossValidationResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RSquared&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$" *************************************************************************************************************"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"* Metrics for Regression model "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"*------------------------------------------------------------------------------------------------------------"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"* Average L1 Loss: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;L1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Average&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="m"&gt;0.&lt;/span&gt;&lt;span class="err"&gt;###&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"* Average L2 Loss: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;L2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Average&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="m"&gt;0.&lt;/span&gt;&lt;span class="err"&gt;###&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"* Average RMS: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;RMS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Average&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="m"&gt;0.&lt;/span&gt;&lt;span class="err"&gt;###&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"* Average Loss Function: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lossFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Average&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="m"&gt;0.&lt;/span&gt;&lt;span class="err"&gt;###&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"* Average R-squared: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;R2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Average&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="m"&gt;0.&lt;/span&gt;&lt;span class="err"&gt;###&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$" *************************************************************************************************************"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bTzWPiS6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ml_net_simple_regression_evaluation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bTzWPiS6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ml_net_simple_regression_evaluation.png" alt="A chart showing a scatter plot of the training data."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now we can test our model and try some predictions. I believe there are more efficient ways to perform predictions over a bulk dataset than what I have done - but I'm familiar with Linq so I chose to use that to perform my predictions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatePredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;mlModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;enumerableTestSet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reuseRowObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;YearsExperience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YearsExperience&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;         
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;preductionResults&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enumerableTestSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;predEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predictionResult&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;preductionResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"\n\nPredicted Salary: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;predictionResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\n\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets put all of this into a chart next to the actual data. (At last, a regression line!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testYears&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enumerableTestSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YearsExperience&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;actualSalaries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reuseRowObject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Salary&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predictedSalaries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;preductionResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scatter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testYears&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;actualSalaries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"markers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Actual"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predicted&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scatter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testYears&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;predictedSalaries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Predicted"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;predicted&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="n"&gt;barmode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithXTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Years"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithYTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Salary"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithLegend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JZMkyM8S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ml_net_simple_regression_plot_and_line.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JZMkyM8S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/ml_net_simple_regression_plot_and_line.png" alt="A chart showing a scatter plot of the training data."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts
&lt;/h2&gt;

&lt;p&gt;This was an interesting exercise to see the two languages / libraries side by side and evaluate each approach. However, this exercise was too simple to really draw any solid conclusions. I'll be looking at a multiple linear regression next to see if a more complex example can reveal more about these different implementations.&lt;/p&gt;

&lt;p&gt;C# is my comfort zone. I felt more secure working with the language. However, in the context of Jupyter Notebooks, C# is pretty rough, and oddly enough, based on the discussion and blog posts I read while researching, it rides in on the coattails on F#. I did a little extra to help out anyone who comes after me who wants to use Binder. &lt;a href="https://www.macivortech.com/blog/how-to-run-dotnet-on-binder"&gt;Here's my How To Guide for .NET with Binder&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That said there are features of both implementations that I like. Python requires a lot less code to accomplish the same tasks. However the mlnet command line tool is amazing and makes it all so easy to spin up a project. We'll see where a more complicated task takes us in my next post.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>mlnet</category>
    </item>
    <item>
      <title>Kendo UI - Quick Guide to Grids</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Fri, 30 Oct 2020 13:26:46 +0000</pubDate>
      <link>https://forem.com/robotoptimist/kendo-ui-quick-guide-to-grids-l5c</link>
      <guid>https://forem.com/robotoptimist/kendo-ui-quick-guide-to-grids-l5c</guid>
      <description>&lt;h1&gt;
  
  
  Kendo UI - Quick Guide to Grids
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kxyZmCHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/kendoui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kxyZmCHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/kendoui.png" alt="Kendo mascot alongside text stating Kendo UI: The Art of Web Development."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You may not have heard of Kendo UI and that's okay. They're yet another one of &lt;a href="https://www.telerik.com/"&gt;Telerik's&lt;/a&gt; products, right up there with Sitefinity. The main thing you need to know is that Kendo UI works great as long as you stay strictly within Telerik's suggested use cases. It becomes a nightmare as soon as you attempt something outside of that. I'm speaking directly about the ASP.NET Core HtmlHelpers. The jQuery, Vue and other versions may work differently.&lt;/p&gt;

&lt;p&gt;Perhaps one of the most useful Kendo features is the Grid. The grid is a table with sorting, paging and filtering all built in. It can also hook into your MVC endpoints to read, update and delete that data. Kendo in .NET Core uses jQuery Ajax methods to talk to those endpoints. (Telerik loves jQuery dependencies.)&lt;/p&gt;

&lt;p&gt;Grids are strongly typed from your Model as well. They can easily create columns based on your model properties and it's also easy to create additional columns. In fact, Grids are pretty damn awesome as long as you adhere to the strict method of creating them.&lt;/p&gt;

&lt;p&gt;Here is some code to get an example grid up and running (This would go inside of a div element on a cshtml page.):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@(Html.Kendo().Grid&amp;lt;MyModel&amp;gt;()
.Name("MyGrid")
.Columns(columns =&amp;gt;
{
    columns.Command(c =&amp;gt; c.Destroy()).Width(50).Title("Command");
    columns.Bound(i =&amp;gt; i.Id).Width(50).MinResizableWidth(50).Title("Id");
    columns.Bound(i =&amp;gt; i.Name).Width(100).MinResizableWidth(100).Title("Name");
    columns.Bound(i =&amp;gt; i.Status).Width(100).MinResizableWidth(100).Title("Status");                
})
.Editable(editable =&amp;gt; editable.Mode(GridEditMode.InLine).DisplayDeleteConfirmation(false))
.HtmlAttributes(new { style = "height: 550px;" })
.Scrollable()
.Groupable()
.Sortable()
.Pageable()
.Filterable()
.Resizable(resize =&amp;gt; resize.Columns(true))
.DataSource(dataSource =&amp;gt; dataSource
    .Custom()
    .Schema(s =&amp;gt; s
        .Parse(@&amp;lt;text&amp;gt; 
        function(data) {                        
            if (!data || typeof(data) === 'string' ) 
                data = [{ Id: "N/A", Name: "NA", Status: "N/A" }]
            return data;
        }
        &amp;lt;/text&amp;gt;)
        .Model(m =&amp;gt;
        {
            m.Id(p =&amp;gt; p.Id);
        }))                    
    .Transport(t =&amp;gt;
    {
        t.Read(r =&amp;gt; r.Action("GetMyModels", "ControllerName"));
        t.Destroy(d =&amp;gt; d.Action("DeleteAModel", "ControllerName"));
    })
    .PageSize(20))
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets talk through all of this. First notice that you have to give the grid a model. This is not optional. There is no dynamic option. You must define a class and provide it to the Grid&amp;lt;&amp;gt; method. You must also give your grid a name. This will become the html id of the grid later. It will allow you to select the grid using jQuery and do ... stuff.&lt;/p&gt;

&lt;p&gt;Next we have columns. As stated, columns can be quickly defined using Model properties, but notice that you can also define their width and resize attributes. You can provide them a title as well. You can also, in place of a property, define a Template &lt;code&gt;column.Template("&amp;lt;div&amp;gt;My Cool Custom Thing&amp;lt;/div&amp;gt;")&lt;/code&gt; from here you can also still access a property by using kendo expression syntax: &lt;code&gt;#=MyModelProperty#&lt;/code&gt; Sometimes you can also use JavaScript expressions between the #= and the #, but that's not always the case. It requires experimentation.&lt;/p&gt;

&lt;p&gt;Notice also we can define a Command column. You can have Destroy, Edit, Create and a few others for your Command. You can also define a custom command which will use your own JavaScript. Nice.&lt;/p&gt;

&lt;p&gt;You will always need to &lt;code&gt;.Editable(editable =&amp;gt; editable.Mode(GridEditMode.InLine).DisplayDeleteConfirmation(false))&lt;/code&gt; in the case of the delete command otherwise it will popup the basic browser alert statement to ask for confirmation.&lt;/p&gt;

&lt;p&gt;Next we have a whole host of things that can be enabled on the grid, &lt;code&gt;Groupable&lt;/code&gt;, &lt;code&gt;Sortable&lt;/code&gt;, &lt;code&gt;Filterable&lt;/code&gt;, &lt;code&gt;Pageable&lt;/code&gt;, etc. all of these enable features on the grid so a user can mess with the data displayed to get it to their liking.&lt;/p&gt;

&lt;p&gt;Finally we have &lt;code&gt;DataSource&lt;/code&gt;. This one is the biggest pain, but it is absolutely necessary. In the Telerik documentation you will see reference to code like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DataSource(datasource =&amp;gt; datasource
    .Ajax()
    .Read(r =&amp;gt; ...))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't have to bother with that crap. Kendo requires that you set up your endpoints a certain way with a specific wrapper around your data. It's probably fine, I guess - but I prefer to return plain old JSON from my endpoints that has the data I need. There might be additional Kendo magic you can unlock by doing it the other way though. However, all of the paging and sorting etc. works just fine without it.&lt;/p&gt;

&lt;p&gt;Instead, the preferable way is to use &lt;code&gt;Custom()&lt;/code&gt; as shown in the first code example. Doing it this way also makes the schema function available (it's not available for &lt;code&gt;Ajax()&lt;/code&gt;) where you can run a parse method and transform data from your endpoint if you wish. You can also define a default row (as shown above) if you don't receive expected results. The Model method allows you to define custom fields and use an alternate Id from your model. This is important if you have MVC methods that need to accept different values than the default Id. For example, if you have a delete endpoint that uses the &lt;code&gt;Name&lt;/code&gt; property then you could said &lt;code&gt;Model(m =&amp;gt; m.Id(p =&amp;gt; p.Name))&lt;/code&gt; making it so that value is what is passed to the MVC destroy method by default.&lt;/p&gt;

&lt;p&gt;Transport wraps all of the MVC actions and you can use API endpoints from different servers as well as your own MVC endpoints. It provides the most flexibility in retrieving and manipulating your data. It is only available under the &lt;code&gt;Custom()&lt;/code&gt; method, which is perhaps another reason to never bother with &lt;code&gt;Ajax()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally you can define the &lt;code&gt;PageSize&lt;/code&gt; for items to be displayed on the page.&lt;/p&gt;

&lt;p&gt;Now, what if you have some detail data that you want displayed in a sub grid? Well Kendo makes that easy as long as you strictly adhere to their process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@(Html.Kendo().Grid&amp;lt;MyModel&amp;gt;()
.Name("MyGrid")
.Columns(columns =&amp;gt;
{
    columns.Command(c =&amp;gt; c.Destroy()).Width(50).Title("Command");
    columns.Bound(i =&amp;gt; i.Id).Width(50).MinResizableWidth(50).Title("Id");
    columns.Bound(i =&amp;gt; i.Name).Width(100).MinResizableWidth(100).Title("Name");
    columns.Bound(i =&amp;gt; i.Status).Width(100).MinResizableWidth(100).Title("Status");                
})
.ClientDetailTemplateId("template")
.Editable(editable =&amp;gt; editable.Mode(GridEditMode.InLine).DisplayDeleteConfirmation(false))
.HtmlAttributes(new { style = "height: 550px;" })
.Scrollable()
.Groupable()
.Sortable()
.Pageable()
.Filterable()
.Resizable(resize =&amp;gt; resize.Columns(true))
.DataSource(dataSource =&amp;gt; dataSource
    .Custom()
    .Schema(s =&amp;gt; s
        .Parse(@&amp;lt;text&amp;gt; 
        function(data) {                        
            if (!data || typeof(data) === 'string' ) 
                data = [{ Id: "N/A", Name: "NA", Status: "N/A" }]
            return data;
        }
        &amp;lt;/text&amp;gt;)
        .Model(m =&amp;gt;
        {
            m.Id(p =&amp;gt; p.Id);
        }))                    
    .Transport(t =&amp;gt;
    {
        t.Read(r =&amp;gt; r.Action("GetMyModels", "ControllerName"));
        t.Destroy(d =&amp;gt; d.Action("DeleteAModel", "ControllerName"));
    })
    .PageSize(20))
)


&amp;lt;script id="template" type="text/kendo-tmpl"&amp;gt;
    @(Html.Kendo().Grid&amp;lt;DetailModel&amp;gt;()
        .Name("template_#=Id#")
        .Columns(columns =&amp;gt;
        {
            columns.Bound(c =&amp;gt; c.Address).Width(100).MinResizableWidth(50).Title("Address");
            columns.Bound(c =&amp;gt; c.State).Width(100).MinResizableWidth(50).Title("State");
            columns.Bound(c =&amp;gt; c.Country).Width(100).MinResizableWidth(50).Title("Country");
        })
        .Scrollable()
        .Groupable()
        .Sortable()
        .Pageable()
        .Filterable()
        .DataSource(dataSource =&amp;gt; dataSource
        .Custom()
        .Schema(s =&amp;gt; s
            .Parse(@&amp;lt;text&amp;gt;
            function(data) {
                if (!data || typeof(data) === 'string' )
                    data = [{ EmailType: "N/A", NoticeType: "N/A" }]
                return data;
            }
            &amp;lt;/text&amp;gt;))
        .Transport(t =&amp;gt;
        {
            t.Read(r =&amp;gt; r.Action("GetBounceTrackingDetails", "EDisclosures", new { Id = "#=Id#" }).Data("getParams"));
        })
        .PageSize(20))
        .ToClientTemplate()
    )
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have another template wrapped in a script tag. The script tag has an id which must match text in this method: &lt;code&gt;ClientDetailTemplateId("template")&lt;/code&gt; in your parent grid.&lt;/p&gt;

&lt;p&gt;The detail grid is pretty much the same, but keep in mind you'll have to escape the # in any &lt;code&gt;#=kendo-expression#&lt;/code&gt; in the Columns area. They still work okay everywhere else, probably. You might have JSON with a sub collection and you might wish to pass the sub collection to the Kendo client template.&lt;/p&gt;

&lt;p&gt;Sorry this is impossible. You cannot pass a collection from the parent to the child and expect it to be used in the grid columns. Instead, you have to define yet another endpoint and Kendo will perform an ajax call to retrieve detail information every time detail information is selected by a user. This is suboptimal but that's what Telerik wants and who are we to disagree?&lt;/p&gt;

&lt;p&gt;Finally, and maybe most importantly, you must call &lt;code&gt;ToClientTemplate()&lt;/code&gt; at the end of the child grid definition.&lt;/p&gt;

&lt;p&gt;Oh, just in case you're like me and you like to use constructors in your models, you must define a parameter-less constructor or the Kendo UI grid will fail silently. Not only will it fail silently, but any HTML defined in your &lt;code&gt;csthml&lt;/code&gt; file below the grid will fail to be added to the page. The lack of error messages will have you laughing for hours, so sorry to steal the joy of this problem. Define parameter-less constructors and you'll be good to go.&lt;/p&gt;

&lt;p&gt;So, Kendo UI is nice when it works and you stay within the defined edge cases. It's a nightmare when you dare to set foot without, so just don't.&lt;/p&gt;

&lt;p&gt;Good luck oh hapless developer. I pray for you.&lt;/p&gt;

</description>
      <category>aspnetcore</category>
      <category>aspnet</category>
      <category>kendoui</category>
      <category>kendo</category>
    </item>
    <item>
      <title>Tale of Terror - A Scary Web Dev Story</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Fri, 30 Oct 2020 13:25:12 +0000</pubDate>
      <link>https://forem.com/robotoptimist/tale-of-terror-a-scary-web-dev-story-23l1</link>
      <guid>https://forem.com/robotoptimist/tale-of-terror-a-scary-web-dev-story-23l1</guid>
      <description>&lt;h1&gt;
  
  
  Tale of Terror - A Scary Web Dev Story
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--capdnqYI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/jackolanterns.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--capdnqYI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/jackolanterns.jpg" alt="A collection of lit jackolanterns with a dark background."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.flickr.com/photos/markybon/1813406901"&gt;Mark Menzies&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://twitter.com/wesbos/status/1313830816154349568"&gt;Wes Bos&lt;/a&gt; asked for scary stories on Twitter recently. I emailed him but was inspired to write about it myself.&lt;/em&gt;&lt;em&gt;Update! My story was read on &lt;a href="https://syntax.fm/"&gt;SyntaxFM ep. 294&lt;/a&gt; starting on 40:19! Thank you Wes Bos and Scott Tolinski!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Warm October
&lt;/h2&gt;

&lt;p&gt;My story takes place many years ago. I was hired by a consulting company to work on an EPiServer (CMS) implementation for a client. My skill level at the time was probably just barely mid-level, but mainly in C# and .NET tech, not so much with JavaScript. I was a pretty fair hand with the internals of the CMS though so I thought I could figure it out, also I reasoned that my tech lead had a decade of experience so I could lean on him a little bit.&lt;/p&gt;

&lt;p&gt;Arriving on site, the first thing the client told us was "No frameworks, plain JavaScript / jQuery only".&lt;/p&gt;

&lt;p&gt;Okay, I thought, but then won't it take longer for us to have a reactive site? But there was more.&lt;/p&gt;

&lt;p&gt;"We'll be ajaxing in the widgets from the CMS to other projects so the JavaScript can't conflict with any of those projects." When I asked what version of jQuery the other projects were using he said "Oh, various. Some are on version 2, others on version 1.9. That's not a problem, right?"&lt;/p&gt;

&lt;p&gt;This sounded bad to me - wouldn't that be rough to know whether our code would work if we couldn't be sure what dependencies were available?&lt;/p&gt;

&lt;p&gt;My tech lead was confident though - "No problem! We can do it!"&lt;/p&gt;

&lt;p&gt;Okay, whew, well at least my tech lead had the goods. Over the next week or so while we familiarized with the project, the client would make some more restrictions around the JavaScript and CSS to use. Like no build steps for JavaScript, no jQuery plugins, etc etc.&lt;/p&gt;

&lt;p&gt;The tech lead would sagely nod his head. "No problem!" He'd say.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Cold November
&lt;/h2&gt;

&lt;p&gt;One day I was working on some frontend stuff and had a problem. I was inexperienced with JavaScript and CSS and some elements just would not position themselves correctly. I asked my tech lead for help. As I'm explaining the problem he gestures at my screen and says "What is this tool you're using here?"&lt;/p&gt;

&lt;p&gt;I had a dual monitor set up. One screen had the page and the other had Chrome Dev Tools. I didn't have anything fancy on the screen so I was puzzled.&lt;/p&gt;

&lt;p&gt;"What do you mean? The console? Or something else?"&lt;/p&gt;

&lt;p&gt;"No, what is this tool? It seems pretty useful."&lt;/p&gt;

&lt;p&gt;"You mean... Chrome Dev Tools?"&lt;/p&gt;

&lt;p&gt;"Oh! Is that what that is? That's built into Chrome then?"&lt;/p&gt;

&lt;p&gt;My heart was in my stomach. Did he not know Chrome Dev Tools? What else might he not know? Am I the one who's wrong? Maybe dev tools aren't standard tooling for all web dev.&lt;/p&gt;

&lt;p&gt;From a place of pure self doubt I withheld judgement and explained the tools. Then I asked "So what do you use?"&lt;/p&gt;

&lt;p&gt;He showed me how he worked through JavaScript using an elaborate system of alert statements to debug his code. I kept my face straight, but internally I knew. I was in danger.&lt;/p&gt;

&lt;p&gt;I soon learned that he also didn't know MVC. He also didn't know JavaScript too well. He asked me if I had ever used Moo Tools. This was the first time I had ever heard of Moo Tools. (It was a competitor for jQuery in like 2006 FYI.)&lt;/p&gt;

&lt;p&gt;"I'm so glad you're on this project and can help me out with this stuff." He said to me one day.&lt;/p&gt;

&lt;p&gt;Cold sweat trickled down my spine. I smiled.&lt;/p&gt;

&lt;p&gt;"Sure. No problem."&lt;/p&gt;

&lt;p&gt;Over the weeks he struggles with user stories and I pick up more. Soon, the client was bypassing him and asking me directly to plan and implement the features.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Horror Begins
&lt;/h2&gt;

&lt;p&gt;I, barely a midlevel developer, start to plan the architecture of our little piece of the project. The .NET piece seems fine just following the established patterns I already knew. The JavaScript left a lot to be desired.&lt;/p&gt;

&lt;p&gt;I read a lot. I made friends with developers on other projects and started asking them lots of questions. I was determined to nail this. I believed I could do it.&lt;/p&gt;

&lt;p&gt;I decided that since we couldn't use frameworks but needed framework like behavior then I would build my own custom framework. A mini-framework. Just enough to get things working. I started writing JavaScript - &lt;strong&gt;a lot of JavaScript!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had read about something called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"&gt;MutationObserver&lt;/a&gt; which is something in JavaScript that allowed me to observe changes to elements and then react to them. I decided that would be the core of my project. I built a central engine around this. I made JavaScript functions representing pieces of functionality I needed and overloaded the prototype to get the functionality I needed.&lt;/p&gt;

&lt;p&gt;I created a custom dependency injection setup for my functions. I believed I could create something similar to Angular.js, just a touch simpler. I worked late every night for weeks. I was marking features as completed but I would frequently revisit them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Really, nothing was ever finished because I kept changing it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I worked into a delirium. I worked until I thought I was crazy.&lt;/p&gt;

&lt;p&gt;I believed, I really believed I was making a fairy tale palace of JavaScript. You read this and you think you know how bad it was.&lt;/p&gt;

&lt;p&gt;It was worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dungeon
&lt;/h2&gt;

&lt;p&gt;Things were getting close to completed, or so I thought. But everything always seemed to be broken.&lt;/p&gt;

&lt;p&gt;Things happened on the site but I had no idea why. Race conditions abounded. The off-by-one errors had off-by-one errors.&lt;/p&gt;

&lt;p&gt;Slowly, painfully, I began to realize that this was my fault. My design was flawed. My assumptions, wrong. All the work, the long hours - wasted. I became bitter.&lt;/p&gt;

&lt;p&gt;I was behind schedule but I was not alone. Every project the client had seemed to be struggling. The depth of the issues in the application I was working were lost as other pieces of the project seemed to fly off the rails. One project team eventually admitted they had completely forgot to implement security. Other teams scrambled to offer personnel for a "security task force" to "shore up" the security. One senior level project manager joked "Well it's like we built the car and forgot to put locks on the doors 🤷‍♂️ Oopsies!"&lt;/p&gt;

&lt;p&gt;I kept sliding more and more fixes in, trying to fix my app - to make it good even. A good result seemed so close, yet so far away. I started to feel trapped within a prison of my own making. No one could really help me either. My tech lead was still there, but seemed lost and confused. He had been marginalized and now I owned this mess. The consultancy had long ago let me know that their hands were tied. I had made them aware of the situation with the tech lead and they had confirmed the problem, but could not act without disrupting the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I had turned this opportunity into an exercise in hubris.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The client, realizing that there might be some issues maintaining the application after I left, assigned one of their own developers to the team. I went over the project and we laughed at it with typical gallows humor. We were now both imprisoned.&lt;/p&gt;

&lt;p&gt;However, things started getting better. Now I had someone to bounce ideas off of. We began to conspire a way out of this mess. We were going to introduce Vue.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perhaps I would not be hoisted by my own petard!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Vue.js was designed around being able to incrementally refactor. It was small and fast and we didn't need anything except the core functionality. It worked just fine alongside jQuery and we found a way to make it work with Angular.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We were saved!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Slowly, we started to see bugs disappear. Things were working faster and more reliably. The code started to look less like a mess and more like something we could maintain.&lt;/p&gt;

&lt;p&gt;But the project as a whole was not getting better. Project management wanted more late nights. They wanted more sacrifices. Code for the Code God!&lt;/p&gt;

&lt;h2&gt;
  
  
  Escape From New Work
&lt;/h2&gt;

&lt;p&gt;At last, I had enough. I'd worked on the project for about 8 months and I didn't see anything that made me want to stay.&lt;/p&gt;

&lt;p&gt;The consultancy I worked for had promised a $500 bonus if I stayed another 7 months, but I wasn't interested in peanuts. The client had pulled me into a room with three project managers and let me know it was time to start working 12 hour days. When I refused ( I wasn't getting paid for that time after all ) the handler from the consultancy emailed me from his tropical vacation letting me know that "You will comply with the overtime request."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It was time to get out.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CGeh-2ZK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/escape-from-new-work.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CGeh-2ZK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/escape-from-new-work.jpg" alt="The box art for the 90s classic film: Escape From New York with title changed from New York to New Work"&gt;&lt;/a&gt; &amp;lt;!----&amp;gt;&lt;/p&gt;

&lt;p&gt;I did have one person who I owed an explanation though, and that was my co-conspirator developer who helped introduce Vue.js. Thing was, he completely understood - in fact he was already looking too. We were going to abandon this project and let this app fend for itself. The Vue.js refactoring had a long way to go, but neither of us could stomach the other dysfunctional pieces of the project.&lt;/p&gt;

&lt;p&gt;I found another job that prioritized work/life balance and left. After a couple of weeks I referred my new friend and he left as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We ran out like the building was on fire.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Beware
&lt;/h2&gt;

&lt;p&gt;That project is still there. Years later I saw wanted ads from the company. "EPiServer developer needed! JavaScript experience a must! Vue.js experience preferred!"&lt;/p&gt;

&lt;p&gt;The refactor is still in progress. The old broken JavaScript still scraping by.&lt;/p&gt;

&lt;p&gt;Fellow developers, the dungeon I created still exists, seeking new inmates. That JavaScript "framework", a monster that few can slay. When you're out there, scrolling through ads thinking about your next gig, remember this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be diligent lest you also be imprisoned!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But Hey I'm Fine
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VLjexXeO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/bad-dev.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VLjexXeO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/bad-dev.jpg" alt="A guy at a computer desk hoists his mug at the camera. A caption reads 'Writes Unmaintainable Code, Leaves Company'"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Is this me? Am I the monster?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>syntaxfm</category>
      <category>halloween</category>
    </item>
    <item>
      <title>Take A Look At ML.NET</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Thu, 17 Sep 2020 19:47:30 +0000</pubDate>
      <link>https://forem.com/robotoptimist/take-a-look-at-ml-net-1940</link>
      <guid>https://forem.com/robotoptimist/take-a-look-at-ml-net-1940</guid>
      <description>&lt;h1&gt;
  
  
  Take A Look At ML.NET
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xvQvf6kf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/robotmlnet-yes.png" alt="The ML.NET logo with a robot face next to it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ML.NET?
&lt;/h2&gt;

&lt;p&gt;ML.NET is a service from Microsoft that allows you to add machine learning to your existing applications. It requires very little setup and can use existing data in your database to start making predictions. If you've been curious about machine learning, but come from a .NET background then this might be a good starting point for you.&lt;/p&gt;

&lt;p&gt;Machine learning has different types of predictions it can make based on the data you give it. Here is a list of what ML.NET can do out of the box along with a short description and an example or two.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prediction&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Classification/Categorization&lt;/td&gt;
&lt;td&gt;Divides records into categories or classifications.&lt;/td&gt;
&lt;td&gt;Can determine if a message is mean or nice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regression/Predict continuous values&lt;/td&gt;
&lt;td&gt;Predicts a value associated with some data.&lt;/td&gt;
&lt;td&gt;Could count objects in an image. Could predict house prices based on size and location.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anomaly Detection&lt;/td&gt;
&lt;td&gt;Detects a piece of data that does not fit with the set.&lt;/td&gt;
&lt;td&gt;Could determine if a collection of similar images has one that is very different. Could find a transaction that differs in a major way from the rest of a set.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recommendations&lt;/td&gt;
&lt;td&gt;Predicts a desired addition to a set based on the set contents.&lt;/td&gt;
&lt;td&gt;Suggests additional products a shopper might want to buy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time series/sequential data&lt;/td&gt;
&lt;td&gt;Makes predictions based on the order of previous data.&lt;/td&gt;
&lt;td&gt;Weather forecasts, stock market predictions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image classification&lt;/td&gt;
&lt;td&gt;Divides images into classifications&lt;/td&gt;
&lt;td&gt;Can determine if an image is a hotdog 😂🌭&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All of these seem to be tremendously useful and ML.NET makes it easy to start using them in your existing application using your existing data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do I Get Started?
&lt;/h2&gt;

&lt;p&gt;You've got to have the .NET Core 3.1 SDK installed on your computer (&lt;a href="https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=netcore31"&gt;windows&lt;/a&gt;, &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=netcore31"&gt;linux or wsl&lt;/a&gt; or &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/install/macos"&gt;macOS&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;It's super easy to start using ML.NET. You can set it up locally by running this command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; mlnet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This worked really well in wsl bash, but I had some problems running it in Powershell. According to the docs, this will work in MacOS and Linux just fine though.&lt;/p&gt;

&lt;p&gt;So for windows you may need to use the visual studio installer, where you select the .NET Core cross-platform development option and elect for ML.NET like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yOmBBI9x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/vs-installer-model-builder.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yOmBBI9x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/vs-installer-model-builder.png" alt="A screenshot of the visual studio installer install screen where ML.NET is selected alongside .NET Core cross-platform development."&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  ML.NET Is Installed, Now What?
&lt;/h2&gt;

&lt;p&gt;Now we can start thinking about how we want to use it.&lt;/p&gt;

&lt;p&gt;Here is a snippet of code from the &lt;a href="https://docs.microsoft.com/en-us/dotnet/machine-learning/how-does-mldotnet-work#hello-mlnet-world"&gt;Microsoft Explanation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;MLContext&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MLContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Import or create training data&lt;/span&gt;
    &lt;span class="n"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;houseData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.1F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.2F&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.9F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2.3F&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2.8F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3.0F&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3.4F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3.7F&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;IDataView&lt;/span&gt; &lt;span class="n"&gt;trainingData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromEnumerable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;houseData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Specify data preparation and model training pipeline&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concatenate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Size"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Regression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Trainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sdca&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;labelColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maximumNumberOfIterations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Train model&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainingData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Make a prediction&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2.5F&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatePredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HouseData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Prediction&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Predicted price for size: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; sq ft= &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;k"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Predicted price for size: 2500 sq ft= $261.98k&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  What Is This Code Doing?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Here we cook up some training data about house sizes, providing a size and a price. &lt;/li&gt;
&lt;li&gt;We create a machine learning pipeline and tell it what to expect from the training data and how many times to go over it (This time it's a regression).&lt;/li&gt;
&lt;li&gt;We take our training data and throw it into the pipeline using the &lt;code&gt;Fit&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;We make up a new house size and feed it to the machine learning model and it spits out a price.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How Can We Use This?
&lt;/h3&gt;

&lt;p&gt;Now, like most &lt;em&gt;starting out&lt;/em&gt; examples, this is pretty trivial. In fact, as you read this you might be thinking "Why do I need machine learning for this? This is a very simple algorithm."&lt;/p&gt;

&lt;p&gt;Ha, yeah, it is. However, we can continue to add additional properties to correlate, such as location. Also, real life data may not be so linear.&lt;/p&gt;

&lt;p&gt;For me, when I saw this example I immediately started thinking about data I have worked with in various databases and how I could use that to start making predictions that would be useful for the business.&lt;/p&gt;

&lt;p&gt;For example, I worked with a company that processed &lt;a href="https://www.ohiopmp.gov/"&gt;OARRS&lt;/a&gt; data in order to track prescription compliance for a patient. OARRS data is kept for patients who are prescribed controlled substances. Using ML.NET we &lt;em&gt;could&lt;/em&gt; classify a patient into high or low risk categories based on prescription history. We could aggregate the prescription history for patients like so:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;List of current prescriptions&lt;/th&gt;
&lt;th&gt;Span of time for continuous prescriptions in days&lt;/th&gt;
&lt;th&gt;Number of unique prescription fill locations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From there, we would need to pull from existing data whether or not a patient was compliant with their prescription instructions. This is something that the medical staff tracks as part of their counseling tasks. We can make a value judgement based on that data. A result of non-compliant would mean that "High Risk" and compliant would be "Low Risk". Using this, we can create a set of training data that more or less looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Current prescriptions&lt;/th&gt;
&lt;th&gt;Continuous prescriptions in days&lt;/th&gt;
&lt;th&gt;Unique fill locations&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;methadone&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oxycontin&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;adderall&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;percocet&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lets say we save this aggregated anonymous data into a csv or tsv file.&lt;/p&gt;

&lt;p&gt;Now, we can use the ML.NET command line tool to generate the code for us so we can adapt it into our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mlnet classification --dataset "anonymous-aggregate-prescription-data.tsv" --label-col "Risk" --train-time 60
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The ML.NET tool will take the data saved in our file and attempt to predict the Risk column. We've specified that it should train on the data for 60 seconds, but really that time should be modified based on how big the dataset is. The bigger the data, the more time it should take.&lt;/p&gt;

&lt;p&gt;Using the command line tool will also show the best machine learning algorithm to use, which is interesting and potentially helpful.&lt;/p&gt;

&lt;p&gt;The tool will generate two projects, one will be a library that has a MLModel.zip file attached - the &lt;code&gt;SampleClassification.Model&lt;/code&gt; project, and the other will be a console app where the model is consumed. The &lt;code&gt;SampleClassification.ConsoleApp&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;Lets take a look at some code that would be spit out in the Model project. The tool will have generated two classes here, ModelInput class and a ModelOutput class. The ModelInput will be a representation of the above information sans the field to be predicted. The ModelOutput class has the field to be predicted and a float type value showing the percent certainty the algorthm made the prediction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsumeModel&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PredictionEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;CreatePredictionEngine&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MLNetModelPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCurrentDirectory&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"MLModel.zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SearchOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllDirectories&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// For more info on consuming ML.NET models, visit https://aka.ms/mlnet-consume&lt;/span&gt;
    &lt;span class="c1"&gt;// Method for consuming model in your app&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ModelOutput&lt;/span&gt; &lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ModelOutput&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PredictionEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;PredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CreatePredictionEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create new MLContext&lt;/span&gt;
        &lt;span class="n"&gt;MLContext&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MLContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Load model &amp;amp; create prediction engine&lt;/span&gt;
        &lt;span class="n"&gt;ITransformer&lt;/span&gt; &lt;span class="n"&gt;mlModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MLNetModelPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;modelInputSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predEngine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatePredictionEngine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ModelOutput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;mlModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;predEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is then consumed by the example console application like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create single instance of sample data from first line of dataset for model input&lt;/span&gt;
        &lt;span class="n"&gt;ModelInput&lt;/span&gt; &lt;span class="n"&gt;sampleData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Prescriptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"methadone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Days&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Locations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Make a single prediction on the sample data and print results&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;predictionResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsumeModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//predictionResult.Risk == "Low"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Moreover, as we continue to explore the &lt;code&gt;SampleClassification.ConsoleApp&lt;/code&gt; we will also see it generate a &lt;code&gt;ModelBuilder.cs&lt;/code&gt; using the algorithm that it had selected. We can adopt this code into our application to continuously refresh the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ModelBuilder&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TRAIN_DATA_FILEPATH&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"/path-origina-training-data.tsv"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MODEL_FILE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsumeModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MLNetModelPath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create MLContext to be shared across the model creation workflow objects &lt;/span&gt;
    &lt;span class="c1"&gt;// Set a random seed for repeatable/deterministic results across multiple trainings.&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MLContext&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MLContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Load Data&lt;/span&gt;
        &lt;span class="n"&gt;IDataView&lt;/span&gt; &lt;span class="n"&gt;trainingDataView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadFromTextFile&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                                        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TRAIN_DATA_FILEPATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;hasHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;separatorChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sc"&gt;'\t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;allowQuoting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                        &lt;span class="n"&gt;allowSparse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Build training pipeline&lt;/span&gt;
        &lt;span class="n"&gt;IEstimator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITransformer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;BuildTrainingPipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Train Model&lt;/span&gt;
        &lt;span class="n"&gt;ITransformer&lt;/span&gt; &lt;span class="n"&gt;mlModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;TrainModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Evaluate quality of Model&lt;/span&gt;
        &lt;span class="nf"&gt;Evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Save model&lt;/span&gt;
        &lt;span class="nf"&gt;SaveModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mlModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MODEL_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainingDataView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IEstimator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITransformer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;BuildTrainingPipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MLContext&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Data process configuration with pipeline data transformations &lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dataProcessPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Conversion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapValueToKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Risk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Risk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FeaturizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Prescriptions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Prescriptions"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FeaturizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FeaturizeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Locations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Locations"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concatenate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Prescriptions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Locations"&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NormalizeMinMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendCacheCheckpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the training algorithm &lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MulticlassClassification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Trainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OneVersusAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BinaryClassification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Trainers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AveragedPerceptron&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;labelColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sentiment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numberOfIterations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;featureColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;labelColumnName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Risk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mlContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Conversion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapKeyToValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PredictedLabel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"PredictedLabel"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataProcessPipeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;trainingPipeline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can see where we could start pulling data from a database at the &lt;code&gt;Load Data&lt;/code&gt; step, and start thinking about how frequently we might want to refresh this data.&lt;/p&gt;

&lt;p&gt;And now, using this anonymous training data, the machine learning model can start making predictions about risk category for a patient based on their existing prescription history. All of this could be incorporated into an existing application by creating a machine learning service within it without any need to share potentially sensitive information with a cloud service.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For this example, I did not move forward with generating this service. While I think that making risk determinations from OARRS data could be interesting, and it's certainly possible, there are ethical concerns that would need to be considered before proceeding, such as: Do I need consent from patients to aggregate this data, even though it's anonymous? So this is all purely hypothetical based on applications I have worked on in the past.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always be careful to consider the ethical ramifications of a machine learning project before diving into it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6k7fcU9p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/ian-malcolm-quote.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6k7fcU9p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/ian-malcolm-quote.jpg" alt="Ian Malcolm in the Jurassic Park movie saying 'Your scientists were so preoccupied with whether they could they didn't stop to think if they should'."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cool! How Do I Start With This?
&lt;/h2&gt;

&lt;p&gt;I recommend doing the &lt;a href="https://dotnet.microsoft.com/learn/ml-dotnet/get-started-tutorial/intro"&gt;ML.NET tutorial from Microsoft&lt;/a&gt;. It will hit on some of the stuff covered here and will get you spun up with your own training data from wikipedia. The code examples in this blog are adapted from that tutorial.&lt;/p&gt;

&lt;p&gt;Good luck and have fun!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>machinelearning</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>Nuxt, Offline-First PWA Tutorial</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Mon, 14 Sep 2020 00:04:39 +0000</pubDate>
      <link>https://forem.com/robotoptimist/nuxt-offline-first-pwa-tutorial-3geb</link>
      <guid>https://forem.com/robotoptimist/nuxt-offline-first-pwa-tutorial-3geb</guid>
      <description>&lt;h1&gt;
  
  
  Nuxt: Offline-First PWA Tutorial
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xgTUOWW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/nuxt_title.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xgTUOWW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/nuxt_title.jpg" alt="The Nuxt logo with the text, Nuxt a great case for vue. The quick and easy way to spin up a Vue.js application"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  I Love Vue
&lt;/h2&gt;

&lt;p&gt;I love Vue.js. It's a fantastic JavaScript framework - easily outclassing in my mind the other offerings - which is saying something because I think they are cool too. And yeah, they do some things better than Vue. Some of them have done things &lt;em&gt;before&lt;/em&gt; Vue and Vue adopted it later. But even so, there are solid reasons for preferring Vue.&lt;/p&gt;

&lt;p&gt;Just to run through them quickly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vue has a great learning curve.&lt;/li&gt;
&lt;li&gt;It encourages incremental adoption into legacy projects.&lt;/li&gt;
&lt;li&gt;Development is driven by community needs.&lt;/li&gt;
&lt;li&gt;Vue maintainers have friendly relationships with other framework maintainers, allowing for a free exchange of ideas and concepts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Maybe Some Things Could be Better
&lt;/h2&gt;

&lt;p&gt;There are more points to make here but, let me move on to a criticism of Vue.js - depending on the type of application you intend to build, or the size of the application, scaffolding a Vue.js app, even with the CLI can be a chore.&lt;/p&gt;

&lt;p&gt;Also, there are details around creating the Vue app that must be observed - it's not difficult, but if you intend to use Vuex or Vue router then a small amount of configuration is required before you can spin up a store or expect your routes to work.&lt;/p&gt;

&lt;p&gt;There can be quite a bit of variation in where files are kept as well, whether there is a components folder, or how to do the layouts. Certainly the Vue CLI helps with this when it scaffolds a new project, however for Vue router for example, you'll need to continue to modify the router index.js in order to keep an accurate record of routes.&lt;/p&gt;

&lt;p&gt;It's small changes, but something that could easily be forgotten as an application continues to grow and change. While it's true that any application, regardless of language or framework will eventually have a checklist of items that need maintained, we should try as best we can to keep the list small and automate when it makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Nuxt.js.
&lt;/h2&gt;

&lt;p&gt;If I'm creating a new Vue project, unless it's very small, it's rare when I'll use the Vue CLI instead of using Nuxt. Nuxt makes everything easy. Routing is already preconfigured out of the box and dynamically determined based on the directory structure. It has easily understood options for determining layout, middleware and components.&lt;/p&gt;

&lt;p&gt;In fact, you can view the structure of this blog as an example. Here is the &lt;a href="https://github.com/RobotOptimist/macivor"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nuxt can fill many roles. In the case of this blog, it's a git based CMS and static site generator. But I've also used it to create a single page application, providing a rich experience for the browser. It can be a universal application, providing prerendered or even server side rendered pages very easily. In fact, to create a server side rendered application instead of a single page application here is the line of configuration from nuxt.config.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// will be a spa if set to false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's difficult to describe how Nuxt can be used without providing an example - so lets create a Nuxt application together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example App Requirements
&lt;/h3&gt;

&lt;p&gt;First, lets get some requirements.&lt;/p&gt;

&lt;p&gt;Let's make this a survey app. It's for fire hydrant inspectors who are required to periodically inspect fire hydrants (pretty sure this is a fake job). So it has these requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Requires login of a user.&lt;/li&gt;
&lt;li&gt;Collects data about a fire hydrant as a form. (Hydrant serial number and condition)&lt;/li&gt;
&lt;li&gt;Must work offline (fire hydrants aren't always in proximity to cell towers or WIFI).&lt;/li&gt;
&lt;li&gt;Must transmit fire hydrant data when connected (or reconnected).&lt;/li&gt;
&lt;li&gt;Made to work on mobile or tablet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Okay great. Lets make some decisions about the app's architecture.&lt;/p&gt;

&lt;p&gt;It needs a login, so we'll use Auth0. Auth0 will allow us to integrate with other authentication systems so we can have a support for a variety of fire hydrant inspector companies.&lt;/p&gt;

&lt;p&gt;It needs to have a form, so we'll use bootstrap to cover that requirement. There are newer, better(?), CSS frameworks available but bootstrap will give us everything we need (and a lot we don't) with very little work.&lt;/p&gt;

&lt;p&gt;Hmm, requirements 3, 4, and 5 really point to PWA (&lt;a href="https://web.dev/what-are-pwas/"&gt;Progressive Web Application&lt;/a&gt;). So we'll make this app a PWA as well.&lt;/p&gt;

&lt;p&gt;OK. Now what? Well all of this can be done via Nuxt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do It In Nuxt
&lt;/h3&gt;

&lt;p&gt;For authentication we could use &lt;a href="https://auth.nuxtjs.org/"&gt;@nuxtjs/auth&lt;/a&gt;. This is perfect because it has a built in integration with Auth0. But if I didn't want to use Auth0, it has built-in support for a handful of other authentication providers, or we can extend it to use any authentication provider we need.&lt;/p&gt;

&lt;p&gt;Nuxtjs/auth has a dependency of &lt;a href="https://axios.nuxtjs.org/"&gt;@nuxtjs/axios&lt;/a&gt; an ajax library - which is perfect since we'll need that to transmit our form data anyway.&lt;/p&gt;

&lt;p&gt;We'll also need to create this form. We selected bootstrap, so we'll use &lt;a href="https://bootstrap-vue.org/docs#getting-started-with-nuxtjs"&gt;bootstrap-vue&lt;/a&gt; which has a handy Nuxt.js module to make all of this easy. Also, bootstrap-vue has a way to specify which bootstrap features we're using so we can use webpack (which is built into Nuxt) to treeshake out the rest. Great! It's not a perfect fix for bootstrap's drawbacks, but it's something.&lt;/p&gt;

&lt;p&gt;Finally, we have this PWA requirement. There is a module for that too. &lt;a href="https://pwa.nuxtjs.org/setup"&gt;@nuxtjs/pwa&lt;/a&gt; looks to have everything we need. It will handle all of the icon and manifest stuff, and allow us to easily register a service worker and determine what routes and behavior should be used when a user is offline.&lt;/p&gt;

&lt;p&gt;Now, notice all of the things I'm not needing to specify. I don't need to call up Vue router because that's already in the mix. Vuex is also in there and it's a dependency of nuxt/auth, but we may not need it ourselves.&lt;/p&gt;

&lt;p&gt;We create the project by running this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-nuxt-app &amp;lt;project-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While this runs it will allow us some options to pull in some of our dependencies. PWA is an option, but an additional npm installation is still required for that. We'll also be using Axios so that could make more sense to use for now. We can also opt for bootstrap, or a bunch of other CSS frameworks.&lt;/p&gt;

&lt;p&gt;Lets go ahead and pull in the auth module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @nuxtjs/auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And, if you haven't already, make sure to install Axios:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @nuxtjs/axios
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and then we modify the nuxt.config.js to add to the modules property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxtjs/axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxtjs/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;

&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Options&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The scaffolding tool also helped us to select a testing tool. I chose Jest.&lt;/p&gt;

&lt;p&gt;Thanks for the reminder Nuxt! Testing is important. But I will not be talking about testing for this tutorial, sorry.&lt;/p&gt;

&lt;p&gt;Finally, @nuxtjs/auth reminds us that we need to initialize the Vuex store by adding an index.js file to the store directory. Nuxt will automatically import Vuex and configure it for use when the index.js file is added. (This prevents Vuex being added to projects that don't need it.)&lt;/p&gt;

&lt;p&gt;Next lets pull in the PWA module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @nuxtjs/pwa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And lets not forget to set up our manifest!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fire hydrant surveyor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;short_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hydrant Surveyor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;standalone&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nuxt PWA is actually 5 different modules in one, only one of which will need some custom code from us.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The icon module - which will configure and make available the icon for the PWA icon.&lt;/li&gt;
&lt;li&gt;The meta module - which will set up some some common options for the mobile application.&lt;/li&gt;
&lt;li&gt;The manifest module - which creates the manifest file with the configured values.&lt;/li&gt;
&lt;li&gt;The workbox module - which allows setting service workers and cacheable resources - this is where we'll be doing the bulk of the work for offline behavior.&lt;/li&gt;
&lt;li&gt;The one signal module - which injects an API which allows for creating push notifications on the mobile device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets take look at what all of this looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/RobotOptimist/demo_survey_app/tree/initial-setup"&gt;Here it is!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like that we have a functioning application with PWA capabilities, a CSS framework and authentication built in.&lt;/p&gt;

&lt;p&gt;At this point we should consider our remaining tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an Auth0 account and add the appropriate info to the nuxt config.&lt;/li&gt;
&lt;li&gt;Build the appropriate pages with the auth0 log in and the fire hydrant survey form.&lt;/li&gt;
&lt;li&gt;Add cacheable assets to workbox (included from PWA module).&lt;/li&gt;
&lt;li&gt;Configure and customize the service worker to handle offline workflow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets go for it. Starting with #1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/signup?&amp;amp;signUpData=%7B%22category%22%3A%22button%22%7D"&gt;Creating an Auth0&lt;/a&gt; account is super easy. You can log in using GitHub. Auth0 will automatically set up an application for you. You can then grab all of the information you need for the nuxtjs/auth module. You will need to set up a few things in Auth0 Application Settings, such as allowed origins, allowed callback URIs and similar. You can refer to &lt;a href="https://auth0.com/docs/get-started/dashboard/application-settings"&gt;Auth0 documentation&lt;/a&gt; on how to do that.&lt;/p&gt;

&lt;p&gt;In nuxt.config.js you'll need to define the redirect and strategies objects. Note that the callback and login cannot be the same value. The module needs to route to a different page in order to finish processing the user data returned from the login event.&lt;/p&gt;

&lt;p&gt;In nuxt.config.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redirectUri&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;auth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The options object is defined in a separate file: auth_config.js. I did this for my project for convenience, but for a real project I would use a .env file so I could inject the correct values for each environment via the CI/CD pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/signed-in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;//or whatever you configure in Auth0 Application Settings&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While the values contained therein are not secret it is nonetheless recommended that the file not be checked into source control. You can later modify this file to have an audience property and to change the values based on build environment.&lt;/p&gt;

&lt;p&gt;Next we'll modify the main route to contain a login button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"links"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"button--green"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Login
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and we'll define a login method in the Vue instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loginWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: As of this writing there is an &lt;a href="https://github.com/nuxt-community/auth-module/issues/750"&gt;issue&lt;/a&gt; that requires the installation of an additional npm package.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;nanoid@2.1.11
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now when you test this out you should be redirected to an Auth0 login page. Upon successfully signing up or logging in you will be redirected back to the redirect_uri, which in this example project I set as &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, lets further modify the component template so as to display something different once we're logged in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"links"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt;
    &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!$auth.loggedIn"&lt;/span&gt;
    &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;
    &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Login
  &lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt;
    &lt;span class="na"&gt;v-else&lt;/span&gt;
    &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"warning"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"logout"&lt;/span&gt;
    &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Logout
  &lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice we're starting to switch to using bootstrap-vue components for the buttons. The b-button component accepts a variant and a size prop among other things.&lt;/p&gt;

&lt;p&gt;Now lets make sure we get the script piece of the page right:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mapGetters&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vuex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loginWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mapGetters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isAuthenticated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great! Now with these simple changes we have an application with authentication. So we've got #1 and part of #2 done now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/RobotOptimist/demo_survey_app/tree/configured-nuxt-auth"&gt;Here's the result in GitHub.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The remainder of #2 is to build the survey form. Lets do that real quick as a component.&lt;/p&gt;

&lt;p&gt;Bootstrap-vue makes it all pretty easy. It encompasses the bootstrap classes as components&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;b-container&lt;/span&gt; &lt;span class="na"&gt;fluid&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-form-row&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;sm=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"serial-serialNumber"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hydrant Serial serialNumber&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;sm=&lt;/span&gt;&lt;span class="s"&gt;"9"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;b-form-input&lt;/span&gt;
          &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
          &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"serialNumber"&lt;/span&gt;
          &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"serial-serialNumber"&lt;/span&gt;
          &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter the hydrant serial serialNumber"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&amp;lt;/b-form-input&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-form-row&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-form-row&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;sm=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"condition"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hydrant Condition&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;sm=&lt;/span&gt;&lt;span class="s"&gt;"9"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;b-form-select&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"condition"&lt;/span&gt; &lt;span class="na"&gt;:options=&lt;/span&gt;&lt;span class="s"&gt;"options"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"condition"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/b-form-select&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-form-row&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-form-row&lt;/span&gt; &lt;span class="na"&gt;align-h=&lt;/span&gt;&lt;span class="s"&gt;"end"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-form-row&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/b-container&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Bootstrap-vue took the route of making the grid system into components. I sometimes quibble with that decision, but it is easy to use. In this template, the container (b-container) has a collection of rows (b-row or b-form-row). Each row can have up to 12 columns. Additional columns wrap to the next line. The b-col component can represent any number of columns between 1 and 12. You can then decide how many columns it should take up for each viewport size.&lt;/p&gt;

&lt;p&gt;For example, you may want bootstrap b-col to take up 12 columns (full width) on mobile so you'd specify cols="12" as an attribute on the b-col component. But then you might decide it should take up 6 columns (half width) on tablet so you'd specify sm="6", which is also an attribute. This allows you to declare in the html how a elements should be displayed, in terms of width, on each viewport. Pretty handy!&lt;/p&gt;

&lt;p&gt;Aside from the fairly decent grid system that bootstrap-vue abstracts for us, there are a number of utility components we can use. In this form, I only need an input, a select, and a button - so I use the bootstrap-vue version of these. Bootstrap has out of the box support for good accessibility on forms and the options on components can remind you of things - like setting a placeholder for example.&lt;/p&gt;

&lt;p&gt;This is a very simple form - we only need a couple of things. We'll be sending the completed result back to the parent by emitting the result. Here is the script portion of the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;serialNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please choose a hydrant condition.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;poor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Poor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fair&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fair&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Good&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;excellent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Excellent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;serialNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now the parent component can handle the result as necessary. Lets take a look at the parent actually. You know what? Lets go ahead and refactor that to use bootstrap-vue as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;b-container&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-row&lt;/span&gt; &lt;span class="na"&gt;align-h=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Fire Hydrant Surveyor&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-row&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-row&lt;/span&gt; &lt;span class="na"&gt;align-h=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"$auth.loggedIn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;sm=&lt;/span&gt;&lt;span class="s"&gt;"9"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;survey-form&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;submitForm=&lt;/span&gt;&lt;span class="s"&gt;"handleFormResult"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/survey-form&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-row&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-row&lt;/span&gt; &lt;span class="na"&gt;align-h=&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-col&lt;/span&gt; &lt;span class="na"&gt;cols=&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!$auth.loggedIn"&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"warning"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"logout"&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/b-col&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-row&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/b-container&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the parent we'll also need to import the component and define the handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;surveyForm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/survey-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;surveyForm&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;handleFormResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//do stuff&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/RobotOptimist/demo_survey_app/tree/bootstrap-form"&gt;Here is the GitHub for this leg of our adventure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to figure out what to do with this form data. We'll be sending the data to our server, but how do we want to do that? Also we'll need to create the service worker to handle the offline behavior.&lt;/p&gt;

&lt;p&gt;I found a nice test API someone created so I can send the form data without having to deploy a whole other project, I just needed to massage the data a little bit for it to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;handleFormResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//https://jsonplaceholder.typicode.com/posts is a test API I'm borrowing &lt;/span&gt;
      &lt;span class="c1"&gt;//I'm making the data fit because I'm too lazy to make my own test API&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serialNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formObj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Okay, so now when I send the request I'm getting a result posted to the console. Everything looks good as long as I'm online.&lt;/p&gt;

&lt;p&gt;But what about when I'm offline?&lt;/p&gt;

&lt;p&gt;Turns out, all we need is the nuxt/pwa module where workbox will take care of it all for us.&lt;/p&gt;

&lt;p&gt;In order to enable the behavior we want - which is resending failed requests that occur when the app is offline - we need to create a special plugin for workbox.&lt;/p&gt;

&lt;p&gt;In the plugins folder I created a file called &lt;code&gt;workbox-sync.js&lt;/code&gt; and added this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bgSyncPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundSync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BackgroundSyncPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formQueue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;maxRetentionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="c1"&gt;// Retry for max of 24 Hours (specified in minutes)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sr"&gt;/https:&lt;/span&gt;&lt;span class="se"&gt;\/\/&lt;/span&gt;&lt;span class="sr"&gt;jsonplaceholder&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;typicode&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;posts/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NetworkOnly&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bgSyncPlugin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are creating a new background sync plugin and then we're registering that plugin on a route. The workbox register route method takes 3 arguments, a regex for the route (so you can potentially use a regex to define the same behavior for a range of similar routes), a strategy, and the http verb.&lt;/p&gt;

&lt;p&gt;Next you need to add this plugin to the workbox via these lines of configuration in &lt;code&gt;nuxt.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;pwa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;cachingExtensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/plugins/workbox-sync.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;//should be off actually per workbox docs due to complications when used in prod&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that you cannot do imports in the &lt;code&gt;workbox-sync.js&lt;/code&gt; file. The reason is that the plugin is injected into the middle of the sw.js script that the workbox module creates for us. Imports cannot be performed in the middle of a script.&lt;/p&gt;

&lt;p&gt;Also, you'll have noticed that I have &lt;code&gt;enabled: true&lt;/code&gt; but per the @nuxtjs/pwa workbox documentation you should not typically do this since it can cause issues when you are switching between dev and production environments. I do it here because it's so convenient. Otherwise, the service worker is not enabled while running in dev.&lt;/p&gt;

&lt;p&gt;Now when I run &lt;code&gt;npm run dev&lt;/code&gt; workbox will create the service worker. If I toggle the application to offline via the browser devtools then the posts to the server will fail, but as soon as I switch back to online then the service worker re-sends the requests.&lt;/p&gt;

&lt;p&gt;Lets see this in action.&lt;/p&gt;

&lt;p&gt;Here we are sending a successful request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GsgoBpWF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/successful-send.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GsgoBpWF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/successful-send.png" alt="Chrome dev tools showing a succesful send to our api"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But lets change our status to offline from the dev tools and watch a request fail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TFgXl79P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/failed-send.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TFgXl79P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/failed-send.png" alt="Chrome dev tools showing a failed send to our api"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the service worker takes care of re-sending the request. It will use the same information as what was originally sent, so beware if you are using any sort of expiring authentication data. But if we're reasonable sure the authentication token will remain valid until our users can get back online then this solution will work great.&lt;/p&gt;

&lt;p&gt;If you cannot count on that, then you may need to go with an alternate solution where you keep the data upon a failed request in an indexdb using &lt;a href="https://github.com/nuxt-community/localforage-module"&gt;localforage&lt;/a&gt;. Then you would need to create a custom service worker that would need to determine if you are back online and resend the data using the most recent authentication credentials available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C7e7YYQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/successful-retry-send.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C7e7YYQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/screen-shots/successful-retry-send.png" alt="Chrome dev tools showing a succesful retry send to our api"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the final result of our code lets take a look &lt;a href="https://github.com/RobotOptimist/demo_survey_app/tree/pwa-config"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We met all of our requirements with very little custom code.&lt;/p&gt;

&lt;p&gt;Now for deployment, Auth0 needs configured to accept my production domain name.&lt;/p&gt;

&lt;p&gt;Also, we need to go ahead and refactor out auth.config.js and replace it with .env.&lt;/p&gt;

&lt;p&gt;I went ahead and did that, first installing cross-env&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;cross-env
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then I created .env file and populated it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOMAIN='...auth0.com'
CLIENTID='aHashFromAuth0'
REDIRECTURI='/signed-in'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I then removed the import of auth.config from nuxt.config and replaced the options with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIRECTURI&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;auth0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOMAIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENTID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now I can inject the variables via the CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://epic-ramanujan-cef898.netlify.app/"&gt;And here is the final result.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nuxt has a huge variety of libraries and plugins that can help you achieve what you're seeking to do. It's great to get something off the ground quickly so you can hone in on the business needs.&lt;/p&gt;

&lt;p&gt;I hope this is helpful for anyone doing an offline first application!&lt;/p&gt;

</description>
      <category>vue</category>
      <category>nuxt</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Cure For Impostor Syndrome</title>
      <dc:creator>James MacIvor</dc:creator>
      <pubDate>Tue, 01 Sep 2020 14:34:39 +0000</pubDate>
      <link>https://forem.com/robotoptimist/the-cure-for-imposter-syndrome-1ln6</link>
      <guid>https://forem.com/robotoptimist/the-cure-for-imposter-syndrome-1ln6</guid>
      <description>&lt;h2&gt;
  
  
  We Suffer From Our Comparisons
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rscq9C0Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/the-four-sights.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rscq9C0Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/heroes/the-four-sights.jpeg" alt="Painting of monks praying and point as a man suffers and dies"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Four Sights, a depiction of the suffering witnessed by Buddha&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every single person I know has had it. &lt;/li&gt;
&lt;li&gt;It can keep you awake at night. &lt;/li&gt;
&lt;li&gt;It makes you timid when you should be bold. &lt;/li&gt;
&lt;li&gt;It's the voice that convinces you to take the easy path. 

&lt;ul&gt;
&lt;li&gt;Don't take risks. &lt;/li&gt;
&lt;li&gt;Don't challenge the senior developer.&lt;/li&gt;
&lt;li&gt;Don't go for that promotion.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, I'm talking about Impostor Syndrome.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Even Is Impostor Syndrome?
&lt;/h3&gt;

&lt;p&gt;Impostor Syndrome runs rampant in our industry, preying on our fragile mental health. It's likely most frequently seen with junior developers, but I've seen it with mid levels and senior developers as well.&lt;/p&gt;

&lt;p&gt;And, in a good team, it can be somewhat mitigated. There is a support structure of people who will reassure you that you're doing a good job. Or will make themselves a little bit vulnerable to show you that you're not alone. This is good healthy communication.&lt;/p&gt;

&lt;p&gt;But not all teams are like that. In fact, that might be a bit of an exception. Too often, there are teams where people feel no obligation to reassure you, or worse they compensate for their own feelings of inadequacy by exacerbating yours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kog2KKQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/kick-em-when-their-down.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kog2KKQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/kick-em-when-their-down.jpg" alt="Meme cartoon where three characters labeled as self doubt, social anxiety and feeling like a fraud bully a nameless fourth character. A fifth character labeled as Your Team offers a helping hand only to suplex the bullied character. Text at the bottom reads An Unsympathetic Team Can Make Things So Much Worse"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;So, the working definition for Impostor Syndrome is this (from Wikipedia):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Impostor Syndrome is a psychological pattern in which an individual doubts their accomplishments and has a persistent internalized fear of being exposed as a "fraud".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And here is the rub - the root of Impostor Syndrome is disbelief in your own success. How does that happen? You're accomplishments are right there in the ol' resume. You can track through a long list of completed user stories. You can see old PRs of your contributions - and yet no matter what evidence is presented this feeling persists.&lt;/p&gt;

&lt;p&gt;This is a defining symptom of Impostor Syndrome. the psychological journal &lt;a href="https://www.verywellmind.com/"&gt;verywellmind&lt;/a&gt; has an &lt;a href="https://www.verywellmind.com/imposter-syndrome-and-social-anxiety-disorder-4156469"&gt;article&lt;/a&gt; that has the following bulleted list of characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-doubt&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An inability to realistically assess your competence and skills&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Attributing your success to external factors&lt;/li&gt;
&lt;li&gt;Berating your performance&lt;/li&gt;
&lt;li&gt;Fear that you won't live up to expectations&lt;/li&gt;
&lt;li&gt;Overachieving&lt;/li&gt;
&lt;li&gt;Sabotaging your own success&lt;/li&gt;
&lt;li&gt;Setting very challenging goals and feeling disappointed when you fall short&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do any of these sound familiar? The fact is that we all experience some of this throughout our careers, but composed together we start to feel as though we are frauds.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Causes It?
&lt;/h3&gt;

&lt;p&gt;It comes from comparing yourself to others, not how they are but how you imagine them to be. There are so many tremendously smart people in tech, it's tempting to see them as giants. Like they, through 100% of their own merit, were given opportunities to work on new technologies and present themselves to the world. Even though many of them would be the first to deny this alt-version of themselves! People like &lt;a href="https://www.hanselman.com/blog/ImAPhonyAreYou.aspx"&gt;Scott Hanselman are quick to remind others that he once stood where you are&lt;/a&gt;. Moreover, Dan Abramov directly addresses reality vs. hype for developer expertise in his &lt;a href="https://overreacted.io/things-i-dont-know-as-of-2018/"&gt;blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More common, but less obvious, is when you compare yourself to people whom you think you should have parity and are shocked when they have deep knowledge of something unfamiliar to you. This happens to all of us. How quick we are to scold ourselves for not making the same decisions, studying the same things, making the same sacrifices. We're not fair to ourselves.&lt;/p&gt;

&lt;p&gt;And yet, it's useless to talk ourselves out of these feelings. It's a rare day when I can negotiate with my feelings regardless! I just hide the negative ones, same as you I expect.&lt;/p&gt;

&lt;p&gt;In the Time article &lt;a href="https://time.com/5312483/how-to-deal-with-impostor-syndrome/#:~:text=How%20to%20deal%20with%20impostor,more%20critically%20question%20those%20thoughts."&gt;"How to Deal With Impostor Syndrome"&lt;/a&gt; Psychologist Audrey Ervine says&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There’s no single answer. Some experts believe it has to do with personality traits—like anxiety or neuroticism—while others focus on family or behavioral causes, Sometimes childhood memories, such as feeling that your grades were never good enough for your parents or that your siblings outshone you in certain areas, can leave a lasting impact.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The verywellmind article further explains the causes of Impostor Syndrome.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We also know that entering a new role can trigger impostor syndrome. For example, starting college or university might leave you feeling as though you don't belong and are not capable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Moreover, we learn that Social Anxiety Disorder and Impostor Syndrome overlap, which makes a lot of sense when you consider that Impostor Syndrome results in a great deal of anxiety regarding how we're perceived by others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait, You Promised A Cure?
&lt;/h3&gt;

&lt;p&gt;Yes, I did.&lt;/p&gt;

&lt;p&gt;I'm sorry to say, but the "cure" takes a while. It comes in the form of constant affirmation. And, if you believe that you're experiencing this in conjunction with Social Anxiety Disorder, then additional steps might be needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FShV83eV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/alter-deal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FShV83eV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/alter-deal.png" alt="Darth Vader stating to Boba Fett 'I have altered the deal, pray I do not alter it further'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing you can do that really helps is talk to someone. This is why being on a team that offers that kind of support is important. If you feel safe talking to someone on your team, then much of this can be dispelled. Don't try to solo it! It's important to be honest with yourself and others.&lt;/p&gt;

&lt;p&gt;Another thing you can do is to focus on other people who might be going through the same thing. I achieved this by becoming much more serious about mentoring. Teaching people how to code and mentoring junior developers helps me see how far I've come. At the same time, I can see where they might feel insecure and it's a good opportunity to listen and reassure them.&lt;/p&gt;

&lt;p&gt;I strongly recommend if you're a junior developer - try teaching a curious friend the basics of coding. If your a mid level or senior then commit yourself to mentoring a junior developer. It can be a great reminder of the learning and leaps you've made since you began this journey when someone else is looking up to you. This is a good way to constructively assess your abilities.&lt;/p&gt;

&lt;p&gt;I also recommend seeking out those stronger developers as mentors for yourself. Put yourself in a position to listen to them rather than compete with them. It's a joy to work with super competent people who care about their work and their team.&lt;/p&gt;

&lt;p&gt;We are constantly comparing ourselves to others. It can't be helped! It's how our brains are wired, but if that's all you do then you'll fail to see your own progress. Even better though, as you continue to teach and mentor you'll see the people you boosted begin to surpass you. This is a good thing! This means you were an EXCELLENT mentor. Be proud. It's also great to have a mentor rooting for you, invested in your success - and ready to dispel any notion that you're a fraud.&lt;/p&gt;

&lt;p&gt;It's natural and right to use mentoring and teaching to improve ourselves and the people around us. It breaks down those social walls that hold us apart and creates a community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dj50pG-7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/expanding-brain-meme.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dj50pG-7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.macivortech.com/expanding-brain-meme.jpg" alt="Expanding brain meme, little brain is feeling inadequate in your job, larger brain is combating your own impostor syndrome, next largest brain is realizing you've been competent all along, enlightened brain is dismantling structural problems in your job that exist to make people feel incompetent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like with anything of this nature, there are no magic pills to make those feelings of inadequacy go away. We've got to do the work of finding our place and accepting ourselves as we are.&lt;/p&gt;

</description>
      <category>developer</category>
      <category>motivation</category>
      <category>career</category>
      <category>mentalhealth</category>
    </item>
  </channel>
</rss>
