Skip to content

Instantly share code, notes, and snippets.

@mattpodwysocki
Created April 8, 2009 05:08
Show Gist options
  • Save mattpodwysocki/91648 to your computer and use it in GitHub Desktop.
Save mattpodwysocki/91648 to your computer and use it in GitHub Desktop.
#light
module Option =
let some (n:'b) (f:'a -> 'b) (opt:'a option) : 'b =
match n, f, opt with
| n, _, None -> n
| _, f, Some x -> f x
module Map =
let insertWith (f:'a -> 'a -> 'a) (k:'tkey) (v:'a) (m:Map<'tkey, 'a>) =
match Map.tryfind k m with
| None -> Map.add k v m
| Some x -> let res = f v x
Map.add k res m
let map' (f:'a -> 'b) : Map<'k, 'a> -> Map<'k, 'b> =
Map.mapi (fun _ v -> f v)
module SlopeOne =
type Rating<'a> = Map<'a, float>
type SlopeOne<'a> = Map<'a, Map<'a, (int * float)>>
let addT (a, b) (c, d) = (a+c, b+d)
let update (s:SlopeOne<'a>) : Rating<'a> seq -> SlopeOne<'a> =
let norm (a, b) = (a, b / float a)
let update' s rm =
let rs = Map.to_seq rm
let prod = seq {for (a, m) in rs do for(b, n) in rs -> ((a, b), (1, m - n))}
let insert m (a, b) v =
let foo = Some << Option.some (Map.add b v Map.empty) (Map.insertWith addT b v)
Map.alter foo a m
Seq.fold(fun m (k, v) -> insert m k v) s prod
Map.map' (Map.map' norm) << Seq.fold update' s
let predict (matrixIn:SlopeOne<'a>) (userRatings:Rating<'a>) =
let insert oldM (item1, foundRating, userRating) =
let (count, normRating) = foundRating
let totalRating = (float count) * (normRating + userRating)
Map.insertWith addT item1 (count, totalRating) oldM
let freqM =
Seq.fold insert Map.empty (
seq { for (item1, innerMap) in Map.to_seq matrixIn do
if not (userRatings.ContainsKey item1) then
for (userItem, userRating) in Map.to_seq userRatings do
if item1 <> userItem then
if Map.exists (fun k _ -> k = userItem) innerMap then
let foundRating = Map.find userItem innerMap
yield (item1, foundRating, userRating)
})
let normM = Map.map' (fun (count, totalRating) -> totalRating / (float count)) freqM
Map.filter (fun _ normRating -> normRating > 0.) normM
let userData =
List.map Map.of_list [
[("squid", 1.0); ("cuttlefish", 0.5); ("octopus", 0.2)];
[("squid", 1.0); ("octopus", 0.5); ("nautilus", 0.2)];
[("squid", 0.2); ("octopus", 1.0); ("cuttlefish", 0.4); ("nautilus", 0.4)];
[("cuttlefish", 0.9); ("octopus", 0.4); ("nautilus", 0.5)]]
let prediction = SlopeOne.predict (SlopeOne.update Map.empty userData) (Map.of_list [("squid", 0.4)])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment