def test_zero_points(self): """Attempt to calculate the average point of an empty graph. Assert :class:`movie_recommender.exceptions.EmptyGraphError` is raised. """ graph = Graph(()) with self.assertRaises(exceptions.EmptyGraphError): graph.avg_point # pylint:disable=pointless-statement
def test_vertical_slope(self): """Attempt to calculate a vertical slope. Assert :class:`movie_recommender.exceptions.VerticalLineOfBestFitGraphError` is raised. """ graph = Graph((Point(1, 1), Point(1, 2))) with self.assertRaises(exceptions.VerticalLineOfBestFitGraphError): graph.slope # pylint:disable=pointless-statement
def setUpClass(cls): """Define a non-trivial graph.""" # The line of best fit is `y = -1x + 10`. cls.graph = Graph(( # Define a line 2 above the line of best fit. Point(-2, 14), Point(2, 10), # Define a line 2 below the line of best fit. Point(-2, 10), Point(2, 6), ))
def main(): """Parse arguments and call business logic.""" args = parse_args() graph = Graph( tuple( get_points( args.input, Columns(args.x_column, args.y_column), header_rows=args.header_rows, ))) print(f'y = {graph.slope:g} × x + {graph.y_intercept:g}') print(f'sse = {graph.sse:g}')
def make_genre_predictor(genre, user_id, forbidden_movie=None): """Make a predictor for the given genre for the given user. :param genre: A genre name, as a string. :param user_id: A user ID. The user for which a predictor is being created. :param forbidden_movie: A movie ID. A movie to ignore when creating the predictor. :return: A function which accepts a movie ID and returns a predicted rating. """ query = """ SELECT movies.genres, ratings.rating FROM movies JOIN ratings USING (movieId) WHERE ratings.userId == ? """ params = [user_id] if forbidden_movie: query += 'AND movieId != ?' params.append(forbidden_movie) # Iterate through movies this user has rated. For each movie, create a # Cartesian point, where X is whether the move has the given genre, and Y # is the rating this user has given to this movie. points = [] with common.get_db_conn() as conn: for row in conn.execute(query, params): genres = row[0].split('|') genre_present = 1 if genre in genres else 0 rating = row[1] points.append(Point(genre_present, rating)) graph = Graph(points) def predictor(movie_id): """Predict a user's rating for the given movie. :param movie_id: A movie ID. :return: A predicted rating for the given movie. """ genres = read.genres(movie_id) genre_present = 1 if genre in genres else 0 try: rating = graph.predict_y(genre_present) except exceptions.VerticalLineOfBestFitGraphError: rating = graph.avg_point.y return clamp_rating(rating) return predictor
def test_positive_intercept(self): """Verify the method can calculate a positive y-intercept.""" graph = Graph((Point(1, 11), Point(2, 12))) self.assertEqual(graph.y_intercept, 10)
def test_flat_slope(self): """Verify the method can calculate a flat slope.""" graph = Graph((Point(1, 1), Point(2, 1))) self.assertEqual(graph.slope, 0)
def test_negative_slope(self): """Verify the method can calculate a negative slope.""" graph = Graph((Point(1, 2), Point(2, 1))) self.assertEqual(graph.slope, -1)
def test_positive_slope(self): """Verify the method can calculate a positive slope.""" graph = Graph((Point(1, 1), Point(2, 2))) self.assertEqual(graph.slope, 1)
def test_two_points(self): """Assert the method behaves correctly when given two points.""" graph = Graph((Point(10, -1), Point(20, -2))) self.assertEqual(graph.avg_point, Point(15, -1.5))
def test_two_points(self): """Create a graph with two points.""" points = (Point(1.2, 3.4), Point(5.6, 7.8)) graph = Graph(points) self.assertEqual(graph.points, points)
def test_one_point(self): """Create a graph with one point.""" points = (Point(-1, 1), ) graph = Graph(points) self.assertEqual(graph.points, points)
def setUpClass(cls): """Define a trivial graph.""" # y = 0.5x + 1 cls.graph = Graph((Point(0, 1), Point(2, 2)))
def test_zero_points(self): """Create a graph with no points.""" points = () graph = Graph(points) self.assertEqual(graph.points, points)
def make_year_predictor(user_id, forbidden_movie=None): """Make a year-based predictor for the given user. If year information can't be extracted from a movie's title, then that movie is skipped when generating a predictor. This is done because so few movies have this issue. See: `class:`movie_recommender.exceptions.NoMovieYearError`. :param user_id: A user ID. The user for which a predictor is being created. :param forbidden_movie: A movie ID. A movie to ignore when creating the predictor. :return: A function which accepts a movie ID and returns a predicted rating. """ query = """ SELECT movies.title, ratings.rating FROM movies JOIN ratings USING (movieId) WHERE ratings.userId == ? """ params = [user_id] if forbidden_movie: query += 'AND movieId != ?' params.append(forbidden_movie) # Iterate through movies this user has rated. For each movie, create a # Cartesian point, where X is the movie's year, and Y is the rating this # user has given to this movie. points = [] with common.get_db_conn() as conn: for row in conn.execute(query, params): try: year = read.year(row[0]) except exceptions.NoMovieYearError: continue rating = row[1] points.append(Point(year, rating)) graph = Graph(points) def predictor(movie_id): """Predict a user's rating for the given movie. :param movie_id: A movie ID. :return: A predicted rating for the given movie. :raise movie_recommender.exceptions.NoMovieYearError: If the given movie's title doesn't include a year, and this predictor makes use of year data. :raise movie_recommender.exceptions.EmptyGraphError: If this predictor can't predict movie ratings at all, due to a lack of relevant data. For example, this will occur if the given movie's title does include a year, but all of the movies this user has rated lack a year. """ title = read.title(movie_id) year = read.year(title) try: rating = graph.predict_y(year) except exceptions.VerticalLineOfBestFitGraphError: rating = graph.avg_point.y return clamp_rating(rating) return predictor
def test_negative_intercept(self): """Verify the method can calculate a positive y-intercept.""" graph = Graph((Point(1, -9), Point(2, -8))) self.assertEqual(graph.y_intercept, -10)
def test_zero_intercept(self): """Verify the method can calculate a zero y-intercept.""" graph = Graph((Point(1, 0.5), Point(2, 1))) self.assertEqual(graph.y_intercept, 0)
def test_one_point(self): """Assert the method behaves correctly when given one point.""" graph = Graph((Point(25, -1.5), )) self.assertEqual(graph.avg_point, graph.points[0])