Skip to content

Instantly share code, notes, and snippets.

@sudipto80
Last active January 21, 2016 02:05
Show Gist options
  • Save sudipto80/7d750a446de3ff1f7f53 to your computer and use it in GitHub Desktop.
Save sudipto80/7d750a446de3ff1f7f53 to your computer and use it in GitHub Desktop.
User User Collaborative Filtering 1
//Average rating for all other rated items by the user
//except the item "except"
let rBaru(u:float list)(except:int)=
let filtered = u |> List.mapi(fun i j -> if i <> except then j else 0.0)
|> List.filter(fun t -> t <> 0.0)
float ( List.sum filtered ) / float filtered.Length
//The following function finds the common item indices
let commonItemIndices (ratings:(float list)list)(a:int)(u:int)=
List.zip ratings.[a] ratings.[u]
|> List.mapi (fun index rating ->
if fst rating <> 0.0
&& snd rating <> 0.0 then index else -1 )
|> List.filter ( fun index -> index <> -1)
//The following function returns the average of user a and u
let mu_au (ratings:(float list)list)(a:int)(u:int)=
let com = commonItemIndices ratings a u
let mu_a = com |> List.map (fun index -> ratings.[a].[index]) |> List.average
let mu_u = com |> List.map (fun index -> ratings.[u].[index]) |> List.average
(mu_a,mu_u)
//Calculates User-User similarity using Pearson's Correlation Coefficient
let Simu (ratings:(float list)list) (a:int)(u:int)=
//Indices of the items rated by both user a and u
let common = commonItemIndices ratings a u
let averages = mu_au ratings a u
let ra = fst averages
let ru = snd averages
let num = common |> List.sumBy (fun index -> (ratings.[a].[index] - ra)*
(ratings.[u].[index] - ru))
let d1 = common |> List.sumBy (fun index -> (ratings.[a].[index] - ra)** 2.0)
let d2 = common |> List.sumBy (fun index -> (ratings.[u].[index] - ru)** 2.0)
//If either d1 or d2 is 0 then we shall hit a divide by zero case
//to avoid that we must return 0.
if d1 = 0.0 || d2 = 0.0 then 0.0 else num / ((sqrt d1) * (sqrt d2 ))
//User-User Basic Collaborative Filtering - basic
let Predictu(ratings:(float list)list)(a:int)(i:int) =
let rb = rBaru ratings.[a] i
let neighborIndices = ratings
|> List.mapi(fun index rating ->
if rating.[i] <> 0.0 then index else -1)
|> List.filter(fun index -> index <> -1)
//Rating of neighbors are obtained
let neighbors = neighborIndices
|> List.map (fun index -> ratings.[index])
let gaps = neighbors |> List.map (fun neighbor -> neighbor.[i] - (rBaru neighbor i))
let simis = neighborIndices |> List.map (fun index -> Simu ratings a index)
let num = List.zip gaps simis |> List.sumBy (fun t -> fst t * snd t)
let den = simis |> List.sumBy (fun similarity -> abs similarity)
if den <> 0.0 then
let div = num / den
let predicted = rb + div
//Sometimes the value of "predicted" can be beyond the range. [1-5]
//so having a 7 is same as 5.0 in practice (meaning the user might love the item)
//so is having a -1 which is same as 1 (meaning the user might hate the item)
if predicted > 5.0 then 5.0 elif predicted < 1.0 then 1.0 else predicted
else
0.0 //We don't know what it is.
//Calculates the standard deviation
let stddev(list:float list)=
sqrt (List.fold (fun acc elem -> acc + (float elem - List.average list) ** 2.0 ) 0.0
list / float list.Length)
//Calculates the z-score
let zscore (ratings:(float list)list)(userIndex:int)(itemIndex:int)=
let rBar = rBaru ratings.[userIndex] itemIndex
let sigma = stddev ratings.[userIndex]
ratings.[userIndex].[itemIndex] - rBar / sigma
let PredictuZ(ratings:(float list)list)(a:int)(i:int) =
let rb = rBaru ratings.[a] i
let neighborIndices = ratings
|> List.mapi(fun index rating ->
if rating.[i] <> 0.0
then index else -1)
|> List.filter(fun index -> index <> -1)
let neighbors = neighborIndices|> List.map (fun index -> ratings.[index])
//This line is changed to use Z-Score instead of just the differences.
let gaps = neighbors |> List.map (fun neighbor -> zscore ratings a i)
let simis = neighborIndices |> List.map (fun index -> Simu ratings a index)
let num = List.zip gaps simis |> List.sumBy (fun t -> fst t * snd t)
let den = simis |> List.sumBy (fun t -> abs t)
if den <> 0.0 then
let div = num / den
let predicted = rb + div * stddev ratings.[a]
//Sometimes the value can be beyond the range.
//so having a 7 is same as 5.0 in practice
//so is having a -1 which is same as zero
if predicted > 5.0 then 5.0 elif predicted < 1.0 then 1.0 else predicted
else
0.0 //Else we don't know
//The above rating matrix is represented as (float list)list in F#
let ratings = [[4.;0.;5.;5.];[4.;2.;1.;0.];[3.;0.;2.;4.];[4.;4.;0.;0.];[2.;1.;3.;5.]]
//Finding the predicted rating for user 1 for item 2
let p12 = Predictu ratings 0 1 //3.95 roughly
let p12Z = PredictuZ ratings 0 1 //5.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment