<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kevin McMahon &#187; Linq</title>
	<atom:link href="http://blog.kevfoo.com/index.php/tag/linq/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.kevfoo.com</link>
	<description>The weblog of a Chicago based .Net and iPhone developer.</description>
	<lastBuildDate>Fri, 27 Aug 2010 15:00:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Retargeting Visual Studio project files with PowerShell</title>
		<link>http://blog.kevfoo.com/index.php/2010/05/retargeting-visual-studio-project-files-with-powershell/</link>
		<comments>http://blog.kevfoo.com/index.php/2010/05/retargeting-visual-studio-project-files-with-powershell/#comments</comments>
		<pubDate>Thu, 06 May 2010 16:00:53 +0000</pubDate>
		<dc:creator>Kevin McMahon</dc:creator>
				<category><![CDATA[.net]]></category>
		<category><![CDATA[build]]></category>
		<category><![CDATA[powershell]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[C Code]]></category>
		<category><![CDATA[C Project]]></category>
		<category><![CDATA[chatsworth]]></category>
		<category><![CDATA[Cinch]]></category>
		<category><![CDATA[Current Projects]]></category>
		<category><![CDATA[Descendants]]></category>
		<category><![CDATA[Element Names]]></category>
		<category><![CDATA[Elements]]></category>
		<category><![CDATA[File Format]]></category>
		<category><![CDATA[Gist]]></category>
		<category><![CDATA[Hack Files]]></category>
		<category><![CDATA[Instantiated]]></category>
		<category><![CDATA[Legacy Code]]></category>
		<category><![CDATA[Linq]]></category>
		<category><![CDATA[Main String]]></category>
		<category><![CDATA[Mandate]]></category>
		<category><![CDATA[Migration]]></category>
		<category><![CDATA[Namespace]]></category>
		<category><![CDATA[Path C]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[Schema]]></category>
		<category><![CDATA[Schemas]]></category>
		<category><![CDATA[Snover]]></category>
		<category><![CDATA[Source Control]]></category>
		<category><![CDATA[Source Tree]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[Visual Studio Project]]></category>
		<category><![CDATA[Xelement]]></category>

		<guid isPermaLink="false">http://blog.kevfoo.com/?p=85</guid>
		<description><![CDATA[I originally set out to write this post a little over a year ago. Back then I threw together a script to retarget all the project files from .Net 2.0 to .Net 3.5 for my previous company and recently I found myself having to a similar upgrade from .Net 3.5 to 4.0. I ended up [...]]]></description>
			<content:encoded><![CDATA[<p>I originally set out to write this post a little over a year ago. Back then I threw together a script to retarget all the project files from .Net 2.0 to .Net 3.5 for my previous company and recently I found myself having to a similar upgrade from .Net 3.5 to 4.0. I ended up using the same script again so I figured I&#8217;d go ahead and publish it.</p>
<p>The upgrade process done by Visual Studio on your current projects and solutions will only migrate the file format to the newest schema and will not retarget the framework to the latest version.&#160; That is where the script I’ve included below comes in.&#160; It will retarget all the C# project files found under the path provided to version 4.0 of the .Net framework</p>
<p>C# project files are XML based and navigating the DOM with Linq to XML is a cinch but there are a couple small but important steps that the script needed to include.&#160; First, you need to append the namespace to the individual element names or else the elements will not be able to be found.&#160; Second, when saving the modified XDocument, a XmlWriterSetting instance needs to be instantiated and the OmitXmlDeclaration property set to true.&#160; Setting this property to true will make sure the XML that we save will be considered a valid project file by Visual Studio.</p>
<p>I’ve included the full script below as well as created a gist that can be found <a href="http://gist.github.com/391885">here</a>.&#160; It is important to note that this script will edit all the csproj files found under the directory specified in the path variable.&#160; Make sure you backup these files or have them under source control prior to running the script.&#160; Enjoy.</p>
<p><em>[UPDATE: Added change suggested in comments by Jeffery Snover]</em></p>
<p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:ec38104f-e001-4999-b72d-fa89358addc8" class="wlWriterEditableSmartContent">
<pre style="background-color:#FFFFFF;white-space:-moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; word-wrap: break-word;overflow: auto;"><span style="color: #000000;">[Reflection.Assembly]</span><span style="color: #000000;">::</span><span style="color: #000000;">LoadWithPartialName(</span><span style="color: #800000;">"</span><span style="color: #800000;">System.Xml.Linq</span><span style="color: #800000;">"</span><span style="color: #000000;">) </span><span style="color: #000000;">|</span><span style="color: #000000;"> Out</span><span style="color: #000000;">-</span><span style="color: #000000;">Null

</span><span style="color: #008000;">#</span><span style="color: #008000;">specify the root of your source tree below</span><span style="color: #008000;">
</span><span style="color: #800080;">$path</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800000;">"</span><span style="color: #800000;">C:\Code\chatsworth</span><span style="color: #800000;">"</span><span style="color: #000000;">

</span><span style="color: #800080;">$ns</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800000;">"</span><span style="color: #800000;">http://schemas.microsoft.com/developer/msbuild/2003</span><span style="color: #800000;">"</span><span style="color: #000000;">
</span><span style="color: #800080;">$xname</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> [System.Xml.Linq.XName]</span><span style="color: #000000;">::</span><span style="color: #000000;">Get(</span><span style="color: #800000;">"</span><span style="color: #800000;">PropertyGroup</span><span style="color: #800000;">"</span><span style="color: #000000;">,</span><span style="color: #800080;">$ns</span><span style="color: #000000;">)
</span><span style="color: #800080;">$tfname</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> [System.Xml.Linq.XName]</span><span style="color: #000000;">::</span><span style="color: #000000;">Get(</span><span style="color: #800000;">"</span><span style="color: #800000;">TargetFrameworkVersion</span><span style="color: #800000;">"</span><span style="color: #000000;">,</span><span style="color: #800080;">$ns</span><span style="color: #000000;">)

</span><span style="color: #800080;">$xws</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> New</span><span style="color: #000000;">-</span><span style="color: #000000;">Object System.Xml.XmlWriterSettings
</span><span style="color: #800080;">$xws</span><span style="color: #000000;">.OmitXmlDeclaration </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000FF;">$true</span><span style="color: #000000;">
</span><span style="color: #800080;">$xws</span><span style="color: #000000;">.Indent </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000FF;">$true</span><span style="color: #000000;">

</span><span style="color: #0000FF;">function</span><span style="color: #000000;"> updatefx(</span><span style="color: #800080;">$filename</span><span style="color: #000000;">)
{
    </span><span style="color: #008000;">#</span><span style="color: #008000;">Write-Host $filename</span><span style="color: #008000;">
</span><span style="color: #000000;">    </span><span style="color: #800080;">$xml</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> [System.Xml.Linq.XDocument]</span><span style="color: #000000;">::</span><span style="color: #000000;">Load(</span><span style="color: #800080;">$filename</span><span style="color: #000000;">)

     </span><span style="color: #800080;">$result</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">$xml</span><span style="color: #000000;">.Descendants(</span><span style="color: #800080;">$xname</span><span style="color: #000000;">)

    </span><span style="color: #0000FF;">foreach</span><span style="color: #000000;"> (</span><span style="color: #800080;">$i</span><span style="color: #000000;"> </span><span style="color: #0000FF;">in</span><span style="color: #000000;"> </span><span style="color: #800080;">$result</span><span style="color: #000000;">)
    {
        </span><span style="color: #800080;">$fxelem</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #800080;">$i</span><span style="color: #000000;">.Element(</span><span style="color: #800080;">$tfname</span><span style="color: #000000;">)
        </span><span style="color: #0000FF;">if</span><span style="color: #000000;">(</span><span style="color: #800080;">$fxelem</span><span style="color: #000000;">)
        {
            </span><span style="color: #800080;">$i</span><span style="color: #000000;">.SetElementValue(</span><span style="color: #800080;">$tfname</span><span style="color: #000000;">,</span><span style="color: #800000;">"</span><span style="color: #800000;">v4.0</span><span style="color: #800000;">"</span><span style="color: #000000;">)
        }
    }

    </span><span style="color: #800080;">$xw</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> [System.Xml.XmlWriter]</span><span style="color: #000000;">::</span><span style="color: #000000;">Create(</span><span style="color: #800080;">$filename</span><span style="color: #000000;">, </span><span style="color: #800080;">$xws</span><span style="color: #000000;">)
    </span><span style="color: #800080;">$xml</span><span style="color: #000000;">.Save(</span><span style="color: #800080;">$xw</span><span style="color: #000000;">)
    </span><span style="color: #800080;">$xw</span><span style="color: #000000;">.Close()
}

</span><span style="color: #800080;">$csprojs</span><span style="color: #000000;"> </span><span style="color: #000000;">=</span><span style="color: #000000;"> Get</span><span style="color: #000000;">-</span><span style="color: #000000;">ChildItem </span><span style="color: #800080;">$path</span><span style="color: #000000;"> </span><span style="color: #000000;">*</span><span style="color: #000000;">.csproj </span><span style="color: #000000;">-</span><span style="color: #000000;">Recurse

</span><span style="color: #0000FF;">foreach</span><span style="color: #000000;"> (</span><span style="color: #800080;">$file</span><span style="color: #000000;"> </span><span style="color: #0000FF;">in</span><span style="color: #000000;"> </span><span style="color: #800080;">$csprojs</span><span style="color: #000000;">)
{
    updatefx </span><span style="color: #800080;">$file</span><span style="color: #000000;">.FullName
}</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.kevfoo.com/index.php/2010/05/retargeting-visual-studio-project-files-with-powershell/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Alt.Net Seattle Recap</title>
		<link>http://blog.kevfoo.com/index.php/2009/03/altnet-seattle-recap/</link>
		<comments>http://blog.kevfoo.com/index.php/2009/03/altnet-seattle-recap/#comments</comments>
		<pubDate>Thu, 05 Mar 2009 16:23:00 +0000</pubDate>
		<dc:creator>Kevin McMahon</dc:creator>
				<category><![CDATA[alt.net]]></category>
		<category><![CDATA[open spaces]]></category>
		<category><![CDATA[recap]]></category>
		<category><![CDATA[Alt]]></category>
		<category><![CDATA[Conference Attendee]]></category>
		<category><![CDATA[Conversations]]></category>
		<category><![CDATA[Developers]]></category>
		<category><![CDATA[Feelings]]></category>
		<category><![CDATA[First Experience]]></category>
		<category><![CDATA[Gist]]></category>
		<category><![CDATA[Hanselman]]></category>
		<category><![CDATA[Introspection]]></category>
		<category><![CDATA[Linq]]></category>
		<category><![CDATA[New Faces]]></category>
		<category><![CDATA[Seattle Conference]]></category>
		<category><![CDATA[Sessions]]></category>
		<category><![CDATA[Side Projects]]></category>

		<guid isPermaLink="false">http://blog.kevfoo.com/?p=87</guid>
		<description><![CDATA[I spent last weekend at the Alt.Net Seattle conference and had some time to reflect on it.&#160; This was my first experience with an open spaces style conference, and I have to admit that I am a big fan of the format.&#160; While I am not convinced this format would work for every type of [...]]]></description>
			<content:encoded><![CDATA[<p>I spent last weekend at the Alt.Net Seattle conference and had some time to reflect on it.&#160; This was my first experience with an open spaces style conference, and I have to admit that I am a big fan of the format.&#160; While I am not convinced this format would work for every type of gathering, it fit the needs for this type of group very well.</p>
<p>There was a lot made during a couple of sessions about being more accessible, newbie friendly and encouraging more new faces and ideas at these events.&#160; If you watch some of the video from Hanselman’s <a href="http://www.kyte.tv/shanselman#uri=channels/240253/361163">Why So Mean</a> session, you can get the general gist of some of the problems and solutions offered.&#160; While I think introspection is generally a good thing, I am not convinced that any progress on this front will be made, and question if these issues are as bad as some may think.&#160; As a first-time Alt.Net conference attendee and having only met one other person there in real life prior to last weekend, I never felt unwelcome or shunned and everyone that I talked to was cordial and friendly and definitely open to discussion.</p>
<p>However I didn’t get on a plane to Seattle to talk about feelings.&#160; What drew me to the conference was the opportunity to talk about the software with some of the most passionate and opinionated developers in the world.&#160; By participating in the workshops and sessions, I was able to soak up and participate in some great conversations about agile and lean, LINQ, DDD, context/spec based testing, etc.&#160; These are topics that I don’t always have the opportunity to discuss daily and certainly not with people who have the kind of expertise as those who were in Seattle.&#160; I’ve already been able to apply at work and on my side projects some of the things I’ve picked up, and I don’t think I’m close to truly synthesizing all of the great conversations that I had.</p>
<p>While my overall experience was positive and worth both the time and expense, I did feel like there were some minuses from the trip.&#160; Some of the sessions turned out to be essentially just presentations and spurred very little discussion or participation.&#160; I expected that there might be some of this due to the lack of experience or expertise with the tech.&#160; Sessions like Miguel de Icaza’s on Mono and the iPhone fell into that category since virtually no one there had any experience with developing for the iPhone and even fewer with Mono, but even things like some of the discussions on more common topics like DDD or Agile were somewhat eyes forward discussions and lacked some of the give and take that really made some of the other sessions.</p>
<p>Some of this may have been the result of people deferring to some of the *ahem* stronger personalities or undisputed domain experts in the field.&#160; The presence of some of these personalities in the sessions clearly defined or dramatically shifted the discussions.&#160; It did not take me long to learn and apply the law of two feet when I found myself at the same sessions as some of these people.&#160; I knew that it was a matter of time until the session’s conversation was going to degrade or grind to a halt.&#160; My last complaint about the conference is about some of the new age crap that that was brought in by the facilitator.&#160; I don’t think the butterfly and bumblebee metaphor stuff or the ringing of the bell really added to the experience and was not needed.&#160; I know that the facilitators were trying to make this a very Zen-like experience, but it was not.</p>
<p>In terms of key take-aways, I think I’ve identified a few.&#160; I talked with quite a few people about their experiences with agile and scrum.&#160; In hearing some other companies’ practices and problems, I was comforted by the knowledge that my current company is not as dysfunctional as I thought.&#160; While I still think we can improve, I also realized that I should probably take a moment to appreciate the good things that we do and celebrate the stuff that what works well.&#160; The other take-away was that I feel like I am in a much better position to clearly articulate the goals that I had set for myself earlier this year.&#160; Prior to this weekend I had not done a good job articulating some of these goals and had yet to express them in SMART terms.&#160; Now I am going to incorporate things like contributions to open source projects and community demos and/or presentations into my measurables when tracking my progress on some of my goals.</p>
<p>The future of these conferences I think will be in the local or region open spaces starting to be held.&#160; Already conferences are planned for Vancouver and Houston that seem to fit this bill because they are drawing in people from the region versus nationally based on a quick perusal of their attendee list.&#160; I speculate that if you reduce the concentration of some of the more well known personalities in the community, you might be able to make up for any potential loss of expertise with an increase in the amount of active participation from fresh new faces.&#160; Additionally, I think some might find the benefits of participating in open discussions with developers in their own communities more meaningful as these types of interactions may yield long-term benefits.</p>
<p>Lastly, I’d like to thank the people of the Alt.Net Seattle group for putting this together, and you should be proud of the success the conference achieved.&#160; Job well done.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.kevfoo.com/index.php/2009/03/altnet-seattle-recap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Crunching some NFL Stats with F#</title>
		<link>http://blog.kevfoo.com/index.php/2009/01/crunching-some-nfl-stats-with-f/</link>
		<comments>http://blog.kevfoo.com/index.php/2009/01/crunching-some-nfl-stats-with-f/#comments</comments>
		<pubDate>Sat, 10 Jan 2009 16:51:44 +0000</pubDate>
		<dc:creator>Kevin McMahon</dc:creator>
				<category><![CDATA[F#]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Berri]]></category>
		<category><![CDATA[Bolognese]]></category>
		<category><![CDATA[C World]]></category>
		<category><![CDATA[Couple Chapters]]></category>
		<category><![CDATA[Csv File]]></category>
		<category><![CDATA[Don Syme]]></category>
		<category><![CDATA[Football Stats]]></category>
		<category><![CDATA[Functional Programming]]></category>
		<category><![CDATA[Interactive Window]]></category>
		<category><![CDATA[Java World]]></category>
		<category><![CDATA[Linq]]></category>
		<category><![CDATA[Nfl Stats]]></category>
		<category><![CDATA[Problem Domain]]></category>
		<category><![CDATA[Qb Rating]]></category>
		<category><![CDATA[Qb Stats]]></category>
		<category><![CDATA[Stat]]></category>
		<category><![CDATA[Turnovers]]></category>
		<category><![CDATA[Wages]]></category>

		<guid isPermaLink="false">http://blog.kevfoo.com/?p=24</guid>
		<description><![CDATA[To explore functional programming, I’ve decided to return to a familiar problem domain, football stats. I used this domain a couple years ago when I was in the process of making the transition from the Unix-based OS/Java world to the Microsoft/C# world. I am the type of person that learns better by doing than studying, [...]]]></description>
			<content:encoded><![CDATA[<p>To explore functional programming, I’ve decided to return to a familiar problem domain, football stats. I used this domain a couple years ago when I was in the process of making the transition from the Unix-based OS/Java world to the Microsoft/C# world. I am the type of person that learns better by doing than studying, so I’m going to try and jump in and cobble something together to start the learning process. I’ve watched the PDC presentation by <a href="http://blogs.msdn.com/lucabol/" target="_blank">Luca Bolognese</a>, and I’ve read through the first couple chapters of <a href="http://blogs.msdn.com/dsyme/" target="_blank">Don Syme’s</a> <em>Expert F#</em>, so consider me armed with an F# Interactive window and dangerous.</p>
<p>The first stat that I plan to look at is the <a href="http://dberri.wordpress.com/2007/09/09/a-new-qb-score/" target="_blank">QB Score Stat</a> as outlined by Berri, et al. in <a href="http://www.amazon.com/gp/product/0804752877?ie=UTF8&amp;tag=kev02-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0804752877" target="_blank"><em>Wages of Wins</em></a>.&#160; The stat is much easier to calculate than the traditional QB Rating used by the NFL, and if you read the link and/or book, you’ll see that it correlates much better to wins and points than QB rating. For our purposes, I’ll just outline the formula here but I do recommend checking out the links for more info.</p>
<p align="center">QB Score = Total Yards &#8211; 3 * Plays &#8211; 30 * Turnovers</p>
<p align="left">I got the <a href="http://sports.yahoo.com/nfl/stats/byposition?pos=QB&amp;conference=NFL&amp;year=season_2008&amp;timeframe=All&amp;sort=626&amp;old_category=QB" target="_blank">2008 QB stats</a> from Yahoo, dumped them into Excel and then saved them off into CSV.&#160; This can be done programmatically fairly easily with <a href="http://www.codeplex.com/htmlagilitypack" target="_blank">HtmlAgilityPack</a> and Linq to Xml but I’ll save that for another post.&#160; I’ve provided a copy of the stats in CSV <a href="http://www.kevfoo.com/files/QB_Stats_2008.csv" target="_blank">here</a>.</p>
<p>So to get started here is what we have to do in order to calculate the raw QB score and the QB score per play for all the NFL QB’s:</p>
<ol>
<li>Read in the CSV file </li>
<li>Grab the relevant stats for our calculation </li>
<li>Calculate the QB score per play for each QB </li>
<li>Return the QB name and the score. </li>
</ol>
<p>I’ll tackle this step by step and we can verify our results via the F# Interactive window.</p>
<p>To read in the file we can leverage the .Net System.IO library. The call pattern to read a file into memory is identical to what you would see in C# or VB and is pretty straight forward.</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;#light    &amp;lt;br /&amp;gt;open System.IO    &amp;lt;br /&amp;gt;let filePath = &amp;amp;quot;D:\code\data\QB_Stats_2008.csv&amp;amp;quot;    &amp;lt;br /&amp;gt;let stream = new FileStream(filePath, FileMode.Open)    &amp;lt;br /&amp;gt;let reader = new StreamReader(stream)    &amp;lt;br /&amp;gt;let csv = reader.ReadToEnd()    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Here is the output of the F# interaction window.   <br /><em>val filePath : string</em>
<p>val stream : System.IO.FileStream</p>
<p>val reader : System.IO.StreamReader</p>
<p>val csv : string</p>
</p>
<p>As we can see from the output, &#8216;csv&#8217; is string that holds the contents of the QB stats file. Since we know that the file is is a CSV file, we can break it down into its individual elements like so:</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;let stats =    &amp;lt;br /&amp;gt;csv.Split([|'\n'|])    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.skip 1    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map(fun line -&amp;amp;gt; line.Split([|','|]))    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map(fun values -&amp;amp;gt;    &amp;lt;br /&amp;gt;string values.[0], // qb name    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[5]), // att    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[7]), // pass yds    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[11]), // int    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[12]), // rushes    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[13]), // rush yds    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[17]), // sacks    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[20])) // fumbles lost    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Since &#8216;csv&#8217; is a string, we can use the Split method to chunk the string up into individual lines using the &#8216;\n&#8217; character as our split token.&#160; Once split into individual lines, the pipeline operator on line 3 further processes each line.&#160; Sequences in F# can be thought of as IEnumerables from C# and come with some nice baked-in methods to help with processing. Our QB stats CSV file has as its first line a key to the data. We&#8217;ll need to skip that first line before we get to process the real data, and to do so we&#8217;ll use one of those nice baked-in methods (Seq.skip) to do so.</p>
<p>Line 4 further deconstructs the csv file into the individual comma delimited values tokenizing each line. After the lines have been tokenized the individual values can be read. Here I&#8217;ve created a tuple to hold each lines values. The tokenized values have been collected in a tuple that holds 8 values.&#160; The mapping of the values is specified by the comments.</p>
<p>Here is the output of the F# interaction window after step 2:</p>
<p><em>val stats : seq </em></p>
<p>After step two we have a sequence of tuples that have only the stats and information that we care about. The next step now becomes calculating the QB score. The calculation of the score requires three sub-steps, so let us revise the outline we laid out earlier to include them.</p>
<ol>
<li>Read in the CSV file </li>
<li>Grab the relevant stats for our calculation </li>
<li>Calculate the QB score per play for each QB
<ol>
<li><strong><em>Create the formula function </em></strong></li>
<li><strong><em>Compute the components of the formula </em></strong></li>
<li><strong><em>Create the desired output</em></strong> </li>
</ol>
</li>
<li>Return the QB name and the score </li>
</ol>
<p>Let&#8217;s tackle the first sub-step and codify the formula now and see what we&#8217;ll need to provide from the data we just acquired.</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;let qbcalc (plays,yards,turnovers) = yards - 3 * plays - 30 * turnovers    &amp;lt;br /&amp;gt;</pre>
</p>
<p>This line of code creates a function called qbcalc that takes in a tuple composed of the plays, yards, and turnovers components of the formula.</p>
<p>If we run the qbcalc function through the interactive window we get:</p>
<p><em>val qbcalc : int * int * int -&gt; int</em></p>
<p>The end result of this is the raw QB score. The arithmetic operations in F# are similar to most languages, so the formula is a straight forward expression without any surprises. Since we know plays, yards and turnovers are all integer values, we could further constrain the types of values that the tuple is composed of, but F#’s type inference already does this for us, so it is not needed. When the compiler analyzed this code, it was able to ascertain from the operations and the integers used that the plays, yards and turnover values were of type int and automatically created the int constraints.</p>
<p>The next step is to compute the individual values of plays, yards, and turnovers. Before we start, I just want to note that I am sure there is a slicker, more concise way to do this, but this is my first go at this, so pardon the mess.</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;let names = stats |&amp;amp;gt; Seq.map(fun(name,_,_,_,_,_,_,_) -&amp;amp;gt; name)    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Here we start to perform operations on the stats sequence we captured from the CSV file. The basic structure of what I am doing here is grabbing the specific values of the components I am looking to either aggregate (names) or calculate (plays, yards, and turnovers) from the sequence and mapping them to a new sequence. Here is an example of how to create the plays sequence.</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;let plays = stats |&amp;amp;gt; Seq.map(fun(_,att,_,_,rush,_,sacks,_) -&amp;amp;gt; att+rush+sacks)    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Here the stats sequence is pushed through the pipeline operator ( |&gt; ) which allows you to chain functions in a sequence. This is happens because, as pointed out in <a href="http://www.amazon.com/gp/product/1590598504?ie=UTF8&amp;tag=kev02-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1590598504">Expert F#</a>, the pipeline operator is just function application in reverse. This can be expressed like so:</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;let (|&amp;amp;gt;) x f = f x     &amp;lt;br /&amp;gt;</pre>
</p>
<p>So in our case when we have the following:</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;stats |&amp;amp;gt; Seq.map (fun(_,att,_,_,rush,_,sacks,_) -&amp;amp;gt; att+rush+sacks)    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Chaining the stats sequence with the the Seq.map function will apply the function we’ve defined in the parenthesis to each element in the stats sequence and return a new sequence with the results of the function.&#160; The function we have defined has a signature that matches the 8 value tuples that compose the stats sequence. Since only a few values are needed to be computed for the various values, ‘_’ can be assigned to the values in the parameter definition and more meaningful names can be given to the values we care about. On the right hand side of the -&gt; (a symbol that represents a function), we do the simple adding of the values. Again the results of this function are collected in a new sequence that is returned from the Seq.map call.</p>
<p>After all the individual components of the QB score formula have been computed, we’re left with a bunch of individual sequence values that need to be reconstructed into something that we can pass to the the qbcalc function. The calculation function is defined as taking a tuple composed of a play, yard, and turnover values, so we need to utilize another method that Seq provides called zip.</p>
<p>Here is the code that crunches the individual components.</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;let getStats =    &amp;lt;br /&amp;gt;let stats = loadQBStats    &amp;lt;br /&amp;gt;let names = stats |&amp;amp;gt; Seq.map(fun(name,_,_,_,_,_,_,_) -&amp;amp;gt; name)    &amp;lt;br /&amp;gt;let plays = stats |&amp;amp;gt; Seq.map(fun(_,att,_,_,rush,_,sacks,_) -&amp;amp;gt; att+rush+sacks)    &amp;lt;br /&amp;gt;let yards = stats |&amp;amp;gt; Seq.map(fun(_,_,passyd,_,_,rushyd,_,_) -&amp;amp;gt; passyd + rushyd)    &amp;lt;br /&amp;gt;let turnovers = stats |&amp;amp;gt; Seq.map( fun(_,_,_,int,_,_,_,fum) -&amp;amp;gt; int+fum)    &amp;lt;br /&amp;gt;Seq.zip3 plays yards turnovers |&amp;amp;gt; Seq.zip names    &amp;lt;br /&amp;gt;</pre>
</p>
<p>The final step to complete is to apply the qbcalc function to each play, yard and turnover tuple, and zipping up the resulting sequence with the names sequence rounds out steps and completes our task. The values were balled up into tuples in previous steps, so a lot of what is left to do is unpacking what we need to do the actual calculation and then reassemble to the output. The unpacking of the tuples are done with the fst and snd functions that are applied to the sequences. These methods return the fst, and the snd functions return the first and second elements of the tuples respectively. The last line of the doCalc function divides the raw QB score over the plays completing the calculation and then back pipes that sequence to be zipped up with the names. The zipped sequence gets returned, and at last we’ve calculated the QB score per play for the 2008 season. The last thing to note with the calculation is that in order to get better precision from the final result, the int values being divided need to be converted to a decimal. If the integers aren’t converted, then the results of the division operation will be rounded down, and we’ll lose precision on the calculation.</p>
<p>
<pre class="brush: fsharp;">    &amp;lt;br /&amp;gt;let doCalc =     &amp;lt;br /&amp;gt;let stats =    &amp;lt;br /&amp;gt;getStats    &amp;lt;br /&amp;gt;let names =    &amp;lt;br /&amp;gt;stats |&amp;amp;gt; Seq.map fst    &amp;lt;br /&amp;gt;let rawScore =    &amp;lt;br /&amp;gt;stats    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map snd    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map qbcalc    &amp;lt;br /&amp;gt;let plays =     &amp;lt;br /&amp;gt;stats    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map snd    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map (fun (plays,_,_) -&amp;amp;gt; plays)    &amp;lt;br /&amp;gt;let components =    &amp;lt;br /&amp;gt;Seq.zip rawScore plays     &amp;lt;br /&amp;gt;Seq.zip names &amp;amp;lt;| Seq.map(fun(x:int, y:int) -&amp;amp;gt; System.Convert.ToDecimal(x)/ System.Convert.ToDecimal(y)) components    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Below is the complete source listing of my first crack at doing something useful with F#. There are a couple things (the packing and repacking of the tuples, the CSV parsing) that scream “optimize me”. In my next F# post, I’ll refactor this code to slim it down and package it up so I can display these results graphically via C#.</p>
<p>
<pre class="brush: fsharp;">   &amp;lt;br /&amp;gt;#light    &amp;lt;br /&amp;gt;open System.IO&amp;lt;/p&amp;gt;  &amp;lt;p&amp;gt;let loadQBStats =   &amp;lt;br /&amp;gt;let filePath = &amp;amp;quot;D:\code\ProFootballDB\Data\QB_Stats_2008.csv&amp;amp;quot;    &amp;lt;br /&amp;gt;let stream = new FileStream(filePath, FileMode.Open)    &amp;lt;br /&amp;gt;let reader = new StreamReader(stream)    &amp;lt;br /&amp;gt;let csv = reader.ReadToEnd()&amp;lt;/p&amp;gt;  &amp;lt;p&amp;gt;let stats =   &amp;lt;br /&amp;gt;csv.Split([|'\n'|])    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.skip 1    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map(fun line -&amp;amp;gt; line.Split([|','|]))    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map(fun values -&amp;amp;gt;    &amp;lt;br /&amp;gt;string values.[0], // qb name    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[5]), // att    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[7]), // pass yds    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[11]), // int    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[12]), // rushes    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[13]), // rush yds    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[17]), // sacks    &amp;lt;br /&amp;gt;System.Int32.Parse(values.[20])) // fumbles lost    &amp;lt;br /&amp;gt;stats&amp;lt;/p&amp;gt;  &amp;lt;p&amp;gt;let qbcalc (plays,yards,turnovers) = yards - 3 * plays - 30 * turnovers&amp;lt;/p&amp;gt;  &amp;lt;p&amp;gt;let getStats =   &amp;lt;br /&amp;gt;let stats = loadQBStats    &amp;lt;br /&amp;gt;let names = stats |&amp;amp;gt; Seq.map(fun(name,_,_,_,_,_,_,_) -&amp;amp;gt; name)    &amp;lt;br /&amp;gt;let plays = stats |&amp;amp;gt; Seq.map(fun(_,att,_,_,rush,_,sacks,_) -&amp;amp;gt; att+rush+sacks)    &amp;lt;br /&amp;gt;let yards = stats |&amp;amp;gt; Seq.map(fun(_,_,passyd,_,_,rushyd,_,_) -&amp;amp;gt; passyd + rushyd)    &amp;lt;br /&amp;gt;let turnovers = stats |&amp;amp;gt; Seq.map( fun(_,_,_,int,_,_,_,fum) -&amp;amp;gt; int+fum)    &amp;lt;br /&amp;gt;Seq.zip3 plays yards turnovers |&amp;amp;gt; Seq.zip names&amp;lt;/p&amp;gt;  &amp;lt;p&amp;gt;let doCalc =    &amp;lt;br /&amp;gt;let stats =    &amp;lt;br /&amp;gt;getStats    &amp;lt;br /&amp;gt;let names =    &amp;lt;br /&amp;gt;stats |&amp;amp;gt; Seq.map fst    &amp;lt;br /&amp;gt;let rawScore =    &amp;lt;br /&amp;gt;stats    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map snd    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map qbcalc    &amp;lt;br /&amp;gt;let plays =     &amp;lt;br /&amp;gt;stats    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map snd    &amp;lt;br /&amp;gt;|&amp;amp;gt; Seq.map (fun (plays,_,_) -&amp;amp;gt; plays)    &amp;lt;br /&amp;gt;let components =    &amp;lt;br /&amp;gt;Seq.zip rawScore plays     &amp;lt;br /&amp;gt;Seq.zip names &amp;amp;lt;| Seq.map(fun(x:int, y:int) -&amp;amp;gt; System.Convert.ToDecimal(x)/ System.Convert.ToDecimal(y)) components    &amp;lt;br /&amp;gt;</pre>
</p>
<p>Useful links:</p>
<ul>
<li><a href="http://www.amazon.com/gp/product/1590598504?ie=UTF8&amp;tag=kev02-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1590598504">Expert F# (Expert&#8217;s Voice in .Net)</a> </li>
<li><a title="http://msdn.microsoft.com/en-us/fsharp/default.aspx" href="http://msdn.microsoft.com/en-us/fsharp/default.aspx">Microsoft F# Developer Center</a> </li>
<li><a href="http://channel9.msdn.com/pdc2008/TL11/" target="_blank">PDC Video : Introduction to Microsoft F#</a> </li>
<li><a href="http://dberri.wordpress.com/2007/09/09/a-new-qb-score/" target="_blank">QB Score Stat</a> from <a href="http://www.amazon.com/gp/product/0804752877?ie=UTF8&amp;tag=kev02-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0804752877">Wages of Wins</a> </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.kevfoo.com/index.php/2009/01/crunching-some-nfl-stats-with-f/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
