Skip to content

Instantly share code, notes, and snippets.

@rcdilorenzo
Last active August 17, 2017 02:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rcdilorenzo/cc1e69601237eb7e404dd37a5e76890c to your computer and use it in GitHub Desktop.
Save rcdilorenzo/cc1e69601237eb7e404dd37a5e76890c to your computer and use it in GitHub Desktop.
Golang port (for fun/school) of simple recommendation system from http://guidetodatamining.com/chapter2/
package main
import (
"fmt"
"math"
"sort"
)
type Recommendation struct {
Name string
Proximity float64
}
type ByProximity []Recommendation
func (a ByProximity) Len() int { return len(a) }
func (a ByProximity) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByProximity) Less(i, j int) bool { return a[i].Proximity > a[j].Proximity }
type UserData map[string](map[string]float64)
type UserRatings map[string]float64
type RatingDistance struct {
Distance float64
Name string
}
type ByDistance []RatingDistance
func (a ByDistance) Len() int { return len(a) }
func (a ByDistance) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByDistance) Less(i, j int) bool { return a[i].Distance < a[j].Distance }
func main() {
users := UserData{
"Angelica": {
"Blues Traveler": 3.5, "Broken Bells": 2.0, "Norah Jones": 4.5,
"Phoenix": 5.0, "Slightly Stoopid": 1.5, "The Strokes": 2.5,
"Vampire Weekend": 2.0,
},
"Bill": {
"Blues Traveler": 2.0, "Broken Bells": 3.5, "Deadmau5": 4.0,
"Phoenix": 2.0, "Slightly Stoopid": 3.5, "Vampire Weekend": 3.0,
},
"Chan": {
"Blues Traveler": 5.0, "Broken Bells": 1.0, "Deadmau5": 1.0,
"Norah Jones": 3.0, "Phoenix": 5, "Slightly Stoopid": 1.0,
},
"Dan": {
"Blues Traveler": 3.0, "Broken Bells": 4.0, "Deadmau5": 4.5,
"Phoenix": 3.0, "Slightly Stoopid": 4.5, "The Strokes": 4.0,
"Vampire Weekend": 2.0,
},
"Hailey": {
"Broken Bells": 4.0, "Deadmau5": 1.0, "Norah Jones": 4.0,
"The Strokes": 4.0, "Vampire Weekend": 1.0,
},
"Jordyn": {
"Broken Bells": 4.5, "Deadmau5": 4.0, "Norah Jones": 5.0,
"Phoenix": 5.0, "Slightly Stoopid": 4.5, "The Strokes": 4.0,
"Vampire Weekend": 4.0,
},
"Sam": {
"Blues Traveler": 5.0, "Broken Bells": 2.0, "Norah Jones": 3.0,
"Phoenix": 5.0, "Slightly Stoopid": 4.0, "The Strokes": 5.0,
},
"Veronica": {
"Blues Traveler": 3.0, "Norah Jones": 5.0, "Phoenix": 4.0,
"Slightly Stoopid": 2.5, "The Strokes": 3.0,
},
}
fmt.Println("Users: ", users)
fmt.Println("Hailey vs Veronica: ", manhattan(users["Hailey"], users["Veronica"]))
fmt.Println("Hailey vs Jordyn: ", manhattan(users["Hailey"], users["Jordyn"]))
fmt.Println("Nearest to Hailey: ", computeNearestNeighbor("Hailey", users))
fmt.Println("Recommendation for Hailey: ", recommend("Hailey", users))
fmt.Println("Recommendation for Chan: ", recommend("Chan", users))
}
func manhattan(ratings1 UserRatings, ratings2 UserRatings) float64 {
distance := float64(0)
for key1, val1 := range ratings1 {
if val2, ok := ratings2[key1]; ok {
distance += math.Abs(val1 - val2)
}
}
return distance
}
func computeNearestNeighbor(name string, users UserData) []RatingDistance {
distances := []RatingDistance{}
for username, ratings := range users {
if username != name {
distance := manhattan(users[name], ratings)
distances = append(distances, RatingDistance{distance, username})
}
}
sort.Sort(ByDistance(distances))
return distances
}
func recommend(username string, users UserData) []Recommendation {
nearest := computeNearestNeighbor(username, users)[0].Name
recommendations := []Recommendation{}
neighborRatings := users[nearest]
userRatings := users[username]
for artist, rating := range neighborRatings {
if _, ok := userRatings[artist]; !ok {
recommendations = append(recommendations, Recommendation{artist, rating})
}
}
return recommendations
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment