0% found this document useful (0 votes)
4 views17 pages

Project

The Smart City Analyzer & Pathfinder is a Python program designed to manage and analyze city data, utilizing various data structures and algorithms for functionalities like pathfinding and population analysis. Key features include a menu system for user interaction, file handling for data storage, and the implementation of search algorithms such as BFS, DFS, and A*. The program also incorporates robust exception handling to ensure reliability during user input and data processing.

Uploaded by

SYED MUJTABA ALI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views17 pages

Project

The Smart City Analyzer & Pathfinder is a Python program designed to manage and analyze city data, utilizing various data structures and algorithms for functionalities like pathfinding and population analysis. Key features include a menu system for user interaction, file handling for data storage, and the implementation of search algorithms such as BFS, DFS, and A*. The program also incorporates robust exception handling to ensure reliability during user input and data processing.

Uploaded by

SYED MUJTABA ALI
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 17

🌍🔍 Project Title: Smart City Analyzer &

Pathfinder

🎯 Project Summary:
A Python program that:

 Stores and manages city data using lists, sets, arrays


 Accepts user input via functions
 Uses file handling to store and load city data
 Uses dataset handling to analyze population or distances
 Uses exception handling for robustness
 Implements search algorithms (BFS, DFS, Binary Search, Hill Climbing, A*) for route-
finding between cities

✅ Features Implemented:
Feature Concept Used
Menu system + input Loops, Functions
City data management Lists, Sets, Arrays
Saving/Loading data File Handling
City population stats Dataset Handling (pandas)
Pathfinding BFS, DFS, A*, Hill Climbing
Searching cities Binary Search
Robust input Exception Handling

🛠️Folder Structure:
project/

├── city_data.csv
├── main.py

📁 city_data.csv (sample content)


City,Population
Karachi,20000000
Lahore,11000000
Islamabad,1200000
Quetta,1100000
Peshawar,1500000

📜 main.py (Full Code)


import pandas as pd
from collections import deque
import heapq

# ---------------- Dataset Handling ----------------


def load_city_dataset(file):
try:
df = pd.read_csv(file)
return df
except FileNotFoundError:
print("City data file not found.")
return pd.DataFrame()

# ---------------- File Handling ----------------


def load_graph():
try:
with open("graph.txt", "r") as f:
graph = eval(f.read())
return graph
except:
return {
'Karachi': ['Lahore', 'Quetta'],
'Lahore': ['Islamabad', 'Peshawar'],
'Islamabad': ['Peshawar'],
'Quetta': ['Karachi'],
'Peshawar': []
}

def save_graph(graph):
with open("graph.txt", "w") as f:
f.write(str(graph))

# ---------------- Search Algorithms ----------------


def bfs(graph, start):
visited = set()
queue = deque([start])
while queue:
node = queue.popleft()
if node not in visited:
print(node, end=" → ")
visited.add(node)
queue.extend(graph[node])
print("END")

def dfs(graph, node, visited=None):


if visited is None:
visited = set()
visited.add(node)
print(node, end=" → ")
for neighbor in graph[node]:
if neighbor not in visited:
dfs(graph, neighbor, visited)

def binary_search(sorted_list, target):


low, high = 0, len(sorted_list) - 1
while low <= high:
mid = (low + high) // 2
if sorted_list[mid] == target:
return mid
elif sorted_list[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1

def hill_climbing(start, get_neighbors, score_func):


current = start
while True:
neighbors = get_neighbors(current)
if not neighbors:
break
next_node = max(neighbors, key=score_func)
if score_func(next_node) <= score_func(current):
break
current = next_node
return current

def a_star(graph, start, goal, heuristic):


open_list = []
heapq.heappush(open_list, (0 + heuristic[start], 0, start))
came_from = {}
visited = set()

while open_list:
f, g, node = heapq.heappop(open_list)

if node == goal:
path = []
while node:
path.append(node)
node = came_from.get(node)
return path[::-1]

if node in visited:
continue
visited.add(node)

for neighbor in graph.get(node, []):


if neighbor not in visited:
came_from[neighbor] = node
heapq.heappush(open_list, (g + 1 + heuristic[neighbor], g + 1,
neighbor))
return None

# ---------------- Functional Logic ----------------


def get_neighbors(city):
return city_graph.get(city, [])

def score(city):
return len(city_graph.get(city, []))

# ---------------- Main Program ----------------


city_graph = load_graph()
city_df = load_city_dataset("city_data.csv")

while True:
print("\n==== Smart City System ====")
print("1. Show City List")
print("2. Search City (Binary Search)")
print("3. Analyze Populations")
print("4. Traverse (BFS/DFS)")
print("5. Hill Climb Best City (by connections)")
print("6. A* Shortest Route")
print("7. Add City Connection")
print("8. Exit")

try:
choice = int(input("Enter choice: "))
if choice == 1:
print("Cities:", list(city_graph.keys()))

elif choice == 2:
target = input("Enter city to search: ")
sorted_cities = sorted(city_graph.keys())
index = binary_search(sorted_cities, target)
if index != -1:
print(f"{target} found at position {index}")
else:
print(f"{target} not found.")

elif choice == 3:
print("\nCity Populations:")
print(city_df[['City', 'Population']])
print("Average Population:", city_df['Population'].mean())

elif choice == 4:
algo = input("Choose algorithm (BFS/DFS): ").strip().upper()
start = input("Enter starting city: ")
if algo == "BFS":
bfs(city_graph, start)
elif algo == "DFS":
dfs(city_graph, start)
print("END")

elif choice == 5:
start = input("Enter starting city: ")
result = hill_climbing(start, get_neighbors, score)
print("Hill Climbing stopped at:", result)
elif choice == 6:
start = input("From city: ")
goal = input("To city: ")
heuristic = {city: 1 for city in city_graph} # Dummy heuristic
path = a_star(city_graph, start, goal, heuristic)
print("Shortest Path:", " → ".join(path) if path else "No path
found")

elif choice == 7:
a = input("From city: ")
b = input("To city: ")
city_graph.setdefault(a, []).append(b)
city_graph.setdefault(b, []).append(a)
save_graph(city_graph)
print("Connection added.")

elif choice == 8:
print("Exiting...")
break

else:
print("Invalid option.")

except Exception as e:
print("Error:", e)

🧠 Concepts in Action:
Concept Where it's used
Lists Storing city names
Sets In BFS/DFS visited tracking
Loops Menu system and traversals
Arrays (You can add array instead of list for efficiency)
Functions All modular blocks
BFS / DFS Traverse city routes
Binary Search Efficient city search
Hill Climbing Select city with most connections
A* Route-finding
File Handling Graph saving and loading
Dataset Handling Analyzing populations from CSV
Exception Handling Safe inputs and file reading
✅ SETS IN PYTHON - LINE BY LINE EXPLANATION
my_set = {1, 2, 3} # Set declaration: unordered, unique
elements
print(my_set) # Print the set

my_set.add(4) # Add a single element to the set


print(my_set) # Set after adding 4

my_set.update([5, 6]) # Add multiple elements using update()


print(my_set) # Set now contains 5 and 6 too

my_set.discard(2) # Safely remove element 2 (no error if not


found)
print(my_set) # Set after discarding 2

my_set.discard(10) # Discarding an element that doesn't exist


— no error
print(my_set) # No change

my_set.pop() # Removes a random element from the set


print(my_set) # Set after pop

if 3 in my_set: # Check if element exists in set


print("3 is in the set")

# Set operations
set_a = {1, 2, 3}
set_b = {3, 4, 5}

print(set_a.union(set_b)) # Union: all elements without


duplication
print(set_a.intersection(set_b)) # Intersection: common elements
print(set_a.difference(set_b)) # Difference: items in A but not in
B
print(set_a.symmetric_difference(set_b)) # Elements in A or B but not
both

my_set.clear() # Clears all elements in the set


print(my_set) # Now it's an empty set

# Set comprehension
squares = {x**2 for x in range(5)} # Create a set of squares from 0 to
4
print(squares) # Output: {0, 1, 4, 9, 16}
✅ LISTS IN PYTHON - LINE BY LINE EXPLANATION
my_list=[1,2,3]#list declaration
my_list.append(4)#Add to list
print(my_list)#print list
nest_list=[[my_list],[1,2,3]]#nested list
print(nest_list)#print nested list
print(my_list[-4])#prints the first in list
my_list.insert(3,5)#inserts in the middle of list
print(my_list)#print new list
my_list.extend([6,7])
print(my_list)
my_list.pop(1)#removes the first occurence
print(my_list)#print list after pop
my_list.remove(1)
print(my_list)#print list after remove
my_list.clear()#clears or empties list
print(my_list)#prints cleared list
new_list=[10,20,30]
if 30 in new_list:
print("found!")
sliced_new_list=new_list[0:1]#slicing list
print(sliced_new_list)#print sliced list
for i in range(len(new_list)):#for loop
print(new_list[i])
for i in new_list:
print(30)
unsorted_list=[10,50,72,41,13,9,208] #unsorted list
print(unsorted_list)
unsorted_list.sort()#sort list
print(unsorted_list)
unsorted_list.reverse()#reverse list
print(unsorted_list)
copy=unsorted_list.copy()#copy list
print(copy)
sum_list_copy=sum(copy)#sum the copy list
print(sum_list_copy)#print the sum
print(len(copy))#check list length
print(min(copy))#check list minimum
print(max(copy))#check list maximum
✅ LOOPS IN PYTHON - LINE BY LINE EXPLANATION
numbers = [10, 20, 30, 40, 50] # Sample list

# ----------- For loop with index -----------


for i in range(len(numbers)): # Using range() for indexing
print(f"Index {i} = {numbers[i]}") # Access element by index

# ----------- For loop directly over elements -----------


for num in numbers: # Loop over each item in the
list
print(f"Value = {num}") # Print the value

# ----------- While loop -----------


i = 0
while i < len(numbers): # Loop while i < number of
elements
print(f"While loop: {numbers[i]}") # Print value at index i
i += 1 # Move to next index

# ----------- Loop with break -----------


for num in numbers:
if num == 30:
break # Stop loop if number is 30
print(f"Before break: {num}")

# ----------- Loop with continue -----------


for num in numbers:
if num == 30:
continue # Skip 30 and continue to next
print(f"After continue: {num}")

# ----------- Enumerate for index + value -----------


for idx, val in enumerate(numbers): # Automatically get index and
value
print(f"Enumerated: {idx} = {val}")

# ----------- Zip to loop over multiple lists -----------


names = ['Alice', 'Bob', 'Cathy']
scores = [85, 90, 78]
for name, score in zip(names, scores): # Pair items from both lists
print(f"{name} scored {score}")
✅ ARRAYS IN PYTHON - LINE BY LINE EXPLANATION

from array import array # Import the array module

arr = array('i', [10, 20, 30, 40]) # Create an integer array using 'i'
print(arr) # Output: array('i', [10, 20, 30, 40])

print(arr[0]) # Access first element → 10


print(arr[-1]) # Access last element → 40

arr.append(50) # Add 50 to the end of the array


print(arr) # Now: [10, 20, 30, 40, 50]

arr.insert(2, 25) # Insert 25 at index 2


print(arr) # Now: [10, 20, 25, 30, 40, 50]
arr.pop() # Remove the last element
print(arr) # Now: [10, 20, 25, 30, 40]

arr.remove(20) # Remove the first occurrence of 20


print(arr) # Now: [10, 25, 30, 40]

print(len(arr)) # Number of elements: 4


print(min(arr)) # Smallest element: 10
print(max(arr)) # Largest element: 40
print(sum(arr)) # Sum of all elements: 105
arr.reverse() # Reverse the array in place
print(arr) # Now: [40, 30, 25, 10]

print(arr.index(25)) # Index of element 25 → 2

for num in arr: # Iterate and print each element


print(num)
arr_copy = arr[:] # Copy the array using slicing
print("Copied Array:", arr_copy)

lst = [1, 2, 3] # Create a regular Python list


arr_from_list = array('i', lst) # Convert list to array
print("Array from list:", arr_from_list)
✅ FILE HANDLING IN PYTHON — EXPLAINED LINE BY LINE# -------- Writing to a file
--------

with open("sample.txt", "w") as file: # Open file in write


mode ('w' = overwrite)
file.write("Hello, world!\n") # Write first line
file.write("This is a second line.\n") # Write second line

# -------- Appending to a file --------


with open("sample.txt", "a") as file: # Open file in append
mode
file.write("This line is appended.\n") # Append new line at the
end

# -------- Reading full content --------


with open("sample.txt", "r") as file: # Open file in read mode
content = file.read() # Read entire content as
a string
print("File content:\n", content) # Print the content

# -------- Reading line by line --------


with open("sample.txt", "r") as file:
lines = file.readlines() # Read all lines into a
list
for line in lines:
print("Line:", line.strip()) # Strip removes newline
characters

# -------- Reading first line only --------


with open("sample.txt", "r") as file:
first_line = file.readline() # Read just one line
print("First line:", first_line.strip())

# -------- Cursor position & partial read --------


with open("sample.txt", "r") as file:
print("Cursor position:", file.tell()) # Get current position
of the file pointer
file.seek(0) # Move cursor back to
start
print("First 10 characters:", file.read(10)) # Read first 10
characters only
✅ DATASET HANDLING IN PYTHON — EXPLAINED LINE BY LINE

📄 Let's assume a CSV file called students.csv with the following content:
Name,Age,Score
Alice,20,85
Bob,22,90
Cathy,19,78
David,21,88

📜 Python Code using pandas:


import pandas as pd # Import the pandas library

# -------- Load dataset from a CSV file --------


df = pd.read_csv("students.csv") # Read CSV into DataFrame
print("Complete Dataset:\n", df) # Print entire dataset

# -------- Basic Information --------


print("Column Names:", df.columns.tolist()) # List of column names
print("Dataset Shape (rows, columns):", df.shape) # (number of rows,
columns)
print("First 2 rows:\n", df.head(2)) # Display first 2 rows

# -------- Accessing Columns --------


print("Names:\n", df["Name"]) # Access 'Name' column
print("Ages and Scores:\n", df[["Age", "Score"]]) # Access multiple
columns

# -------- Accessing Rows --------


print("First row using iloc:\n", df.iloc[0]) # Access row by index
print("Multiple rows:\n", df.iloc[1:3]) # Access rows 1 and 2

# -------- Filtering / Conditional Selection --------


adults = df[df["Age"] >= 21] # Filter students aged 21 or
older
print("Students aged 21+:\n", adults)

# -------- Add a new column --------


df["Grade"] = ["B", "A", "C", "A"] # Add a new column manually
print("With Grade Column:\n", df)

# -------- Statistical Summary --------


print("Average Score:", df["Score"].mean()) # Mean score
print("Maximum Age:", df["Age"].max()) # Max age
print("Minimum Score:", df["Score"].min()) # Min score

# -------- Sorting the Data --------


sorted_df = df.sort_values("Score", ascending=False) # Sort by Score
descending
print("Sorted by Score:\n", sorted_df)

# -------- Handling Missing Data --------


print("Missing values per column:\n", df.isnull().sum()) # Check for
nulls

# -------- Save modified dataset to new CSV --------


df.to_csv("students_updated.csv", index=False) # Save to a new CSV file
print("Saved updated data to students_updated.csv")

🧠 Key Functions Used:


Function Purpose

pd.read_csv() Load dataset from CSV

df.head() / df.iloc[] Access rows

df["Column"] Access column

df[condition] Filter rows

df.mean(), df.max() Statistical analysis

df.to_csv() Save DataFrame to file

df.isnull() Check for missing values


✅ EXCEPTION HANDLING IN PYTHON — EXPLAINED LINE BY LINE
# -------- Basic Try-Except --------
try:
num = int(input("Enter a number: ")) # Try converting input to
integer
print("Result:", 100 / num) # Try dividing 100 by input
except ValueError: # If input is not an integer
print("That's not a valid number!")
except ZeroDivisionError: # If input is zero
print("You can't divide by zero!")

# -------- Try-Except-Else --------


try:
num = int(input("Enter another number: ")) # Try to convert
except ValueError:
print("Invalid input.")
else:
print("Valid input:", num) # Runs only if no exception
occurs

# -------- Try-Finally --------


try:
f = open("sample.txt", "r") # Try to open a file
print("File opened successfully!")
finally:
print("This block runs no matter what.") # Always executes
f.close() # Safely close the file

# -------- Catch Any Exception --------


try:
result = 10 / 0 # Division by zero error
except Exception as e: # Catch any error type
print("An error occurred:", e)

# -------- Raise Custom Exception --------


age = -5
if age < 0:
raise ValueError("Age cannot be negative!") # Raise a custom error
✅ EXCEPTION HANDLING IN PYTHON — EXPLAINED LINE BY LINE
# -------- Basic Try-Except --------
try:
num = int(input("Enter a number: ")) # Try converting input to
integer
print("Result:", 100 / num) # Try dividing 100 by input
except ValueError: # If input is not an integer
print("That's not a valid number!")
except ZeroDivisionError: # If input is zero
print("You can't divide by zero!")

# -------- Try-Except-Else --------


try:
num = int(input("Enter another number: ")) # Try to convert
except ValueError:
print("Invalid input.")
else:
print("Valid input:", num) # Runs only if no exception
occurs

# -------- Try-Finally --------


try:
f = open("sample.txt", "r") # Try to open a file
print("File opened successfully!")
finally:
print("This block runs no matter what.") # Always executes
f.close() # Safely close the file

# -------- Catch Any Exception --------


try:
result = 10 / 0 # Division by zero error
except Exception as e: # Catch any error type
print("An error occurred:", e)

# -------- Raise Custom Exception --------


age = -5
if age < 0:
raise ValueError("Age cannot be negative!") # Raise a custom error
✅ SEARCH ALGORITHMS IN PYTHON — EXPLAINED LINE BY LINE
from collections import deque
import heapq

# -------- Sample Graph --------


graph = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': [],
'E': ['F'],
'F': []
}

# -------- Breadth-First Search (BFS) --------


def bfs(graph, start):
visited = set()
queue = deque([start])
while queue:
node = queue.popleft()
if node not in visited:
print(node, end=" → ")
visited.add(node)
queue.extend(graph[node])
print("END")

# -------- Depth-First Search (DFS) --------


def dfs(graph, node, visited=None):
if visited is None:
visited = set()
visited.add(node)
print(node, end=" → ")
for neighbor in graph[node]:
if neighbor not in visited:
dfs(graph, neighbor, visited)

# -------- Binary Search --------


def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1

# -------- Hill Climbing --------


def hill_climbing(start, get_neighbors, score_func):
current = start
while True:
neighbors = get_neighbors(current)
if not neighbors:
break
next_node = max(neighbors, key=score_func)
if score_func(next_node) <= score_func(current):
break
current = next_node
return current

# -------- A* Search --------


def a_star(graph, start, goal, heuristic):
open_list = []
heapq.heappush(open_list, (0 + heuristic[start], 0, start))
came_from = {}
visited = set()

while open_list:
f, g, node = heapq.heappop(open_list)
if node == goal:
path = []
while node:
path.append(node)
node = came_from.get(node)
return path[::-1]

if node in visited:
continue
visited.add(node)

for neighbor in graph.get(node, []):


if neighbor not in visited:
came_from[neighbor] = node
heapq.heappush(open_list, (g + 1 + heuristic[neighbor], g + 1,
neighbor))

return None

# -------- Helper Functions --------


def get_neighbors(node):
return graph.get(node, [])

def score_func(node):
return len(graph.get(node, [])) # You can customize scoring logic

# -------- Run Examples --------


print("🔁 BFS Traversal:")
bfs(graph, 'A')
print("\n🔁 DFS Traversal:")
dfs(graph, 'A')
print("END")

print("\n🔍 Binary Search:")


arr = [1, 3, 5, 7, 9]
index = binary_search(arr, 7)
print("Index of 7:", index)

print("\n Hill Climbing from A:")


best = hill_climbing('A', get_neighbors, score_func)
print("Best city (most connections):", best)

print("\n⭐ A* Search from A to F:")


heuristic = {node: 1 for node in graph} # Simplified heuristic
path = a_star(graph, 'A', 'F', heuristic)
print("A* path:", " → ".join(path) if path else "No path found")

You might also like