{ "cells": [ { "cell_type": "markdown", "id": "5138f02d", "metadata": {}, "source": [ "# Lesson 09 activity: NumPy\n", "\n", "In this activity, you'll solve problems that build on what you've learned about NumPy in lesson 03. These problems will require you to **apply NumPy concepts**, **work with arrays**, and **perform data analysis operations**.\n", "\n", "## Instructions:\n", "- Each problem has a clear objective\n", "- You may need to research, experiment, or combine multiple concepts\n", "- Test your solutions in the code cells provided\n", "- There are multiple ways to solve each problem: be creative!" ] }, { "cell_type": "code", "execution_count": null, "id": "6c12be51", "metadata": {}, "outputs": [], "source": [ "# Import NumPy\n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "9ec4f0fe", "metadata": {}, "source": [ "---\n", "## Problem 1: The grade book analyzer\n", "\n", "**Your task:**\n", "Write a function called `analyze_grades(grades)` that:\n", "- Takes a 2D NumPy array where each row represents a student and each column represents a test score\n", "- Calculates and returns a dictionary containing:\n", " - `'class_average'`: The overall average of all grades\n", " - `'student_averages'`: Array of average scores for each student\n", " - `'test_averages'`: Array of average scores for each test\n", " - `'highest_score'`: The highest individual score\n", " - `'lowest_score'`: The lowest individual score\n", " - `'passing_rate'`: Percentage of grades >= 60\n", "\n", "**Test case:**\n", "```python\n", "grades = np.array([[85, 90, 78, 92],\n", " [76, 88, 81, 79],\n", " [93, 95, 89, 97],\n", " [67, 72, 65, 70]])\n", "```\n", "\n", "**Hints:**\n", "- Use `np.mean()` with `axis` parameter for averages\n", "- Use `np.max()` and `np.min()` for highest/lowest\n", "- Use boolean indexing to find passing grades" ] }, { "cell_type": "code", "execution_count": null, "id": "f158bc1d", "metadata": {}, "outputs": [], "source": [ "# Problem 1: Your solution here\n", "\n", "def analyze_grades(grades):\n", " '''\n", " Analyzes a grade book and returns various statistics.\n", " \n", " Args:\n", " grades (np.ndarray): 2D array of grades (students x tests)\n", " \n", " Returns:\n", " dict: Dictionary containing various grade statistics\n", " '''\n", "\n", " # Write your code here (remove the pass statement)\n", " pass\n", "\n", "# Test case\n", "grades = np.array([[85, 90, 78, 92],\n", " [76, 88, 81, 79],\n", " [93, 95, 89, 97],\n", " [67, 72, 65, 70]])\n", "\n", "results = analyze_grades(grades)\n", "\n", "print('Grade analysis results:')\n", "\n", "for key, value in results.items():\n", " print(f'{key}: {value}')" ] }, { "cell_type": "markdown", "id": "2604df35", "metadata": {}, "source": [ "---\n", "## Problem 2: The array transformer\n", "\n", "**Your task:**\n", "Write a function called `transform_array(arr, operation='normalize', axis=None)` that:\n", "- Accepts a NumPy array of any dimension\n", "- Performs different transformations based on the `operation` parameter:\n", " - `'normalize'`: Scale values to range [0, 1] using min-max normalization\n", " - `'standardize'`: Apply z-score standardization (subtract mean, divide by std)\n", " - `'square'`: Square all values\n", " - `'sqrt'`: Take square root of all values\n", "- Supports an optional `axis` parameter for operations that can be done along specific axes\n", "- Returns the transformed array\n", "\n", "**Formulas:**\n", "- Min-max normalization: `(x - min) / (max - min)`\n", "- Z-score standardization: `(x - mean) / std`\n", "\n", "**Test cases:**\n", "- `transform_array(np.array([1, 2, 3, 4, 5]), 'normalize')` → array from 0 to 1\n", "- `transform_array(np.array([1, 4, 9, 16]), 'sqrt')` → [1, 2, 3, 4]\n", "\n", "**Hints:**\n", "- Use `np.min()`, `np.max()`, `np.mean()`, `np.std()`\n", "- Use `np.sqrt()` for square root\n", "- Remember to handle division by zero for edge cases" ] }, { "cell_type": "code", "execution_count": null, "id": "fd7a48f3", "metadata": {}, "outputs": [], "source": [ "# Problem 2: Your solution here\n", "\n", "def transform_array(arr, operation='normalize', axis=None):\n", " '''\n", " Transforms an array using various mathematical operations.\n", " \n", " Args:\n", " arr (np.ndarray): Input array\n", " operation (str): Type of transformation to apply\n", " axis (int): Axis along which to apply the operation (if applicable)\n", " \n", " Returns:\n", " np.ndarray: Transformed array\n", " '''\n", "\n", " # Write your code here (remove the pass statement)\n", " pass\n", "\n", "# Test cases\n", "print('Test 1 (normalize):')\n", "arr1 = np.array([1, 2, 3, 4, 5])\n", "print(f'Original: {arr1}')\n", "print(f'Normalized: {transform_array(arr1, \"normalize\")}')\n", "\n", "print('\\nTest 2 (sqrt):')\n", "arr2 = np.array([1, 4, 9, 16])\n", "print(f'Original: {arr2}')\n", "print(f'Square root: {transform_array(arr2, \"sqrt\")}')\n", "\n", "print('\\nTest 3 (standardize):')\n", "arr3 = np.array([10, 20, 30, 40, 50])\n", "print(f'Original: {arr3}')\n", "print(f'Standardized: {transform_array(arr3, \"standardize\")}')" ] }, { "cell_type": "markdown", "id": "0d989570", "metadata": {}, "source": [ "---\n", "## Problem 3: The matrix operations toolkit\n", "\n", "**Your task:**\n", "Write a function called `matrix_operations(matrix1, matrix2=None, operation='transpose')` that:\n", "- Performs different matrix operations based on the `operation` parameter:\n", " - `'transpose'`: Return the transpose of matrix1 (only needs matrix1)\n", " - `'multiply'`: Element-wise multiplication of matrix1 and matrix2\n", " - `'matmul'`: Matrix multiplication of matrix1 and matrix2\n", " - `'add'`: Add matrix1 and matrix2\n", " - `'flatten'`: Flatten matrix1 to 1D array\n", "- Returns the result of the operation\n", "- Handles incompatible matrix shapes by returning an error message\n", "\n", "**Test cases:**\n", "```python\n", "A = np.array([[1, 2], [3, 4]])\n", "B = np.array([[5, 6], [7, 8]])\n", "```\n", "\n", "**Hints:**\n", "- Use `.T` or `np.transpose()` for transpose\n", "- Use `@` or `np.matmul()` for matrix multiplication\n", "- Use `.flatten()` to flatten arrays\n", "- Check shapes before operations using `.shape`" ] }, { "cell_type": "code", "execution_count": null, "id": "64751e21", "metadata": {}, "outputs": [], "source": [ "# Problem 3: Your solution here\n", "\n", "def matrix_operations(matrix1, matrix2=None, operation='transpose'):\n", " '''\n", " Performs various matrix operations.\n", " \n", " Args:\n", " matrix1 (np.ndarray): First matrix\n", " matrix2 (np.ndarray): Second matrix (optional)\n", " operation (str): The operation to perform\n", " \n", " Returns:\n", " np.ndarray or str: Result of the operation or error message\n", " '''\n", "\n", " # Write your code here (remove the pass statement)\n", " pass\n", "\n", "# Test cases\n", "A = np.array([[1, 2], [3, 4]])\n", "B = np.array([[5, 6], [7, 8]])\n", "\n", "print('Matrix A:')\n", "print(A)\n", "print('\\nMatrix B:')\n", "print(B)\n", "\n", "print('\\nTranspose of A:')\n", "print(matrix_operations(A, operation='transpose'))\n", "\n", "print('\\nElement-wise multiplication (A * B):')\n", "print(matrix_operations(A, B, operation='multiply'))\n", "\n", "print('\\nMatrix multiplication (A @ B):')\n", "print(matrix_operations(A, B, operation='matmul'))\n", "\n", "print('\\nFlatten A:')\n", "print(matrix_operations(A, operation='flatten'))" ] }, { "cell_type": "markdown", "id": "8e11312b", "metadata": {}, "source": [ "---\n", "## Problem 4: Fixing NumPy bugs\n", "\n", "**Your task:**\n", "Below are three code snippets that contain bugs. For each one:\n", "1. Identify the error\n", "2. Fix the code\n", "3. Test that it works correctly\n", "4. Add a comment explaining what was wrong\n", "\n", "Run each fixed snippet to verify it works!" ] }, { "cell_type": "markdown", "id": "492685b4", "metadata": {}, "source": [ "### Bug 1\n", "\n", "**Expected output:** A 3x3 array with values [1, 2, 3, 4, 5, 6, 7, 8, 9] reshaped\n", "\n", "**Current error:** ValueError" ] }, { "cell_type": "code", "execution_count": null, "id": "f0a61168", "metadata": {}, "outputs": [], "source": [ "# Bug 1: Fix the code below\n", "\n", "arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])\n", "reshaped = arr.reshape(3, 3)\n", "\n", "print(reshaped)" ] }, { "cell_type": "markdown", "id": "c8c06720", "metadata": {}, "source": [ "### Bug 2\n", "\n", "**Expected output:** Should extract the second row from the matrix\n", "\n", "**Current error:** Wrong output (extracts column instead of row)" ] }, { "cell_type": "code", "execution_count": null, "id": "2b2bc678", "metadata": {}, "outputs": [], "source": [ "# Bug 2: Fix the code below\n", "\n", "matrix = np.array([[1, 2, 3],\n", " [4, 5, 6],\n", " [7, 8, 9]])\n", "\n", "print('Matrix:')\n", "print(matrix)\n", "\n", "# This should get the second row [4, 5, 6]\n", "second_row = matrix[:, 1]\n", "print('\\nSecond row:', second_row)" ] }, { "cell_type": "markdown", "id": "353e22d3", "metadata": {}, "source": [ "### Bug 3\n", "\n", "**Expected output:** Array should contain decimal values for division results\n", "\n", "**Current (wrong) output:** Integer division (truncated results)" ] }, { "cell_type": "code", "execution_count": null, "id": "60a72a9e", "metadata": {}, "outputs": [], "source": [ "# Bug 3: Fix the code below\n", "\n", "# Create an array of numbers to divide\n", "numbers = np.array([10, 15, 20, 25, 30])\n", "divisor = 3\n", "\n", "# Divide each number by the divisor\n", "result = numbers / divisor\n", "\n", "print(f'Numbers: {numbers}')\n", "print(f'Divided by {divisor}: {result}')\n", "print(f'Data type: {result.dtype}')\n", "\n", "# Expected: [3.33333333, 5., 6.66666667, 8.33333333, 10.]\n", "# Current output has integers instead of floats" ] }, { "cell_type": "markdown", "id": "6c7a9a45", "metadata": {}, "source": [ "---\n", "## Bonus challenge: The data filter\n", "\n", "**Your task:**\n", "Write a function called `filter_data(data, condition='positive', threshold=0)` that:\n", "- Takes a 1D or 2D NumPy array\n", "- Filters data based on the condition:\n", " - `'positive'`: Keep only values > 0\n", " - `'negative'`: Keep only values < 0\n", " - `'threshold'`: Keep only values > threshold\n", " - `'range'`: Keep only values between -threshold and +threshold\n", "- Returns the filtered array (1D result)\n", "- Also returns the count of filtered elements\n", "\n", "**Test case:**\n", "```python\n", "data = np.array([-5, 10, -3, 15, 0, -8, 20, 3])\n", "```\n", "\n", "**Hints:**\n", "- Use boolean indexing with conditions\n", "- Combine conditions using `&` (and) or `|` (or)\n", "- Use `.flatten()` if input is 2D\n", "- Return multiple values as a tuple" ] }, { "cell_type": "code", "execution_count": null, "id": "56d7ee1f", "metadata": {}, "outputs": [], "source": [ "# Bonus challenge: Your solution here\n", "\n", "def filter_data(data, condition='positive', threshold=0):\n", " '''\n", " Filters data based on various conditions.\n", " \n", " Args:\n", " data (np.ndarray): Input data array\n", " condition (str): The filtering condition to apply\n", " threshold (float): Threshold value for certain conditions\n", " \n", " Returns:\n", " tuple: (filtered_data, count)\n", " '''\n", " \n", " # Write your code here (remove the pass statement)\n", " pass\n", "\n", "# Test cases\n", "data = np.array([-5, 10, -3, 15, 0, -8, 20, 3])\n", "print(f'Original data: {data}\\n')\n", "\n", "filtered, count = filter_data(data, condition='positive')\n", "print(f'Positive values: {filtered}')\n", "print(f'Count: {count}\\n')\n", "\n", "filtered, count = filter_data(data, condition='threshold', threshold=10)\n", "print(f'Values > 10: {filtered}')\n", "print(f'Count: {count}\\n')\n", "\n", "filtered, count = filter_data(data, condition='range', threshold=5)\n", "print(f'Values in range [-5, 5]: {filtered}')\n", "print(f'Count: {count}')" ] }, { "cell_type": "markdown", "id": "c73fad6e", "metadata": {}, "source": [ "---\n", "## Reflection questions\n", "\n", "After completing the challenges, answer these questions:\n", "\n", "1. How does NumPy's vectorization make operations faster compared to Python loops? Give an example from the activities.\n", "2. What is the purpose of the `axis` parameter in NumPy functions like `mean()`, `sum()`, and `max()`?\n", "3. Explain the difference between element-wise multiplication (`*`) and matrix multiplication (`@` or `np.matmul()`).\n", "4. Why is it important to pay attention to array shapes when performing operations? What errors can occur?\n", "5. How can boolean indexing be used for data filtering? Give a real-world example where this would be useful.\n", "\n", "**Write your answers in the markdown cell below:**" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }