diff --git a/additional_employees.json b/additional_employees.json new file mode 100644 index 0000000..4a54e2d --- /dev/null +++ b/additional_employees.json @@ -0,0 +1 @@ +[{"Name":"Eve","Age":28,"City":"Miami","Salary":60000},{"Name":"Frank","Age":40,"City":"Seattle","Salary":95000}] \ No newline at end of file diff --git a/assignment1/assignment1.py b/assignment1/assignment1.py index fafa187..cd8140c 100644 --- a/assignment1/assignment1.py +++ b/assignment1/assignment1.py @@ -1 +1,167 @@ -# Write your code here. \ No newline at end of file +# Write your code here. + +# task 1 +def hello(): + return "Hello!" + +# task 2 + + +def greet(name): + return f"Hello, {name}!" + +# task 3 + + +def calc(a, b, operation="multiply"): + try: + match operation: + case "add": + return a + b + case "subtract": + return a - b + case "multiply": + return a * b + case "divide": + return a/b + case "modulo": + return a % b + case "int_divide": + return a // b + case "power": + return a ** b + except ZeroDivisionError: + return "You can't divide by 0!" + except TypeError: + return "You can't multiply those values!" + +# task 4 + + +def data_type_conversion(value, requested_type): + try: + if requested_type == "float": + return float(value) + elif requested_type == "str": + return str(value) + elif requested_type == "int": + return int(value) + except (ValueError, TypeError): + return f"You can't convert {value} into a {requested_type}." + +# task 5 + + +# Task 5 +def grade(*args): + try: + average = sum(args) / len(args) + + if average >= 90: + return "A" + elif average >= 80: + return "B" + elif average >= 70: + return "C" + elif average >= 60: + return "D" + else: + return "F" + except (TypeError, ZeroDivisionError): + return "Invalid data was provided." + +# task 6 + + +def repeat(text, count): + result = "" + for _ in range(count): + result += text + return result + + +print(repeat("hi", 3)) + +# task 7 + + +def student_scores(option, **kwargs): + if option == "best": + best_student = "" + highest_score = -1 + + for key, value in kwargs.items(): + if value > highest_score: + highest_score = value + best_student = key + + return best_student + elif option == "mean": + total = 0 + + for value in kwargs.values(): + total += value + + return total / len(kwargs) + + +print(student_scores("mean", Alice=90, Bob=85, Carol=95)) + + +# task 8 + +def titleize(text): + little_words = ["a", "on", "an", "the", "of", "and", "is", "in"] + words = text.split() + + for index, word in enumerate(words): + lower_word = word.lower() + + if index == 0 or index == len(words) - 1: + words[index] = word.capitalize() + elif word in little_words: + words[index] = word.lower() + else: + words[index] = word.capitalize() + return " ".join(words) + + +# task 9 +def hangman(secret, guess): + result = "" + + for letter in secret: + if letter in guess: + result += letter + else: + result += "_" + return result + + +print(hangman("alphabet", "ab")) + + +# Task 10 +def pig_latin(text): + vowels = "aeiou" + words = text.split() + result = [] + + for word in words: + if word[0] in vowels: + result.append(word + "ay") + else: + index = 0 + + while index < len(word) and word[index] not in vowels: + if word[index:index + 2] == "qu": + index += 2 + break + index += 1 + + result.append(word[index:] + word[:index] + "ay") + + return " ".join(result) + + +print(pig_latin("apple")) diff --git a/assignment1/tempCodeRunnerFile.py b/assignment1/tempCodeRunnerFile.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assignment1/tempCodeRunnerFile.py @@ -0,0 +1 @@ + diff --git a/assignment2/assignment2.py b/assignment2/assignment2.py new file mode 100644 index 0000000..ae84fee --- /dev/null +++ b/assignment2/assignment2.py @@ -0,0 +1,226 @@ +# Task 2: Read a CSV File +import csv +import traceback +import os +import custom_module +from datetime import datetime + + +def read_employees(): + employees_dict = {} + rows = [] + + try: + with open("../csv/employees.csv", "r", newline="") as file: + reader = csv.reader(file) + + for index, row in enumerate(reader): + if index == 0: + employees_dict["fields"] = row + else: + rows.append(row) + + employees_dict["rows"] = rows + return employees_dict + + except (Exception, KeyboardInterrupt) as e: + trace_back = traceback.extract_tb(e.__traceback__) + stack_trace = [] + + for trace in trace_back: + stack_trace.append( + f"File : {trace[0]} , Line : {trace[1]}, Func.Name : {trace[2]}, Message : {trace[3]}" + ) + + print("An exception occurred.") + print(f"Exception type: {type(e).__name__}") + + message = str(e) + if message: + print(f"Exception message: {message}") + + print(f"Stack trace: {stack_trace}") + raise + + +employees = read_employees() +print(employees) + + +# Task 3: Find the Column Index +def column_index(column_name): + return employees["fields"].index(column_name) + + +employee_id_column = column_index("employee_id") +print(employee_id_column) + + +# Task 4: Find the Employee First Name +def first_name(row_number): + first_name_column = column_index("first_name") + return employees["rows"][row_number][first_name_column] + + +# Task 5: Find the Employee +def employee_find(employee_id): + def employee_match(row): + return int(row[employee_id_column]) == employee_id + + matches = list(filter(employee_match, employees["rows"])) + return matches + + +# Task 6: Find the Employee with a Lambda +def employee_find_2(employee_id): + matches = list( + filter(lambda row: int(row[employee_id_column]) + == employee_id, employees["rows"]) + ) + return matches + + +# Task 7: Sort the Rows by last_name Using a Lambda +def sort_by_last_name(): + last_name_column = column_index("last_name") + employees["rows"].sort(key=lambda row: row[last_name_column]) + return employees["rows"] + + +sort_by_last_name() +print(employees) + + +# Task 8: Create a dict for an Employee +def employee_dict(row): + employee = {} + + for index, field in enumerate(employees["fields"]): + if field != "employee_id": + employee[field] = row[index] + + return employee + + +print(employee_dict(employees["rows"][0])) + + +# Task 9: A dict of dicts, for All Employees +def all_employees_dict(): + all_employees = {} + + for row in employees["rows"]: + employee_id = row[employee_id_column] + all_employees[employee_id] = employee_dict(row) + + return all_employees + + +print(all_employees_dict()) + + +# Task 10: Use the os Module +def get_this_value(): + return os.getenv("THISVALUE") + + +# Task 11: Creating Your Own Module +def set_that_secret(new_secret): + custom_module.set_secret(new_secret) + + +set_that_secret("open sesame") +print(custom_module.secret) + + +# Task 12: Read minutes1.csv and minutes2.csv +def read_csv_to_dict(path): + data = {} + rows = [] + + try: + with open(path, "r", newline="") as file: + reader = csv.reader(file) + + for index, row in enumerate(reader): + if index == 0: + data["fields"] = row + else: + rows.append(tuple(row)) + + data["rows"] = rows + return data + + except (Exception, KeyboardInterrupt) as e: + trace_back = traceback.extract_tb(e.__traceback__) + stack_trace = [] + + for trace in trace_back: + stack_trace.append( + f"File : {trace[0]} , Line : {trace[1]}, Func.Name : {trace[2]}, Message : {trace[3]}" + ) + + print("An exception occurred.") + print(f"Exception type: {type(e).__name__}") + + message = str(e) + if message: + print(f"Exception message: {message}") + + print(f"Stack trace: {stack_trace}") + raise + + +def read_minutes(): + minutes1 = read_csv_to_dict("../csv/minutes1.csv") + minutes2 = read_csv_to_dict("../csv/minutes2.csv") + return minutes1, minutes2 + + +minutes1, minutes2 = read_minutes() +print(minutes1) +print(minutes2) + + +# Task 13: Create minutes_set +def create_minutes_set(): + set1 = set(minutes1["rows"]) + set2 = set(minutes2["rows"]) + return set1.union(set2) + + +minutes_set = create_minutes_set() +print(minutes_set) + + +# Task 14: Convert to datetime +def create_minutes_list(): + minutes = list(minutes_set) + minutes = list( + map(lambda x: (x[0], datetime.strptime(x[1], "%B %d, %Y")), minutes) + ) + return minutes + + +minutes_list = create_minutes_list() +print(minutes_list) + + +# Task 15: Write Out Sorted List +def write_sorted_list(): + minutes_list.sort(key=lambda x: x[1]) + + converted_list = list( + map(lambda x: (x[0], datetime.strftime( + x[1], "%B %d, %Y")), minutes_list) + ) + + with open("./minutes.csv", "w", newline="") as file: + writer = csv.writer(file) + writer.writerow(minutes1["fields"]) + writer.writerows(converted_list) + + return converted_list + + +print(write_sorted_list()) diff --git a/assignment2/custom_module.py b/assignment2/custom_module.py new file mode 100644 index 0000000..2ced9c1 --- /dev/null +++ b/assignment2/custom_module.py @@ -0,0 +1,7 @@ + +secret = "shazam!" + + +def set_secret(new_secret): + global secret + secret = new_secret diff --git a/assignment2/diary.py b/assignment2/diary.py new file mode 100644 index 0000000..b1c97e5 --- /dev/null +++ b/assignment2/diary.py @@ -0,0 +1,36 @@ +# Task 1: Diary +import traceback + +try: + with open("diary.txt", "a") as file: + first_prompt = True + + while True: + if first_prompt: + line = input("What happened today? ") + first_prompt = False + else: + line = input("What else? ") + + file.write(line + "\n") + + if line == "done for now": + break + +except (Exception, KeyboardInterrupt) as e: + trace_back = traceback.extract_tb(e.__traceback__) + stack_trace = [] + + for trace in trace_back: + stack_trace.append( + f"File : {trace[0]} , Line : {trace[1]}, Func.Name : {trace[2]}, Message : {trace[3]}" + ) + + print("An exception occurred.") + print(f"Exception type: {type(e).__name__}") + + message = str(e) + if message: + print(f"Exception message: {message}") + + print(f"Stack trace: {stack_trace}") diff --git a/assignment2/minutes.csv b/assignment2/minutes.csv index 0acabb7..84a339b 100644 --- a/assignment2/minutes.csv +++ b/assignment2/minutes.csv @@ -1,46 +1,46 @@ Name,Date Jason Tucker,"September 20, 1980" -Austin Hester,"March 8, 1981" -Daniel Jackson,"October 2, 1981" +Austin Hester,"March 08, 1981" +Daniel Jackson,"October 02, 1981" Mrs. Samantha Johnson,"December 17, 1981" -Joseph Harris,"March 3, 1982" +Joseph Harris,"March 03, 1982" Mrs. Samantha Johnson,"March 12, 1982" Gina Maldonado,"December 16, 1982" Mrs. Samantha Johnson,"December 23, 1982" Yesenia Smith,"August 10, 1983" -Lori Martin,"February 5, 1984" +Lori Martin,"February 05, 1984" Jonathan Parrish,"June 12, 1984" Mrs. Samantha Johnson,"July 20, 1984" -Amanda Brown,"August 8, 1984" +Amanda Brown,"August 08, 1984" Sarah Murray,"October 30, 1984" Mrs. Samantha Johnson,"November 28, 1984" -Austin Hester,"June 4, 1985" +Austin Hester,"June 04, 1985" Jonathan Parrish,"March 18, 1986" -Yesenia Smith,"May 6, 1986" +Yesenia Smith,"May 06, 1986" Daniel Jackson,"December 13, 1986" Gina Maldonado,"February 13, 1987" Kimberly Stewart,"December 12, 1987" Mrs. Samantha Johnson,"July 10, 1988" Sarah Murray,"August 16, 1988" Aaron Kaufman,"October 24, 1988" -Tony Henderson,"November 7, 1988" +Tony Henderson,"November 07, 1988" Sarah Murray,"November 19, 1988" Austin Hester,"January 18, 1989" -Joseph Harris,"March 1, 1989" -Gina Maldonado,"April 7, 1989" +Joseph Harris,"March 01, 1989" +Gina Maldonado,"April 07, 1989" Aaron Kaufman,"November 14, 1989" -Daniel Jackson,"April 8, 1990" -Aaron Kaufman,"May 1, 1990" +Daniel Jackson,"April 08, 1990" +Aaron Kaufman,"May 01, 1990" Aaron Kaufman,"July 21, 1990" -Tony Henderson,"October 4, 1990" +Tony Henderson,"October 04, 1990" Yesenia Smith,"November 23, 1990" -Joseph Harris,"April 3, 1991" +Joseph Harris,"April 03, 1991" Jason Tucker,"April 30, 1991" Matthew Russell,"May 31, 1991" -Lori Martin,"July 8, 1991" +Lori Martin,"July 08, 1991" Mrs. Samantha Johnson,"July 23, 1991" Tony Henderson,"November 15, 1991" -Gina Maldonado,"February 9, 1992" +Gina Maldonado,"February 09, 1992" Sarah Murray,"June 27, 1992" Gina Maldonado,"October 31, 1992" Austin Hester,"December 10, 1992" diff --git a/assignment3/decorator.log b/assignment3/decorator.log new file mode 100644 index 0000000..782d1a2 --- /dev/null +++ b/assignment3/decorator.log @@ -0,0 +1,56 @@ +function: add_numbers +positional parameters: [3, 5] +keyword parameters: none +return: 8 +function: add_numbers +positional parameters: none +keyword parameters: {'a': 10, 'b': 20} +return: 30 +function: add_numbers +positional parameters: [7] +keyword parameters: {'b': 8} +return: 15 +function: add_numbers +positional parameters: [3, 5] +keyword parameters: none +return: 8 +function: add_numbers +positional parameters: none +keyword parameters: {'a': 10, 'b': 20} +return: 30 +function: add_numbers +positional parameters: [7] +keyword parameters: {'b': 8} +return: 15 +function: say_hello +positional parameters: none +keyword parameters: none +return: None +function: add_numbers +positional parameters: [3, 5] +keyword parameters: none +return: 8 +function: add_numbers +positional parameters: none +keyword parameters: {'a': 10, 'b': 20} +return: 30 +function: add_numbers +positional parameters: [7] +keyword parameters: {'b': 8} +return: 15 +function: say_hello +positional parameters: none +keyword parameters: none +return: None +function: add_numbers +positional parameters: [3, 5] +keyword parameters: none +return: 8 +function: add_numbers +positional parameters: none +keyword parameters: {'a': 10, 'b': 20} +return: 30 +function: add_numbers +positional parameters: [7] +keyword parameters: {'b': 8} +return: 15 diff --git a/assignment3/extend-point-to-vector.py b/assignment3/extend-point-to-vector.py new file mode 100644 index 0000000..23fbe6f --- /dev/null +++ b/assignment3/extend-point-to-vector.py @@ -0,0 +1,77 @@ + + +# Task 5: Extending a Class + +import math + + +class Point: + # The __init__ method runs when we create a new Point object. + def __init__(self, x, y): + self.x = x + self.y = y + + # Equality method. + # This lets us compare two Point objects using ==. + def __eq__(self, other): + return self.x == other.x and self.y == other.y + + # String representation method. + # This controls how the Point prints. + def __str__(self): + return f"Point({self.x}, {self.y})" + + # Euclidean distance method. + # Formula: square root of ((x2 - x1)^2 + (y2 - y1)^2) + def distance_to(self, other): + return math.sqrt((other.x - self.x) ** 2 + (other.y - self.y) ** 2) + + +class Vector(Point): + # Vector uses the same __init__ method from Point. + # So we do not need to write __init__ again. + + # Override string representation. + # This makes Vector print differently from Point. + def __str__(self): + return f"Vector<{self.x}, {self.y}>" + + # Override the + operator for vector addition. + def __add__(self, other): + new_x = self.x + other.x + new_y = self.y + other.y + return Vector(new_x, new_y) + + +# Mainline code: demonstrate all classes and methods + +# Create Point objects +point1 = Point(3, 4) +point2 = Point(3, 4) +point3 = Point(6, 8) + +print(point1) +print(point3) + +# Demonstrate equality +print(point1 == point2) # True +print(point1 == point3) # False + +# Demonstrate Euclidean distance +print(point1.distance_to(point3)) + +# Create Vector objects +vector1 = Vector(2, 3) +vector2 = Vector(5, 7) + +# Demonstrate Vector string representation +print(vector1) +print(vector2) + +# Demonstrate vector addition +vector3 = vector1 + vector2 +print(vector3) + +# Demonstrate that Vector still has Point methods +print(vector1.distance_to(vector2)) +print(vector1 == Vector(2, 3)) diff --git a/assignment3/hangman-closure.py b/assignment3/hangman-closure.py new file mode 100644 index 0000000..9f990b8 --- /dev/null +++ b/assignment3/hangman-closure.py @@ -0,0 +1,53 @@ + +# Task 4: Closure Practice + +def make_hangman(secret_word): + # This list belongs to the outer function. + # The inner function will remember it. + guesses = [] + + def hangman_closure(letter): + # Add each guessed letter to the guesses list + guesses.append(letter) + + # Build the display word + # If the letter was guessed, show it. + # If not, show an underscore. + display_word = "" + + for char in secret_word: + if char in guesses: + display_word += char + else: + display_word += "_" + + print(display_word) + + # If there are no underscores left, + # the full word has been guessed. + if "_" not in display_word: + return True + else: + return False + + return hangman_closure + + +# Mainline code: Hangman game + +secret_word = input("Enter the secret word: ").lower() + +# Create the closure function +hangman_game = make_hangman(secret_word) + +game_finished = False + +while not game_finished: + guess = input("Guess a letter: ").lower() + + # Optional: only use the first character if user types more than one letter + guess = guess[0] + + game_finished = hangman_game(guess) + +print("You guessed the full word!") diff --git a/assignment3/list-comprehensions.py b/assignment3/list-comprehensions.py new file mode 100644 index 0000000..3195201 --- /dev/null +++ b/assignment3/list-comprehensions.py @@ -0,0 +1,27 @@ + +# Task 3: List Comprehensions Practice + +import csv + +# Read the contents of ../csv/employees.csv into a list of lists +with open("../csv/employees.csv", mode="r") as file: + reader = csv.reader(file) + employees = list(reader) + +# Create a list of employee full names. +# employees[1:] skips the heading/header row. +employee_names = [ + employee[1] + " " + employee[2] + for employee in employees[1:] +] + +print(employee_names) + +# Create another list that includes only names containing the letter "e". +names_with_e = [ + name + for name in employee_names + if "e" in name +] + +print(names_with_e) diff --git a/assignment3/log-decorator.py b/assignment3/log-decorator.py new file mode 100644 index 0000000..e8c18ef --- /dev/null +++ b/assignment3/log-decorator.py @@ -0,0 +1,81 @@ + +# Task 2: Create logger_decorator + +import logging + +# One-time logging setup +logger = logging.getLogger(__name__ + "_parameter_log") +logger.setLevel(logging.INFO) +logger.addHandler(logging.FileHandler("./decorator.log", "a")) + + +def logger_decorator(func): + def wrapper(*args, **kwargs): + # Call the original function and store its return value + result = func(*args, **kwargs) + + # If there are positional arguments, show them as a list. + # If not, show "none". + positional_parameters = list(args) if args else "none" + + # If there are keyword arguments, show them as a dictionary. + # If not, show "none". + keyword_parameters = kwargs if kwargs else "none" + + # Write information to decorator.log + logger.log(logging.INFO, f"function: {func.__name__}") + logger.log( + logging.INFO, f"positional parameters: {positional_parameters}") + logger.log(logging.INFO, f"keyword parameters: {keyword_parameters}") + logger.log(logging.INFO, f"return: {result}") + + # Return the original function's result + return result + + return wrapper + +# Task 3: Function with no parameters and no return value + + +@logger_decorator +def say_hello(): + print("Hello, World!") + + +say_hello() + +# Example test function + + +@logger_decorator +def add_numbers(a, b): + return a + b + + +# Example calls +add_numbers(3, 5) +add_numbers(a=10, b=20) +add_numbers(7, b=8) + +# Task 4: Function with variable positional arguments + + +@logger_decorator +def positional_args_function(*args): + return True + + +# Task 5: Function with variable keyword arguments + +@logger_decorator +def keyword_args_function(**kwargs): + return logger_decorator + + +# Task 6: Mainline code - call each decorated function + +say_hello() + +positional_args_function("apple", "banana", "orange") + +keyword_args_function(name="Raz", course="Python", assignment=3) diff --git a/assignment3/tictactoe.py b/assignment3/tictactoe.py new file mode 100644 index 0000000..c6e1e8e --- /dev/null +++ b/assignment3/tictactoe.py @@ -0,0 +1,134 @@ + +# Task 6: More on Classes + + +class TictactoeException(Exception): + # This creates our own custom exception type. + def __init__(self, message): + self.message = message + super().__init__(message) + + +class Board: + # Class variable: shared by all Board objects + valid_moves = [ + "upper left", "upper center", "upper right", + "middle left", "center", "middle right", + "lower left", "lower center", "lower right" + ] + + def __init__(self): + # Create a 3x3 board filled with empty spaces + self.board_array = [ + [" ", " ", " "], + [" ", " ", " "], + [" ", " ", " "] + ] + + # X always starts first + self.turn = "X" + + # Optional: store the last move + self.last_move = None + + def __str__(self): + lines = [] + 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): + # Check if the move is valid + if move_string not in Board.valid_moves: + raise TictactoeException("That's not a valid move.") + + # Convert move string into board position + move_index = Board.valid_moves.index(move_string) + row = move_index // 3 + column = move_index % 3 + + # Check if the spot is already taken + if self.board_array[row][column] != " ": + raise TictactoeException("That spot is taken.") + + # Put X or O on the board + self.board_array[row][column] = self.turn + + # Store the last move + self.last_move = move_string + + # Change turns + if self.turn == "X": + self.turn = "O" + else: + self.turn = "X" + + def whats_next(self): + # Check rows + for i in range(3): + if self.board_array[i][0] != " ": + if self.board_array[i][0] == self.board_array[i][1] == self.board_array[i][2]: + return True, f"{self.board_array[i][0]} has won" + + # Check columns + for i in range(3): + if self.board_array[0][i] != " ": + if self.board_array[0][i] == self.board_array[1][i] == self.board_array[2][i]: + return True, f"{self.board_array[0][i]} has won" + + # Check diagonals + if self.board_array[1][1] != " ": + if self.board_array[0][0] == self.board_array[1][1] == self.board_array[2][2]: + return True, f"{self.board_array[1][1]} has won" + + if self.board_array[0][2] == self.board_array[1][1] == self.board_array[2][0]: + return True, f"{self.board_array[1][1]} has won" + + # Check if board is full + board_is_full = True + + for row in self.board_array: + for space in row: + if space == " ": + board_is_full = False + + if board_is_full: + return True, "Cat's Game" + + # If no winner and board is not full, continue game + return False, f"{self.turn}'s turn" + + +# Mainline code: Play the game + +board = Board() +game_over = False + +print("Welcome to Tic Tac Toe!") +print("Valid moves are:") +print(Board.valid_moves) +print() + +while not game_over: + print(board) + + move_string = input(f"{board.turn}'s turn. Enter your move: ") + + try: + board.move(move_string) + except TictactoeException as error: + print(error.message) + print("Please try again.") + continue + + game_over, message = board.whats_next() + print(message) + +print(board) +print("Game over!") diff --git a/assignment3/type-decorator.py b/assignment3/type-decorator.py new file mode 100644 index 0000000..2ed09ee --- /dev/null +++ b/assignment3/type-decorator.py @@ -0,0 +1,39 @@ + +# Task 2: A Decorator that Takes an Argument + +def type_converter(type_of_output): + def decorator(func): + def wrapper(*args, **kwargs): + x = func(*args, **kwargs) + return type_of_output(x) + + return wrapper + + return decorator + + +# This function returns an integer, +# but the decorator converts the return value to a string. +@type_converter(str) +def return_int(): + return 5 + + +# This function returns a string, +# but the decorator tries to convert it to an integer. +# This will cause a ValueError because "not a number" cannot become an int. +@type_converter(int) +def return_string(): + return "not a number" + + +# Mainline code + +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!") diff --git a/assignment4/additional_employees.json b/assignment4/additional_employees.json new file mode 100644 index 0000000..4a54e2d --- /dev/null +++ b/assignment4/additional_employees.json @@ -0,0 +1 @@ +[{"Name":"Eve","Age":28,"City":"Miami","Salary":60000},{"Name":"Frank","Age":40,"City":"Seattle","Salary":95000}] \ No newline at end of file diff --git a/assignment4/assignment4.py b/assignment4/assignment4.py index e69de29..1879978 100644 --- a/assignment4/assignment4.py +++ b/assignment4/assignment4.py @@ -0,0 +1,192 @@ +import pandas as pd + +# Task 1: Introduction to Pandas - Creating a DataFrame + +# Step 1: Create a dictionary. +# A dictionary stores data as key-value pairs. +# Here, each key will become a column name in the DataFrame. +data = { + "Name": ["Alice", "Bob", "Charlie"], + "Age": [25, 30, 35], + "City": ["New York", "Los Angeles", "Chicago"] +} + +# Step 2: Convert the dictionary into a Pandas DataFrame. +# The test file expects this variable name exactly: task1_data_frame +task1_data_frame = pd.DataFrame(data) + +# Step 3: Print the DataFrame to verify it was created correctly. +print(task1_data_frame) + +# Add a new column +# Make a copy of the original DataFrame so we do not change task1_data_frame directly. +task1_with_salary = task1_data_frame.copy() + +# Add a new column called Salary. +task1_with_salary["Salary"] = [70000, 80000, 90000] + +# Print the new DataFrame to verify the Salary column was added. +print(task1_with_salary) + + +# Modify an existing column +# Make a copy of task1_with_salary so we do not change it directly. +task1_older = task1_with_salary.copy() + +# Increase each person's age by 1. +task1_older["Age"] = task1_older["Age"] + 1 + +# Print the modified DataFrame to verify the Age column changed. +print(task1_older) + + +# Save the DataFrame as a CSV file +# index=False means do not include the DataFrame index numbers in the CSV file. +task1_older.to_csv("employees.csv", index=False) + +# Task 2: Loading Data from CSV and JSON + +# Read data from the CSV file created in Task 1. +# This loads employees.csv into a new DataFrame. +task2_employees = pd.read_csv("employees.csv") + +# Print to verify the CSV file loaded correctly. +print(task2_employees) + + +# Create a JSON file with two additional employees. +# Each dictionary represents one employee. +additional_employees = [ + { + "Name": "Eve", + "Age": 28, + "City": "Miami", + "Salary": 60000 + }, + { + "Name": "Frank", + "Age": 40, + "City": "Seattle", + "Salary": 95000 + } +] + +# Save the list of dictionaries into a JSON file. +# orient="records" means each row becomes one JSON object. +pd.DataFrame(additional_employees).to_json( + "additional_employees.json", orient="records") + + +# Read data from the JSON file. +json_employees = pd.read_json("additional_employees.json") + +# Print to verify the JSON file loaded correctly. +print(json_employees) + + +# Combine DataFrames. +# pd.concat joins the CSV employees and JSON employees into one DataFrame. +# ignore_index=True resets the row numbers to 0, 1, 2, 3, 4. +more_employees = pd.concat( + [task2_employees, json_employees], ignore_index=True) + +# Print to verify the combined DataFrame. +print(more_employees) + +# Task 3: Data Inspection - Using head(), tail(), shape, and info() + +# Use the head() method. +# .head(3) returns the first 3 rows of the DataFrame. +first_three = more_employees.head(3) + +# Print the first three rows. +print(first_three) + + +# Use the tail() method. +# .tail(2) returns the last 2 rows of the DataFrame. +last_two = more_employees.tail(2) + +# Print the last two rows. +print(last_two) + + +# Get the shape of the DataFrame. +# .shape returns a tuple: (number_of_rows, number_of_columns) +employee_shape = more_employees.shape + +# Print the shape. +print(employee_shape) + + +# Use the info() method. +# .info() prints a summary showing column names, data types, and non-null counts. +more_employees.info() + +# Task 4: Data Cleaning + +# Create a DataFrame from dirty_data.csv. +dirty_data = pd.read_csv("dirty_data.csv") + +# Print the dirty/original data. +print(dirty_data) + + +# Create a copy of dirty_data. +# We clean the copy so the original dirty_data stays unchanged. +clean_data = dirty_data.copy() + + +# Remove duplicate rows. +# inplace=True updates clean_data directly. +clean_data.drop_duplicates(inplace=True) + +print(clean_data) + + +# Convert Age to numeric. +# errors="coerce" changes invalid values into NaN. +clean_data["Age"] = pd.to_numeric(clean_data["Age"], errors="coerce") + +print(clean_data) + + +# Convert Salary to numeric. +# First replace known placeholder values with NaN. +# Then convert the column to numeric. +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) + + +# Fill missing numeric values. +# Fill missing Age values with the mean age. +# Fill missing Salary values with the median salary. +clean_data["Age"] = clean_data["Age"].fillna(clean_data["Age"].mean()) +clean_data["Salary"] = clean_data["Salary"].fillna( + clean_data["Salary"].median()) + +print(clean_data) + + +# Convert Hire Date to datetime. +# errors="coerce" turns invalid dates into NaT. +clean_data["Hire Date"] = pd.to_datetime( + clean_data["Hire Date"], errors="coerce") + +# Fill missing/invalid dates with a valid replacement date. +# We use the most common date from the column. +clean_data["Hire Date"] = clean_data["Hire Date"].fillna( + clean_data["Hire Date"].mode()[0]) + +print(clean_data) + + +# Strip extra whitespace and standardize Name and Department as uppercase. +# .str.strip() removes spaces before and after the text. +# .str.upper() changes the text to 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