diff --git a/assignments/A3/a3_part1.py b/assignments/A3/a3_part1.py index 38a6769..8750d14 100644 --- a/assignments/A3/a3_part1.py +++ b/assignments/A3/a3_part1.py @@ -19,7 +19,7 @@ This file is Copyright (c) 2022 Mario Badr, David Liu, and Isaac Waller. """ from __future__ import annotations import csv -from typing import Any +from typing import Any, Literal # Make sure you've installed the necessary Python libraries (see assignment handout # "Installing new libraries" section) @@ -143,7 +143,7 @@ class Graph: else: raise ValueError - def get_all_vertices(self, kind: str = '') -> set: + def get_all_vertices(self, kind: Literal['', 'user', 'book'] = '') -> set: """Return a set of all vertex items in this graph. If kind != '', only return the items of the given vertex kind. diff --git a/assignments/A3/a3_part2_recommendations.py b/assignments/A3/a3_part2_recommendations.py index e94c83d..64fa2f3 100644 --- a/assignments/A3/a3_part2_recommendations.py +++ b/assignments/A3/a3_part2_recommendations.py @@ -20,7 +20,7 @@ This file is Copyright (c) 2022 Mario Badr, David Liu, and Isaac Waller. """ from __future__ import annotations import csv -from typing import Any, Union +from typing import Any, Union, Literal from a3_part1 import Graph @@ -175,7 +175,7 @@ class WeightedGraph(Graph): # Part 2, Q2 ############################################################################ def get_similarity_score(self, item1: Any, item2: Any, - score_type: str = 'unweighted') -> float: + score_type: Literal['unweighted', 'strict'] = 'unweighted') -> float: """Return the similarity score between the two given items in this graph. score_type is one of 'unweighted' or 'strict', corresponding to the @@ -194,7 +194,7 @@ class WeightedGraph(Graph): return self._vertices[item1].similarity_score_strict(self._vertices[item2]) def recommend_books(self, book: str, limit: int, - score_type: str = 'unweighted') -> list[str]: + score_type: Literal['unweighted', 'strict'] = 'unweighted') -> list[str]: """Return a list of up to recommended books based on similarity to the given book. score_type is one of 'unweighted' or 'strict', corresponding to the diff --git a/assignments/A3/a3_part3.py b/assignments/A3/a3_part3.py index 1295a82..aa491ec 100644 --- a/assignments/A3/a3_part3.py +++ b/assignments/A3/a3_part3.py @@ -18,8 +18,9 @@ please consult our Course Syllabus. This file is Copyright (c) 2022 Mario Badr, David Liu, and Isaac Waller. """ import random +from typing import Literal -from a3_part2_recommendations import WeightedGraph, load_weighted_review_graph +from a3_part2_recommendations import WeightedGraph ################################################################################ @@ -27,7 +28,7 @@ from a3_part2_recommendations import WeightedGraph, load_weighted_review_graph ################################################################################ def create_book_graph(review_graph: WeightedGraph, threshold: float = 0.05, - score_type: str = 'unweighted') -> WeightedGraph: + score_type: Literal['unweighted', 'strict'] = 'unweighted') -> WeightedGraph: """Return a book graph based on the given review_graph. The score_type parameter plays the same role as in WeightedGraph.get_similarity_score. @@ -46,6 +47,28 @@ def create_book_graph(review_graph: WeightedGraph, Preconditions: - score_type in {'unweighted', 'strict'} """ + # Add all books as vertices + book_graph = WeightedGraph() + book_names: set[str] = review_graph.get_all_vertices('book') + for b in book_names: + book_graph.add_vertex(b, 'book') + + # Add all edges + for b1 in book_names: + for b2 in book_names: + if b1 == b2: + continue + + # Calculate similarity score + score = review_graph.get_similarity_score(b1, b2, score_type) + if score <= threshold: + continue + + # Add edge + book_graph.add_edge(b1, b2, score) + + # Done + return book_graph ################################################################################ @@ -60,7 +83,21 @@ def cross_cluster_weight(book_graph: WeightedGraph, cluster1: set, cluster2: set - cluster1 != set() and cluster2 != set() - cluster1.isdisjoint(cluster2) - Every item in cluster1 and cluster2 is a vertex in book_graph + + >>> bg = WeightedGraph() + >>> for b in range(4): \ + bg.add_vertex(f'B{b}', 'book') + >>> bg.add_edge('B0', 'B1', .5) + >>> bg.add_edge('B0', 'B2', .4) + >>> bg.add_edge('B1', 'B2', .3) + >>> bg.get_weight('B0', 'B1') + 0.5 + >>> cross_cluster_weight(bg, {'B0', 'B1'}, {'B2', 'B3'}) == (.4 + .3) / 4 + True """ + numerator = sum(book_graph.get_weight(v1, v2) for v1 in cluster1 for v2 in cluster2) + denominator = len(cluster1) * len(cluster2) + return numerator / denominator ################################################################################ @@ -151,3 +188,16 @@ if __name__ == '__main__': 'allowed-io': ['find_clusters_greedy', 'find_clusters_random'], 'max-nested-blocks': 4 }) + + # Q1 Test + # review_graph = load_weighted_review_graph('data/reviews_full.csv', 'data/book_names.csv') + # book_graph = create_book_graph(review_graph, 0.03) + # from a3_visualization import visualize_graph + # visualize_graph(book_graph) + + # Q3 Test + # review_graph = load_weighted_review_graph('data/reviews_full.csv', 'data/book_names.csv') + # book_graph = create_book_graph(review_graph, threshold=0.01, score_type='strict') + # clusters = find_clusters_random(book_graph, 15) + # from a3_visualization import visualize_graph_clusters + # visualize_graph_clusters(book_graph, clusters)