Exemple #1
0
    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
Exemple #2
0
    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
Exemple #3
0
 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),
     ))
Exemple #4
0
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}')
Exemple #5
0
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
Exemple #6
0
 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)
Exemple #7
0
 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)
Exemple #8
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)
Exemple #9
0
 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)
Exemple #10
0
 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))
Exemple #11
0
 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)
Exemple #12
0
 def test_one_point(self):
     """Create a graph with one point."""
     points = (Point(-1, 1), )
     graph = Graph(points)
     self.assertEqual(graph.points, points)
Exemple #13
0
 def setUpClass(cls):
     """Define a trivial graph."""
     # y = 0.5x + 1
     cls.graph = Graph((Point(0, 1), Point(2, 2)))
Exemple #14
0
 def test_zero_points(self):
     """Create a graph with no points."""
     points = ()
     graph = Graph(points)
     self.assertEqual(graph.points, points)
Exemple #15
0
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
Exemple #16
0
 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)
Exemple #17
0
 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)
Exemple #18
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])