{ "cells": [ { "cell_type": "markdown", "id": "173acd43", "metadata": {}, "source": [ "# Week 2: Data Structures, NumPy and Vectorised Computing\n", "\n", "*Kmex Consult — Data Science Fundamentals Course*\n", "\n", "Work through each code cell in order. Edit and re-run to experiment." ] }, { "cell_type": "markdown", "id": "eb6a23cf", "metadata": {}, "source": [ "*Data Science Fundamentals Course*" ] }, { "cell_type": "markdown", "id": "d37844f0", "metadata": {}, "source": [ "## Week 2 Overview\n", "Week 2 builds on your Python fundamentals by introducing data structures that are essential for organizing and manipulating data. You will learn how to work with collections of data using lists, tuples, dictionaries, and sets. Additionally, you will learn how to read and write data to files, which is crucial for data science work. Finally, we will cover exception handling to make your programs more robust.\n", "\n", "By the end of Week 2, you will be able to:\n", "- Create and manipulate lists, tuples, dictionaries, and sets\n", "- Understand the differences between mutable and immutable data structures\n", "- Work with strings: indexing, slicing, and common methods\n", "- Read data from files in various formats\n", "- Write data to files\n", "- Handle errors with try-except blocks\n", "- Understand when to use each data structure\n", "\n", "Week 2 is divided into three 2-hour sessions:\n", "- Session 1: Lists, Tuples, and Common Sequence Operations\n", "- Session 2: Dictionaries, Sets, and String Manipulation\n", "- Session 3: File Handling and Exception Handling" ] }, { "cell_type": "markdown", "id": "baf364b1", "metadata": {}, "source": [ "## SESSION 1: Lists, Tuples, and Common Sequence Operations" ] }, { "cell_type": "markdown", "id": "40bf25a5", "metadata": {}, "source": [ "### Duration: 2 hours" ] }, { "cell_type": "markdown", "id": "689aae66", "metadata": {}, "source": [ "### 1.1 Lists: Creating and Understanding\n", "A list is an ordered, mutable collection of items. \"Mutable\" means you can change, add, or remove items after creating the list. Lists are one of the most commonly used data structures in Python and essential for data science.\n", "\n", "Key characteristics of lists:\n", "- Ordered: Items have a specific position (index)\n", "- Mutable: You can modify items after creation\n", "- Heterogeneous: Can contain different data types\n", "- Dynamic: Can grow or shrink as needed" ] }, { "cell_type": "markdown", "id": "d6b77b87", "metadata": {}, "source": [ "#### Creating Lists" ] }, { "cell_type": "code", "execution_count": null, "id": "c64a53f7", "metadata": {}, "outputs": [], "source": [ "# Empty list\n", "empty_list = []\n", "print(empty_list) # Output: []\n", "\n", "# List with initial values\n", "numbers = [1, 2, 3, 4, 5]\n", "print(numbers) # Output: [1, 2, 3, 4, 5]\n", "\n", "# List with different data types\n", "mixed_list = [1, \"Alice\", 3.14, True, None]\n", "print(mixed_list)\n", "# Output: [1, 'Alice', 3.14, True, None]\n", "\n", "# List with repeated values\n", "zeros = [0] * 5\n", "print(zeros) # Output: [0, 0, 0, 0, 0]\n", "\n", "# Convert other types to list\n", "string_to_list = list(\"ABC\")\n", "print(string_to_list) # Output: ['A', 'B', 'C']\n", "\n", "# List of lists (nested)\n", "matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n", "print(matrix)\n", "# Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]" ] }, { "cell_type": "markdown", "id": "3f8137ab", "metadata": {}, "source": [ "#### Accessing List Elements (Indexing)\n", "Lists use zero-based indexing, meaning the first element is at index 0." ] }, { "cell_type": "code", "execution_count": null, "id": "eb425613", "metadata": {}, "outputs": [], "source": [ "fruits = [\"apple\", \"banana\", \"cherry\", \"date\", \"elderberry\"]\n", "\n", "# Access by positive index (from beginning)\n", "print(fruits[0]) # Output: apple\n", "print(fruits[1]) # Output: banana\n", "print(fruits[4]) # Output: elderberry\n", "\n", "# Access by negative index (from end)\n", "print(fruits[-1]) # Output: elderberry (last item)\n", "print(fruits[-2]) # Output: date (second to last)\n", "print(fruits[-5]) # Output: apple (first item)\n", "\n", "# Access nested list element\n", "matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n", "print(matrix[0]) # Output: [1, 2, 3]\n", "print(matrix[0][1]) # Output: 2\n", "print(matrix[2][2]) # Output: 9\n", "\n", "# Get list length\n", "print(len(fruits)) # Output: 5\n", "print(len(matrix)) # Output: 3" ] }, { "cell_type": "markdown", "id": "8779f91e", "metadata": {}, "source": [ "#### Slicing Lists\n", "Slicing extracts a portion of a list using the syntax [start:stop:step]." ] }, { "cell_type": "code", "execution_count": null, "id": "95a0f739", "metadata": {}, "outputs": [], "source": [ "numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", "\n", "# Basic slicing [start:stop] - stop is excluded\n", "print(numbers[2:5]) # Output: [2, 3, 4]\n", "print(numbers[0:3]) # Output: [0, 1, 2]\n", "\n", "# From beginning to index\n", "print(numbers[:4]) # Output: [0, 1, 2, 3]\n", "\n", "# From index to end\n", "print(numbers[7:]) # Output: [7, 8, 9]\n", "\n", "# With step\n", "print(numbers[::2]) # Every 2nd element: [0, 2, 4, 6, 8]\n", "print(numbers[1::2]) # Every 2nd element starting at index 1: [1, 3, 5, 7, 9]\n", "\n", "# Reverse a list\n", "print(numbers[::-1]) # Output: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]\n", "\n", "# Slice with negative indices\n", "print(numbers[-3:]) # Last 3 elements: [7, 8, 9]\n", "print(numbers[:-2]) # All except last 2: [0, 1, 2, 3, 4, 5, 6, 7]\n", "\n", "# Real-world: Get first 10 items from a long list\n", "data = list(range(1000)) # List of 0-999\n", "first_10 = data[:10]\n", "print(first_10) # Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" ] }, { "cell_type": "markdown", "id": "84343505", "metadata": {}, "source": [ "#### Modifying Lists\n", "Since lists are mutable, you can change, add, or remove elements." ] }, { "cell_type": "code", "execution_count": null, "id": "e41c1160", "metadata": {}, "outputs": [], "source": [ "numbers = [1, 2, 3, 4, 5]\n", "\n", "# Change a single element\n", "numbers[2] = 30\n", "print(numbers) # Output: [1, 2, 30, 4, 5]\n", "\n", "# Change multiple elements with slicing\n", "numbers[1:3] = [20, 300]\n", "print(numbers) # Output: [1, 20, 300, 4, 5]\n", "\n", "# Add single element at end\n", "numbers.append(6)\n", "print(numbers) # Output: [1, 20, 300, 4, 5, 6]\n", "\n", "# Add multiple elements\n", "numbers.extend([7, 8, 9])\n", "print(numbers) # Output: [1, 20, 300, 4, 5, 6, 7, 8, 9]\n", "\n", "# Insert at specific position\n", "numbers.insert(0, 0) # Insert 0 at beginning\n", "print(numbers) # Output: [0, 1, 20, 300, 4, 5, 6, 7, 8, 9]\n", "\n", "# Remove by value\n", "numbers.remove(20)\n", "print(numbers) # Output: [0, 1, 300, 4, 5, 6, 7, 8, 9]\n", "\n", "# Remove by index\n", "del numbers[2]\n", "print(numbers) # Output: [0, 1, 4, 5, 6, 7, 8, 9]\n", "\n", "# Pop removes and returns element at index\n", "last = numbers.pop() # Remove last element\n", "print(last) # Output: 9\n", "print(numbers) # Output: [0, 1, 4, 5, 6, 7, 8]\n", "\n", "# Clear all elements\n", "numbers.clear()\n", "print(numbers) # Output: []" ] }, { "cell_type": "markdown", "id": "9a1dc51c", "metadata": {}, "source": [ "#### Common List Methods" ] }, { "cell_type": "code", "execution_count": null, "id": "a9b2cc90", "metadata": {}, "outputs": [], "source": [ "# index() and count()\n", "fruits = [\"apple\", \"banana\", \"cherry\", \"banana\"]\n", "print(fruits.index(\"banana\")) # Output: 1 (first position)\n", "print(fruits.count(\"banana\")) # Output: 2\n", "\n", "# sort()\n", "numbers = [3, 1, 4, 1, 5, 9, 2, 6]\n", "numbers.sort()\n", "print(numbers) # Output: [1, 1, 2, 3, 4, 5, 6, 9]\n", "\n", "# reverse()\n", "numbers.reverse()\n", "print(numbers) # Output: [9, 6, 5, 4, 3, 2, 1, 1]\n", "\n", "# copy() - important for avoiding unintended changes\n", "original = [1, 2, 3]\n", "copy_list = original.copy()\n", "copy_list[0] = 99\n", "print(original) # Output: [1, 2, 3] (unchanged)\n", "print(copy_list) # Output: [99, 2, 3]" ] }, { "cell_type": "markdown", "id": "73646b29", "metadata": {}, "source": [ "### 1.2 Tuples: Immutable Sequences\n", "A tuple is an ordered, immutable collection of items. \"Immutable\" means once created, you cannot change, add, or remove elements. Tuples are useful when you want to ensure data doesn't change accidentally.\n", "\n", "Key characteristics:\n", "- Ordered: Items have specific positions\n", "- Immutable: Cannot modify after creation\n", "- Lightweight: Often faster and use less memory than lists\n", "- Hashable: Can be used as dictionary keys (more on this later)" ] }, { "cell_type": "code", "execution_count": null, "id": "55aa928a", "metadata": {}, "outputs": [], "source": [ "# Create tuples\n", "empty_tuple = ()\n", "print(empty_tuple) # Output: ()\n", "\n", "single_item = (1,) # Note the comma - important!\n", "print(single_item) # Output: (1,)\n", "\n", "coordinates = (10, 20, 30)\n", "print(coordinates) # Output: (10, 20, 30)\n", "\n", "mixed_tuple = (1, \"Alice\", 3.14, True)\n", "print(mixed_tuple) # Output: (1, 'Alice', 3.14, True)\n", "\n", "# Tuple unpacking\n", "x, y, z = coordinates\n", "print(x, y, z) # Output: 10 20 30\n", "\n", "# Accessing elements (same as lists)\n", "print(coordinates[0]) # Output: 10\n", "print(coordinates[-1]) # Output: 30\n", "\n", "# Slicing (same as lists)\n", "print(coordinates[1:]) # Output: (20, 30)\n", "\n", "# Tuple methods\n", "numbers = (1, 2, 3, 2, 4, 2)\n", "print(numbers.count(2)) # Output: 3\n", "print(numbers.index(3)) # Output: 2\n", "\n", "# Converting between list and tuple\n", "list_data = [1, 2, 3]\n", "tuple_data = tuple(list_data)\n", "print(tuple_data) # Output: (1, 2, 3)\n", "\n", "# Back to list\n", "list_again = list(tuple_data)\n", "print(list_again) # Output: [1, 2, 3]\n", "\n", "# Real-world: Return multiple values as tuple\n", "def get_user_info():\n", "name = \"Alice\"\n", "age = 25\n", "city = \"Lagos\"\n", "return name, age, city # Returns tuple\n", "\n", "user = get_user_info()\n", "print(user) # Output: ('Alice', 25, 'Lagos')\n", "name, age, city = get_user_info()\n", "print(f\"{name} is {age} and lives in {city}\")" ] }, { "cell_type": "markdown", "id": "1a9492cd", "metadata": {}, "source": [ "#### When to Use Lists vs Tuples\n", "Use Lists when:\n", "- You need to modify the data (add, remove, change elements)\n", "- You're working with a collection that might grow or shrink\n", "- You need performance for frequently modified data\n", "\n", "Use Tuples when:\n", "- Data should not change (immutable protection)\n", "- Using the data as dictionary keys (lists cannot be keys)\n", "- Returning multiple values from functions\n", "- Memory efficiency is important\n", "- You want to prevent accidental modifications" ] }, { "cell_type": "markdown", "id": "554c8274", "metadata": {}, "source": [ "## SESSION 2: Dictionaries, Sets, and String Manipulation" ] }, { "cell_type": "markdown", "id": "5f637807", "metadata": {}, "source": [ "### Duration: 2 hours" ] }, { "cell_type": "markdown", "id": "7f987d14", "metadata": {}, "source": [ "### 2.1 Dictionaries: Key-Value Pairs\n", "A dictionary is a mutable, unordered collection of key-value pairs. Instead of accessing items by position like lists, dictionaries use keys to access values. Dictionaries are incredibly useful for organizing structured data.\n", "\n", "Key characteristics:\n", "- Unordered: Order of items doesn't matter (in older Python)\n", "- Mutable: Can add, remove, or change items\n", "- Key-value pairs: Each value is associated with a unique key\n", "- Keys must be unique and immutable (usually strings or numbers)" ] }, { "cell_type": "code", "execution_count": null, "id": "bcd4c1e5", "metadata": {}, "outputs": [], "source": [ "# Create dictionaries\n", "empty_dict = {}\n", "print(empty_dict) # Output: {}\n", "\n", "# Dictionary with initial values\n", "student = {\"name\": \"Alice\", \"age\": 25, \"grade\": \"A\"}\n", "print(student)\n", "# Output: {'name': 'Alice', 'age': 25, 'grade': 'A'}\n", "\n", "# Dictionary with mixed key types\n", "mixed = {1: \"one\", \"two\": 2, 3.0: \"three\"}\n", "print(mixed)\n", "# Output: {1: 'one', 'two': 2, 3.0: 'three'}\n", "\n", "# Accessing values by key\n", "print(student[\"name\"]) # Output: Alice\n", "print(student[\"age\"]) # Output: 25\n", "\n", "# Using get() method (safer than direct access)\n", "print(student.get(\"name\")) # Output: Alice\n", "print(student.get(\"email\", \"N/A\")) # Output: N/A (key doesn't exist)\n", "\n", "# Adding new key-value pairs\n", "student[\"email\"] = \"alice@example.com\"\n", "print(student)\n", "# Output: {'name': 'Alice', 'age': 25, 'grade': 'A', 'email': 'alice@example.com'}\n", "\n", "# Modifying existing values\n", "student[\"age\"] = 26\n", "print(student)\n", "# Output: {'name': 'Alice', 'age': 26, 'grade': 'A', 'email': 'alice@example.com'}\n", "\n", "# Removing key-value pairs\n", "del student[\"grade\"]\n", "print(student)\n", "\n", "# pop() removes and returns value\n", "email = student.pop(\"email\")\n", "print(email) # Output: alice@example.com\n", "\n", "# Clear all items\n", "student.clear()\n", "print(student) # Output: {}" ] }, { "cell_type": "markdown", "id": "a3998d39", "metadata": {}, "source": [ "#### Dictionary Methods" ] }, { "cell_type": "code", "execution_count": null, "id": "fd76a8c6", "metadata": {}, "outputs": [], "source": [ "person = {\"name\": \"Bob\", \"age\": 30, \"city\": \"Lagos\"}\n", "\n", "# keys(), values(), items()\n", "print(person.keys()) # Output: dict_keys(['name', 'age', 'city'])\n", "print(person.values()) # Output: dict_values(['Bob', 30, 'Lagos'])\n", "print(person.items()) # Output: dict_items([('name', 'Bob'), ('age', 30), ('city', 'Lagos')])\n", "\n", "# Iterating through dictionary\n", "for key in person:\n", "print(f\"{key}: {person[key]}\")\n", "\n", "for key, value in person.items():\n", "print(f\"{key}: {value}\")\n", "\n", "# update()\n", "person.update({\"age\": 31, \"email\": \"bob@example.com\"})\n", "print(person)\n", "# Output: {'name': 'Bob', 'age': 31, 'city': 'Lagos', 'email': 'bob@example.com'}\n", "\n", "# Real-world: Store student grades\n", "grades = {\n", "\"Alice\": 85,\n", "\"Bob\": 92,\n", "\"Charlie\": 78,\n", "\"Diana\": 95\n", "}\n", "\n", "# Find student with highest grade\n", "top_student = max(grades, key=grades.get)\n", "print(f\"Top student: {top_student} with grade {grades[top_student]}\")\n", "# Output: Top student: Diana with grade 95" ] }, { "cell_type": "markdown", "id": "8cf147bb", "metadata": {}, "source": [ "#### Nested Dictionaries" ] }, { "cell_type": "code", "execution_count": null, "id": "f827679a", "metadata": {}, "outputs": [], "source": [ "# Dictionary containing dictionaries\n", "company = {\n", "\"employees\": {\n", "\"emp1\": {\"name\": \"Alice\", \"dept\": \"Data Science\", \"salary\": 60000},\n", "\"emp2\": {\"name\": \"Bob\", \"dept\": \"Engineering\", \"salary\": 70000},\n", "\"emp3\": {\"name\": \"Charlie\", \"dept\": \"Sales\", \"salary\": 50000}\n", "},\n", "\"departments\": [\"Data Science\", \"Engineering\", \"Sales\"],\n", "\"founded\": 2020\n", "}\n", "\n", "# Accessing nested values\n", "print(company[\"employees\"][\"emp1\"][\"name\"]) # Output: Alice\n", "print(company[\"employees\"][\"emp2\"][\"salary\"]) # Output: 70000\n", "\n", "# Iterating through nested structure\n", "for emp_id, emp_info in company[\"employees\"].items():\n", "print(f\"{emp_id}: {emp_info['name']} - {emp_info['dept']}\")\n", "\n", "# Real-world: Store customer data\n", "customers = {\n", "\"cust001\": {\n", "\"name\": \"Alice Smith\",\n", "\"email\": \"alice@example.com\",\n", "\"purchases\": [100, 200, 150]\n", "},\n", "\"cust002\": {\n", "\"name\": \"Bob Jones\",\n", "\"email\": \"bob@example.com\",\n", "\"purchases\": [300, 250]\n", "}\n", "}\n", "\n", "# Calculate total spent by customer\n", "for cust_id, cust_data in customers.items():\n", "total = sum(cust_data[\"purchases\"])\n", "print(f\"{cust_data['name']} spent: {total}\")" ] }, { "cell_type": "markdown", "id": "7a24554e", "metadata": {}, "source": [ "### 2.2 Sets: Unique Collections\n", "A set is an unordered collection of unique items. Sets are useful when you need to:\n", "- Remove duplicates from a collection\n", "- Test membership (is an item in the set?)\n", "- Perform mathematical set operations (union, intersection, difference)\n", "\n", "Key characteristics:\n", "- Unordered: Order doesn't matter\n", "- Unique: Duplicates are automatically removed\n", "- Mutable: Can add and remove items\n", "- Cannot contain unhashable types (like lists or dicts)" ] }, { "cell_type": "code", "execution_count": null, "id": "afc574e9", "metadata": {}, "outputs": [], "source": [ "# Create sets\n", "empty_set = set() # Note: {} creates dict, not set\n", "print(empty_set) # Output: set()\n", "\n", "numbers = {1, 2, 3, 4, 5}\n", "print(numbers) # Output: {1, 2, 3, 4, 5}\n", "\n", "# Create from list\n", "duplicates = [1, 2, 2, 3, 3, 3, 4]\n", "unique = set(duplicates)\n", "print(unique) # Output: {1, 2, 3, 4}\n", "\n", "# Adding elements\n", "numbers.add(6)\n", "print(numbers) # Output: {1, 2, 3, 4, 5, 6}\n", "\n", "# Adding multiple elements\n", "numbers.update([7, 8, 9])\n", "print(numbers) # Output: {1, 2, 3, 4, 5, 6, 7, 8, 9}\n", "\n", "# Removing elements\n", "numbers.remove(9) # Raises error if not found\n", "print(numbers)\n", "\n", "# discard() - doesn't raise error if not found\n", "numbers.discard(10) # No error even though 10 isn't in set\n", "numbers.discard(8)\n", "print(numbers)\n", "\n", "# Membership testing\n", "print(5 in numbers) # Output: True\n", "print(10 in numbers) # Output: False\n", "\n", "# Set operations\n", "set_a = {1, 2, 3, 4}\n", "set_b = {3, 4, 5, 6}\n", "\n", "# Union: all elements from both sets\n", "union = set_a | set_b\n", "print(union) # Output: {1, 2, 3, 4, 5, 6}\n", "\n", "# Intersection: elements in both sets\n", "intersection = set_a & set_b\n", "print(intersection) # Output: {3, 4}\n", "\n", "# Difference: elements in set_a but not in set_b\n", "difference = set_a - set_b\n", "print(difference) # Output: {1, 2}\n", "\n", "# Real-world: Find common programming languages between developers\n", "dev1_languages = {\"Python\", \"JavaScript\", \"Java\", \"C++\"}\n", "dev2_languages = {\"Python\", \"Ruby\", \"Java\", \"Go\"}\n", "\n", "common = dev1_languages & dev2_languages\n", "print(f\"Languages they both know: {common}\")\n", "# Output: Languages they both know: {'Java', 'Python'}" ] }, { "cell_type": "markdown", "id": "32caf134", "metadata": {}, "source": [ "### 2.3 String Manipulation\n", "Strings are immutable sequences of characters. Understanding string operations is essential for data science, especially for cleaning and processing text data." ] }, { "cell_type": "markdown", "id": "ca3af33f", "metadata": {}, "source": [ "#### String Indexing and Slicing" ] }, { "cell_type": "code", "execution_count": null, "id": "d5b65e8f", "metadata": {}, "outputs": [], "source": [ "text = \"Data Science\"\n", "\n", "# Indexing\n", "print(text[0]) # Output: D\n", "print(text[5]) # Output: S\n", "print(text[-1]) # Output: e\n", "\n", "# Slicing\n", "print(text[0:4]) # Output: Data\n", "print(text[5:]) # Output: Science\n", "print(text[:4]) # Output: Data\n", "print(text[::2]) # Every 2nd character: DtSine\n", "print(text[::-1]) # Reverse: ecneicS ataD\n", "\n", "# String length\n", "print(len(text)) # Output: 12" ] }, { "cell_type": "markdown", "id": "4c0b4044", "metadata": {}, "source": [ "#### Common String Methods" ] }, { "cell_type": "code", "execution_count": null, "id": "8fd59383", "metadata": {}, "outputs": [], "source": [ "text = \" Hello, World! \"\n", "\n", "# Case operations\n", "print(text.upper()) # Output: HELLO, WORLD!\n", "print(text.lower()) # Output: hello, world!\n", "print(text.capitalize()) # Output: hello, world! (weird!)\n", "print(text.title()) # Output: Hello, World!\n", "\n", "# strip() removes whitespace\n", "clean = text.strip()\n", "print(clean) # Output: Hello, World!\n", "\n", "# replace()\n", "message = \"I like apples. Apples are great.\"\n", "new_message = message.replace(\"apples\", \"oranges\")\n", "print(new_message)\n", "# Output: I like oranges. Apples are great.\n", "\n", "# split() and join()\n", "sentence = \"Python is awesome\"\n", "words = sentence.split()\n", "print(words) # Output: ['Python', 'is', 'awesome']\n", "\n", "rejoined = \" \".join(words)\n", "print(rejoined) # Output: Python is awesome\n", "\n", "# find()\n", "text = \"Hello, World!\"\n", "index = text.find(\"World\")\n", "print(index) # Output: 7\n", "\n", "# startswith() and endswith()\n", "print(sentence.startswith(\"Python\")) # Output: True\n", "print(sentence.endswith(\"awesome\")) # Output: True\n", "\n", "# Type checking\n", "print(\"123\".isdigit()) # Output: True\n", "print(\"abc\".isalpha()) # Output: True\n", "print(\"12a\".isdigit()) # Output: False\n", "\n", "# Real-world: Process CSV-like data\n", "csv_line = \"Alice, 25, Lagos, Data Science\"\n", "fields = [field.strip() for field in csv_line.split(\",\")]\n", "print(fields)\n", "# Output: ['Alice', '25', 'Lagos', 'Data Science']" ] }, { "cell_type": "markdown", "id": "be410cf6", "metadata": {}, "source": [ "#### String Formatting" ] }, { "cell_type": "code", "execution_count": null, "id": "87009e22", "metadata": {}, "outputs": [], "source": [ "# f-strings (Python 3.6+) - Recommended\n", "name = \"Alice\"\n", "age = 25\n", "city = \"Lagos\"\n", "\n", "print(f\"Name: {name}, Age: {age}, City: {city}\")\n", "# Output: Name: Alice, Age: 25, City: Lagos\n", "\n", "# With expressions in f-strings\n", "x = 10\n", "y = 20\n", "print(f\"Sum of {x} and {y} is {x + y}\")\n", "# Output: Sum of 10 and 20 is 30\n", "\n", "# Formatting numbers\n", "pi = 3.14159\n", "print(f\"Pi rounded: {pi:.2f}\") # Output: Pi rounded: 3.14\n", "print(f\"Percentage: {0.875:.1%}\") # Output: Percentage: 87.5%\n", "\n", "# format() method\n", "template = \"Hello {}, you are {} years old\"\n", "message = template.format(\"Bob\", 30)\n", "print(message) # Output: Hello Bob, you are 30 years old\n", "\n", "# Older % formatting (still seen in legacy code)\n", "message = \"Hello %s, you are %d years old\" % (\"Charlie\", 28)\n", "print(message) # Output: Hello Charlie, you are 28 years old\n", "\n", "# Real-world: Create formatted output for report\n", "data = {\"name\": \"Diana\", \"score\": 92.5, \"percentile\": 0.95}\n", "report = f\"\"\"\n", "Student Report:\n", "Name: {data['name']}\n", "Score: {data['score']:.1f}\n", "Percentile: {data['percentile']:.0%}\n", "\"\"\"\n", "print(report)" ] }, { "cell_type": "markdown", "id": "7f749a46", "metadata": {}, "source": [ "## SESSION 3: File Handling and Exception Handling" ] }, { "cell_type": "markdown", "id": "63477ce4", "metadata": {}, "source": [ "### Duration: 2 hours" ] }, { "cell_type": "markdown", "id": "3c2e66c3", "metadata": {}, "source": [ "### 3.1 Reading and Writing Files\n", "File handling is essential for data science. You need to read data from files and save results. Python provides simple methods to work with files." ] }, { "cell_type": "markdown", "id": "733b2820", "metadata": {}, "source": [ "#### Opening and Closing Files" ] }, { "cell_type": "code", "execution_count": null, "id": "17f8cafe", "metadata": {}, "outputs": [], "source": [ "# Basic file operations\n", "# Modes: 'r' (read), 'w' (write), 'a' (append), 'rb' (read binary), etc.\n", "\n", "# Method 1: Manual close (not recommended)\n", "file = open(\"data.txt\", \"r\")\n", "content = file.read()\n", "file.close()\n", "\n", "# Method 2: with statement (RECOMMENDED)\n", "# Automatically closes file even if error occurs\n", "with open(\"data.txt\", \"r\") as file:\n", "content = file.read()\n", "\n", "print(content)\n", "\n", "# After 'with' block, file is automatically closed" ] }, { "cell_type": "markdown", "id": "193b03b3", "metadata": {}, "source": [ "#### Reading Files" ] }, { "cell_type": "code", "execution_count": null, "id": "1cba3527", "metadata": {}, "outputs": [], "source": [ "# Create a sample file first\n", "with open(\"sample.txt\", \"w\") as f:\n", "f.write(\"Line 1: Hello\\n\")\n", "f.write(\"Line 2: World\\n\")\n", "f.write(\"Line 3: Python\")\n", "\n", "# Read entire file as string\n", "with open(\"sample.txt\", \"r\") as f:\n", "content = f.read()\n", "print(content)\n", "\n", "# Output:\n", "# Line 1: Hello\n", "# Line 2: World\n", "# Line 3: Python\n", "\n", "# Read one line at a time\n", "with open(\"sample.txt\", \"r\") as f:\n", "line1 = f.readline()\n", "line2 = f.readline()\n", "print(line1) # Output: Line 1: Hello\n", "print(line2) # Output: Line 2: World\n", "\n", "# Read all lines as list\n", "with open(\"sample.txt\", \"r\") as f:\n", "lines = f.readlines()\n", "print(lines)\n", "# Output: ['Line 1: Hello\\n', 'Line 2: World\\n', 'Line 3: Python']\n", "\n", "# Iterate through lines (memory efficient for large files)\n", "with open(\"sample.txt\", \"r\") as f:\n", "for line in f:\n", "print(line.strip()) # strip() removes newline character\n", "\n", "# Output:\n", "# Line 1: Hello\n", "# Line 2: World\n", "# Line 3: Python" ] }, { "cell_type": "markdown", "id": "462d846b", "metadata": {}, "source": [ "#### Writing and Appending Files" ] }, { "cell_type": "code", "execution_count": null, "id": "74eeeeb2", "metadata": {}, "outputs": [], "source": [ "# Write mode: overwrites existing file\n", "with open(\"output.txt\", \"w\") as f:\n", "f.write(\"First line\\n\")\n", "f.write(\"Second line\\n\")\n", "\n", "# Append mode: adds to end of file\n", "with open(\"output.txt\", \"a\") as f:\n", "f.write(\"Third line (appended)\\n\")\n", "\n", "# Write multiple lines at once\n", "lines = [\"Data Science\\n\", \"Machine Learning\\n\", \"Artificial Intelligence\\n\"]\n", "\n", "with open(\"output.txt\", \"w\") as f:\n", "f.writelines(lines)\n", "\n", "# Read back what we wrote\n", "with open(\"output.txt\", \"r\") as f:\n", "content = f.read()\n", "print(content)\n", "\n", "# Output:\n", "# Data Science\n", "# Machine Learning\n", "# Artificial Intelligence\n", "\n", "# Real-world: Save analysis results\n", "results = [\n", "\"Analysis Results\\n\",\n", "\"==================\\n\",\n", "\"Total records: 1000\\n\",\n", "\"Valid records: 950\\n\",\n", "\"Invalid records: 50\\n\"\n", "]\n", "\n", "with open(\"results.txt\", \"w\") as f:\n", "f.writelines(results)" ] }, { "cell_type": "markdown", "id": "e0d1898c", "metadata": {}, "source": [ "#### Working with CSV Files" ] }, { "cell_type": "code", "execution_count": null, "id": "870a54d7", "metadata": {}, "outputs": [], "source": [ "# Create sample CSV file\n", "csv_content = \"\"\"name,age,city\n", "Alice,25,Lagos\n", "Bob,30,Accra\n", "Charlie,28,Nairobi\n", "Diana,32,Johannesburg\"\"\"\n", "\n", "with open(\"people.csv\", \"w\") as f:\n", "f.write(csv_content)\n", "\n", "# Read CSV manually (without pandas)\n", "with open(\"people.csv\", \"r\") as f:\n", "# Skip header\n", "header = f.readline().strip().split(\",\")\n", "print(\"Header:\", header) # Output: Header: ['name', 'age', 'city']\n", "\n", "# Read data rows\n", "for line in f:\n", "fields = line.strip().split(\",\")\n", "name, age, city = fields\n", "print(f\"{name}: {age} years old, from {city}\")\n", "\n", "# Output:\n", "# Alice: 25 years old, from Lagos\n", "# Bob: 30 years old, from Accra\n", "# Charlie: 28 years old, from Nairobi\n", "# Diana: 32 years old, from Johannesburg\n", "\n", "# Better: Use list comprehension to parse CSV\n", "def read_csv(filename):\n", "\"\"\"Read CSV file and return list of dictionaries\"\"\"\n", "with open(filename, \"r\") as f:\n", "header = f.readline().strip().split(\",\")\n", "data = []\n", "for line in f:\n", "values = line.strip().split(\",\")\n", "row = dict(zip(header, values))\n", "data.append(row)\n", "return data\n", "\n", "people = read_csv(\"people.csv\")\n", "for person in people:\n", "print(person)\n", "\n", "# Output:\n", "# {'name': 'Alice', 'age': '25', 'city': 'Lagos'}\n", "# {'name': 'Bob', 'age': '30', 'city': 'Accra'}\n", "# etc." ] }, { "cell_type": "markdown", "id": "ba67b2eb", "metadata": {}, "source": [ "### 3.2 Exception Handling with Try-Except\n", "Exception handling allows your program to handle errors gracefully instead of crashing. This is essential for robust data science applications." ] }, { "cell_type": "markdown", "id": "41c1faff", "metadata": {}, "source": [ "#### Basic Try-Except" ] }, { "cell_type": "code", "execution_count": null, "id": "591dd489", "metadata": {}, "outputs": [], "source": [ "# Without exception handling - crashes\n", "# result = 10 / 0 # ZeroDivisionError!\n", "\n", "# With exception handling\n", "try:\n", "result = 10 / 0\n", "except ZeroDivisionError:\n", "print(\"Error: Cannot divide by zero\")\n", "\n", "# Output: Error: Cannot divide by zero\n", "\n", "# Catching generic exceptions\n", "try:\n", "x = int(\"not_a_number\")\n", "except ValueError:\n", "print(\"Error: Could not convert to integer\")\n", "\n", "# Output: Error: Could not convert to integer\n", "\n", "# Catch multiple specific exceptions\n", "try:\n", "# Some operation that might fail\n", "data = [1, 2, 3]\n", "print(data[10]) # IndexError\n", "except IndexError:\n", "print(\"Error: Index out of range\")\n", "except KeyError:\n", "print(\"Error: Key not found\")\n", "\n", "# Output: Error: Index out of range\n", "\n", "# Catch any exception\n", "try:\n", "# Unknown operation\n", "result = 10 / 0\n", "except Exception as e:\n", "print(f\"An error occurred: {e}\")\n", "\n", "# Output: An error occurred: division by zero" ] }, { "cell_type": "markdown", "id": "476bc592", "metadata": {}, "source": [ "#### Try-Except-Else-Finally" ] }, { "cell_type": "code", "execution_count": null, "id": "3e966d15", "metadata": {}, "outputs": [], "source": [ "# Complete try-except-else-finally structure\n", "try:\n", "# Code that might raise an exception\n", "num = int(input(\"Enter a number: \"))\n", "result = 10 / num\n", "except ValueError:\n", "print(\"Error: Invalid input, please enter a number\")\n", "except ZeroDivisionError:\n", "print(\"Error: Cannot divide by zero\")\n", "else:\n", "# Executed if no exception occurs\n", "print(f\"Result: {result}\")\n", "finally:\n", "# Always executed, whether exception or not\n", "print(\"Operation completed\")\n", "\n", "# Real-world: Reading file safely\n", "def read_data_file(filename):\n", "try:\n", "with open(filename, \"r\") as f:\n", "data = f.read()\n", "return data\n", "except FileNotFoundError:\n", "print(f\"Error: File '{filename}' not found\")\n", "return None\n", "except IOError:\n", "print(f\"Error: Could not read file '{filename}'\")\n", "return None\n", "\n", "content = read_data_file(\"data.txt\")\n", "if content:\n", "print(\"File contents:\")\n", "print(content)" ] }, { "cell_type": "markdown", "id": "5df8e40b", "metadata": {}, "source": [ "#### Common Exception Types" ] }, { "cell_type": "markdown", "id": "b5167133", "metadata": {}, "source": [ "#### Raising Exceptions" ] }, { "cell_type": "code", "execution_count": null, "id": "90e54a7d", "metadata": {}, "outputs": [], "source": [ "# Raise exceptions manually\n", "def validate_age(age):\n", "if age < 0:\n", "raise ValueError(\"Age cannot be negative\")\n", "if age > 150:\n", "raise ValueError(\"Age seems unrealistic\")\n", "return f\"Age {age} is valid\"\n", "\n", "# Using the function\n", "try:\n", "print(validate_age(-5))\n", "except ValueError as e:\n", "print(f\"Validation error: {e}\")\n", "\n", "# Output: Validation error: Age cannot be negative\n", "\n", "# Real-world: Data validation\n", "def process_data(data):\n", "if not data:\n", "raise ValueError(\"Data cannot be empty\")\n", "if not isinstance(data, list):\n", "raise TypeError(\"Data must be a list\")\n", "if len(data) == 0:\n", "raise ValueError(\"Data list is empty\")\n", "return sum(data) / len(data) # Average\n", "\n", "try:\n", "avg = process_data([10, 20, 30])\n", "print(f\"Average: {avg}\")\n", "except (ValueError, TypeError) as e:\n", "print(f\"Error: {e}\")" ] }, { "cell_type": "markdown", "id": "b3ef7b2e", "metadata": {}, "source": [ "## Week 2 Summary\n", "By completing Week 2, you have learned:\n", "- Lists: creating, indexing, slicing, and modifying\n", "- List methods: append, extend, insert, remove, sort, etc.\n", "- Tuples: immutable sequences and when to use them\n", "- Tuple unpacking for returning multiple values\n", "- Dictionaries: key-value pairs and common operations\n", "- Nested dictionaries for structured data\n", "- Sets: unique collections and set operations\n", "- String operations: indexing, slicing, methods\n", "- String formatting with f-strings\n", "- Reading and writing files with proper file handling\n", "- Working with CSV files manually\n", "- Exception handling with try-except blocks\n", "- Raising and handling different exception types" ] }, { "cell_type": "markdown", "id": "13928546", "metadata": {}, "source": [ "## Week 2 Assignments" ] }, { "cell_type": "markdown", "id": "6f2095f0", "metadata": {}, "source": [ "### Assignment 1: Data Structure Manipulation\n", "Create a program that:\n", "- Stores student data in a list of dictionaries (name, ID, grades list)\n", "- Calculates average grade for each student\n", "- Finds the student with the highest average\n", "- Sorts students by average grade\n", "- Displays results in formatted text" ] }, { "cell_type": "markdown", "id": "b0e6506a", "metadata": {}, "source": [ "### Assignment 2: File Operations and Data Processing\n", "Write a program that:\n", "- Creates a CSV file with sample data (at least 5 rows)\n", "- Reads the CSV file line by line\n", "- Processes the data (e.g., calculate statistics)\n", "- Writes results to a new output file\n", "- Handles potential errors gracefully" ] }, { "cell_type": "markdown", "id": "24a010d3", "metadata": {}, "source": [ "### Assignment 3: Text Processing\n", "Develop a text processing tool that:\n", "- Reads a text file\n", "- Counts words and unique words\n", "- Finds longest and shortest words\n", "- Converts all words to lowercase and counts frequencies\n", "- Saves analysis results to a file" ] }, { "cell_type": "markdown", "id": "60103c31", "metadata": {}, "source": [ "## Recommended Practice Problems\n", "- Create a function that flattens a nested list into a single list\n", "- Write a program that counts character frequencies in a string\n", "- Create a simple phonebook using a dictionary (phone number lookup)\n", "- Write a program that removes duplicates from a list while preserving order\n", "- Create a function that converts a list of dictionaries to CSV format\n", "- Write a program that finds and displays lines in a file matching a pattern\n", "- Create a student grade tracker that reads from file, processes, and saves results\n", "- Write a function that validates data and raises appropriate exceptions" ] }, { "cell_type": "markdown", "id": "459c0523", "metadata": {}, "source": [ "## Additional Resources" ] }, { "cell_type": "markdown", "id": "a0428b12", "metadata": {}, "source": [ "### Books\n", "- Automate the Boring Stuff with Python by Al Sweigart - Great for file handling\n", "- Chapters 3-4 from "Python for Data Analysis" by Wes McKinney" ] }, { "cell_type": "markdown", "id": "35788959", "metadata": {}, "source": [ "### Online Resources\n", "- Python Documentation on built-in types: https://docs.python.org/3/library/stdtypes.html\n", "- Real Python: String Manipulation - https://realpython.com/python-strings/\n", "- Real Python: Working with Files - https://realpython.com/working-with-files-in-python/" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.11" } }, "nbformat": 4, "nbformat_minor": 5 }