-
Notifications
You must be signed in to change notification settings - Fork 1
/
MovieGraph.h
106 lines (100 loc) · 4.69 KB
/
MovieGraph.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#pragma once
#include <sstream>
#include <fstream>
#include <unordered_map>
#include <unordered_set>
#include <regex>
#include <map>
#include "Movie.h"
class MovieGraph {
private:
unordered_map<string, Movie*> movieMap;
unordered_map<string, vector<Movie*>> genreMap;
unordered_map<int, vector<Movie*>> tagMap;
unordered_map<int, vector<double>> ratings;
struct MovieGreater {
bool operator()(Movie* lhs, Movie* rhs) {
return lhs->rating > rhs->rating;
}
};
public:
void GetInfo(); //Parses data, fills unordered maps.
const Movie* GetMovie(string name) {
auto iter = movieMap.find(name);
return (iter == movieMap.end()) ? nullptr : iter->second; //Simple to see if the movie exists.
}
const vector<Movie*> getGenreMovies(string genre) { //returns all movies in one genre
vector<Movie*> genreVector = genreMap[genre];
return genreVector;
}
vector<Movie*> recommendationAlgorithm(string name, int steps) {
if(steps == 0) {
return vector<Movie*>{movieMap[name]}; //No steps no go
} else {
Movie* og = movieMap[name]; //Start with the source
unordered_set<string> traversed; //Where have we been
traversed.insert(name);
unordered_map<string, double> rater; //How close is everybody
vector<Movie*> recommended; //All the movies
for(auto iter : og->tags) { //Do this for all tags
vector<Movie*> tagged = tagMap[iter.first]; //find all the tagged movies
for(Movie* similar : tagged) { //Go through them
if(traversed.find(similar->name) == traversed.end()) { //if we've used them for traversing, we don't need them
double addition = similar->popularity > 1500 ? 1 : .9; //Super unpopular movies are rated lower
addition *= (.5) / (abs(similar->tags[iter.first] - iter.second) + .1); //algorithm where similar weights in tags are proritized.
auto raterIter = rater.find(similar->name); //Have we dealt with this movie befor?
if (raterIter != rater.end()) { //If we have, then we don't need to re add it to the vector
raterIter->second += addition;
} else {
addition *= similar->rating; //If we haven't seen it, we need it's rating
recommended.push_back(similar);
rater.emplace(similar->name, addition);
}
}
}
}
std::sort(recommended.begin(), recommended.end(), [&](const Movie* lhs, const Movie* rhs){ //lambda comparator with unordered map
double lrater = rater[lhs->name];
double rrater = rater[rhs->name];
return lrater > rrater;
});
for(int i = 1; i < steps; i++) {
og = recommended[i-1]; //take then next lowest number
int offset = 1;
while(traversed.find(og->name) != traversed.end()) { //make sure we haven't somehow been there
og = recommended[i-1+offset];
offset++;
}
traversed.insert(og->name); //Do the last thing all over again
for(auto iter : og->tags) {
vector<Movie*> tagged = tagMap[iter.first];
for(Movie* similar : tagged) {
if (traversed.find(similar->name) == traversed.end()) {
double addition = similar->popularity > 1500 ? 1.0 / (i + 1) : .9 / (i + 1); //Since we're further away these are worth less. Quasi Zipf's law
addition *= (.5) / (abs(similar->tags[iter.first] - iter.second) + .1);
addition *= similar->rating;
auto raterIter = rater.find(similar->name);
if (raterIter != rater.end()) {
raterIter->second += addition;
} else {
recommended.push_back(similar);
rater.emplace(similar->name, addition);
}
}
}
}
std::sort(recommended.begin(), recommended.end(), [&](const Movie* lhs, const Movie* rhs){ //Resort for next time
double lrater = rater[lhs->name];
double rrater = rater[rhs->name];
return lrater > rrater;
});
}
return recommended;
}
}
~MovieGraph() {
for(auto i : movieMap) {
delete i.second;
}
}
};