F# List of String Manipulation

I am learning a valuable lesson about stack overflow – if I write down my question 1st and then search the key words using my favorite search engine, I can sometimes find the answer.

For example, I wanted to turn a list of string into 1 long string: ["A"; "B"; "C"] becomes “ABC”

I first tried via the functions found in the List class:

  1. let stringList = ["A"; "B"; "C"]
  2. let sumValues = List.sum stringList

 

The problem was I got the Red Squiggly Line Of Approbation on the stringList

 image

So then I typed a question for stack overflow with the words “string concatenation” and then I thought to try and try that into Google.  The 1st result was this: http://msdn.microsoft.com/en-us/library/ee353761.aspx

And low and behold, the example is exactly what I want to do

  1. let stringList = ["A"; "B"; "C"]
  2. let sumValues = String.concat "" stringList

image

I then applied that to a list of ints:

  1. let intList = [0..10]
  2. let concatValues =
  3.     intList
  4.     |> Seq.map string
  5.     |> String.concat ""

 

and after feeling good about figuring this out, I immediately began wondering how I can reduce that to 1 line of code

Smile

Advertisements

F#: “Unexpected InFix” and “Incomplete value”

Dear Future Jamie:

If you are doing some pipe forwards like this:

  1. let createConcatRandomList = List.init 9 (fun _ -> randomNumberGenerator.Next(0,9))
  2.     |> Seq.map string
  3.     |> String.concat ""

 

and you are getting this:

image

with the RSLA  on the let saying:

Incomplete value or function definition. If this is in an expression, the body of the expression must be indented to the same column as the ‘let’ keyword.

and the RSLA on the |> saying:

Unexpected infix operator in binding. Expected incomplete structured construct at or before this point or other token.

Then your pipe is in the wrong place.  Tab it over to inside the List like this:

  1. let createConcatRandomList = List.init 9 (fun _ -> randomNumberGenerator.Next(0,9))
  2.                                 |> Seq.map string
  3.                                 |> String.concat ""

 

Love,

Current Jamie

 

PS: you really should exercise more…

WPF and Images In Subdirectories

Dear Jamie of the future:

When you have a WPF project and you want to add images in a subfolder to a page, you need to do this:

  1. Image x:Name="TestImage" Source="/Images/TestImage.png">

Note the forward slashes.

Also, if you have two images and you change the location on both, you will get the blue squiggly line of approbation like this:

image

If you fix the first one, BOTH will still have the BSLA:

image

So it looks to be all or none – perhaps b/c the IDE can’t parse anything so it leaves the last BSLA in place?  In any event, fix all of them and the BSLA goes away.

Sincerely,

Jamie of the past

PS: you really should exercise more….

MVVM on a Windows Store App

I blog for a couple of reasons:

  1. Professionalism – to think and write about a topic at least once a week
  2. Marketability – when I talk to people about jobs, I can point them to my blog to see how I code (and think about coding).  To me, it is much more effective and insightful than a exam or esoteric interview questions
  3. To keep track of stuff for me -  I write stuff down so I don’t have to remember something.

That last point came back to me today.  I wanted to take a break from F# so I looked at MVVM in a Windows RT application.  I went to refresh my brain on MVVM so I hit up Bing and Google.  All of the recent articles that I ran across talked about MVVM –as an after thought.  They were all pimping MVVM helpers like relay command, frameworks like MVVMLight, and other non-core MVVM concepts.  All important to be sure, but non related to MVVM.

I then hit up my own blog and sure enough – I blogged about MVVM 2 years ago when I did a Windows Phone 7 app and I could see just the MVVM in action.  So then I fired up a basic Windows RT application and added 3 folders to it: Models, Views, and ViewModels.

I then added a Model like so:

  1. public class Person
  2. {
  3.     public Int32 Id { get; set; }
  4.     public String FirstName { get; set; }
  5.     public String LastName { get; set; }
  6. }

 

I then added a View Model

  1. public class PersonViewModel: INotifyPropertyChanged
  2. {
  3.     private Person _person = null;
  4.     public event PropertyChangedEventHandler PropertyChanged;
  5.  
  6.     public PersonViewModel(Person person)
  7.     {
  8.         _person = person;
  9.     }
  10.  
  11.     public Int32 Id
  12.     {
  13.         get { return _person.Id; }
  14.         set
  15.         {
  16.             PropertyChanged(this, new PropertyChangedEventArgs("Id"));
  17.             _person.Id = value;
  18.         }
  19.     }
  20.     public String FirstName
  21.     {
  22.         get { return _person.FirstName; }
  23.         set
  24.         {
  25.             PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
  26.             _person.FirstName = value;
  27.         }
  28.     }
  29.     public String LastName
  30.     {
  31.         get { return _person.LastName; }
  32.         set
  33.         {
  34.             PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
  35.             _person.LastName = value;
  36.         }
  37.     }
  38.  
  39. }

I then added a View like so:

image

and then in the code behind of the View:

  1. public PersonView(PersonViewModel viewModel)
  2. {
  3.     InitializeComponent();
  4.     this.mainGrid.DataContext = viewModel;
  5. }

 

Then in the main page, the ViewModel is injected into the View:

  1. public partial class MainWindow : Window
  2. {
  3.     public MainWindow()
  4.     {
  5.         InitializeComponent();
  6.         Person person = new Person(){Id=0,FirstName="Test",LastName="Person"};
  7.         PersonViewModel viewModel = new PersonViewModel(person);
  8.         PersonView view = new PersonView(viewModel);
  9.         view.Show();
  10.     }
  11. }

 

And we have data binding:

image

 

Now I know this is not a complete project and that many patterns are helpful (esp. the relay command one), but this is the core of making MVVM work the MSFT way: data binding and IPropertyNotifyChanged.

Multiple Linear Regression Using R and F#

Following up on my previous post, I decided to test calling R from F# for a multiple linear regression.  I decided to use the dataset from chapter 1 of Machine Learning For Hackers (ufo sightings).

Step #1 was to open R from F#

  1. #r @"C:\TFS\Tff.RDotNetExample_Solution\packages\R.NET.1.5.3\lib\net40\RDotNet.dll"
  2. #r @"C:\TFS\Tff.RDotNetExample_Solution\packages\R.NET.1.5.3\lib\net40\RDotNet.NativeLibrary.dll"
  3.  
  4. open System.IO
  5. open RDotNet
  6.  
  7.  
  8. //open R
  9. let environmentPath = System.Environment.GetEnvironmentVariable("PATH")
  10. let binaryPath = @"C:\Program Files\R\R-3.0.1\bin\x64"
  11. System.Environment.SetEnvironmentVariable("PATH",environmentPath+System.IO.Path.PathSeparator.ToString()+binaryPath)
  12.  
  13. let engine = RDotNet.REngine.CreateInstance("RDotNet")
  14. engine.Initialize()

 

Step #2 was to import the ufo dataset and clean it:

  1. //open dataset
  2. let path = @"C:\TFS\Tff.RDotNetExample_Solution\Tff.RDotNetExample\ufo_awesome.txt"
  3. let fileStream = new FileStream(path,FileMode.Open,FileAccess.Read)
  4. let streamReader = new StreamReader(fileStream)
  5. let contents = streamReader.ReadToEnd()
  6. let usStates = [|"AL";"AK";"AZ";"AR";"CA";"CO";"CT";"DE";"DC";"FL";"GA";"HI";"ID";"IL";"IN";"IA";
  7.                     "KS";"KY";"LA";"ME";"MD";"MA";"MI";"MN";"MS";"MO";"MT";"NE";"NV";"NH";"NJ";"NM";
  8.                     "NY";"NC";"ND";"OH";"OK";"OR";"PA";"RI";"SC";"SD";"TN";"TX";"UT";"VT";"VA";"WA";
  9.                     "WV";"WI";"WY"|]
  10. let cleanContents =
  11.     contents.Split([|'\n'|])
  12.     |> Seq.map(fun line -> line.Split([|'\t'|]))
  13.     |> Seq.filter(fun values -> values |> Seq.length = 6)
  14.     |> Seq.filter(fun values -> values.[0].Length = 8)
  15.     |> Seq.filter(fun values -> values.[1].Length = 8)
  16.     |> Seq.filter(fun values -> System.Int32.Parse(values.[0].Substring(0,4)) > 1900)
  17.     |> Seq.filter(fun values -> System.Int32.Parse(values.[1].Substring(0,4)) > 1900)
  18.     |> Seq.filter(fun values -> System.Int32.Parse(values.[0].Substring(0,4)) < 2100)
  19.     |> Seq.filter(fun values -> System.Int32.Parse(values.[1].Substring(0,4)) < 2100)
  20.     |> Seq.filter(fun values -> System.Int32.Parse(values.[0].Substring(4,2)) > 0)
  21.     |> Seq.filter(fun values -> System.Int32.Parse(values.[1].Substring(4,2)) > 0)
  22.     |> Seq.filter(fun values -> System.Int32.Parse(values.[0].Substring(4,2)) <= 12)
  23.     |> Seq.filter(fun values -> System.Int32.Parse(values.[1].Substring(4,2)) <= 12)      
  24.     |> Seq.filter(fun values -> System.Int32.Parse(values.[0].Substring(6,2)) > 0)
  25.     |> Seq.filter(fun values -> System.Int32.Parse(values.[1].Substring(6,2)) > 0)
  26.     |> Seq.filter(fun values -> System.Int32.Parse(values.[0].Substring(6,2)) <= 31)
  27.     |> Seq.filter(fun values -> System.Int32.Parse(values.[1].Substring(6,2)) <= 31)
  28.     |> Seq.filter(fun values -> values.[2].Split(',').[1].Trim().Length = 2)
  29.     |> Seq.filter(fun values -> Seq.exists(fun elem -> elem = values.[2].Split(',').[1].Trim().ToUpperInvariant()) usStates)
  30.     |> Seq.map(fun values ->
  31.         System.DateTime.ParseExact(values.[0],"yyyymmdd",System.Globalization.CultureInfo.InvariantCulture),
  32.         System.DateTime.ParseExact(values.[1],"yyyymmdd",System.Globalization.CultureInfo.InvariantCulture),
  33.         values.[2].Split(',').[0].Trim(),
  34.         values.[2].Split(',').[1].Trim().ToUpperInvariant(),
  35.         values.[3],
  36.         values.[4],
  37.         values.[5])
  38. cleanContents
  39.  
  40. let relevantContents =
  41.     cleanContents
  42.     |> Seq.map(fun (a,b,c,d,e,f,g) -> a.Year,d,g.Length)

 

Step #3 was to run the regression using the dataset.  You will notice that I made the length of the report the Y (dependent) variable – not that I think I will find any causality but it was a good enough to use).  Also, notice the Seq.Map of each column in the larger Seq(Int*String*Int) into the Vector.

  1. let reportLength = engine.CreateIntegerVector(relevantContents |> Seq.map (fun (a,b,c) -> c))
  2. engine.SetSymbol("reportLength", reportLength)
  3. let year = engine.CreateIntegerVector(relevantContents |> Seq.map (fun (a,b,c) -> a))
  4. engine.SetSymbol("year", year)
  5. let state = engine.CreateCharacterVector(relevantContents |> Seq.map (fun (a,b,c) -> b))
  6. engine.SetSymbol("state", state)
  7.  
  8. let calcExpression = "lm(formula = reportLength ~ year + state)"
  9. let testResult = engine.Evaluate(calcExpression).AsList()

 

Sure enough, you can get the results of the regression.  The challenge is teasing out the values that are interesting from the real data structure that is returned (testResult in this example)

> testResult.Item(0).AsCharacter();;
val it : CharacterVector =
  seq
    ["31775.5599180962"; "-15.2760355122386"; "37.8028841898059";
     "-91.2309146099364"; …]

Intercepts, I think, are Item(0).

RDotNet and F#: Example from the Code Project

I added the R type provider via NuGet and it showed up in my references tab:

image

When I go to reference the library in code:

image

But if I fully-qualify the reference, it works:

image

Very Strange, but then I get yummy intellisense

image

 

I then wanted to add in the RDotNet assembly:

Filly qualified does not work

image

but if you add the “@” symbol, it does…

image

I then added the test case that is on codeplex(https://rdotnet.codeplex.com/).

After figuring out that your can only have 1 instance of the engine going at any point in time and the FSI holds a reference (my issue logged here), I wrote this very much procedural code:

  1. #r @"C:\TFS\Tff.RDotNetExample_Solution\packages\R.NET.1.5.3\lib\net40\RDotNet.dll"
  2. #r @"C:\TFS\Tff.RDotNetExample_Solution\packages\R.NET.1.5.3\lib\net40\RDotNet.NativeLibrary.dll"
  3.  
  4. open System.IO
  5. open RDotNet
  6.  
  7. let environmentPath = System.Environment.GetEnvironmentVariable("PATH")
  8. let binaryPath = @"C:\Program Files\R\R-3.0.1\bin\x64"
  9. System.Environment.SetEnvironmentVariable("PATH",environmentPath+System.IO.Path.PathSeparator.ToString()+binaryPath)
  10.  
  11. let engine = RDotNet.REngine.CreateInstance("RDotNet")
  12. engine.Initialize()
  13.  
  14. let group1 = engine.CreateNumericVector([30.02;29.99;30.11;29.97;30.01;29.99]);
  15. engine.SetSymbol("group1", group1)
  16. let expression = "group2 <- c(29.89,29.93,29.72,29.98,30.02,29.98)"
  17. let group2 = engine.Evaluate(expression).AsNumeric()
  18.  
  19. let calcExpression = "t.test(group1, group2)"
  20. let testResult = engine.Evaluate(calcExpression).AsList()
  21.  
  22. printfn "P-Value = %A" (testResult.Item("p.value").AsNumeric())

With the results coming out:

image