diff --git a/assignment3/decorator.log b/assignment3/decorator.log new file mode 100644 index 0000000..d1a53bd --- /dev/null +++ b/assignment3/decorator.log @@ -0,0 +1,5 @@ + +function: hello +positional parameters: none +keyword parameters: none +return: None diff --git a/assignment3/extend-point-to-vector.py b/assignment3/extend-point-to-vector.py new file mode 100644 index 0000000..5c7a7d0 --- /dev/null +++ b/assignment3/extend-point-to-vector.py @@ -0,0 +1,74 @@ +# Task 5: Extending a Class +import math + +# Task 5: step 2 +# Create class Point to represent a point in 2d space +class Point: + # with x and y values passed to the __init__() method. + def __init__(self, x, y): + self.x = x + self.y = y + + # include method for equality + def __eq__(self, other): + # Checks if two points have the same coordinates + if not isinstance(other, Point): + return False + return self.x == other.x and self.y == other.y + + # include methods for string + def __str__(self): + return f"Point({self.x}, {self.y})" + + # include methods for Euclidian distance to another point + def distance(self, other): + return math.sqrt((other.x - self.x)**2 + (other.y - self.y)**2) + +# Task 5: step 3 +# Create a class called Vector +class Vector(Point): + + # method to override the string representation so Vectors print differently than Points + def __str__(self): + return f"Vector<{self.x}, {self.y}>" + + # Override the + operator so that it implements vector addition, + def __add__(self, other): + if isinstance(other, Vector): + # summing the x and y values and returning a new Vector + return Vector(self.x + other.x, self.y + other.y) + return NotImplemented + +# Task 5: step 4 +# Print results which demonstrate all of the classes and methods which have been implemented +if __name__ == "__main__": + p1 = Point(0, 0) + p2 = Point(3, 4) + p3 = Point(0, 0) + + print(f"Point 1: {p1}") + print(f"Point 2: {p2}") + print(f"P1 equals P3 : {p1 == p3}") + print(f"Distance between P1 and P2: {p1.distance(p2)}") + + print("_" * 10) + + #Vector + v1 = Vector(1, 2) + v2 = Vector(3, 4) + + print(f"Vector 1: {v1}") + print(f"Vector 2: {v2}") + + # Vector addition + v3 = v1 + v2 + print(f"Sum of V1 and V2: {v3}") + + # Vector inheriting distance from Point + print(f"Distance from origin for V1: {p1.distance(v1)}") + + + + + + diff --git a/assignment3/hangman-closure.py b/assignment3/hangman-closure.py new file mode 100644 index 0000000..a1940b7 --- /dev/null +++ b/assignment3/hangman-closure.py @@ -0,0 +1,53 @@ +# # Task 4: Closure Practice + +# # Task 4: step 2 +# Declare a function called make_hangman() that has one argument called secret_word +def make_hangman(secret_word): + # declare an empty array called guesses + guesses = [] + + # declare a function called hangman_closure() that takes a letter + def hangman_closure(letter): + # appended the new letter to the guesses array + guesses.append(letter.lower()) + + display_word = "" + all_guessed = True + + # build display string with the current guesses + for char in secret_word.lower(): + if char in guesses: + display_word += char + else: + display_word += "_" + all_guessed = False + + print(display_word) + return all_guessed + + return hangman_closure + +# Task 4: step 3 +# Implement a hangman game that uses make_hangman() +if __name__ == "__main__": + + # Use the input() function to prompt for the secret word + secret = input("Enter the secret word: ").strip() + + play_round = make_hangman(secret) + + is_finished = False + print("\nGame started!") + + + # use the input() function to prompt guesses until the full word is guessed + while not is_finished: + guess = input("Guess a letter: ").strip().lower() + + if len(guess) == 1 and guess.isalpha(): + is_finished = play_round(guess) + else: + print("Invalid input. Please enter exactly one letter.") + + # Task 4: step 4 + print("Great! You have guessed the full word") diff --git a/assignment3/list-comprehensions.py b/assignment3/list-comprehensions.py new file mode 100644 index 0000000..941a8c6 --- /dev/null +++ b/assignment3/list-comprehensions.py @@ -0,0 +1,31 @@ +# Task 3: List Comprehensions Practice +import csv + +# Task 3: step 1 + +# file path related to the assignment3 folder +csv_file_path = '../csv/employees.csv' + +# read the contents of the filepath into a list of lists using the csv module +with open(csv_file_path, mode='r' , newline='') as file: + reader = csv.reader(file) + employee_data = list(reader) + +# Task 3: step 2 + +# create a list of the employee names, first_name + space + last_name. +# The list comprehension should iterate through the items in the list read from the csv file. +# Print the resulting list. Skip the item created for the heading of the csv file. + +full_name = [f"{row[1]} {row[2]}" for row in employee_data[1:]] +print("Full Name: ") +print(full_name) + +# Task 3: step 3 +# filter list to include only those names that contain the letter "e" +names_with_e = [name for name in full_name if 'e' in name.lower()] + +# Print this list +print("\n Names with letter 'e': ") +print(names_with_e) + diff --git a/assignment3/log-decorator.py b/assignment3/log-decorator.py new file mode 100644 index 0000000..f4385e6 --- /dev/null +++ b/assignment3/log-decorator.py @@ -0,0 +1,62 @@ + + +import logging +from functools import wraps + +# Task 1: Writing and Testing a Decorator + +# one time setup +logger = logging.getLogger(__name__ + "_parameter_log") +logger.setLevel(logging.INFO) +logger.addHandler(logging.FileHandler("./decorator.log","a")) + +#Task1 -step 2 + +# Declare decorator(logger_decorator) to log function name (func.__name__) +def logger_decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + # formatt positional parameters + pos_parameters = list(args) if args else "none" + + # formatt keyword parameters + key_parameters = kwargs if kwargs else "none" + + # call the function for return value + result = func(*args, **kwargs) + + # To write a log record: + logger.log(logging.INFO, f"function: {func.__name__}") + logger.log(logging.INFO, f"positional parameters: {pos_parameters}") + logger.log(logging.INFO, f"keyword parameters: {key_parameters}") + logger.log(logging.INFO, f"return: {result}") + return result + return wrapper + +#Task1 -step 3 +# Declare a function that takes no parameters and returns nothing +@logger_decorator +def hello(): + print("Hello, World") + +#Task1 -step 4 +# Declare a function that takes a variable number of positional arguments and returns True +@logger_decorator +def check_position(*args): + return True + + +#Task1 -step 5 +# Declare a function that takes no positional arguments and a variable number of keyword arguments, and that returns logger_decorator +@logger_decorator +def return_decorator(**kwargs): + return logger_decorator + +#Task1 -step 6 +# mainline code +if __name__ == "__main__": + hello() + check_position(1, "Apple", 3) + return_decorator(user="admin", level='high') + + print("Please check ./decorator.log for the results.") diff --git a/assignment3/tictactoe.py b/assignment3/tictactoe.py new file mode 100644 index 0000000..898e416 --- /dev/null +++ b/assignment3/tictactoe.py @@ -0,0 +1,131 @@ +# Task 6: More on Classes + +# Task 6: step 2 +# declare class TictactoeException that inherit from the Exception class. +class TictactoeException(Exception): + + # Add an __init__ method + def __init__(self, message): + + #stores an instance variable called message + self.message = message + + #calls the __init__ method of the superclass + super().__init__(message) + +# Task 6: step 3 +# Declare also a class called Board. +class Board: + + # The Board class that has variable called valid_moves + valid_moves=["upper left", "upper center", "upper right", + "middle left", "center", "middle right", + "lower left", "lower center", "lower right"] + + # __init__ function that only has the self argument + def __init__(self): + + # Create a 3x3 list of lists, all containing a space " " + self.board_array = [[" " for _ in range(3)] for _ in range(3)] + + # Create instance variables self.turn, which is initialized to "X" + self.turn = "X" + + # Task 6: step 1(part 2) + + # Add a __str__() method- converts the board into a displayable string + def __str__(self): + + # show the current state of the game + lines = [] + + # The rows to be displayed are separated by newlines ("\n") + lines.append(f" {self.board_array[0][0]} | {self.board_array[0][1]} | {self.board_array[0][2]} \n") + lines.append("-----------\n") + lines.append(f" {self.board_array[1][0]} | {self.board_array[1][1]} | {self.board_array[1][2]} \n") + lines.append("-----------\n") + lines.append(f" {self.board_array[2][0]} | {self.board_array[2][1]} | {self.board_array[2][2]} \n") + return "".join(lines) + + + def move(self, move_string): + if not move_string in Board.valid_moves: + raise TictactoeException("That's not a valid move.") + move_index = Board.valid_moves.index(move_string) + row = move_index // 3 # row + column = move_index % 3 #column + if self.board_array[row][column] != " ": + raise TictactoeException("That spot is taken.") + self.last_move = move_string + + self.board_array[row][column] = self.turn + if self.turn == "X": + self.turn = "O" + else: + self.turn = "X" + + def whats_next(self): + # check for cat's Game + cat = True + for i in range(3): + for j in range(3): + if self.board_array[i][j] == " ": + cat = False + else: + continue + break + else: + continue + break + if (cat): + return (True, "Cat's Game.") + + # Check for win + win = False + for i in range(3): # check rows + if self.board_array[i][0] != " ": + if self.board_array[i][0] == self.board_array[i][1] and self.board_array[i][1] == self.board_array[i][2]: + win = True + break + if not win: + for i in range(3): # check columns + if self.board_array[0][i] != " ": + if self.board_array[0][i] == self.board_array[1][i] and self.board_array[1][i] == self.board_array[2][i]: + win = True + break + if not win: + if self.board_array[1][1] != " ": # check diagonals + if self.board_array[0][0] == self.board_array[1][1] and self.board_array[2][2] == self.board_array[1][1]: + win = True + if self.board_array[0][2] == self.board_array[1][1] and self.board_array[2][0] == self.board_array[1][1]: + win = True + + # Determine return state + if win: + # if win is true then winner is the player who just moved + winner = "X" if self.turn == "O" else "O" + return (True, f"{winner} has won") + if cat: + return(True, "Cat's Game.") + return(False, f"{self.turn}'s turn.") + +# Mainline Game loop +if __name__ == "__main__": + game_board = Board() + over = False + + print("TicTacToe Started") + print(game_board) + + while not over: + prompt = f"{game_board.turn}'s turn. Enyter move: " + user_input = input(prompt).strip().lower() + + try: + game_board.move(user_input) + print(game_board) + over, message = game_board.whats_next() + print(message) + except TictactoeException as e: + print(f"------Error: {e.message} ----") + diff --git a/assignment3/type-decorator.py b/assignment3/type-decorator.py new file mode 100644 index 0000000..4c79de2 --- /dev/null +++ b/assignment3/type-decorator.py @@ -0,0 +1,45 @@ +# Task 2: A Decorator that Takes an Argument + +# Task 2- step 2 +import functools + +# Task 2 - step 2 +# Declare decorator(type_converter) that has one argument +def type_converter(type_of_output): + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + # execute func for the return value + x = func(*args, **kwargs) + return type_of_output(x) + return wrapper + return decorator + +# Task 2 - step 3 +# return_int() function no arguments and returns the int 5 +# Decorate with type-decorator by passing str as the parameter to type_decorator +@type_converter(str) +def return_int(): + return 5 + +# Task 2 - step 4 +# return_string()function takes no arguments and returns the string - "not a number" +@type_converter(int) +def return_string(): + return ("not a number") + +# Task 2 - step 5 +# mainline of the program +if __name__ == "__main__": + y = return_int() + print(type(y).__name__) # This should print "str" + try: + y = return_string() + print("shouldn't get here!") + except ValueError: + print("can't convert that string to an integer!") # This is what should happen + + + + + diff --git a/assignment4/additional_employees.json b/assignment4/additional_employees.json new file mode 100644 index 0000000..aa8a70f --- /dev/null +++ b/assignment4/additional_employees.json @@ -0,0 +1,4 @@ +[ + {"Name": "Eve", "Age": 28, "City": "Miami", "Salary": 60000}, + {"Name": "Frank", "Age": 40, "City": "Seattle", "Salary": 95000} +] diff --git a/assignment4/assignment4.py b/assignment4/assignment4.py index e69de29..c3692a2 100644 --- a/assignment4/assignment4.py +++ b/assignment4/assignment4.py @@ -0,0 +1,186 @@ +import pandas as pd + +# Task 1 - Create a DataFrame from a dictionary +# Create dictionary +data = { + 'Name': ['Alice', 'Bob', 'Charlie'], + 'Age': [25, 30, 35], + 'City': ['New York', 'Los Angeles', 'Chicago'] +} + +# Convert the dictionary into a DataFrame using Pandas. +task1_data_frame= pd.DataFrame(data) + +# Print the DataFrame to verify its creation. +print(task1_data_frame) + + +# Task1 - Add a new column +# Make a copy of the dataFrame +task1_with_salary = task1_data_frame.copy() + +# Add a column called Salary +task1_with_salary['Salary'] = [70000, 80000, 90000] + +# Print the new DataFrame +print(task1_with_salary) + +# Task1 - Modify an existing column +# Make a copy of task1_with_salary in a variable named task1_older +task1_older = task1_with_salary.copy() + +# Increment the Age column by 1 for each entry +task1_older['Age'] = task1_older['Age'] + 1 + +# Print the modified DataFrame +print(task1_older) + +# Task1 - Save the DataFrame as a CSV file +# Save the task1_older DataFrame to a file named employees.csv +task1_older.to_csv('employees.csv', index= False) + +# Look at the contents of the CSV file +print("CSV file created!") + + +# Task 2: Loading Data from CSV and JSON +# Task 2: Read data from a CSV file +# # Load the CSV file from Task 1 into a new DataFrame saved to a variable task2_employees +task2_employees = pd.read_csv('employees.csv') + +# Print it and run the tests to verify the contents +print(task2_employees) + +# Task 2: Read data from a JSON file + +# Create a JSON file (additional_employees.json). +# # json +# [ +# {"Name": "Eve", "Age": 28, "City": "Miami", "Salary": 60000}, +# {"Name": "Frank", "Age": 40, "City": "Seattle", "Salary": 95000} +# ] +# Load this JSON file into a new DataFrame and assign it to the variable json_employees +# Change this line in assignment4.py +#json_employees = pd.read_json('assignment4/additional_employees.json') +json_employees = pd.read_json('additional_employees.json') + +# Print the DataFrame to verify it loaded correctly and run the tests +print(json_employees) + +# Task 2: Combine DataFrames +# Combine the data 'assignment4/additional_employees.json' and task2_employees, +# save it as more_employees +more_employees = pd.concat([task2_employees, json_employees], ignore_index= True) + +# Print the combined Dataframe and run the tests +print(more_employees) + +# Task 3: Data Inspection - Using Head, Tail, and Info Methods +# Task 3:Use the head() + +# Assign the first three rows of the more_employees DataFrame to the variable first_three +first_three = more_employees.head(3) + +# Print the variable +print(first_three) + +# Task 3: Use the tail() method + +# Assign the last two rows of the more_employees DataFrame to the variable last_two +last_two = more_employees.tail(2) + +# Print the variable +print(last_two) + +# Task 3: Get the shape of a Dataframe + +# Assign the shape of the more_employees DataFrame to the variable employee_shape +employee_shape = more_employees.shape + +# Print the variable +print(employee_shape) + +# Task 3: Use the info() method + +# Print a concise summary +more_employees.info() + + +# Task 4: Data Cleaning + +# Task 4: Create a DataFrame from dirty_data.csv + +# Create a DataFrame from dirty_data.csv file and assign it to the variable dirty_data +dirty_data = pd.read_csv('dirty_data.csv') + +# Print dirty_data +print(dirty_data) + +# Create a copy of the dirty data +clean_data = dirty_data.copy() + +# Task 4: Remove duplicate rows and print +clean_data = clean_data.drop_duplicates() +print(clean_data) + +# Task 4: Convert Age to numeric +clean_data['Age'] = pd.to_numeric(clean_data['Age'], errors='coerce') + +# handle missing values and print +clean_data['Age'] = clean_data['Age'].fillna(clean_data['Age'].mean()) +print(clean_data) + +# Task 4 - Convert Salary to numeric, replace known placeholders (unknown, n/a) with NaN and print +clean_data['Salary'] = clean_data['Salary'].replace(['unknown', 'n/a'], pd.NA) +clean_data['Salary'] = pd.to_numeric(clean_data['Salary'], errors= 'coerce') +print(clean_data) + + +# Task 4 - Fill missing numeric values +# Fill Age which the mean +clean_data['Age'] = clean_data['Age'].fillna(clean_data['Age'].mean()) + +# Salary with the median +clean_data['Salary'] = clean_data['Salary'].fillna(clean_data['Salary'].median()) + +print(clean_data) + + +# Task 4 - Convert Hire Date to datetime +clean_data['Hire Date'] = pd.to_datetime(clean_data['Hire Date'], errors='coerce') + +clean_data['Hire Date'] = clean_data['Hire Date'].fillna(method='ffill') +print(clean_data) + +# Task 4 - Strip extra whitespace and standardize Name and Department as uppercase +clean_data['Name'] = clean_data['Name'].str.strip().str.upper() +clean_data['Department'] = clean_data['Department'].str.strip().str.upper() + +print(clean_data) + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assignment4/employees.csv b/assignment4/employees.csv new file mode 100644 index 0000000..2bd2f60 --- /dev/null +++ b/assignment4/employees.csv @@ -0,0 +1,4 @@ +Name,Age,City,Salary +Alice,26,New York,70000 +Bob,31,Los Angeles,80000 +Charlie,36,Chicago,90000 diff --git a/employees.csv b/employees.csv new file mode 100644 index 0000000..2bd2f60 --- /dev/null +++ b/employees.csv @@ -0,0 +1,4 @@ +Name,Age,City,Salary +Alice,26,New York,70000 +Bob,31,Los Angeles,80000 +Charlie,36,Chicago,90000