Intial commit, with some work done to the 'shortest_path' method.

This commit is contained in:
2020-05-12 22:20:58 -05:00
commit 5a03db8fd5
9 changed files with 2578627 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
__pycache__
+172
View File
@@ -0,0 +1,172 @@
import csv
import sys
from util import Node, StackFrontier, QueueFrontier
# Maps names to a set of corresponding person_ids
names = {}
# Maps person_ids to a dictionary of: name, birth, movies (a set of movie_ids)
people = {}
# Maps movie_ids to a dictionary of: title, year, stars (a set of person_ids)
movies = {}
def load_data(directory):
"""
Load data from CSV files into memory.
"""
# Load people
with open(f"{directory}/people.csv", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
people[row["id"]] = {
"name": row["name"],
"birth": row["birth"],
"movies": set()
}
if row["name"].lower() not in names:
names[row["name"].lower()] = {row["id"]}
else:
names[row["name"].lower()].add(row["id"])
# Load movies
with open(f"{directory}/movies.csv", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
movies[row["id"]] = {
"title": row["title"],
"year": row["year"],
"stars": set()
}
# Load stars
with open(f"{directory}/stars.csv", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
try:
people[row["person_id"]]["movies"].add(row["movie_id"])
movies[row["movie_id"]]["stars"].add(row["person_id"])
except KeyError:
pass
def main():
if len(sys.argv) > 2:
sys.exit("Usage: python degrees.py [directory]")
directory = sys.argv[1] if len(sys.argv) == 2 else "large"
# Load data from files into memory
print("Loading data...")
load_data(directory)
print("Data loaded.")
source = person_id_for_name(input("Name: "))
if source is None:
sys.exit("Person not found.")
target = person_id_for_name(input("Name: "))
if target is None:
sys.exit("Person not found.")
path = shortest_path(source, target)
if path is None:
print("Not connected.")
else:
degrees = len(path)
print(f"{degrees} degrees of separation.")
path = [(None, source)] + path
for i in range(degrees):
person1 = people[path[i][1]]["name"]
person2 = people[path[i + 1][1]]["name"]
movie = movies[path[i + 1][0]]["title"]
print(f"{i + 1}: {person1} and {person2} starred in {movie}")
def shortest_path(source, target):
"""
Returns the shortest list of (movie_id, person_id) pairs
that connect the source to the target.
If no possible path, returns None.
"""
frontier = QueueFrontier()
explored = StackFrontier()
frontier.add(Node(source, None, None))
results = [[]]
while frontier:
currentNode = frontier.remove()
if currentNode.state == target:
tmp = currentNode
print("Found %s" %(people[tmp.state]))
print("Found!")
cost = 0
r = []
r.append(tmp)
index = len(results)
while tmp:
if not tmp.parent:
r.append(tmp.parent)
print("Parent found! %s" %(people[tmp.state]))
tmp = tmp.parent
cost+=1
results.append(r)
print("Estimated Cost %d" %(cost))
#break
if not explored.contains_state(currentNode.state):
# currentNode.state has person ID.
neighbors = neighbors_for_person(currentNode.state)
explored.add(currentNode)
for i in neighbors:
frontier.add(Node(i[1], currentNode, None))
return results
def person_id_for_name(name):
"""
Returns the IMDB id for a person's name,
resolving ambiguities as needed.
"""
person_ids = list(names.get(name.lower(), set()))
if len(person_ids) == 0:
return None
elif len(person_ids) > 1:
print(f"Which '{name}'?")
for person_id in person_ids:
person = people[person_id]
name = person["name"]
birth = person["birth"]
print(f"ID: {person_id}, Name: {name}, Birth: {birth}")
try:
person_id = input("Intended Person ID: ")
if person_id in person_ids:
return person_id
except ValueError:
pass
return None
else:
return person_ids[0]
def neighbors_for_person(person_id):
"""
Returns (movie_id, person_id) pairs for people
who starred with a given person.
"""
movie_ids = people[person_id]["movies"]
neighbors = set()
for movie_id in movie_ids:
for person_id in movies[movie_id]["stars"]:
neighbors.add((movie_id, person_id))
return neighbors
if __name__ == "__main__":
main()
+344277
View File
File diff suppressed because it is too large Load Diff
+1044500
View File
File diff suppressed because it is too large Load Diff
+1189595
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
id,title,year
112384,"Apollo 13",1995
104257,"A Few Good Men",1992
109830,"Forrest Gump",1994
93779,"The Princess Bride",1987
95953,"Rain Man",1988
1 id title year
2 112384 Apollo 13 1995
3 104257 A Few Good Men 1992
4 109830 Forrest Gump 1994
5 93779 The Princess Bride 1987
6 95953 Rain Man 1988
+17
View File
@@ -0,0 +1,17 @@
id,name,birth
102,"Kevin Bacon",1958
129,"Tom Cruise",1962
144,"Cary Elwes",1962
158,"Tom Hanks",1956
1597,"Mandy Patinkin",1952
163,"Dustin Hoffman",1937
1697,"Chris Sarandon",1942
193,"Demi Moore",1962
197,"Jack Nicholson",1937
200,"Bill Paxton",1955
398,"Sally Field",1946
420,"Valeria Golino",1965
596520,"Gerald R. Molen",1935
641,"Gary Sinise",1955
705,"Robin Wright",1966
914612,"Emma Watson",1990
1 id name birth
2 102 Kevin Bacon 1958
3 129 Tom Cruise 1962
4 144 Cary Elwes 1962
5 158 Tom Hanks 1956
6 1597 Mandy Patinkin 1952
7 163 Dustin Hoffman 1937
8 1697 Chris Sarandon 1942
9 193 Demi Moore 1962
10 197 Jack Nicholson 1937
11 200 Bill Paxton 1955
12 398 Sally Field 1946
13 420 Valeria Golino 1965
14 596520 Gerald R. Molen 1935
15 641 Gary Sinise 1955
16 705 Robin Wright 1966
17 914612 Emma Watson 1990
+21
View File
@@ -0,0 +1,21 @@
person_id,movie_id
102,104257
102,112384
129,104257
129,95953
144,93779
158,109830
158,112384
1597,93779
163,95953
1697,93779
193,104257
197,104257
200,112384
398,109830
420,95953
596520,95953
641,109830
641,112384
705,109830
705,93779
1 person_id movie_id
2 102 104257
3 102 112384
4 129 104257
5 129 95953
6 144 93779
7 158 109830
8 158 112384
9 1597 93779
10 163 95953
11 1697 93779
12 193 104257
13 197 104257
14 200 112384
15 398 109830
16 420 95953
17 596520 95953
18 641 109830
19 641 112384
20 705 109830
21 705 93779
+38
View File
@@ -0,0 +1,38 @@
class Node():
def __init__(self, state, parent, action):
self.state = state
self.parent = parent
self.action = action
class StackFrontier():
def __init__(self):
self.frontier = []
def add(self, node):
self.frontier.append(node)
def contains_state(self, state):
return any(node.state == state for node in self.frontier)
def empty(self):
return len(self.frontier) == 0
def remove(self):
if self.empty():
raise Exception("empty frontier")
else:
node = self.frontier[-1]
self.frontier = self.frontier[:-1]
return node
class QueueFrontier(StackFrontier):
def remove(self):
if self.empty():
raise Exception("empty frontier")
else:
node = self.frontier[0]
self.frontier = self.frontier[1:]
return node