Last active
August 17, 2017 02:19
-
-
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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