{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5138f02d",
   "metadata": {},
   "source": [
    "# Lesson 09: NumPy Challenge\n",
    "\n",
    "In this activity, you'll solve problems that build on what you've learned about NumPy in Lesson 09. 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",
    "**Objective:** Create a grade analysis system using NumPy arrays and statistical functions.\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",
    "**Objective:** Create a flexible array manipulation function using NumPy operations.\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",
    "**Objective:** Build a function that performs various matrix operations.\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",
    "**Objective:** Debug and fix code snippets that contain common NumPy-related errors.\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: The Shape Mismatch\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: The Indexing Error\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: The Data Type Problem\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",
    "**Objective:** Apply multiple NumPy concepts to solve a real-world data filtering problem.\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:**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca0fff62",
   "metadata": {},
   "source": [
    "### Your Reflections:\n",
    "\n",
    "1. \n",
    "\n",
    "2. \n",
    "\n",
    "3. \n",
    "\n",
    "4. \n",
    "\n",
    "5. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc179201",
   "metadata": {},
   "source": [
    "---\n",
    "## Congratulations!\n",
    "\n",
    "You've completed the Lesson 09 NumPy Challenge! You've practiced:\n",
    "- Creating and manipulating NumPy arrays\n",
    "- Using statistical functions and aggregations\n",
    "- Performing matrix operations and transformations\n",
    "- Applying boolean indexing for data filtering\n",
    "- Understanding array shapes and dimensions\n",
    "- Debugging common NumPy errors\n",
    "\n",
    "NumPy is the foundation of scientific computing in Python and is essential for data science, machine learning, and numerical analysis. These skills will be crucial as you move forward with pandas, scikit-learn, and other data science libraries. Keep practicing!"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
