diff --git a/NumPy for Business Analysis.ipynb b/NumPy for Business Analysis.ipynb new file mode 100644 index 0000000..de08f8e --- /dev/null +++ b/NumPy for Business Analysis.ipynb @@ -0,0 +1,2067 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e12ee604-c9bd-41d2-b8e7-043eecddc343", + "metadata": {}, + "source": [ + "# Introduction to NumPy" + ] + }, + { + "cell_type": "raw", + "id": "c57f5ec0-10d9-4cbe-9506-149a3511087c", + "metadata": {}, + "source": [ + "Module 1: Introduction to NumPy\n", + "1.\tOverview of NumPy and its importance in business analytics\n", + "2.\tInstalling and importing NumPy\n", + "3.\tUnderstanding ndarray and its structure\n", + "4.\tCreating arrays (from lists, tuples, and built-in functions)\n" + ] + }, + { + "cell_type": "markdown", + "id": "48202bd7-961e-4f12-82e2-49d3c6f9abf7", + "metadata": {}, + "source": [ + "# 1.1 Overview" + ] + }, + { + "cell_type": "raw", + "id": "d69a504a-fae0-470b-a665-d6e70867d72e", + "metadata": {}, + "source": [ + "NumPy is a powerful Python library for numerical and statistical data analysis. \n", + "It provides high-performance multidimensional arrays and tools to perform complex mathematical operations efficiently. \n", + "In business analytics, NumPy helps process large datasets quickly for tasks like trend analysis, forecasting, and optimization. \n", + "Its vectorized operations make computations faster than traditional Python loops. \n", + "Overall, it forms the foundation for data-driven decision-making in business." + ] + }, + { + "cell_type": "markdown", + "id": "1a290016-73ae-46dd-9f69-4b92cd04c5c4", + "metadata": {}, + "source": [ + "# Why Use NumPy?" + ] + }, + { + "cell_type": "raw", + "id": "5ced04d2-5d52-4acc-ad2c-ce53921c65ef", + "metadata": {}, + "source": [ + "-- Efficient handling of arrays and matrices: NumPy arrays are much faster than Python lists because of their efficient memory management.\n", + "\n", + "-- Vectorized operations: NumPy provides efficient operations over entire arrays without needing explicit loops.\n", + "\n", + "-- Mathematical functions: It has a vast collection of mathematical functions to perform operations on arrays (like linear algebra, statistics, etc.).\n", + "\n", + "-- Compatibility: It integrates well with other Python libraries such as Pandas, Matplotlib, and SciPy.\n" + ] + }, + { + "cell_type": "markdown", + "id": "05c2358f-a2b6-4ecb-9148-e63dcd6121e3", + "metadata": {}, + "source": [ + "# 1.2. Installing NumPy" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "544029d2-f6da-45a5-8cf0-7b63bf4f62a1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: numpy in c:\\users\\nhasa\\anaconda3\\lib\\site-packages (2.1.3)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install numpy" + ] + }, + { + "cell_type": "markdown", + "id": "45eb94ad-0bab-4c84-9389-3ea18e345863", + "metadata": {}, + "source": [ + "# 1.3. Understanding ndarray and its structure" + ] + }, + { + "cell_type": "raw", + "id": "e632c9c2-7506-43fa-a8aa-e547321a6834", + "metadata": {}, + "source": [ + "An ndarray (n-dimensional array) is the core data structure of NumPy used to store numerical data efficiently. \n", + "It can hold elements of the same data type in a grid-like format with one or more dimensions. \n", + "Each dimension is called an axis, and the number of axes defines the array’s rank (e.g., 1D, 2D, or 3D). \n", + "The ndarray has attributes like shape, size, and dtype that describe its structure. \n", + "This uniform, compact design allows for fast computation and easy manipulation of large datasets." + ] + }, + { + "cell_type": "markdown", + "id": "13b8f219-6a63-4140-8b13-2ed39d7b46c7", + "metadata": {}, + "source": [ + "# 1.4. Creating arrays (from lists, tuples, and built-in functions)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "617c9665-5b01-432c-b309-6c721dae4cbc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1 2 3 4 5]\n" + ] + } + ], + "source": [ + "# From a Python list\n", + "# Creating a 1D Array (Matrix)\n", + "\n", + "import numpy as np\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "print(arr) # Output: [1 2 3 4 5]" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d18600fb-f5d5-4bff-9270-40842de96997", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 2 3]\n", + " [4 5 6]]\n" + ] + } + ], + "source": [ + "# Creating a 2D Array (Matrix)\n", + "\n", + "arr_2d = np.array([[1, 2, 3], [4, 5, 6]])\n", + "print(arr_2d)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f45fd753-51ab-4386-84b0-6bc58601b2c8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0. 0. 0.]\n", + " [0. 0. 0.]]\n" + ] + } + ], + "source": [ + "# Creating Arrays with Functions\n", + "\n", + "zeros_arr = np.zeros((2, 3)) # 2x3 array of zeros\n", + "ones_arr = np.ones((3, 3)) # 3x3 array of ones\n", + "range_arr = np.arange(0, 10, 2) # Array from 0 to 10 with a step of 2\n", + "linspace_arr = np.linspace(0, 1, 5) # 5 evenly spaced numbers between 0 and 1\n", + "\n", + "print(zeros_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e0e2839d-4c4d-4452-9d58-f71a2481936e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1. 1. 1.]\n", + " [1. 1. 1.]\n", + " [1. 1. 1.]]\n" + ] + } + ], + "source": [ + "print(ones_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7887f2b2-575b-497e-a6aa-4be1e3fe559b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0 2 4 6 8]\n" + ] + } + ], + "source": [ + "print(range_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cad0d8c5-62f7-401f-b319-db22ab11b112", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0. 0.25 0.5 0.75 1. ]\n" + ] + } + ], + "source": [ + "print(linspace_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "152de51a-030c-4d44-9750-ef18be0ce699", + "metadata": {}, + "outputs": [], + "source": [ + "zeros = np.zeros((3, 3)) # 3x3 zeros matrix\n", + "ones = np.ones((2, 4)) # 2x4 ones matrix\n", + "identity = np.eye(3) # Identity matrix\n", + "range_arr = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]\n", + "linspace = np.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]\n", + "random_arr = np.random.rand(2, 3) # Random values (0-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6c7278c2-6f81-4b93-b466-b3ba2d3e9cb5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0. 0. 0.]\n", + " [0. 0. 0.]\n", + " [0. 0. 0.]]\n", + "[[1. 1. 1. 1.]\n", + " [1. 1. 1. 1.]]\n", + "[[1. 0. 0.]\n", + " [0. 1. 0.]\n", + " [0. 0. 1.]]\n", + "[0 2 4 6 8]\n", + "[0. 0.25 0.5 0.75 1. ]\n", + "[[0.37141507 0.99942232 0.06040848]\n", + " [0.68986753 0.93638736 0.62317336]]\n" + ] + } + ], + "source": [ + "print(zeros)\n", + "print(ones)\n", + "print(identity)\n", + "print(range_arr)\n", + "print(linspace)\n", + "print(random_arr)" + ] + }, + { + "cell_type": "raw", + "id": "8050f386-bafa-435d-964f-55ded48e1ef5", + "metadata": {}, + "source": [ + "Module 2: Array Operations and Manipulation\n", + "1.\tArray attributes (shape, size, dtype, ndim)\n", + "2.\tIndexing, slicing, and subsetting arrays\n", + "3.\tCopy vs view — understanding memory behavior\n", + "4.\tReshaping, flattened arrays\n" + ] + }, + { + "cell_type": "markdown", + "id": "1e620d76-cc31-40b9-b20e-ed7107a28d70", + "metadata": {}, + "source": [ + "# 2.1. Array Operations and Manipulation" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4cfe4181-1fc9-4a04-8973-3b8794cee639", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(2, 3)\n", + "2\n", + "6\n", + "int64\n" + ] + } + ], + "source": [ + "#\tArray attributes (shape, size, dtype, ndim)\n", + "\n", + "arr = np.array([[1, 2, 3], [4, 5, 6]])\n", + "\n", + "print(arr.shape) # (2, 3) → 2 rows, 3 columns\n", + "print(arr.ndim) # 2 → dimensions\n", + "print(arr.size) # 6 → total elements\n", + "print(arr.dtype) # int64 → data type" + ] + }, + { + "cell_type": "markdown", + "id": "1394ae72-9aca-4728-a677-0459d9fcf82d", + "metadata": {}, + "source": [ + "# 2.2. Indexing, slicing, and subsetting arrays" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "42fde2b6-b20e-49c9-bc72-773a3109b9e6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10\n", + "[20 30 40]\n", + "6\n", + "[2 5]\n" + ] + } + ], + "source": [ + "# Indexing & Slicing\n", + "\n", + "arr = np.array([10, 20, 30, 40, 50])\n", + "print(arr[0]) # 10 (first element)\n", + "print(arr[1:4]) # [20 30 40] (slicing) # # Slicing elements from index 1 to 3\n", + "\n", + "matrix = np.array([[1, 2, 3], [4, 5, 6]])\n", + "print(matrix[1, 2]) # 6 (row 1, column 2)\n", + "print(matrix[:, 1]) # [2 5] (all rows, column 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0e3700af-72c7-4d29-8f3e-24aa33ffabef", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6\n", + "[4 5 6]\n", + "[2 5 8]\n", + "[[1 2]\n", + " [4 5]]\n" + ] + } + ], + "source": [ + "# subsetting arrays\n", + "\n", + "import numpy as np\n", + "\n", + "# Create a 2D NumPy array\n", + "arr = np.array([[1, 2, 3], \n", + " [4, 5, 6], \n", + " [7, 8, 9]])\n", + "\n", + "# Subset the array to get a single element\n", + "single_element = arr[1, 2] # Output: 6 (element at row 1, column 2)\n", + "\n", + "# Subset the array to get a specific row\n", + "row_subset = arr[1, :] # Output: [4, 5, 6] (all columns of row 1)\n", + "\n", + "# Subset the array to get a specific column\n", + "column_subset = arr[:, 1] # Output: [2, 5, 8] (all rows of column 1)\n", + "\n", + "# Subset the array using slicing (getting a sub-array)\n", + "sub_array = arr[0:2, 0:2] # Output: [[1, 2], [4, 5]] (first 2 rows, first 2 columns)\n", + "\n", + "print(single_element)\n", + "print(row_subset)\n", + "print(column_subset)\n", + "print(sub_array)\n" + ] + }, + { + "cell_type": "markdown", + "id": "01f24d8a-02e1-4243-996c-147e27b9275e", + "metadata": {}, + "source": [ + "# 2.3. Copy and View" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "283b8294-351a-40be-bdf9-3406f8c35e7b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1 2 3 4]\n", + "[10 2 3 4]\n" + ] + } + ], + "source": [ + "# Copy\n", + "\n", + "import numpy as np\n", + "arr = np.array([1, 2, 3, 4])\n", + "arr_copy = arr.copy() # Creating a copy\n", + "\n", + "arr_copy[0] = 10\n", + "print(arr) # Output: [1, 2, 3, 4] (original array is unchanged)\n", + "print(arr_copy) # Output: [10, 2, 3, 4] (copy is changed)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "0ec3ea7e-fed3-4ce0-a0c3-b65d45d9c1b4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[10 2 3 4]\n", + "[10 2 3 4]\n" + ] + } + ], + "source": [ + "# View\n", + "\n", + "arr = np.array([1, 2, 3, 4])\n", + "arr_view = arr.view() # Creating a view\n", + "\n", + "arr_view[0] = 10\n", + "print(arr) # Output: [10, 2, 3, 4] (original array is changed)\n", + "print(arr_view) # Output: [10, 2, 3, 4] (view reflects the change)\n" + ] + }, + { + "cell_type": "raw", + "id": "88ec5613-f9d0-4617-98fe-8f97e621c036", + "metadata": {}, + "source": [ + "Copy creates a new array with independent memory, while view creates a new array object that points to the same memory as the original array." + ] + }, + { + "cell_type": "markdown", + "id": "5b78069d-a0dd-452b-97c5-9bf2519d4c87", + "metadata": {}, + "source": [ + "# 2.4. Reshaping, Flattened arrays" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "6dd01fad-b1da-4d0b-b3da-25453e1ef39c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 2 3]\n", + " [4 5 6]\n", + " [7 8 9]]\n" + ] + } + ], + "source": [ + "# Reshaping Arrays\n", + "\n", + "arr = np.arange(1, 10) # [1 2 3 4 5 6 7 8 9]\n", + "reshaped = arr.reshape(3, 3)\n", + "print(reshaped)\n", + "# Output:\n", + "# [[1 2 3]\n", + "# [4 5 6]\n", + "# [7 8 9]]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "bc7eb115-ef8c-41ac-8800-4e38fc7e2ca7", + "metadata": {}, + "outputs": [], + "source": [ + "# Reshaping & Flattened\n", + "\n", + "arr = np.arange(1, 10) # [1, 2, 3, 4, 5, 6, 7, 8, 9]\n", + "reshaped = arr.reshape(3, 3) # 3x3 matrix\n", + "flattened = reshaped.flatten() # Back to 1D" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "837eb1a4-9ec5-4cd4-be98-3e594ee6c6ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 2 3]\n", + " [4 5 6]\n", + " [7 8 9]]\n", + "[1 2 3 4 5 6 7 8 9]\n" + ] + } + ], + "source": [ + "print(reshaped)\n", + "print(flattened)" + ] + }, + { + "cell_type": "raw", + "id": "ca7f05b0-b5b5-4d5e-a3c9-8c6863453c36", + "metadata": {}, + "source": [ + "Module 3: Numerical Operations\n", + "1.\tElement-wise arithmetic and broadcasting\n", + "2.\tAggregation functions (sum, mean, median, std, var)\n", + "3.\tLogical and comparison operations\n", + "4.\tWorking with NaN and missing values\n" + ] + }, + { + "cell_type": "markdown", + "id": "62c55b89-db39-41e7-98d0-8344afbab9c8", + "metadata": {}, + "source": [ + "# 3.1.\tElement-wise arithmetic and broadcasting" + ] + }, + { + "cell_type": "raw", + "id": "4a34f0c4-d680-4eeb-975f-bbc6f71a0a17", + "metadata": {}, + "source": [ + "Element-wise Arithmetic:\n", + "\n", + "Element-wise arithmetic operations in NumPy allow you to perform calculations on arrays element by element. This means that operations like addition, subtraction, multiplication, and division are applied to each corresponding element in the arrays." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "9b83e57a-9620-44a9-aae0-6624ce39033e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[5 7 9]\n", + "[-3 -3 -3]\n", + "[ 4 10 18]\n", + "[0.25 0.4 0.5 ]\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Create two arrays\n", + "A = np.array([1, 2, 3])\n", + "B = np.array([4, 5, 6])\n", + "\n", + "# Element-wise addition\n", + "sum_array = A + B # Output: [5, 7, 9]\n", + "\n", + "# Element-wise subtraction\n", + "diff_array = A - B # Output: [-3, -3, -3]\n", + "\n", + "# Element-wise multiplication\n", + "prod_array = A * B # Output: [4, 10, 18]\n", + "\n", + "# Element-wise division\n", + "div_array = A / B # Output: [0.25, 0.4, 0.5]\n", + "\n", + "print(sum_array)\n", + "print(diff_array)\n", + "print(prod_array)\n", + "print(div_array)\n" + ] + }, + { + "cell_type": "raw", + "id": "ae7bd8f7-e4c3-4f91-b5c1-c656cd2c9c5e", + "metadata": {}, + "source": [ + "Broadcasting:\n", + "\n", + "Broadcasting is a feature in NumPy that allows you to perform element-wise arithmetic operations on arrays of different shapes and sizes. It automatically expands the smaller array to match the shape of the larger array without copying data, making it computationally efficient." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "b6c4c257-a16c-49b3-bd48-b31fa2344ceb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[11 22 33]\n", + " [14 25 36]]\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Create a 2D array and a 1D array\n", + "A = np.array([[1, 2, 3], [4, 5, 6]]) # Shape (2, 3)\n", + "B = np.array([10, 20, 30]) # Shape (3,)\n", + "\n", + "# Broadcasting arr2 to match the shape of arr1\n", + "result = A + B # Broadcasting happens here\n", + "print(result)\n" + ] + }, + { + "cell_type": "raw", + "id": "8a5e1649-0774-484b-a158-c313653ec6dd", + "metadata": {}, + "source": [ + "Element-wise operations are simple arithmetic operations performed on each element of the arrays.\n", + "\n", + "Broadcasting enables element-wise operations on arrays with different shapes, expanding the smaller array to match the dimensions of the larger array for efficient computation." + ] + }, + { + "cell_type": "markdown", + "id": "840b877b-a83e-4c46-a9c3-f1f012698071", + "metadata": {}, + "source": [ + "# 3.2 Aggregation functions (sum, mean, median, std, var)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a4698f11-e7f5-4795-a354-b14c78a0aac3", + "metadata": {}, + "outputs": [], + "source": [ + "# Common \n", + "# np.function_name(array, axis=None)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "eca3b21d-3342-4aae-968b-59bef019922b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "15\n", + "15\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Sum - np.sum()\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "print(np.sum(arr)) # 15 (sum of all elements)\n", + "print(np.sum(arr, axis=0)) # Sum along specified axis" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "689cf1b5-6ad3-4fd7-a8ea-277ff0786a66", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.0\n", + "3.0\n" + ] + } + ], + "source": [ + "# Mean - `np.mean()\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "print(np.mean(arr)) # 3.0 (average)\n", + "print(np.mean(arr, axis=0)) # Mean along specified axis" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "13cc9287-bfb5-4d98-b4aa-dd53f7f58296", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.5\n" + ] + } + ], + "source": [ + "# Median - `np.median()\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5, 100]) # Outlier included\n", + "\n", + "print(np.median(arr)) # 3.5 (robust to outliers)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "1eabbd39-934f-4b47-9e95-dfe2be3dbb4d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.4142135623730951\n" + ] + } + ], + "source": [ + "# Standard Deviation - `np.std()\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "print(np.std(arr)) # 1.414 (spread of data)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "0b203a25-e1a9-4941-963e-f9fd897b84e6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.0\n" + ] + } + ], + "source": [ + "# Variance - `np.var()\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "print(np.var(arr)) # 2.0 (squared standard deviation)" + ] + }, + { + "cell_type": "markdown", + "id": "95431642-47f6-4922-bb86-c96fd9810175", + "metadata": {}, + "source": [ + "# 2D Array Examples with Axis Parameter" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "31b1ec52-74a2-4c17-acb1-abfd35a2e790", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sum axis=0: [12 15 18]\n", + "Mean axis=0: [4. 5. 6.]\n", + "Std axis=0: [2.44948974 2.44948974 2.44948974]\n", + "Sum axis=1: [ 6 15 24]\n", + "Mean axis=1: [2. 5. 8.]\n" + ] + } + ], + "source": [ + "arr_2d = np.array([[1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]])\n", + "\n", + "# axis=0 (down columns)\n", + "print(\"Sum axis=0:\", np.sum(arr_2d, axis=0)) # [12, 15, 18]\n", + "print(\"Mean axis=0:\", np.mean(arr_2d, axis=0)) # [4., 5., 6.]\n", + "print(\"Std axis=0:\", np.std(arr_2d, axis=0)) # [2.45, 2.45, 2.45]\n", + "\n", + "# axis=1 (across rows)\n", + "print(\"Sum axis=1:\", np.sum(arr_2d, axis=1)) # [6, 15, 24]\n", + "print(\"Mean axis=1:\", np.mean(arr_2d, axis=1)) # [2., 5., 8.]" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "64c52be1-648b-44b7-b458-c22d23529409", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "5\n", + "1\n", + "4\n" + ] + } + ], + "source": [ + "# Minimum & Maximum\n", + "\n", + "arr = np.array([3, 1, 4, 1, 5])\n", + "\n", + "print(np.min(arr)) # 1\n", + "print(np.max(arr)) # 5\n", + "print(np.argmin(arr)) # 1 (index of minimum)\n", + "print(np.argmax(arr)) # 4 (index of maximum)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "660dce1c-0bc4-400c-b29a-1bea1e0f73bf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "24\n" + ] + } + ], + "source": [ + "# Product\n", + "\n", + "arr = np.array([1, 2, 3, 4])\n", + "\n", + "print(np.prod(arr)) # 24 (1*2*3*4)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "57571998-12a9-4b14-a569-9ab97abdd3b7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.0\n", + "2.0\n" + ] + } + ], + "source": [ + "# Percentile\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "print(np.percentile(arr, 50)) # 3.0 (50th percentile = median)\n", + "print(np.percentile(arr, 25)) # 2.0 (25th percentile)" + ] + }, + { + "cell_type": "markdown", + "id": "bf1c5c60-0711-4a06-a4fd-b8dff2494ea2", + "metadata": {}, + "source": [ + "# Practical Example: Student Grades Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "20c0eff7-38ac-4b00-8bd9-661b54f30524", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Subject means: [85.25 86.25 86. ]\n", + "Student means: [84.33333333 88. 82.66666667 88.33333333]\n", + "Overall mean: 85.83333333333333\n", + "Subject std: [5.88960949 3.03108891 5.47722558]\n" + ] + } + ], + "source": [ + "# Student grades for 3 subjects across 4 students\n", + "grades = np.array([[85, 90, 78],\n", + " [92, 88, 84],\n", + " [76, 82, 90],\n", + " [88, 85, 92]])\n", + "\n", + "print(\"Subject means:\", np.mean(grades, axis=0)) # Average per subject\n", + "print(\"Student means:\", np.mean(grades, axis=1)) # Average per student\n", + "print(\"Overall mean:\", np.mean(grades)) # Overall average\n", + "print(\"Subject std:\", np.std(grades, axis=0)) # Variability per subject" + ] + }, + { + "cell_type": "markdown", + "id": "2ac3c5f6-f8d6-403d-b17d-c6fbad06d7ab", + "metadata": {}, + "source": [ + "# 3.3 Logical and Comparison Operations" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "fab8e670-9e2d-4478-b9b8-5aaecea0bddc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Equal: [False False True False False]\n", + "Not equal: [ True True False True True]\n", + "Greater than: [False False False True True]\n", + "Less than: [ True True False False False]\n", + "Greater or equal: [False False True True True]\n" + ] + } + ], + "source": [ + "# Basic Comparison Operators\n", + "\n", + "import numpy as np\n", + "\n", + "arr1 = np.array([1, 2, 3, 4, 5])\n", + "arr2 = np.array([5, 4, 3, 2, 1])\n", + "\n", + "# Element-wise comparisons\n", + "print(\"Equal:\", arr1 == arr2) # [False, False, True, False, False]\n", + "print(\"Not equal:\", arr1 != arr2) # [True, True, False, True, True]\n", + "print(\"Greater than:\", arr1 > arr2) # [False, False, False, True, True]\n", + "print(\"Less than:\", arr1 < arr2) # [True, True, False, False, False]\n", + "print(\"Greater or equal:\", arr1 >= arr2) # [False, False, True, True, True]" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "4f3afd36-c48a-4c2a-9c05-ecc889bbebf6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Greater than 3: [False False False True True]\n", + "Equal to 3: [False False True False False]\n" + ] + } + ], + "source": [ + "# With Scalars\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "print(\"Greater than 3:\", arr > 3) # [False, False, False, True, True]\n", + "print(\"Equal to 3:\", arr == 3) # [False, False, True, False, False]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "ae22edf3-ab5c-4c9c-ab16-6341cc4fbd5d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ True False False False]\n", + "[False False True True False]\n", + "[3 4]\n" + ] + } + ], + "source": [ + "# Logical Operations \n", + "# AND - np.logical_and()\n", + "\n", + "a = np.array([True, True, False, False])\n", + "b = np.array([True, False, True, False])\n", + "\n", + "print(np.logical_and(a, b)) # [True, False, False, False]\n", + "\n", + "# Practical example\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "mask = np.logical_and(arr > 2, arr < 5)\n", + "print(mask) # [False, False, True, True, False]\n", + "print(arr[mask]) # [3, 4] (filtered elements)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "48fdb814-3855-45d8-a2bc-6559cab40a09", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ True True True False]\n", + "[ True False False False True]\n" + ] + } + ], + "source": [ + "# OR - np.logical_or()\n", + "\n", + "a = np.array([True, True, False, False])\n", + "b = np.array([True, False, True, False])\n", + "\n", + "print(np.logical_or(a, b)) # [True, True, True, False]\n", + "\n", + "# Practical example\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "mask = np.logical_or(arr < 2, arr > 4)\n", + "print(mask) # [True, False, False, False, True]" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "749470ab-7090-43ab-a8db-32e0a608cc75", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[False True False True]\n", + "[ True True True False False]\n" + ] + } + ], + "source": [ + "# NOT - np.logical_not()\n", + "\n", + "a = np.array([True, False, True, False])\n", + "\n", + "print(np.logical_not(a)) # [False, True, False, True]\n", + "\n", + "# Practical example\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "mask = np.logical_not(arr > 3)\n", + "print(mask) # [True, True, True, False, False]" + ] + }, + { + "cell_type": "markdown", + "id": "c72eeabb-ace9-47f4-8bb8-761227e9217c", + "metadata": {}, + "source": [ + "# Practical Examples with 2D Arrays" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "123cd2d7-7aef-45be-a771-2484332d2e83", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Comfortable temperatures mask:\n", + "[[ True True True]\n", + " [False False True]\n", + " [False False True]]\n", + "Comfortable temperatures:\n", + "[25 28 30 29 27]\n" + ] + } + ], + "source": [ + "# Temperature data for multiple days\n", + "temperatures = np.array([[25, 28, 30],\n", + " [22, 35, 29],\n", + " [18, 32, 27]])\n", + "\n", + "# Find temperatures between 25 and 30 degrees\n", + "comfortable = np.logical_and(temperatures >= 25, temperatures <= 30)\n", + "print(\"Comfortable temperatures mask:\")\n", + "print(comfortable)\n", + "print(\"Comfortable temperatures:\")\n", + "print(temperatures[comfortable])" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "47331968-9853-46c0-b2a9-eae7c2bde093", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[False False True True True]\n", + "[30 40 50]\n", + "[30 40 50]\n" + ] + } + ], + "source": [ + "# Using Boolean Arrays as Masks\n", + "\n", + "arr = np.array([10, 20, 30, 40, 50])\n", + "\n", + "# Create boolean mask\n", + "mask = arr > 25\n", + "print(mask) # [False, False, True, True, True]\n", + "\n", + "# Apply mask to filter elements\n", + "filtered = arr[mask]\n", + "print(filtered) # [30, 40, 50]\n", + "\n", + "# One-liner filtering\n", + "print(arr[arr > 25]) # [30, 40, 50]" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "20a95256-ad15-4784-961c-486fa450df74", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Even numbers: [ 2 4 6 8 10]\n", + "Between 3 and 8: [3 4 5 6 7 8]\n" + ] + } + ], + "source": [ + "# Multiple Conditions\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n", + "\n", + "# Multiple conditions\n", + "even_numbers = arr[arr % 2 == 0]\n", + "print(\"Even numbers:\", even_numbers) # [2, 4, 6, 8, 10]\n", + "\n", + "# Between 3 and 8\n", + "between = arr[(arr >= 3) & (arr <= 8)]\n", + "print(\"Between 3 and 8:\", between) # [3, 4, 5, 6, 7, 8]" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "376448e9-86f1-416a-9c9e-acb5e24995c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[False False True True True]\n", + "[0 0 1 1 1]\n", + "3\n", + "3\n" + ] + } + ], + "source": [ + "# Mathematical Operations with Boolean Arrays\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "# Boolean arrays can be used in mathematical operations\n", + "bool_arr = arr > 2\n", + "print(bool_arr) # [False, False, True, True, True]\n", + "\n", + "# Convert boolean to integer (True=1, False=0)\n", + "int_arr = bool_arr.astype(int)\n", + "print(int_arr) # [0, 0, 1, 1, 1]\n", + "\n", + "# Count True values\n", + "print(np.sum(bool_arr)) # 3 (number of elements > 2)\n", + "print(np.count_nonzero(bool_arr)) # 3 (alternative method)" + ] + }, + { + "cell_type": "markdown", + "id": "e2745cdd-299e-419d-8edd-eb195077fc1b", + "metadata": {}, + "source": [ + "# Real-World Example: Student Grade Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "67f9330b-ea09-4e44-a54b-15671f094c86", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Passed all subjects: [ True False False True]\n", + "Failed any subject: [False True True False]\n", + "Has excellent grade: [ True True False True]\n" + ] + } + ], + "source": [ + "# Student grades data\n", + "grades = np.array([[85, 92, 78],\n", + " [45, 88, 92],\n", + " [76, 52, 90],\n", + " [88, 95, 82]])\n", + "\n", + "# Find students who passed all subjects (grade >= 60)\n", + "passed_all = np.all(grades >= 60, axis=1)\n", + "print(\"Passed all subjects:\", passed_all) # [True, False, False, True]\n", + "\n", + "# Find students who failed any subject\n", + "failed_any = np.any(grades < 60, axis=1)\n", + "print(\"Failed any subject:\", failed_any) # [False, True, True, False]\n", + "\n", + "# Find excellent students (any grade > 90)\n", + "excellent = np.any(grades > 90, axis=1)\n", + "print(\"Has excellent grade:\", excellent) # [True, True, False, True]" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "423d4a6d-cd86-4ab3-8392-3d1588e724b3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 1 2 3 40 50]\n" + ] + } + ], + "source": [ + "# np.where() - Conditional Selection\n", + "\n", + "arr = np.array([1, 2, 3, 4, 5])\n", + "\n", + "# Replace elements based on condition\n", + "result = np.where(arr > 3, arr * 10, arr) # If >3 then *10, else keep as is\n", + "print(result) # [1, 2, 3, 40, 50]" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "bee2ec99-9cf2-4442-8e7d-97c31a7f7213", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n", + "True\n" + ] + } + ], + "source": [ + "# np.all() and np.any()\n", + "\n", + "arr = np.array([True, True, False])\n", + "\n", + "print(np.all(arr)) # False (all elements must be True)\n", + "print(np.any(arr)) # True (at least one element is True)" + ] + }, + { + "cell_type": "markdown", + "id": "c22ac7d0-cb30-4ad9-9e28-5f26dc1b0698", + "metadata": {}, + "source": [ + "# 3.4 Working with NaN and missing values" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "bedc9f34-0f2c-4cf3-910b-14cf32f14b14", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NaN mask: [False False True False True False]\n", + "Number of NaN values: 2\n" + ] + } + ], + "source": [ + "# np.isnan() - Detect NaN values\n", + "\n", + "arr = np.array([1, 2, np.nan, 4, np.nan, 6])\n", + "\n", + "# Check which elements are NaN\n", + "nan_mask = np.isnan(arr)\n", + "print(\"NaN mask:\", nan_mask) # [False, False, True, False, True, False]\n", + "\n", + "# Count NaN values\n", + "nan_count = np.sum(np.isnan(arr))\n", + "print(\"Number of NaN values:\", nan_count) # 2" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "1004523c-d348-46f9-885a-4f91b532a6bb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Non-NaN mask: [ True True False True False True]\n" + ] + } + ], + "source": [ + "# ~np.isnan() - Detect non-NaN values\n", + "\n", + "arr = np.array([1, 2, np.nan, 4, np.nan, 6])\n", + "\n", + "non_nan_mask = ~np.isnan(arr)\n", + "print(\"Non-NaN mask:\", non_nan_mask) # [True, True, False, True, False, True]" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "9fbf02dc-774c-4816-8117-cd621be5266b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sum: nan\n", + "Mean: nan\n", + "Max: nan\n" + ] + } + ], + "source": [ + "# Handling NaN in Mathematical Operations (How NaN Propagates)\n", + "\n", + "arr = np.array([1, 2, np.nan, 4])\n", + "\n", + "print(\"Sum:\", np.sum(arr)) # nan\n", + "print(\"Mean:\", np.mean(arr)) # nan\n", + "print(\"Max:\", np.max(arr)) # nan" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "81ac7409-16b1-44d3-9ce0-5edda0dea5b0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Regular sum: nan\n", + "NaN-safe sum: 12.0\n", + "NaN-safe mean: 3.0\n", + "NaN-safe max: 5.0\n", + "NaN-safe min: 1.0\n", + "NaN-safe std: 1.5811388300841898\n" + ] + } + ], + "source": [ + "# NaN-Safe Aggregation Functions\n", + "\n", + "arr = np.array([1, 2, np.nan, 4, 5])\n", + "\n", + "# Regular functions return nan\n", + "print(\"Regular sum:\", np.sum(arr)) # nan\n", + "\n", + "# NaN-safe functions (skip NaN values)\n", + "print(\"NaN-safe sum:\", np.nansum(arr)) # 12.0 (1+2+4+5)\n", + "print(\"NaN-safe mean:\", np.nanmean(arr)) # 3.0 (12/4)\n", + "print(\"NaN-safe max:\", np.nanmax(arr)) # 5.0\n", + "print(\"NaN-safe min:\", np.nanmin(arr)) # 1.0\n", + "print(\"NaN-safe std:\", np.nanstd(arr)) # 1.58" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "f63408cb-2913-4029-a06d-ea23dd74e8a8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nansum: 9.0\n", + "nanmean: 3.0\n", + "nanmedian: 3.0\n", + "nanstd: 1.632993161855452\n", + "nanvar: 2.6666666666666665\n", + "nanmin: 1.0\n", + "nanmax: 5.0\n", + "nanpercentile: 3.0\n" + ] + } + ], + "source": [ + "# Complete List of NaN-Safe Functions\n", + "\n", + "arr = np.array([1, np.nan, 3, np.nan, 5])\n", + "\n", + "print(\"nansum:\", np.nansum(arr)) # 9.0\n", + "print(\"nanmean:\", np.nanmean(arr)) # 3.0\n", + "print(\"nanmedian:\", np.nanmedian(arr)) # 3.0\n", + "print(\"nanstd:\", np.nanstd(arr)) # 1.63\n", + "print(\"nanvar:\", np.nanvar(arr)) # 2.67\n", + "print(\"nanmin:\", np.nanmin(arr)) # 1.0\n", + "print(\"nanmax:\", np.nanmax(arr)) # 5.0\n", + "print(\"nanpercentile:\", np.nanpercentile(arr, 50)) # 3.0" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "96d724e4-cb07-4a0d-8850-975d4709cbe0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Clean array: [1. 2. 4. 6.]\n", + "Clean array: [1. 2. 4. 6.]\n" + ] + } + ], + "source": [ + "# Filtering and Replacing NaN Values (Removing NaN Values)\n", + "\n", + "arr = np.array([1, 2, np.nan, 4, np.nan, 6])\n", + "\n", + "# Method 1: Boolean indexing\n", + "clean_arr = arr[~np.isnan(arr)]\n", + "print(\"Clean array:\", clean_arr) # [1. 2. 4. 6.]\n", + "\n", + "# Method 2: Using np.isnan() with negation\n", + "clean_arr = arr[np.logical_not(np.isnan(arr))]\n", + "print(\"Clean array:\", clean_arr) # [1. 2. 4. 6.]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "af613a83-cad3-4f7d-bf38-62cfa15b9e7a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NaN replaced with 0: [1. 2. 0. 4. 0. 6.]\n", + "NaN replaced with mean: [1. 2. 3.25 4. 3.25 6. ]\n", + "NaN replaced with -999: [ 1. 2. -999. 4. -999. 6.]\n" + ] + } + ], + "source": [ + "# Replacing NaN Values\n", + "\n", + "arr = np.array([1, 2, np.nan, 4, np.nan, 6])\n", + "\n", + "# Replace NaN with 0\n", + "arr_zero = np.nan_to_num(arr, nan=0)\n", + "print(\"NaN replaced with 0:\", arr_zero) # [1. 2. 0. 4. 0. 6.]\n", + "\n", + "# Replace NaN with mean of non-NaN values\n", + "mean_val = np.nanmean(arr)\n", + "arr_mean = np.where(np.isnan(arr), mean_val, arr)\n", + "print(\"NaN replaced with mean:\", arr_mean) # [1. 2. 3.25 4. 3.25 6.]\n", + "\n", + "# Replace NaN with specific value\n", + "arr_filled = arr.copy()\n", + "arr_filled[np.isnan(arr_filled)] = -999\n", + "print(\"NaN replaced with -999:\", arr_filled)" + ] + }, + { + "cell_type": "markdown", + "id": "c2d95970-6727-4f40-a0ac-56e21cc1b5a5", + "metadata": {}, + "source": [ + "# Working with 2D Arrays and NaN" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "ab4293e9-cd39-4e6f-9938-ff9171f5c480", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original 2D array:\n", + "[[ 1. 2. nan]\n", + " [ 4. nan 6.]\n", + " [nan 8. 9.]]\n", + "NaN count per column: [1 1 1]\n", + "NaN count per row: [1 1 1]\n", + "Column means (NaN-safe): [2.5 5. 7.5]\n", + "Row means (NaN-safe): [1.5 5. 8.5]\n" + ] + } + ], + "source": [ + "# 2D array with missing values\n", + "data_2d = np.array([[1, 2, np.nan],\n", + " [4, np.nan, 6],\n", + " [np.nan, 8, 9]])\n", + "\n", + "print(\"Original 2D array:\")\n", + "print(data_2d)\n", + "\n", + "# Count NaN per column\n", + "nan_per_column = np.sum(np.isnan(data_2d), axis=0)\n", + "print(\"NaN count per column:\", nan_per_column) # [1, 1, 1]\n", + "\n", + "# Count NaN per row\n", + "nan_per_row = np.sum(np.isnan(data_2d), axis=1)\n", + "print(\"NaN count per row:\", nan_per_row) # [1, 1, 1]\n", + "\n", + "# Column-wise NaN-safe operations\n", + "print(\"Column means (NaN-safe):\", np.nanmean(data_2d, axis=0))\n", + "print(\"Row means (NaN-safe):\", np.nanmean(data_2d, axis=1))" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "c54f313d-129c-4cb8-8eab-a1b112db2ec2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 2. -1. 6. 8. -1.]\n" + ] + } + ], + "source": [ + "# Advanced NaN Handling Techniques (Using np.where() with NaN)\n", + "\n", + "arr = np.array([1, np.nan, 3, 4, np.nan])\n", + "\n", + "# Replace NaN with specific values based on condition\n", + "result = np.where(np.isnan(arr), -1, arr * 2)\n", + "print(result) # [2., -1., 6., 8., -1.]" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "14975172-6e16-4b41-935a-944cae55d28f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Indices of NaN values: [2 4]\n" + ] + } + ], + "source": [ + "# Finding Indices of NaN Values\n", + "\n", + "arr = np.array([1, 2, np.nan, 4, np.nan, 6])\n", + "\n", + "# Get indices of NaN values\n", + "nan_indices = np.where(np.isnan(arr))[0]\n", + "print(\"Indices of NaN values:\", nan_indices) # [2, 4]" + ] + }, + { + "cell_type": "markdown", + "id": "edff1ddb-742e-4f8b-ae56-672333220418", + "metadata": {}, + "source": [ + "# Real-World Data Cleaning Example" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "35faeb06-c7e6-40e9-92b6-1bc1fa847855", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original dataset:\n", + "[[ 1. 2. 3.]\n", + " [ 4. nan 6.]\n", + " [nan nan nan]\n", + " [ 7. 8. 9.]\n", + " [10. nan 12.]]\n", + "\n", + "Cleaned dataset:\n", + "[[ 1. 2. 3.]\n", + " [ 4. 5. 6.]\n", + " [ 7. 8. 9.]\n", + " [10. 5. 12.]]\n" + ] + } + ], + "source": [ + "# Simulating real dataset with missing values\n", + "def clean_dataset(data):\n", + " \"\"\"Clean dataset by handling missing values\"\"\"\n", + " \n", + " # Create a copy to avoid modifying original\n", + " cleaned = data.copy()\n", + " \n", + " # Strategy 1: Remove rows with too many NaN values\n", + " nan_per_row = np.sum(np.isnan(cleaned), axis=1)\n", + " valid_rows = nan_per_row < cleaned.shape[1] / 2 # Keep rows with <50% NaN\n", + " cleaned = cleaned[valid_rows]\n", + " \n", + " # Strategy 2: Fill remaining NaN with column means\n", + " for col in range(cleaned.shape[1]):\n", + " col_data = cleaned[:, col]\n", + " if np.any(np.isnan(col_data)):\n", + " col_mean = np.nanmean(col_data)\n", + " col_data[np.isnan(col_data)] = col_mean\n", + " cleaned[:, col] = col_data\n", + " \n", + " return cleaned\n", + "\n", + "# Example usage\n", + "dataset = np.array([[1, 2, 3],\n", + " [4, np.nan, 6],\n", + " [np.nan, np.nan, np.nan],\n", + " [7, 8, 9],\n", + " [10, np.nan, 12]])\n", + "\n", + "print(\"Original dataset:\")\n", + "print(dataset)\n", + "\n", + "cleaned_data = clean_dataset(dataset)\n", + "print(\"\\nCleaned dataset:\")\n", + "print(cleaned_data)" + ] + }, + { + "cell_type": "markdown", + "id": "d49f8136-f819-461f-b39a-2f13f5eb1614", + "metadata": {}, + "source": [ + "# 4. Real-World Data Analytics Example" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "6dee829b-5297-4fda-afe4-0648564c0a94", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean score: 86.625\n", + "Standard Deviation: 6.203577596838779\n" + ] + } + ], + "source": [ + "# A dataset of exam scores\n", + "scores = np.array([85, 90, 78, 92, 88, 76, 95, 89])\n", + "\n", + "# Calculate the mean and standard deviation\n", + "mean_score = np.mean(scores)\n", + "std_dev_score = np.std(scores)\n", + "\n", + "print(f\"Mean score: {mean_score}\")\n", + "print(f\"Standard Deviation: {std_dev_score}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "ba4b37f3-2a77-480d-939b-791fccc58407", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total sales per product: [850 495 320]\n" + ] + } + ], + "source": [ + "# Sample sales data (3 products, 5 days)\n", + "sales = np.array([\n", + " [100, 150, 200, 180, 220], # Product 1\n", + " [80, 90, 110, 95, 120], # Product 2\n", + " [50, 60, 70, 65, 75] # Product 3\n", + "])\n", + "\n", + "# Total sales per product\n", + "total_sales = np.sum(sales, axis=1)\n", + "print(\"Total sales per product:\", total_sales)" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "b50f16dc-10a8-4cf6-863c-0db23174274e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average daily sales: [ 76.66666667 100. 126.66666667 113.33333333 138.33333333]\n" + ] + } + ], + "source": [ + "# Average daily sales\n", + "avg_daily = np.mean(sales, axis=0)\n", + "print(\"Average daily sales:\", avg_daily)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "3078897d-f1da-442b-9bb5-f7b9596ff963", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best-selling day: 4\n" + ] + } + ], + "source": [ + "# Best-selling day\n", + "best_day = np.argmax(np.sum(sales, axis=0))\n", + "print(\"Best-selling day:\", best_day)" + ] + }, + { + "cell_type": "markdown", + "id": "e0e6ca18-a01c-4f15-9722-e54112724e15", + "metadata": {}, + "source": [ + "# Practical Example: Predicting House Prices" + ] + }, + { + "cell_type": "raw", + "id": "f4fa9bb9-e0a3-4786-8713-43714bd983dc", + "metadata": {}, + "source": [ + "Let’s walk through a practical example using NumPy to perform some basic data analysis with a real-life dataset: house prices.\n", + "\n", + "Suppose we have a CSV file (house_prices.csv) with the following columns:\n", + "Area (in square feet)\n", + "Bedrooms (number of bedrooms)\n", + "Price (price of the house)\n", + "\n", + "We want to analyze the relationship between area, number of bedrooms, and house price." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "57a9552a-2775-44cb-b0e3-153f937c8be3", + "metadata": {}, + "outputs": [], + "source": [ + "# Load the dataset\n", + "data = np.genfromtxt('Housing.csv', delimiter=',', skip_header=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "2728f5be-b423-49e2-af3c-3509738f7c2a", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract features (Area and Bedrooms) and target (Price)\n", + "X = data[:, 0:2] # Features: Area and Bedrooms\n", + "y = data[:, 2] # Target: Price" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "6bc95711-a19b-4662-835d-26dcd039cbff", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean area: 5150.54128440367\n", + "Mean price: 4766729.247706422\n" + ] + } + ], + "source": [ + "# Perform basic statistical analysis\n", + "print(\"Mean area:\", np.mean(X[:, 0]))\n", + "print(\"Mean price:\", np.mean(y))" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "243faca8-2a1a-4afd-a0e8-64d0b06f1e49", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Correlation between Area and Price: 0.5359973457780797\n" + ] + } + ], + "source": [ + "# Correlation between area and price\n", + "correlation = np.corrcoef(X[:, 0], y)[0, 1]\n", + "print(f\"Correlation between Area and Price: {correlation}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "938feae9-0e14-438e-9aae-67f617e5dc78", + "metadata": {}, + "outputs": [], + "source": [ + "# Perform a simple linear regression model (Using only area as the predictor)\n", + "from sklearn.linear_model import LinearRegression\n", + "model = LinearRegression()" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "37ec8edc-141f-4d45-9542-7981420def71", + "metadata": {}, + "outputs": [], + "source": [ + "# Fit the model\n", + "model.fit(X[:, [0]], y) # Using Area as the only feature\n", + "predictions = model.predict(X[:, [0]])" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "3611dcd7-e988-4ec1-80ec-209dc88e3219", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Slope (Coefficient): [461.97489427]\n", + "Intercept: 2387308.4823964313\n" + ] + } + ], + "source": [ + "# Print coefficients (slope and intercept)\n", + "print(\"Slope (Coefficient):\", model.coef_)\n", + "print(\"Intercept:\", model.intercept_)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "c9c963de-b11f-42b9-b405-87a92fa0870d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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yJEjodPpsHz5cpf7KRQKXj0pl112GdLT07F69WqrhPba2lp88cUXlhlUhISqZt1z40pubi7WrFmD8+fPIy0tDYCp63nz5s1YtmwZnn/+eav99Xo9Vq1ahccffzwQzSVh5Nprr8Xw4cPx2GOPoaqqCv3797fMlurevTsmT55s2Xfy5Mn473//i6effhpXX301jh07hrfffhsxMTFWx+zduzdGjx6NLl26oEWLFjh+/Dg++eQTq5vY+++/j5EjR2L48OGYNm0a0tPTcfHiRRw/fhx//PEHPvvsM79+DgDwzjvvYMyYMejTpw/mzJmDVq1aIT8/H1u2bMGqVasAAG+88QYGDBiAgQMH4t5770Xr1q1RXV2N06dP4+uvv7bkMPkK38/tqaeewldffYXBgwfj6aefhlqtxjvvvIPa2lq355g4cSKWLVuGmTNn4uTJkxg0aBCMRiP27NmDjh074tZbbwUAdO7cGdu3b8fXX3+N1NRUaDQahz1uEokEL774IiZNmoTRo0fjnnvugV6vx0svvYSKigosXrxY3A+JEH8LdEZzsADANmzYYPn3unXrGAAWGRlp9SWTydj48ePt3r969Womk8lYUVGRH1tNQoF5ttTvv//ucPuoUaOsZksxxphWq2WPPfYYy8zMZBERESw1NZXde++97NKlS1b76fV69p///IdlZGQwlUrFrr76anbw4EG72VKPP/44y8nJYS1atGAKhYK1adOGzZkzh5WVlVkd79ChQ2z8+PEsKSmJRUREsJSUFDZ48GD23nvvub1OAOz+++93uY951s5nn33mdFvT2VKMMbZ79242cuRIFhMTwxQKBcvOzmZz5syx2icvL4/deeedLD09nUVERLDExETWr18/tnDhQrftzszMZKNGjXK5j3m21EsvveRwO9/P7ddff2V9+vRhCoWCpaSksEcffZR98MEHbmdLMWb6mXj66adZu3btmFwuZ/Hx8Wzw4MFs165dln0OHjzI+vfvz9RqNQNgOYazz3bjxo2sd+/eTKlUssjISDZkyBD266+/Wu1jni1VWlpq9br557ppuwkJFhxjNkU2mimO47BhwwaMGzcOALB27VpMmjQJf/75p11CZFRUlF0S4JAhQxAdHY0NGzb4q8mEEEIIcYCGpZzo3r07GhsbUVJS4jaHJi8vD9u2bcNXX33lp9YRQgghxJlmHdzU1NTg9OnTln/n5eXh4MGDiIuLQ/v27TFp0iRMmTIFr7zyCrp3746ysjL89NNP6Ny5M6677jrL+5YuXYrU1FSXM68IIYQQ4h/Nelhq+/btGDRokN3rU6dOxfLly9HQ0ICFCxdixYoVKCgoQHx8PPr27Yv58+ejc+fOAACj0YjMzExMmTIF//vf//x9CYQQQgix0ayDG0IIIYSEH6pzQwghhJCwQsENIYQQQsJKs0soNhqNKCwshEaj4VUCnhBCCCGBxxhDdXU1r3XWml1wU1hYiIyMjEA3gxBCCCEeOHfunMsFdYFmGNyY13k5d+4coqOjA9waQgghhPBRVVWFjIwMXuvcNbvgxjwUFR0dTcENIYQQEmL4pJRQQjEhhBBCwgoFN4QQQggJKxTcEEIIISSsNLucG0II8Sej0Yj6+vpAN4OQkCCXy91O8+aDghtCCPGR+vp65OXlwWg0BrophIQEiUSCrKwsyOVyr45DwQ0hhPgAYwxFRUWQSqXIyMgQ5WmUkHBmLrJbVFSEVq1aeVVol4IbQgjxAYPBgLq6OqSlpUGtVge6OYSEhMTERBQWFsJgMCAiIsLj49CjBCGE+EBjYyMAeN29TkhzYv59Mf/+eIqCG0II8SFaw44Q/sT6faFhKUKIFcYYyrT10BmMUMokSFDJ6QZNCAkpFNwQQiwKqrU4XFIFreHf2T0qmQRdkqKRrlEFsGWEEMIfDUsRQgCYAps9hRVWgQ0AaA1G7CmsQEG1NkAtI/42bdo0cBwHjuMQERGB5ORkXHvttVi6dKmgae3Lly9HbGys7xpKiBMU3BBCwBjD4ZIql/scLqkCY8xPLSJmjDGU1ulxrkqL0jq9374HI0aMQFFREf7++2989913GDRoEB566CGMHj0aBoPBL20gxFMU3BBCUKatt+uxsaU1GFGmpUq7/lRQrcXmMyXYce4ifi+qwI5zF7H5TIlfetEUCgVSUlKQnp6OHj164IknnsCXX36J7777DsuXLwcAvPrqq+jcuTMiIyORkZGB++67DzU1NQCA7du344477kBlZaWlF+jZZ58FAKxcuRI5OTnQaDRISUnBbbfdhpKSEp9fE2k+KLghhEDnJrARuh/xXjAOEw4ePBhdu3bF+vXrAZiqyb755ps4evQoPv74Y/z000/4z3/+AwDo168fXn/9dURHR6OoqAhFRUV45JFHAJgqNy9YsACHDh3Cxo0bkZeXh2nTpvn9ekj4ooRiQgiUMn7POXz3I97hO0yYFqX0+0y2Dh064PDhwwCA2bNnW17PysrCggULcO+992LJkiWQy+WIiYkBx3FISUmxOsadd95p+f82bdrgzTffRK9evVBTU4OoqCi/XAcJb/SXihCCBJUcKjeBi+qfaeHE94J5mJAxZgmotm3bhmuvvRbp6enQaDSYMmUKysvLUVtb6/IYBw4cwPXXX4/MzExoNBpcc801AID8/HxfN580ExTcEELAcRy6JEW73KdLUjTVu/GTYB4mPH78OLKysnD27Flcd9116NSpE7744gvs378f77zzDgCgoaHB6ftra2sxbNgwREVFYeXKlfj999+xYcMGAKDV04loaFiKEAIASNeo0DsNVOcmCATrMOFPP/2EI0eOYM6cOdi3bx8MBgNeeeUVy6Kg69ats9pfLpfbldE/ceIEysrKsHjxYmRkZAAA9u3b558LIM0GBTeEEIt0jQppUUqqUBxg5mFCV0NTvh4m1Ov1KC4uRmNjIy5cuIDNmzdj0aJFGD16NKZMmYIjR47AYDDgrbfewpgxY/Drr7/ivffeszpG69atUVNTgx9//BFdu3aFWq1Gq1atIJfL8dZbb2HmzJk4evQoFixY4LPrIM0TDUsRQqxwHIdEtQIZ0SokqhUU2ARAMAwTbt68GampqWjdujVGjBiBbdu24c0338SXX34JqVSKbt264dVXX8ULL7yATp06YdWqVVi0aJHVMfr164eZM2diwoQJSExMxIsvvojExEQsX74cn332GS6//HIsXrwYL7/8ss+ugzRPHGtmVbmqqqoQExODyspKREe7/uNBCCGe0ul0yMvLQ1ZWFpRKpUfHoOUwSHPj6vdGyP2bhqUIISRI0TAhIZ6h4IYQQoKYeZiQEMIf5dwQQgghJKxQcEMIIYSQsELBDSGEEELCCgU3hBBCCAkrFNwQQgghJKxQcEMIIYSQsELBDSGEEELCCgU3hBBCeOM4Dhs3bgx0M5qd5cuXIzY2NqBt+Pvvv8FxHA4ePBjQdvBBwQ0hhBCLadOmYdy4cU63FxUVYeTIkf5rkEAcx1m+oqKi0LVrVyxfvjzQzfLahAkT8Ndff/n8PNdccw1mz57tcFtGRgaKiorQqVMnn7fDWxTcEEII4S0lJQUKRWArJjPGYDAYnG5ftmwZioqKcOjQIUyYMAF33HEHtmzZ4tM21dfX+/T4KpUKSUlJPj2HO1KpFCkpKZDJgn9xAwpuCCGE8NZ0WMo8TLF+/XoMGjQIarUaXbt2xe7du63es2vXLlx11VVQqVTIyMjArFmzUFtba9m+cuVK5OTkQKPRICUlBbfddhtKSkos27dv3w6O47Blyxbk5ORAoVBgx44dTtsYGxuLlJQUZGdn44knnkBcXBy+//57y/bKykrcfffdSEpKQnR0NAYPHoxDhw5ZHWPhwoVISkqCRqPBjBkz8Pjjj6Nbt26W7eYerkWLFiEtLQ3t27cHABQUFGDChAlo0aIF4uPjcf311+Pvv/+2upZevXohMjISsbGx6N+/P86ePQsAOHToEAYNGgSNRoPo6Gj07NkT+/btA+B4WOrdd99FdnY25HI5LrvsMnzyySd236sPP/wQN9xwA9RqNdq1a4evvvrK6efmju2wlPn78uOPPyInJwdqtRr9+vXDyZMnrd739ddfo2fPnlAqlWjTpg3mz5/vMjgVAwU3hBDiD4wBtbWB+WLMp5f25JNP4pFHHsHBgwfRvn17TJw40XLzOnLkCIYPH44bb7wRhw8fxtq1a7Fz50488MADlvfX19djwYIFOHToEDZu3Ii8vDxMmzbN7jz/+c9/sGjRIhw/fhxdunRx267GxkasW7cOFy9eREREBABTr8+oUaNQXFyMTZs2Yf/+/ejRoweGDBmCixcvAgBWrVqF//3vf3jhhRewf/9+tGrVCu+++67d8X/88UccP34cW7duxTfffIO6ujoMGjQIUVFR+OWXX7Bz505ERUVhxIgRqK+vh8FgwLhx43D11Vfj8OHD2L17N+6++27LQqiTJk1Cy5Yt8fvvv2P//v14/PHHLe22tWHDBjz00EN4+OGHcfToUdxzzz244447sG3bNqv95s+fj/Hjx+Pw4cO47rrrMGnSJMt1iuXJJ5/EK6+8gn379kEmk+HOO++0bNuyZQtuv/12zJo1C8eOHcP777+P5cuX43//+5+obbDDmpnKykoGgFVWVga6KYSQMKbVatmxY8eYVqs1vVBTw5gpzPD/V00N73ZPnTqVXX/99U63A2AbNmxgjDGWl5fHALAPP/zQsv3PP/9kANjx48cZY4xNnjyZ3X333VbH2LFjB5NIJP9+Njb27t3LALDq6mrGGGPbtm1jANjGjRvdth8AUyqVLDIykkmlUgaAxcXFsVOnTjHGGPvxxx9ZdHQ00+l0Vu/Lzs5m77//PmOMsd69e7P777/fanv//v1Z165dLf+eOnUqS05OZnq93vLaRx99xC677DJmNBotr+n1eqZSqdiWLVtYeXk5A8C2b9/usO0ajYYtX77c4bZly5axmJgYy7/79evH7rrrLqt9brnlFnbddddZfRZPPfWU5d81NTWM4zj23XffOTwHY4xdffXV7KGHHnK4zfz9PnDgAGPs3+/LDz/8YNnn22+/ZQAs39uBAwey559/3uo4n3zyCUtNTXV4DrvfmyaE3L+p54YQQohXmvaipKamAoBlWGn//v1Yvnw5oqKiLF/Dhw+H0WhEXl4eAODAgQO4/vrrkZmZCY1Gg2uuuQYAkJ+fb3WenJwcXu157bXXcPDgQWzduhXdunXDa6+9hrZt21raU1NTg/j4eKs25eXlITc3FwBw8uRJ9OrVy+qYtv8GgM6dO0Mul1v+vX//fpw+fRoajcZy3Li4OOh0OuTm5iIuLg7Tpk3D8OHDMWbMGLzxxhsoKiqyvH/u3LmYMWMGhg4disWLF1va48jx48fRv39/q9f69++P48ePW73W9HsTGRkJjUZjNeQnBnff/+eee87qs77rrrtQVFSEuro6UdvRVPBnBRFCSDhQq4GamsCd24eaDp2Yh1iMRqPlv/fccw9mzZpl975WrVqhtrYWw4YNw7Bhw7By5UokJiYiPz8fw4cPt0vSjYyM5NWelJQUtG3bFm3btsVnn32G7t27IycnB5dffjmMRiNSU1Oxfft2u/c1zWkxX4cZczC0Z9seo9GInj17YtWqVXb7JiYmAjAlO8+aNQubN2/G2rVr8dRTT2Hr1q3o06cPnn32Wdx222349ttv8d133+GZZ57Bp59+ihtuuMHhdTpqo+1rtsNaHMdZvjdicff9nz9/Pm688Ua79ymVSlHb0RQFN4SQsMEYQ5m2HjqDEUqZBAkqud0fe3+dO4qzuRlyHMDz5hxOevTogT///NPSc2LryJEjKCsrw+LFi5GRkQEAliRaMbRt2xY33XQT5s2bhy+//BI9evRAcXExZDIZWrdu7fA9l112Gfbu3YvJkydbXuPTph49emDt2rWWRGVnunfvju7du2PevHno27cvVq9ejT59+gAA2rdvj/bt22POnDmYOHEili1b5jC46dixI3bu3IkpU6ZYXtu1axc6duzotp3+1KNHD5w8edLp999XKLghhDgUyEDBEwXVWhwuqYLW8O9TqUomQZekaKRrVH4/dxTXiFSjbxN5faWystKuUFtcXBxatWol+FiPPfYY+vTpg/vvvx933XUXIiMjLUm4b731Flq1agW5XI633noLM2fOxNGjR7FgwQKRrsTk4YcfRteuXbFv3z4MHToUffv2xbhx4/DCCy/gsssuQ2FhITZt2oRx48YhJycHDz74IO666y7k5OSgX79+WLt2LQ4fPow2bdq4PM+kSZPw0ksv4frrr8dzzz2Hli1bIj8/H+vXr8ejjz6KhoYGfPDBBxg7dizS0tJw8uRJ/PXXX5gyZQq0Wi0effRR3HzzzcjKysL58+fx+++/46abbnJ4rkcffRTjx4+3JEN//fXXWL9+PX744QevP6/S0lK7739KSopHx3r66acxevRoZGRk4JZbboFEIsHhw4dx5MgRLFy40Ou2OkPBDSHETiADBU8UVGuxp7DC7nWtwYg9hRXonQaftdvZuXWNRtT/8+W7znff2L59O7p372712tSpUz0qhtelSxf8/PPPePLJJzFw4EAwxpCdnY0JEyYAMA3XLF++HE888QTefPNN9OjRAy+//DLGjh0rxqUAMOXGDB06FE8//TQ2bdqETZs24cknn8Sdd96J0tJSpKSk4KqrrkJycjIAU5By5swZPPLII9DpdBg/fjymTZuGvXv3ujyPWq3GL7/8gsceeww33ngjqqurkZ6ejiFDhiA6OhparRYnTpzAxx9/jPLycqSmpuKBBx7APffcA4PBgPLyckyZMgUXLlxAQkICbrzxRsyfP9/hucaNG4c33ngDL730EmbNmoWsrCwsW7bMkq/kjdWrV2P16tVWrz3zzDMOZ7C5M3z4cHzzzTd47rnn8OKLLyIiIgIdOnTAjBkzvG6nKxxzNJAYxqqqqhATE4PKykqX3YaENFfObtZmvdNigyrAYYxh85kSq0DMlkomwYg2SaL3PLk6t8zYgPSGKrTKzERCdFRQ93oR96699lqkpKTY1ZIh4tLpdMjLy0NWVpZdTo6Q+zf13BBCLBhjOFxS5XKfwyVVSItSBs3Nukxb7zKwAUw9OGXaeiSqxa2sy+fcRjAYjAwR0uD4vIh7dXV1eO+99zB8+HBIpVKsWbMGP/zwA7Zu3RrophGeKLghhFgEMlDwlM5Ne4Xu54tzN6vu8TDAcRw2bdqEhQsXQq/X47LLLsMXX3yBoUOHBrpphCcKbgghFoEMFDyllPEr18V3P1+cm/psQotKpRIlMZcEDhXxI4RYBDJQ8FSCSg6Vm/ao/pntFYhzS8BBJqHwhhB/CuhfqF9++QVjxoxBWlqa1WJszqxfvx7XXnstEhMTER0djb59+/p8pVdCmpNABgqe4jgOXZJcJxd2SYr2SY6Q63ObzqeQSoImP4mQYCfWHKeABje1tbXo2rUr3n77bV77//LLL7j22msti50NGjQIY8aMwYEDB3zcUkKah0AGCt5I16jQOy3WLjBTySQ+n93l7NxymRRyKQfW6NvVjwkJJ+aq1FKp1KvjBM1UcI7jsGHDBowbN07Q+6644gpMmDABTz/9NK/9aSo4Ie6FWp0bs2CqUByvjMC5c+fQ0NCAtLQ0SCTBM5RHSDAyGo0oLCxEREQEWrVqZfe722ymghuNRlRXVyMuLi7QTSEkrKRrVEiLUoZUhWLA9JAUqFlcjs6dmpqKvLw8nD17NiBtIiTUSCQSh4GNUCEd3Lzyyiuora3F+PHjne6j1+uh1+st/66qcl3DgxBiEshAIVzI5XK0a9fObgFIQohjcrlclF7OkA1u1qxZg2effRZffvklkpKSnO63aNEip+WrCSHE1yQSiU9XPyaE2AvJQeC1a9di+vTpWLdunduiSvPmzUNlZaXl69y5c35qJSGEEEICIeR6btasWYM777wTa9aswahRo9zur1AooFBQ1zohhBDSXAQ0uKmpqcHp06ct/87Ly8PBgwcRFxeHVq1aYd68eSgoKMCKFSsAmAKbKVOm4I033kCfPn1QXFwMwFRNMiYmJiDXQAghhJDgEtBhqX379qF79+7o3r07AGDu3Lno3r27ZVp3UVER8vPzLfu///77MBgMuP/++5Gammr5euihhwLSfkIIIYQEn6Cpc+MvVOeGEEIICT1C7t8hmVBMCCGEEOIMBTeEEEIICSsU3BBCCCEkrFBwQwghhJCwQsENIYQQQsIKBTeEEEIICSshV6GYEOJbjLGQWw2cEEKaouCGEGJRUK3F4ZIqaA1Gy2sqmQRdkqKRrlEFsGWEEMIfDUsRQgCYAps9hRVWgQ0AaA1G7CmsQEG1NkAtI4QQYSi4IYSAMYbDJVUu9zlcUoVmVtCcEBKiKLghhKBMW2/XY2NLazCiTFvvpxYRQojnKLghhEDnJrARuh8hhAQSBTeEEChl/P4U8N2PEEICif5SEUKQoJJD5SZwUf0zLZwQQoIdBTeEEHAchy5J0S736ZIUTfVuCCEhgYIbQggAIF2jQu+0WLseHJVMgt5psVTnhhASMqiIHyHEIl2jQlqUkioUE0JCGgU3hBArHMchUa0IdDMIIcRjNCxFCCGEkLBCwQ0hhBBCwgoFN4QQQggJKxTcEEIIISSsUEIxIcQhxhjNmiKEhCQKbgghdgqqtThcUmW1mKZKJkGXpGiqd0MICXo0LEUIsVJQrcWewgq7VcK1BiP2FFagoFoboJYRQgg/FNwQQiwYYzhcUuVyn8MlVWCM+alFhBAiHAU3hBCLMm29XY+NLa3BiDJtvZ9aRAghwlFwQwix0LkJbITuRwghgUDBDSHEQinj9yeB736EEBII9BeKEGKRoJLbrQpuS/XPtHBCCAlWFNwQQiw4jkOXpGiX+3RJiqZ6N4SQoEbBDSHESrpGhd5psXY9OCqZBL3TYqnODSEk6FERP0KInXSNCmlRSqpQTAgJSRTcEEIc4jgOiWpFoJtBCCGC0bAUIYQQQsIKBTeEEEIICSsU3BBCCCEkrFBwQwghhJCwQsENIYQQQsIKBTeEEEIICSsU3BBCCCEkrFBwQwghhJCwQsENIYQQQsIKVSgmhAQ1xhgtA0FIiAiW31cKbgghQaugWovDJVXQGoyW11QyCbokRdMCnoQEmWD6faVhKUJIUCqo1mJPYYXVH0oA0BqM2FNYgYJqbYBaRgixFWy/rxTcEEKCDmMMh0uqXO5zuKQKjDE/tYgQ4kww/r5ScEMICTpl2nq7J0BbWoMRZdp6P7WIEOJMMP6+BjS4+eWXXzBmzBikpaWB4zhs3LjR7Xt+/vln9OzZE0qlEm3atMF7773n+4YSQvxK5+YPpdD9CCG+E4y/rwENbmpra9G1a1e8/fbbvPbPy8vDddddh4EDB+LAgQN44oknMGvWLHzxxRc+bikhxJ+UMn5/mvjuRwjxnWD8fQ3obKmRI0di5MiRvPd/77330KpVK7z++usAgI4dO2Lfvn14+eWXcdNNN/molYQQf0tQyaGSSVx2dav+mWZKCAmsYPx9DanHnt27d2PYsGFWrw0fPhz79u1DQ0ODw/fo9XpUVVVZfRFC/IsxhtI6Pc5VaVFap3ebWMhxHLokRbvcp0tSNNW7ISQIBOPva0jVuSkuLkZycrLVa8nJyTAYDCgrK0NqaqrdexYtWoT58+f7q4mEEBue1r5I16jQOw1BUzeDEOJcsP2+hlRwA8Au8jM/ATqLCOfNm4e5c+da/l1VVYWMjAzfNZA0O8FSkTMYmWtf2DLXvuidBrcBTlqUkj5f4hH63fSvYPp9DangJiUlBcXFxVavlZSUQCaTIT4+3uF7FAoFFAqFP5pHmqFgqsgZbPjWvkiLUrr848dxHBLV9DtMhKHfzcDgGhqQuOFzYMAAIDMzYO0IqZybvn37YuvWrVavff/998jJyUFERESAWkWaq2CryBlsgrH2BWke6HczAP7+G0hNBRQK4PbbgfHjA9qcgAY3NTU1OHjwIA4ePAjANNX74MGDyM/PB2AaUpoyZYpl/5kzZ+Ls2bOYO3cujh8/jqVLl+Kjjz7CI488Eojmk2ZMSEVOocm04SIYa1+Q8BeM1XLD2pdfAhwHZGUBTUdWxo0LWJOAAA9L7du3D4MGDbL825wbM3XqVCxfvhxFRUWWQAcAsrKysGnTJsyZMwfvvPMO0tLS8Oabb9I0cOJ3fHslTpTX4O/KumbZNR6MtS9I+BPSY0jDnR5qbATmzgXefNN+W58+wLffAnFx/m9XExxrZuFrVVUVYmJiUFlZieho11PXCHHmXJUWvxdVeHWM3mmxYR3gMMaw+UyJ29oXI9okUZInEQ3f380rU2ORER2+v38+ceECMHQocPSo/banngKee87Ui+MjQu7f9MhEiAfE6G0I967xYKx9QcIf9Rj6wLZtpqAlJcU+sPn+e4AxYMECnwY2QtF3lxAPmCtyeqM5JNOaal/E2n1WKpkk7HuuSGDw+d2k6tY8MPZvT8zgwdbb2rcHCgpM+1x7bWDa50ZITQUnJFiYeyUc1XARojkk0wZT7QsS/vj8blKPoQsVFcD11wO//GK/7b77gDfeAGTBHzoEfwsJCVKuKnK2jlHheHmt22M0l65xqlVD/CnYquWGhP37gZwcx9s++wy4+Wb/tsdLFNwQ4gVnvRIA8HelNqgWkiOkOaEeQ57eeQd44AH71xMSgN9+A7Kz/d8mEVBwQ4iXnPVKUNc4IYFFPYZO1NUBkyYBGzfab7v1VmDZMkCp9HuzxETBDSE+Ql3jhJCgcuIEcOWVQE2N/bb/+z9gxgz/t8lHKLghxIeoa5wQEnCrV5t6amxJJMCBA0CXLv5vk49RcEOIj4nRNU6rGxNCBGloAGbOBJYutd82bBjw+eeARuP/dvkJBTeEBDla3ZgQwlt+PjBwoOm/tl58EXjkkaAqtucrFNwQ3qj3wP/MqxvbMq9u3DsNAQ9w6OeCkCDw7bfA6NGOt+3cCfTv79/2BBgFN4QX6j3wP76rG6dFKQMWTNDPBSEBZDQCjz0GvPyy/bacHOC770xTupuh5lFBjHjF3HtgW7PF3HtQUK0NUMvCm5DVjQOBfi4ICZDSUqB7d0AqtQ9sHn/ctGr3778328AGoJ4b4kYo9B6EK75LMwRiCQf6uSAkAHbsAK66yvG2774DRozwb3uCGPXcEJeCvfcgFDDGUFqnx7kqLUrr9LxXAg/m1Y3p54IQP2EMWLTIlARsG9i0aQOcO2fahwIbK9RzQ1wK5t6DUOBNTop5dWNXQYRcwgVkCQf6uSDEx6qqgBtuAH76yX7b3XcDb78NRET4v10hgnpuiEvB3HsQ7LzNSTGvbuxKvZGhsEbndVuFop8LQnzk4EFTL01MjH1gs2aNqZfm/fcpsHGD/vIQl8y9B67QApD2+OakuBuiSotSQi5xnbPC5zhio58LQkT2/vumoKZ7d+vXY2OBkydNQc2ttwakaaGIghviEp/eA1oA0p5YOSll2nrUG10HLoHIbfH1z4WneUqEhBSdDrjlFlNQM3Om9babbzYtcHnpEtC+fWDaF8Io54a4RQtACidWTkow57b46ueCaueQsHfqFNCrF1BRYb/tnXeA++7ze5PCDQU3hJdgWgAyFCriipWTEuy5LWL/XIRCRWZCPLZuHTBhguNt+/cDPXr4tz1hjIIbwpsYC0B6K1Se6vnMdHKVk2IO4LQNjZBLOJdDU4HObRHr54Jq55CwZDAA998PfPCB/bbBg4H1603Jw0RUFNyQkBFKT/XmnBRH7TVzlpPiKIBzJVxynoTkKQU6yCbErYICU12aM2fsty1cCDzxRLNYwDJQKLghISEUn+o9yUlxFsA5Eow9Vt4I5vwiQnjbssV5Qb3t24Grr/Zrc5orCm5I0HGUUxOqT/VCclL4BHAKKYfOidFQRUiDMtfIG8GeX0SIU0Yj8OSTwOLF9tu6dAG2bgWSkvzfrmaMghsSVJzl1KRFKXm9Pxif6vnmpPAJ4PSNDKoIaVAFcGLxNk+JEL8rLzf10uzbZ79t7lzgxRdNi1sSv6PghgQNVzk1uRV1vI4Ryk/1/hqWCdbZZt7kKRHiV7t3A/36Od721VfAmDH+bQ+xQ8ENCQp8hmTcCfWnen8My/h7tpnQQIpqKpGgxRjw8svAf/5jv61lS2DnTiAz0//tIg5RcEOCAp8hGXdC/ane18My/p5t5mkgFUw1lQhBTQ1w003A99/bb5s2zbRsgjx0H6rCFQU3JCh4O9TSroU65J/qfTks4+/ZZt4GUnzzlIJ1iI2EgSNHTEX1DAb7bStWAJMn+79NhDcKbkhQ8DZX5ny1Dp0SQ7vnBnA+LCOXcMiIVkEulYAxJvg6/TnbzF+BVKgUdCQhZulSYPp0+9fValPicMeO/m8TESx0sy9JWOGzyrQrgVg80lfSNSqMaJOEgRlxyI5VQy41VSjOrajDjnMXsflMCQqqtYKO6c8aMmItGuqKuWfI9jzmniGhnw9p5vR6YNIkU1E928Bm7Figttb0RYFNyKDghgQFPqtMuxOM08A9xXEc6htNs8TqG62XXvDkBu7PGjK+DqT49gzRSuLErTNngORkQKkEVq+23vbGG6Yk4i+/NPXakJBCwQ0JGqYhmVi7Hhy5hN/QRbBOA2eMobROj3NVWpTW6XnddMW+gfPpGRNrtpmvAyl/9AyRMLd+vamXJjsbKCmx3rZ3rymomTUrMG0joqCcGxJUHM2UiVdGYEteacCKu3mTtOppXohYOTJN2946RoXj5bVO922arOzNNccrI3y62Cct00A80tgIzJ4NvP22/bYBA0z1aVq08HuziG9QcEOCjqOZMoEq7uZN0qo3M4bEuIE7aru5F6xp4GF7Pd5e8+GSKpeBDeDd94tvj09NvYNZLqT5KSoyrb594oT9tmefBZ5+mhawDEMU3JCQEIjibt4EJ97OGPJ2aMdZ281BR8f4KETJZXa9Mt5cM59FP8X4fvGpBwQAx8trEK2Q0cyp5urHH4GhQ51vGzzYv+0hfkXBDQkZ/izu5m1w4u2wkjcF/fi0/e/KOoxok2TVdm+ume+in8OzEiGReJcbxacekFmwrRRPfIwxU2/Mc8/Zb7v8clNQk5Li92YR/wvODExCnDAPWWVEq5CoVji9aXmSxNuUt0mr3g4r8Zk95mxox9O2e3PNfBf9LNc1uNyHr3SNCh3jI93uR4nFzcSlS0D//oBEYh/YzJplKsT3558U2DQj1HNDwo4Yxd28DU7EmDHk6VCcp2335poDkeQbJY/w+zlJkNm7F+jd2/G29euBG27wb3tI0KDghoQVsdZP8jY4EWudKE+G4jxtuzfX7M86OoE8JwkSb7xhmvlkKyUF2LULyMrye5NIcKHfehI2xKwN421dGG+GlRwdi89QnJmnbffmmv1ZRyeQ5yQBVFsLjBljmtlkG9jcfjug05lmRlFgQ0DBDQkjYhZ3Eys4cVSAUC7h0Dst1mezeDxtuzfXLGYwx1cgzkkC4NgxQKUCoqKAb76x3rZ0qSmJ+JNPAIV3a6KR8ELBDQkbYud9OKuYrJJJ3AYn5uExR/Ve3NWA8VTTJGq5VIJeqTGC2+7NNXvzXk8F4pzET1asMPXSXHGFqVfGTC43rdjNGHDHHYFrHwlqlHNDwoYvcjA8yXnx16rYTTlLou6UoIGu0YjahkZERkiRHat2OxXbmyn3/pyuH8hzEh+prwfuussU2NgaORJYt87Ug0OIGxTckLAhVhKvLUcVk10Ra+kEvlwlUf9eXGn12ulLtbxmjXEchwSV3BIwlGnreQcMQj8vMQTinEREZ88C/foBhYX22155BZgzh6oIE0EouCFhg09xN3/kYPhzWjSfXqKm+M4aE2M6PSFuffUVcP31jrft3g306ePf9pCwEfCcmyVLliArKwtKpRI9e/bEjh07XO6/atUqdO3aFWq1GqmpqbjjjjtQXl7up9aSYBcMORj+nKLMp5fIEVezxsw9QbbHNQdGBdVaj9rqT94WcSQ+1Nj4b0+MbWDTqxdQVmbKp6HAhnghoD03a9euxezZs7FkyRL0798f77//PkaOHIljx46hVatWdvvv3LkTU6ZMwWuvvYYxY8agoKAAM2fOxIwZM7Bhw4YAXAEJRoHOweAzPCaXcKJMUfa098fZsFgg8oXERr1OQerCBdNaT0eP2m978klTZWEvl+YgxCygP0mvvvoqpk+fjhkzZqBjx454/fXXkZGRgXfffdfh/r/99htat26NWbNmISsrCwMGDMA999yDffv2+bnlJNgJrQ0j9rndTVGuNzIU1uhc7sOHN70/jgIjMafTB0I49DqFnZ9/NvXSpKTYBzZbtph6aRYupMCGiCpgP0319fXYv38/hg0bZvX6sGHDsGvXLofv6devH86fP49NmzaBMYYLFy7g888/x6hRo5yeR6/Xo6qqyuqLEF9Li1I6rHHTFN+CgoDzYRY+heyccRQYBWIZBbGIWcSReIkxYMECU1BzzTXW29q2Bc6fN+1j8/efELF4FdycPn0aW7ZsgVZrehoS8kejrKwMjY2NSE5Otno9OTkZxcXFDt/Tr18/rFq1ChMmTIBcLkdKSgpiY2Px1ltvOT3PokWLEBMTY/nKyMjg3UZCPFWmrXdbz8ZRD4ijIKagWovNZ0qw49xF/F5UgR3nLmLzmRIUVGt59RI54mzWWCgvaRDqvU5hobLSFMxIJMDTT1tvmzkTaGgATp0C0tMD0jzSfHj0F6q8vBxDhw5F+/btcd1116GoqAgAMGPGDDz88MOCjmU7XMAYczqEcOzYMcyaNQtPP/009u/fj82bNyMvLw8zZ850evx58+ahsrLS8nXu3DlB7SPEE9qGRsH7OQpivj19we0wi7MkaleczRoL5SUNQrnXKeTt32/qpYmNNQ1DNbV2ramX5t13ARlN0CX+4VFwM2fOHMhkMuTn50OtVltenzBhAjZv3szrGAkJCZBKpXa9NCUlJXa9OWaLFi1C//798eijj6JLly4YPnw4lixZgqVLl1oCLFsKhQLR0dFWX4T4mr6R3w3UvJ+zXBF3vT/mYZZ0jQoj2iRhYEYcrkyNxcCMOPRKFT5rLJSXNAjlXqeQ9c47pqAmJ8f69bg4Uw8NY8D48YFpG2nWPAqjv//+e2zZsgUtW7a0er1du3Y4e/Ysr2PI5XL07NkTW7duxQ1NlqXfunUrrndS96Curg4ym8hfKpUCEDYkRoivKaT8bqAKqURwrZqmms56clTILl0jfNaYqScIITfjyFdFHIkNrRaYNAlwNEN1/Hjg448BpdL/7SKkCY+Cm9raWqseG7OysjIoBCxeNnfuXEyePBk5OTno27cvPvjgA+Tn51uGmebNm4eCggKs+KcU95gxY3DXXXfh3XffxfDhw1FUVITZs2ejV69eSEtL8+RSCPEJVYSU936e1qoxczXM4mnl3kBPp/dEsBRxDFsnTwJXXglUV9tve/994O67/d8mQpzwKLi56qqrsGLFCixYsACA6Y+K0WjESy+9hEGDBvE+zoQJE1BeXo7nnnsORUVF6NSpEzZt2oTMzEwAQFFREfLz8y37T5s2DdXV1Xj77bfx8MMPIzY2FoMHD8YLL7zgyWUQ4jNCehHOV3s3JdxXwyyhuKRBqPY6BbU1a4DbbnO87eBBoGtXvzaHED445sF4zrFjx3DNNdegZ8+e+OmnnzB27Fj8+eefuHjxIn799VdkZ2f7oq2iqKqqQkxMDCorKyn/hvjUkZJKnLpU53R7uxZqdE6KQWmdHjvOXfToHCqZBCPaJFFvhA3GWEj1OgWdhgbg3nuBjz6y3zZ0KPDFFwD9/SR+JuT+7dEj3+WXX47Dhw+jV69euPbaa1FbW4sbb7wRBw4cCOrAhhB/YYy57ZE5X60DY8yrWjU0zOJYIIs4hrRz54DWrQG53D6wWbwYMBqBrVspsCFBz+N5eSkpKZg/f76YbSEkbAhdGdxdrogjHeMjaZiFiOO774DrrnO8bccOYMAA/7aHEC959Li4bNkyfPbZZ3avf/bZZ/j444+9bhQhoU5ozRVPatVEySM8ahshAEy9MI89ZprKbRvY9OgBlJSYpnJTYENCkEfBzeLFi5GQkGD3elJSEp5//nmvG0VIqPOk5oq5Vk3nRA2v9yqkNNRCPFBWBvTsCUilwIsvWm/7z39Mq3bv3w8kJgamfYSIwKNhqbNnzyIrK8vu9czMTKvZTYQIFS6JoHEKfr9atvtxHIe2LSJx+lKt22GtfcWV6JrEaGiK8PPrr857Yb791vmwFCEhyKPgJikpCYcPH0br1q2tXj906BDi4+PFaBdphgqqtWEzhTe3wvksKdv92sebemqaBnatY1Q4Xl7r8r26f5Zg6J0GUT8fo9GI3Io61DY0IjJCiuxYNSS0YnNoYszUO/P44/bbWrcGfvkFoPX2SBjyKLi59dZbMWvWLGg0Glx11VUAgJ9//hkPPfQQbr31VlEbSJoH8/IDtrQ+uoH7Wrm2QdB+jgI7uYQDA0ODm/SdwyVVSItSitLD5Wj6+pHSasu0dRIiqquBG28EfvjBftuMGcCSJUAE5WyR8OVRcLNw4UKcPXsWQ4YMsSyHYDQaMWXKFMq5IYLxWX5AzBu4+Zy+HP6SSfgdSybhnAZ27taVMms668obrurymF+nACfIHToEdOvmeNvq1cDEiXYvh8tQMCFNeRTcyOVyrF27FgsWLMChQ4egUqnQuXNnS2VhQoQQOm3aW/4Y/moVrcI5HpWHMzRKHPBwXammvF3p2mg0uiw4CJgCnCsSNDREFYz+7/8cL3+g0QC//w5cdpnDt4XTUDAhTXm1/nz79u3Rvn17sdpCmimh06a94a/hr6RIfkGYRMJ5ta6UmbdLMAjJEWoXF+XVuYhIdDpg6lRg3Tr7bTfcAKxcCThYA9As3IaCCWmKd3Azd+5cLFiwAJGRkZg7d67LfV999VWvG0aaD0+mTXvCn8NfRiO/gKWu3uDVeQBxVrqubWgUdb9AC+uhltOngd69gYsOlux45x3gvvvcHiIQQ8GE+BPv4ObAgQNoaDAlP/7xxx9Of+DpF4EIJWSRSW/4c/jrcKmDlZMdKKzVe3UeQJwlGCJ5rmIeGSEN+sAhbIdaPvsMGD/e8bZ9+0y1a3jy91AwIf7GO7jZtm2b5f+3b9/ui7aQZorjOLfLD4hxA/fn8BffHo5GI+MV2HVO1OBIabXPbtjZsWoc4RGQqWQSbD5TErSBQ9gNtRgMwIMPAu+9Z7/t6quBL78EYoQnefvzd4GQQBCcc2MwGKBUKnHw4EF06tTJF20izZBp+QH49InbX8NfAP+ekCi5DG1aRLoN7NI1KqRrVD7rMZFIJGjXQu0yqTg1Uo69RZV2rwdL4BBWQy2Fhabg5fRp+20LFgBPPmlaNsFD/vxdICQQBAc3MpkMmZmZaGwMjbF3EjrSNSqkRSl9dgP31/AXAHROiEJepZbXfjKZjFdgZ17p2lfM07wdBThtY1UoqHE9hBbowCEshlq+/x4YPtzxtm3bgGuuEeU0/vxdICQQPJot9dRTT2HevHlYuXIl4uLixG4TacZ8eQP31/AXAFyq5xf8X9QbwNU3wsiAnimm4ELfyAKWy9I5KQZXJGjsKhSX6xpwusJ1sBbIwIExhhKe+UtBN9RiNAL//S/gqEZY587A1q1AcrKop/Tn7wIhgeBRcPPmm2/i9OnTSEtLQ2ZmJiIjI622//HHH6I0jhCx+WP4CwC0PHNu9hRWoKFJsT6llENWrBpABMq09QEJcCQSid1072DO0XCUQOxK0Ay1XLwIjBwJ7N1rv232bODll02LW/qIv34XCAkEj4KbcePGgeM4MMavgiohwcTXw18AoG/kd6NtsKlCrGtkVmtKBcuNJlhzNJwlEDsTFEMtv/0G9O3reNuXXwJjx4p6Olez2/zxu0BIIAgKburq6vDoo49i48aNaGhowJAhQ/DWW28hISHBV+0jxCd8nb+ikIpzkw+WZN1gzNHgk0BsK2BDLYwBr74KPPKI/ba0NNOK3TYLEYuBz7R4X/8uEBIIgv4CP/PMM1i+fDlGjRqFiRMn4ocffsC9997rq7YRErJUPGdL8XW4pCqgPaXmHA1X/B048EkgNlPJJOidFuv/ALGmxjT0JJHYBzZTpgB6PVBQ4LPAZk9hhd1nZA6YC6rdJ7yHOsYYSuv0OFelRWmdnkYbmhFBPTfr16/HRx99ZFn5e9KkSejfvz8aGxsh9eHYMCGhhk9PhxDBMMvHnKNx6EIldI3WeUJdk2P8Hjjwze+5LC4Slydo/Ntj8+efQPfuQIOD1eE//tgU2PhQWE2L91DYFnMkvAjquTl37hwGDhxo+XevXr0gk8lQWFgoesMICWV8ejqECppZPrY3wwDdHPnm9yRFKvx3A1+2zPR5dOpkHdgolaaAhzGfBzaAsGnx4Yh6rYig4KaxsRFyufWYukwmg8Hg/fo4hISbdI0K7Vo4XrhQ5sG9NtCzfMw3DNsgSxegG0a8MkLU/Tym1wOTJpmCmjvvtN42erRpaEqrBS6/3LftaCKYZ7f5Gt9eKxqiCm+ChqUYY5g2bRoUin+7xnU6HWbOnGk1HXz9+vXitZCEjWBfk0hsBdVapxV/DQL/rgZ6lk8wDnOU6xwM+TjZzyfDeXl5QL9+QHGx/bbXXwceekj8c/IUyNltgf49D4tijsRrgoKbqVOn2r12++23i9YYEr6a2/g3n2BALuEg4WCVv+JMoAuqBeMNI2C9Exs3Ajfc4Hjbnj1Ar17ins8DgZrdFgy/582514r8S1Bws2zZMl+1g4SxYFjM0N9Pk3yCgXojw4CWLcBxHHQGI2rqDcirrLP6oxssAWAw3jD82jvR2AjMnQu8+ab9tn79gG++AVq08P48IglEBeJg+D0HgrcmE/Evj4r4EcJXMAxnBOJpku9NXt/IkBGttPy7Q3yU0yAskN39wXjD8EvvRHExMGQIcOyY/bannwaefTZgCdXu+LMCcTD8npsFY00m4n8U3BCfEjKckaCSi37z9uXTpKtgQyHl127b/cwF1czHPlelhb7RiLoGA85V6VDfpKKxP3t1gvGG4dPeiZ9+MgU1jmzdCgwdKvyYAeCvCsTBNGxJ62YRgIIb4mN8ezAKq3XYV1Qh6hOmL58mfdkbxHetJH929wfrDUPU3gnGgOeeM/XG2OrQwRTwpKZ632iXTRC/d84fFYiDbdiS1s0iFNwQn+I7TJFbYT+ryNubt6+eJvn0Bhl5zobS2yQTC10rCfBfd3+w3jC87p2oqADGjAF27rTf9sADwGuvATLf/6kMhmRcTwXjsCWtm9W8UXBDfCpBJUeEBGjw4oHN05u3L54m+fYG9UjmV8Cv6bCUJ2slAf6dpRSsNwyPeif27QOuvNLxti++AG680fuG8RQsybieCsZhS4DWzWrOKF2ciMbZOi4M3t34PK2k6ounSb69QRU8a7A0LSQmZK0ku3M2NHr0Pk+YbxgZ0Sokqj2r/hvQNX/eesuUBGwb2CQlAbm5puEpPwY24VB0LhjXHiPNG/XcEFE461JvHaOGge8YjQuejNX74mmSbzvKtfyCm9K6eiRHqQQd25HDpVWQSjivn+79MSMrIMMvdXXAxInAV1/Zb7vtNmDpUkARmCf8YErG9UawDluS5omCG+I1V13qx8trRDmHJ2P1vkiC5duORp5P2U1vAt7kI9Q3Mq+HL/wRdHgy/OJVwHX8OJCTYwpubH34ITB9ugdXIa5gS8b1RrAOW5Lmh4Ib4hVP80SE8Gas3rS+U73DZRDatVALvmnz7Q1qoZSjlEfvjTpCKujY7hy6UOnx7C9f53x4MnvN44Br5Upg8mT716VS4I8/gC5dPLoGXwjGZFxvUJ4LCQah8dtCgpY3eSJ8Celdsc3lOF9V53R9p1OX6gQv9sg3tyApkl8wlqj+dz8xVhLXNTKcENhb5q+cD6ErVQte2bm+HrjjDlM+jW1gM2IEUF0NGAxBFdgA/wa1rlDROUKEoZ4b4hUxusrToxRoGa3yekiEb32YpjyZicUnt4AxBrmEsyq6Z0susX/CdXZsIY6X1yBaIeP9ufkr50PI8IugXp78fGDAAOD8efudXnoJePjhoK0iDARvDSFCQhkFN8QrYnSVp2lUXo/Ve1IfBvD8pu2uvRzHoXtKjMs2dU+JcXh9TY+tbWiEvtEIhVQCfaMRR0qrebVPSNDmr5wPIcMvfAKumK1bwN1rv5gvAODXX01rPoUISsYlRFwU3BCviJEnojM0gjHm8Vi9t3k/nt603bXXfMM6eKHSqlifUsqha3KM0xtW0wRaVYQUGdEqcBwHo9GIk+U1LnuDzNwFbU3PoTPwm0bubSArZPba+Wqd4x2MRnR+cQHaLX/fftuVVwLffQfEx5uur04fUkmtlIxLiHgouCFe4dOl7s6R0mqcvlTr8ROqt3k//k7UdBWaOEugbalR4ny1jldgY+YsaPNk+E6MnA8hwy+23xNFeRn633krYk86WMBy3jxg4UJAYnpPKFf6pWRcQsRBCcXEa6Yeili7pEiVTIJ2LdS8juE0SZQHb4ZL+Ny0PS04Zx4qs11iQf/PtG3ba3WVQHvqUp3gAM5R0ObsHO6IlfPh6meld1qsJfgw9/LE//4bbuyQhlH9u9gFNr9/tBrMaASef94qsBGUhEwICUvUc0NE4apLPU4l591T4EmCrzc9L+5u2gXVWhwsroC+SdMVEqBbSqzLXgDGGA4UV7o894Hif6dtiz2l3lHQ5sk5fNHj4Xb4hTFwzz+PkU89ZffemoxM7PjkC2hT0tA7Ldbqe+fLhVIJIaGFghsiGmdd6uab2elLtW4TYp3lirgq5OZJ3g+fm7azJGW9EW5rv5TW6d0OIdUbTT1CSZFK0afUOwra+J6jc6IGSpnUpzkfDn9WKiuBceOA7dvt9j8zYTIO/fd/YDKZqZfHwfcuXCr9EkK8R8ENsfBl6X1THoXU/Y6wH2Zyl0PBJ5ejV2osFDIJ72tjjOF3N3lE+4sqnPYClNbxWwurtK4eSZFKFDpLoBXIVdDGd/hOKTMlMfvNgQNAjx6Ot336Kdj48dBo65Hj5nsXTpV+CSHeoeCGAPBPEibf4SOdoRHnqrRQyiTQGxqxt8h+eMe2cq7YU2mPl1XB3S3QwICSWp1lbShPMcZwzstckAyNEq1j1S6DtqCrhPvuu8B999m9bIyNBffbb+AuuwwAwAG8elp8dX3+WG+LECIuCm6IX0rvA/yHj/jWcgGscyjEmkrLGHNa1dhWfpXj4CZRLcfJi7Vu35+olqNMW4/6Ru+q/yZHKtwGAL5YSFQwrRa4/XZg/Xq7TeeHj8a+F96AUakyBaXVWkE/d764vlCeeUVIc0bBTTPnzyRMMaaN27LNoRBjKm2Zth58Yw1nK54nqhW8KxQ7rekiQE29AaX/1HVRSE3fJ30jswrw/F0Jt2mPR2ReLlpc1R9clf3P2oFnFyPv1ilWr3kSWIt9ff4K+gkh4gv4VPAlS5YgKysLSqUSPXv2xI4dO1zur9fr8eSTTyIzMxMKhQLZ2dlYunSpn1obfoSu9+MtZ1OBvSF2DoWQ48WrIhy+bq5Q7Iq5QrEYw0AnLtZix7mL+L2oAjvPX8LO85fwe1EFdpy7iM1nSixToPlOxfZWQbUWm8+UIO+9pciIUSOuW2e7wIbt34/vThfbBTZNCV3TSqzr89d6W4QQ3whoz83atWsxe/ZsLFmyBP3798f777+PkSNH4tixY2jVqpXD94wfPx4XLlzARx99hLZt26KkpAQGg8HPLQ8fYiRhCs1JsB0+0hkaBQ1F2RI7R0TI8dq2iHS6zZwHdKikyurzsx3WiFdGuO3l8Ya5p6FjfAM6xGssn39pnd6S+JygigDHcZZcJ2/ySgouVqF+5n0Y+dkqu20lfQbA8PnnSEtPRlmdHtpzF922XejsJjGGJ2nmFWlOwjGvLKDBzauvvorp06djxowZAIDXX38dW7ZswbvvvotFixbZ7b9582b8/PPPOHPmDOLi4gAArVu39meTw463SZie5iQ0HT46V+V5Mq0vckT45ga1jVVBInH9+bm70Zo/P18FNk0dL69FXqUWXf9Zebzp9+2kzb4e5ZWcPw82cCDS//7bbtPROfPw190PABwHlQ5IZcyns5u8HZ6kmVekuQjXvLKADUvV19dj//79GDZsmNXrw4YNw65duxy+56uvvkJOTg5efPFFpKeno3379njkkUeg1Tq/Oer1elRVVVl9kX+Zb+SuOAsgxKoGK2YRPk+rCTdlzt1wJTVSji7JsbyPl6hWICNahUS1wiqw8aRasDd0/3xv3J1X0Pdw82bTqtsZGeBsApufP1mP9ScK8dc9D1pW5jb3eATd7C0PzhmItvmCGL83JPSEc0XvgPXclJWVobGxEcnJyVavJycno7i42OF7zpw5g507d0KpVGLDhg0oKyvDfffdh4sXLzrNu1m0aBHmz58vevvDhadJmGImIotVhE/MJxDzkNKB4kq7XhW5hEOrGH7LSjgjdkViX3H6PTQagSeeAF54we49FR2vwM6PPkV9XLzT4+oMRrTUKAM/e8uJoJhZ5ifh+uROXAv3it4Bf+xwdNN09kEajUZwHIdVq1ahV69euO666/Dqq69i+fLlTntv5s2bh8rKSsvXuXPnRL+GUOdJEqaYich8ekp6pcZiYEYcrvznvyPaJNkFNp48gbh7YnU0XFRvdLw2lJCnX7ErEvuK3fewrAzIyQGkUvvA5pFHUFpdh582bHUZ2ACmHg8+33cxZ28JEcxtE1M4P7kT1/w9mcTfAtZzk5CQAKlUatdLU1JSYtebY5aamor09HTExPw7C6Vjx45gjOH8+fNo166d3XsUCgUUCkr4c0doEqbYOQneFOHz9AnE1RNrWpRS0DHdHcv2cw2lXA2dwQjs2gX07+94h6+/BkaPBgAkMCaox0Ps4otiCua2iSHcn9yJa+GeVxaw4EYul6Nnz57YunUrbrjhBsvrW7duxfXXX+/wPf3798dnn32GmpoaREVFAQD++usvSCQStGzZ0i/tDmdCkjB9kZMgNMAyZ/iX1Op5PYGcvlRrWTPJXeXjDnGRvJ9q6huNLuuh2M6EUskkaO3lsJZZhkaJcyIt3WCHMbT7aAkyXv6f/bZWrYAdO0z/bcKTYU6xii/6QjC3zVs0I6x5C/e8soDOlpo7dy4mT56MnJwc9O3bFx988AHy8/Mxc+ZMAKYhpYKCAqxYsQIAcNttt2HBggW44447MH/+fJSVleHRRx/FnXfeCZUqtJ+iQo2vchL4BliOekrcETLd/ASP6sIAoG1oxJ9lro9rO7SlNRhxvLxGlOnfPVNikKZRCv4sXJHV1KD3Q3ch+def7TfeeSfw3ntAhOP6PoBnPR5iFF/0lWBumzfC/cmduBbueWUBDW4mTJiA8vJyPPfccygqKkKnTp2wadMmZGZmAgCKioqQn59v2T8qKgpbt27Fgw8+iJycHMTHx2P8+PFYuHBhoC6h2fJ3tdumnFWODQR9ozFguTMtFDKU6xqQFqW06l3QNhhwtKxG8PEUZaXI+nQFLn/7FfuNK1cCkybxPlY493iEi3B/cieuBfJvuD9wrJnN+auqqkJMTAwqKysRHe06YZC45++ZFowxbD5TEhTJuCqZBJfHR2H/Bc9nPXWMj0ReRR10XqwtZft5n6vS4veiCt7vj/3zMLJXfISWm76EtOHf5EFjVBQkv/8OdOjgcdtI8OLzu6SSSTCiTVLI3uCIe6E0W07I/ZvWliJe8fcTejDNMuqSFI26hkavjhElj0BOaix2nr/k8TFs1zri86TNGQxI/WEz2n7yIRL277W8rs+5EvoHHoTm1vGQUCK+3xmNRuRW1KG2oRGREVJkx6rdFor0VLg/uRN+wrWXlYIb4jVf5SQ4KgkeLOP/7Vqoka5RIb+S3+rhzihlEtGuyTyzxdVYekTFJbT+fDWyVy+HurAAAGCUyaC74SaoH54DRe/eoJAmMI6UVNqtRn+ktBrtWqjROcn1OmWeCvcZYYSfcMwro+CG2BGyzoiv1iRx1lUq1iwjb526VIc4lRyqCKnHx5BLOMQrI1CuaxClTeaZLQkqOVrHqHC8/N+kaM3pv5D9yUdo9eVnkOlMs6sa4xOgmzED6gcfgDo9XZQ2EM84CmzMzK/7MsAJxyd30rxRcBOmPA06hIy/+mqs1lnCsHmWUYQEaAiCDpzDJVUYnpUouLqyWb2RYUteKTonakRrU2G1DvuK/inKZjQi5ZefkL3iQyTv+sWyT0OnzoiYMxvS225DpFIp2rmbCseF+HzFaDQ6DWzMTl2qwxUJGp8OUYXbkztp3ii4CUNCgo6mN6Ga+garp30z25wO8zlc1XZpuq8QfAqLMXAAAp8HrzUYUa5rcJu34GrKt9ZgdFhvx1O5FXWQ1dSgzYa1yF65DJqzZwAATCJBzYjrEPXIXERcc41lnSdfCKUExWCQW8FvaDO3og7t4qJ83BpCwgMFN2FGSNAhtFaMOafD/P989hX6tM4nYdjghxW0+dIZjMiIdp23kBqpwKYzJaj3YkYUH+pzZ5G9cilaf/EpImpMtXfqNdE4e9NE5N5+B1jr1hjRJsnngY0vgt5wVsszKZ3vfoQQCm7CipBy6oU1OsG1YpquM+KryqbBkjDMl3lmkqu8hdI6ve8CG8aQuOdXZK/4EKnbtoL7p7JDdes2yJ08HWfHjUdjZKRpXx9Xm6Vy/p6J5Jm3xXc/QggFN2GFbzn10jq9xytSCwk+PAlU+BYME6O6r7dsq3c6y1vQ+uCJW6LTotXX65H9yVLE/HXc8vqFAdfg9JQZuDDgGsBBfoYvg0cq5++Z7Fg1r+rZ2bHBkUxPSCig4CaM8L1xldZ5XitGSLVSTyqb8i0J3jlRI2quipmQoIlvDRB9o3gBhaq4EG1Wf4zW61ZCUWGqjWNQqZA/bjxOT74TNW3sF49tyhfVZs15WwU817gKtd45X5NIJGjXQu0yqbhdC9/VuyEkHFFwE0Z8XSa9aU+Fr9Yk4VNYrHOiBgqZqcDZuSqty2BEJZOgpUaJ89U6t+3tkmSqeOluuE5ocqxC6uX3hTHEHdyP7E8+RPr3myAxGAAAtWktcWbSHfj75oloiIl1exhfrBPjyRpfVM7fnnmat6MAx5d1bggJVxTchBG+vR6JajlO8lwYsqmmPRXuApDWMSqcr9YJmgZs7gEwMsfLEpgDlSOl1VbXKJdyyNCokKZRWurG2Oa9dEqMtsqHcbQfYBpacRQ0RUiAFko5kiMVgqvGeloLh6uvR8st3yB7xYeIO3LQ8nppTh/kTpmBosHDwGT8f4XFrjbryRpfrgIs2+njzr6XwcLT6e7O3tc5KQZXJGj8VqGYkHBGwU0Y4VtOPVGtEFSbxVFPhbPKpnKJ6Y970ynlfHo6HPUAKGUSdIyPRJRcBoWUQ1ldvcPVuusbGXIr6pCglkMikTjM53CUD9P0347OL5dyaKGIwCV9A+obGUrq6lFSV4/Tl2oF9dzoBQ7DKMrLkLX2E2StWQFV6QUAQGOEHOdHj8PpKTNQ2bGToOPJJRy6JUdDLpXgXJVWlMCBT/KwI84CLD49QME0ndzT6e7u3ieRSGi6NyEioIUzwxCfP7zunrrTo5RI0ygFVSiuqTfgeLnz1ah7p8U6/MPvri3tWqhxrlrnNlfD00X+PF1l3Nn1eHrsmONH0XbFh2j57ZeQ1usBALrEJJyZOBV5EyZDH58guI0KKYeuSdF2vV22hAYOpXV67Dh3kXc7XB1f6OfP53P3JXft9fTnPNDXRUiwo4Uzmzk+5dTTNSq0a1HvNImxoEaHltGmdYpcHcfcI2JeYdgVR9OA+fQAuKveaubJTBxPeyAA99OaeR27sRFpP25B9ooPkbjvN8vLFzt3Q+6UGTg/fDSY3PM8mVbRKl6J10Lr0PBNCk5Sux7K8+TzD+R0ck+nu9M0eUL8i4KbMOVsWrJ51eGaegPOVWldHuNAcSUOSaqsbmTOnsA9nQYs9irfQmfieHN+d8GUq2NHVFWi9Wer0WbVMkQWngcAMKkU3M03o3TGTOxo2d6rYnvmGWV8phg3xfcGyzcp2N1QnieffyCnk/vy5zwQ10XLZJBwRcFNM+JqcT5H6o0MsJmJ5OwJn29QYbuf2NOChc7E8fb82oZGlNbpHd4cHB076swptP1kKVptXAeZ1hRcGuLiILvnHnD33Qe0bIlEANkXKnmX5TdTSDm0/Cex2tzj5qvAgU/yuu1xvfm5sRWo6eS+/jn353XRMhkknFFw00wIDWzcOVBciUYjgypCigSVnHdQYbufmNOCVf8kyjoLNvi0R6jDpVVW1Yeb3hwsxzYakbxzO9qu+BDJO7db9q1s1wG5U6Yjf8yNuLJNqtUNJU2j5BXcdE7UQCmTOrxWMQMHR0/47pLXHbHtGfL08w/UdHJf/5w720/sHhZaJoOEOwpumgE+qw4LVW9k2FdsyuUwDYFEe1T7RmgPgCstNUpsySsV9CTq7fltl1VoenNIgwGXffoxWn38ITR5uQAAxnEoGnQtcqfMQGnv/pahJ9ubPt92qWRStIx2fG1iBQ6unvB7p8UKqnOjNRhxrKwaSZEKJKjkHn3+vqjXwxffcgue/Jw7uy6xe1go/4c0B1RAoRkQOrwhlGll6wq01Chd7udoGrB5+ro3lFLOUuHV9uZhDjYKqh3nF4lxflvq8/nQz5kLZGTgimfnQZOXi4bIKJyaehe+3/IrfluyHKV9Bljl1DRdt0tIu/YWOb828w1VCNsbrPkJ39nnWqVvwPCsRAzMiEMbnssDnLxYix3nLmLzmRIU1ugEf/5i1+sRgs/3xdOfc0fvc/f5O/veuyIk/4eQUEXBTZhijKG0To9zVVqU1fnnj9T5ah16pcbY3VBVMonLaa6mmjmxgm/EANAxPgoj2iThvJvS/4dLquCs6oGz86tkprL4tq+ba/lYYQwJe3ejzwN3Yviwfmjz0XvgKithyM5G7vxF+O7nP3Bk3nzUtmrttI22w0HpGhV6pbqvTOvs2jwJ3JreYPk84R8vr8XmvFLUNxqR7ia4tWW+QQPg9f1393PkL65+Xjz5OXf2Pr49LEKreQRj/g8hYqNhqTDkSUn8ppRSDkYGwQtTag1GKGRSjGiT5DY/wDxrq7ahEWqZBDEKGa5I0EDfaERtvQFnKl0/kcolHLomaaCMkOF4eQ2vJ9HTl2qd5qfYTp9XSE3b9I0MyZEKy/8rZRJoGxotQ3ISvQ4Z32xE9icfIvbEMcvxLvS7CnlTZ6Bw4GCHC1g6ojM0WorsmdunkLmvbmy+trYtIu0+Z2fFFm05Gubgm5Cs+ydI6ZUa69EQ3+GSKoxok2RXviCYKxTzKbfg7ft8NcPK2/yfYEEzvYgrFNyEGU8L0gFAz+RoqOUyJKjkKKzReXQcncHodBq6mRjJzfVGht+LhdVHaTot2tHN3Nzugmot9hdXOsxxSFQrUFqnh/JCMdp8ugKt134C5cVyAIBBqUT+9bcgd/J0VLdtL/iaHLWPb3x5pLTa6XRrRzdUPoGD0Cf3I6VVHi1o2vQG7aqKdLBx93Pu7ft81cPiTf5PsKCZXsQdCm7CiDcF6VIj5ciMjbT8212RP2fcPe2JPWvLU85mhbibRTKg8Hckfvg+RqxbZ1nAsi41DbmT7sDfN9+GhtgWoravYzz/Uvzm92TH1lumg5sDFnfLTzgi9Mnd3HMnNMkYoCEQR3zVw8J3mZZg7QWhmV6EDwpuwog3Beku6RrAGLPKt3CXx2LL3dOeL2ZteavprBBnwSHX0ID0779F9oqPEH9ov+k1AGU9e+H05BkoGjpC0AKWQvxdWQellLNaQNSd3Io65FbUWQr5KWRSj7ruPZnJpDMYkRH9b09RSa2e1yKtwT4EEgi+7GFxNlwZ7L0fNNOL8EXBTRjx5ulX18isxu49CZTcPe35etaWJ5oOidhes/xSObLWrkKbNR9DdaEIgGkBy4ZbboFy7hzo21+OsuJKMIG5SULb1zE+0mohUiHvtR0iEnLz4vOEb8scpJh7ihJUcuRV1LnM3/LXEEgw5GgIaYOve1g8zRsKpGCt9EyCDwU3YcTbp9+mwZGQQInvDbO2odHjtvmS+VrN/40+eRxtP/kQGV9vgFRv6r3SxScg79YpOHPrFHTp0h4Z0SqkA2hsUu/HV4wM6JUa43bxSz6Edt2bn/APXah023vkKEgprNG5TUz3xxBIMORoeNIGX/eweJo3FCg004vwRcFNGPG2IF3T4IhvoJQVo0K8Sg65VGI1rOVIZIT7mT+BUKVvQGk1g+a7bzHgtTeQtOdXy7ZLl3c2LWB53VgY5aabQNPPRuWHazp5sdZSKFFraBS8XpQjQrruzU/4J8prXK76bhuk8BlCkEs4pEUJm0IulBg5Gt72+njThlDsYfGVcJnpRXyPgpsw4skwgplSylk9dccrI3i9L69Si7x/pm0rpRyyYtWIkkc4/AOcHasW5cYsJll1FRqXfwDVqmWIOncWAGCUSlF47XU4PWU6Lna/0qrYnm3vhJgVll0xF0r0dLq1o+MJ6brnOA4dEzSIVsh49yLwGUKoNzKfDiGIkaPhba+PGG0ItR4WXwmHmV7EPyi4CTN865rY6pocY/WHtVzXIPjcukZmlRviKNgxVxIOtKi8XGSvXIpWG9Yhos7U5vqYWOTdchvO3DYN2rSWDt/XUqO0Srou09YjLYrfOlBi8HS6tSOedN0L6UUIhiEEb3M0xOj1oTwR8YT6TC/iPxTchKGmwwinL9WiwUXOg1zCoXtKjN0faDFuOM6CnWR1BC7UCQ+evMYYkn79GW0/+QgpP/9oebmqbXvk3j4d+WNvQqPa9RIC56t16JQYjcIanVeFEs1kHGAQkI/szXRrW5523fPtRQiGIQRvAiyxZuYEQ5AXTkJ1phfxLwpuQpS7HIDCGp3L/Ii0KAXaxKqRqFY4/MPsixuObbDjLaVMgqwYNaLkMtTUG5xer7SuDq2++hzZn3yE6NxTlteLrh6K3CnTUdLvKquhJ1e0BqPb3JPsWDXUEVJeQ3BpGiXyq4RNubedbl1YrRPcc+SPrnuhQwi+mM3kTYAlVo9LMAR54YbykIg7FNyEIHc5AHyeOC/pGpwGNoD/ckm8oTMYcby8Br3TYh3mg6gKziN79TK0/mw15FWmYZwGdSTO3jgBubffidrWbTw67+lLrgO0whodrkjQ8DpWklqB0jph0+5tp1snqhVIUMsF9eT4o+teyBCCr2YzeZOjwbcnpaRW7/IGS3kivkF5SMQVCm5CDJ8cALnUfVDi7onTm+RkfzMPDaRrVEiLVKDyx22Qv/02VN98Bc5o+hxqMjKRe/udOHvjBBg03q0C7mqYDzB9tvpGfjdGVYRU0Ofs7Cbo6ElWbzDiSGlgu+75DCH4suKsNzkafHtSmhYpdLasB+WJEOJfFNyEEL45AHx7DcxPps6GAzxdgsHftAYjyiqqkfjVBnBvvIHYAwcs20r6DEDulOkounooIPV+2jbfHBm9wYgICecyEFI1+az5JoG7ugk6epJN1wS+697V2lb5lXU+rzjraY6GJ72XzgIyyhMhxL8ouAkhfHMA+PYaKGUSl8MBaVFKwUsw+JuitARt1nyMuM9WAaUlpheVSrBJk/DLDZNQ3raDqOfjmyPzl5uhK8A6UDENJwIHSypR76BYnqc3wWDpum/ajoJqLbbklfIOGsSYSeRJjoY3vZeOAjLKEyHEfyi4CSF8cwAUUgmvMX79P7VTbDVdtDFYc25ijxxC208+RMvvvoKk4Z+ZV+npwP33A3fdhVJVFMrPXxL9vJ7kyNhyFKgUVGsdfi/MOieGx9O9p6vWizGTyJNAz9PSCs4CsmAJNgkJdxTchBC+OQB88jg6J2pwpNT1cEAuj94HX+kYH4UouQxyCfBrQQUAgDMYkLZ1E9qu+BDxB/ZZ9i3v1hMt/vMwJDffDEREoKBai995BjZpUQpo5DJeizsCwnNkmpJLOPRKi7VL5OYz3HiktArpmtBeDNCbVesDOZPItselSt/A6+eFpnYTEjgU3PiJGNNchcy6KKxxPXRSXd/Iq3psIHSMj0THf/KGSuv0kF+6iNafrUKb1cuhLjYtYGmMiMD5EWOQO2UGLnXuhoEZcUj8J7AREnhkt4hEgkqOs5V1vNdOEpIj01S90bQ8he33ne9wY2mdHkmRvl2qwJc8XbU+GGYSNe1xKa2T0ErnhAQ5Cm78QKxprnxnXQBw+4TMt1dGaJE5b6lkEnSINwU27OhRSF98GSM+WwuZ7p8FLOPikXfrZOTdOhW6pGTL+3QGIxhjOHiBf+Ve85ITHMeha3KM26DINkdGrKd5vk/4ewor0MNBwcVQ4WlPRrDNJBJzancwrFROSDii4MbH+E5z5ftHjs+si9I6vXi9MhwHMP9FN10SosB9+y10L78C5c/bEffP6xUdr8DpyTNwftT1MCrsey+UMgnKtPXQu+l9aSorVm0VrPROAw4UV9p9Ns6qOIv1NM/3Cb/ByLyeGi02ITdnoT0ZwTqTSKyp3cGwUjkh4YqCGx/iO3WbMQiqR+Ju1oW2oZFX+9xNVQYAg5+GpmQ11Wizfi3S1n4M5OZCCYBJJCgcMgKnp8xAeU5vp1WEzT0wQmd2RcmtFwc1f66ldXqU1tUDABLVcpfFDs28eZoXOuX40IVKREg46BtZQJ/2hd6c+VynQsqhc2I0VBHSoO7F8HZqty9r+xBCKLjxKb65FK5nLDU4XGXb3GtgfnI+X62DUiaBrsGAgzyTNtu2UIu6HIInIs/mIXvlUmSuX4uIWtOSBvXRMfj75ok4c9sdqGuZ4fYYrWNU4DhOcM+AQmp/4+Q4DkmRSqvcFsYYSutcV6HlOM7tgpbOnuY5jkOsQgatoZ5Xu3WNDDubJEy7uqH6atjDk5sznx6PbsmhM+zm6dRusdasIoQ4R8GND4kxW6Jp8GF7E3P05CyERh7hcAFGuYTzbTIxY0j8bSfarvgQKdt/APfPsFdVm7b4e/IM5I29CY2RkbwPl1uhRYxSjrQoJRRSjvfQ1L7iSnRNYi5vpnx7JwqqtTh4wfENy9mwlpnRaERRLb/AxhFnAYWvhj28uTm7KgzZroU6ZAIbM0+mdtMq4YT4HgU3PiT2bImmNzEAXi+NsLeoAr3TYjGiTZLV06e2oRH7ivkn5vIl1dYh4+sNyP7kI8ScOmF5vfiqwTg9eQZK+l8FSIR/Zv/mosSiVbSKd0VlnZshAL69E+5maNUbGcrq6hEhMd3obYeThC566UzTgMKXwx7e3JwLqrVOvz+nLtUhTiUPuQBHKFolnBDfo+DGh3y1+KQpT0ecnhXzDbHpTai0Tu/1cWUSzpKvoyoqQJvVH6P1ulVQVJqGUwxqNc6OG4/c2+9ETZu2Xp8PAPYXVXg0s8tRLwOf3okDxZVIUctxiMcwYG5FnV0QY+5FqeWZI+WOOaBIUMl9Ouzh6c2ZhmNMaJVwQnyPghsf8tXik2IGS46esMUIygyNRvRfuxTJz/4XRqkUkkbTDbw2PcO0gOVNt6IhOsbr9lud08N4z9FnwKd3ot7IsOlMqdukbFfn3VNYgVbR4tWu0f1zLb4c9vD05kzDMSa0SjghvkfBjY85m1XBZ6aSvxRW66xuJt4EZdLaWvR56C4k79xueU3S2IjSXv1wesp0FA0aJsoClmKz7WXgO+NMjO+heWaWGJQyic+HPTy9OdNwjAmtEk6I7wW833PJkiXIysqCUqlEz549sWPHDl7v+/XXXyGTydCtWzffNlAE6RoVRrRJwsCMOFyZGouBGXHonRYb6GZZ5FbUoaBaa/VaukaFjvH8k3qjTx7HuE6tcH3PdlaBDQD89uaH2LHicxQNHRmUgQ1g3ctQUK3FYTdLU4hJazAiPcp1T0WsQmbJ2XHGHFD4etjDfHN2xdHNmYZj/mV66ImFyuZaVTIJeqfFhn3eESG+FtCem7Vr12L27NlYsmQJ+vfvj/fffx8jR47EsWPH0KpVK6fvq6ysxJQpUzBkyBBcuHDBjy32nO2sCsaYx0M/KpkEjDG3ywUI4SjXoUO8Bn9drIWr02R+sQY9n3zY7nWDUoltn29G12t6o9gHC1iKyVwnB/B8YUdvpWlUUEdInSbbVugNbo9hDij8MezhSZ0XGo6xRquEE+I7HBMrM9UDvXv3Ro8ePfDuu+9aXuvYsSPGjRuHRYsWOX3frbfeinbt2kEqlWLjxo04ePAg73NWVVUhJiYGlZWViI52/fTpa57eSM29PmLfhAdmxFnVztE2GLCv2L4HQ1KvR48n5qLVNxvsthUOHobfX16CRrUaAJAWqUBhrfcJyr5kWssqGowxbD5TEpCV0M2fvdFoRG5FHUpq9bjAc7jK2dR0Vz8f5t4Bb+vg8Hl/031q6htc1laiXgtCiDNC7t8B67mpr6/H/v378fjjj1u9PmzYMOzatcvp+5YtW4bc3FysXLkSCxcudHsevV4Pvf7fm2tVlf+GG9xxVfMjNVKOCr3B5VMxn8UbFVIOsYoIXjfKgmodyur0yKtwvIik+txZXHPrGCjLy+y2HXriOeROmWH3erAHNsC/lYo9XdjRTALAk3c37a2QSCRo2yISp92s/eWuki+fnhUx6uC4q/Pi6Bzyf4bXmtZSomUHCCFiClhwU1ZWhsbGRiQnJ1u9npycjOLiYofvOXXqFB5//HHs2LEDMhm/pi9atAjz58/3ur1iMj/JFlbrnNY4KaqtR6/UWCj+SRB19FTsqFs7XhmBcl2D1XvKtPW4UHfRbbvOOGlL2veb0GeWfeACANvWfYtLXbrzuGr/aNdCjfPVOkFBijnHg28SsSMqmQQ9kmPwa4HwITjb/BQ+QZa+kUEVIXUZWLj6+Th0odLhz56Y5f+d9R6Zg5qO8VGIkstoOIYQIrqAz5Zy1IXt6I9cY2MjbrvtNsyfPx/t27fnffx58+Zh7ty5ln9XVVUhI8N9SX9fEVJV+OCFSoxqm+zyj76jJ2fbf8crrddQ4qWxEV2ffxrZq5bZbSrrcSV2v/sxGmJihR/XR5o++XdKjP5nWK0Rh0urUO8iacjca+JtEnGXpGgkRSoE5VE5660Qc1ZR05+PgmottuSV8mqft/Vm+NS0+buyDiPaJFFQQwgRXcCCm4SEBEilUrtempKSErveHACorq7Gvn37cODAATzwwAMATGXrGWOQyWT4/vvvMXjwYLv3KRQKKBTBUTNDaI5NvZHhRHkNOiZorF7nmydh3q9EwNCQsuQCBky7BdFnTtttO37fHBx/8BGnC1gGSqeEKLSLiwLHcVafjSpCim5J0W7Xeyqs0Xmcv9R0aQXGGFrHqFzmlHSIj4TGwVphTYkxq8j2Z0RvaHT5Odjytt4M35o2x8qqkRSpsAzLUXItIUQMAQtu5HI5evbsia1bt+KGG26wvL5161Zcf/31dvtHR0fjyJEjVq8tWbIEP/30Ez7//HNkZWX5vM3e4PMk60jupVp0iI+y/JEXstaRkHWnEnfvwMA7JjjctmPZWpT2HSi47Z6QAhAyOCSXcJbAxtln42ioyvyZpUUpsflMicftdZW/0pSQnBJvZxV5u+aYmTf1Zvi+9+TFWpy8WEt5OIQQUQV0WGru3LmYPHkycnJy0LdvX3zwwQfIz8/HzJkzAZiGlAoKCrBixQpIJBJ06tTJ6v1JSUlQKpV2rwcjT5NV643M8gQt1lpHFoyh41svo+OS1+w2VbVpi53LP4Muyb4XzZfax0fisrgo5FbUobahEQajEflVOqf7d0+JcbuW0qlLdU7zl0rr9F4FAaoIqdvPu0NcJBLUcugbTauLu+uR8KbIm5hT2b2pNyP0vY4WahUz/4cQ0rwENLiZMGECysvL8dxzz6GoqAidOnXCpk2bkJmZCQAoKipCfn5+IJsoGm+fgvmuy5MaqXC7X0RlBfrNnIL4A/vstuVOugOHnnguYMX2ouQRkEgkaBcXZXktNUqLQyVVVp9h06d6Pp/NkdIqh/kd3nxf5BIO2gYDDpdUu9zvxMVa4KLz1d0d8aSOjKe9g454W29GzHXVmsN6U4QQcQW0zk0gBKrOTWmdHjvOuZ+x5MiAli3AcRyv93dO1OBIqeObbYvDBzBo/CiH235780MUDrvOo/aJqXOiBkqZ1C7nwlWeEd/P1lxLpilvvi/e4lPTRUgdGjGvRYx6M2L2Ijn63hFCmpeQqHPT3HjzJLuvuBLpUfwWV3S0wnT2ig/R9fmn7V7XxSdg+6dfoy4jU3CbfKVpYMY358Kb2UW+WrmdDz49Eu7qyDQlxppMYua5OOt98kS4rzdFCBEXBTd+4s1ilDqD0Wk9HFuREabhJGldHXrNnYnU7T/Y7ZM/+gb88fyrMMqD+0nYnHPRrkU98ivroG9yf1NIgG4ppt4Fb2YX+Wrldj7EXgHbmxyZ7Fg10jRK0WcoNa21U1Krx8mLrosTOtMc1psihIiHghs/cvYkK5eYpjA3eDlAqJJJ0KYgD22u7AWp3j4Jd///XsHZmyZ6dxKeOABijXc6quCsN8KSbJoWpfRqdpGYPQxCidkj4UkvlD9mJJl7nxJUcuRXaQV/xs1pvSlCiDgouPEzZ1VjT1+qxdGyGo+P22rjZ8h5/CG7140yGX76YguqLuvoTbMF4zjAH9lcvxdWYGy7ZLe9L61jXN+8xephEErMHgk+vVCuql7/u6ZYI/SNRiikEqfLO/iqfY44mxnmK96ut0X8h75XxBlKKA6wgmotDl2o5L3Cd3asGoU1ppotXH09evz3UWR++ZndfsUDB2HP6x+gMTLSq/Zlx6pxpqJOtF4YXzAX0gNc977w6aVgzDRde09hBRocTE8WU4SEw2g3Fag94cmaUa5q44jduxPM602Jsd4W8Q/6XjU/Qu7fFNz4kbdVYwHTrJGEkiIY+/WHtKjQbnv184uw9YYpolURHpgRh78r6nCu2nmtmWDROy0WaVFKnCivwfFy571gzmYCiVX8ji/zauS+IOSJlu+sJjFX7HbUPiCwFYr5rqROAo++V80TzZYKQmLcODO3b0XizKkATJV8m9q+5itc7J4DpUwCuZE5LIomlASmdan21gX/yt4AcOhCJWQc53ZFbUezlPje4OUSTpTPVi7h0CFe435HD/GdZSWkNo6Y9WactS9Q07351pGiejuBR98rwgcFN37gVb2PxkZ0fvE5tPv4/+w2XezSHbve/wT1LeIsr4mZoGqEqTy+nueQWaDpGhmvVbltZynx+WMpl3DolRaLRLUChTU6h8MqQoIec2XlQBNSOVvs2V3BhO9aWOF6/aGEvleEDwpufMzTqrGKslIMuGMCYk6dsD/mvHnYfMeD0Lq5J0VIgAYvY51TFz1Pcg5mhdU6yx8+Pn8s642m1eo5jnOYFK5taMS+Yn5DjNmx6qDpMhcaDIdrvRkxV2InvkXfK8IHBTc+JnRNqYS9u3HVlJscb9y8GWzYMJy+VAutkyrETTUYgY7xUYiSy6AzNDqtXOyKITQ6bQTLrahDglqOdI3Koz+WtsMqpQKG7mQSjtcaU/4gdLZWuNabEWMlduIf9L0ifFBw42O8bpyMocO7r+PyN1+y26TPagPFjl+A9HRT3s6ZEkHB0t+VdRjRJgkAcLK8RpR8EX9SySRIj1LgdIVW9GObx+XF+GOZoJJD+c8Ua3fMK2EHw8wOIbVxwrnejLcrsRP/oe8V4YNCWx9zdUOUVVdh4JSbcWPHdLvA5sytU7DhaD4qDh+1BDZ7CisEJySbx545jkO35MBPfReqhSICMgkHhVT8Hg7zZ2P+Y+mKuz+WHMchQ8NviYym599TWIGCavEDN77MtWf48He9GX/i8zmE8/WHEvpeET6o58bHHD1lxB49jME3j3C4/57X3kPByLFWr3m72rO5N6FltBr5VVoU19Z7fCx/K6zVo9CH9fR0BiOv4nLu/lgyxnDew+nygZ7Z4a5CczD0MPmDJyuxk8Cg7xVxh4IbkTirK9L0xtlm1TJ0W/Ck3Xv1MS2wfe3XqG3dxm5bmbYBEol3Czs27T1qFxeF4trArIIdjMyfjbd/LIXmVjXlzcwOsSq0Nk2S9lWFYn/y9HNxlCweitffHND3irhCwY0IXFbKlAHp027HjevX273v/Igx2PfCGzAqXA9naB2s9C1EvDLC6v+lHMB3dreYa0QFG9uhJmd/LAFTwrCrP6Dezszw5P1iV2gVsgJ5MPP2cwmXz6E5oO8VcYaCGy85q2Ej/esvJHUbBdTYz1C6+NqbKJs8jddaUhwHHC71fEgKAMp1DUhUK1BQrcWB4kregQ0ARMmlqK73LrgKVo6Gmmz/WPK9UXo7M0Po+5393JnzeHqnIai65v21BlCofS6EEN+g4MYLjnJhWn6zAb0eud/xGw4cALp1QxyAFozhr4u1LmcvSQCcKPc+4URnMHpUSJADQiKwSYuUo1BgHlG7Fu5rzQi5UXqyIreZ0JkdoVah1V9rAIXa50II8R2aLeUFqzwLxjCq9xV2gU1JnwEoKyoxLZHdrZvldY77d7FHZ8QqQVWlb8ABngXmmkpQhUbsmxWrRq/UWMhtZlSZF2N05NSlOhwvq4KzpdX43ijN7xcy68iW0JkdQiq0Btr5qjqHs/x8MVMslD4XQohvUXDjBas8CaMR0oZ/C7kdnTMP648XYOfyddCqoxy+Py1K6fIGLJaTbnqInLmoM/igNeLbW1QJjgNGZSdjYEYcrkyNxYCWLSBx89keL6/Fd2dKHN5gPblRmpKSY+2mlatkErRroXb4uicL/IVKhdbzVVq3C8M2DRC9FSqfCyHE90Lj0TxIWeVJSKXYsmUXpHod6lq2cr5fE2Xaeq+L6mXHqpFbUefVMZwJkSWl0GBk/wwT/RsomBOA3dE5ycXw9EaZFqVEhIRDaZ0p6ElUy5GoVoDjOHRKjPY674QxBp2B31BhICu0FlRrsbeowu1+Yq4BRJVrCSFmFNx4wTbPQp+YZLePq3wKMZ4g0zRKSDjTMEs4kHGeL/nQNJ9C6Gdrm4vhyY3SUW5JfpXWklvi7cwOISvLB7JCq9C6TGL1pFDlWkKIGT3CeMHbSpnePkGqZBLoDcawCWwAQOJFomfTYSKhn63tEFO8MsLtkGHTG6WzCtLm3JLjZVU4V6VFaZ3eo2EYoRWqA1mhVWjNH7F6UqhyLSHEjHpuvORN8TdvZtgAQOdEDY54OU08WKhkErSOUeG4l7PDzL0ATWv78FVSa1rMsrBGh8MlVW6HDM03Sj49FU2vS+hMISE9IcFQoVVIT4zYPSlUuZYQAlBwIwpPK2XyKfvfroUa56t1Dv9Qy6XeVS72NwkHZEarECWXoU2MChf1BqvPy9PlC5oy9wKU6xoEv/fkxVrkVdS5DWpsb5RCeyqE1lzhe/zOiRq0bREZ8J4JIT0xvuhJocq1hBAKbkTiaT4FnydNZ4mohy4In94dSEYG5FWaZiadvlSLLknRyIgWrxBe014AT/M43AU2CimH4VmJkEj+baun5+Jbc4Xv8ZUyaVDcwPn2SPZKjfFZTwpVriWkeaPgJgi4e9J09IeaMYazVaGba+Oo98KToaSmmvYC+GpGjL6RWSo+m3l6Lr4zhfgeX2doxLkqbcB7Kvj0SPZKjUXLaBoiIoT4BgU3QULok2ZpnR7BPiIlAwAJB4OL3pADxZWW3gu+Q0lyCWfVw+Ion8LbfCZXbHtSvDkXn14Zvsc/UvrvUh+BzjGh3BdCSCBRcBMAnqyzY/ueklq9y/3NoiKkSNcoUddgwLlqfu8RQ6toJVpFq7Dz/CWX+9UbGU6U16Bjgob38EuXpGioIqQuPz8+vQeesu1J8eZcfHplPDl+MKylRLkvhJBAoeDGzzxZZ8fRe2Q8bxA1DY04ebEWSpnEqxoyQsglHHqmxPJOED51sQYd4qN4D7+oIqS8ermc9R7Y9vwI4Wx2j7NzeXIsRzw5PhD4tZQo94UQEggU3PiRJysWO3uPQWCtFH+WnM+MMRWsq6nnt3yDgZmG2RLVCtGLsDnqPYhXRmBLXqlHw0iuZvfYnqum3oDj5c5Xfhc6U8j2+DpDo9VQlCNiVgAmhJBQQUX8/EToQox83xOMzlfrYDQakVfJP+G5pFbPqwhbS43wXghz70FGtAqJagUkEonb89gW8OO7DlTTc3VM0Dhda8qTNaVsj6+USXm9h9ZSIoQ0N9Rz4ydCFmI0P2ULrZ8iVHasGueqtagXeREprcGI3Io6QTfVugbTeknpGhXatah3WnX51KU6xKnkXueRuEt4FStXxJd5J7SWEiGEOEbBjZ94shAj3/dESDg0eJBDkl9VhwYfxU61DfwWdzQz3+wZY25zdcTKI3EXeIg1lOOrvBNaS4kQQhyj4MZPPHnK5vue3mmx4DgOJbV6nLzIf/kCXwU2ABAZwW/IxEz9z/6e9HB5I5QTXvnMoqK1lAghzRH1V/uJ+SnbFdunbL7vSVQrkKhW4PIEjdv9PeFuAUlbUs405CWkLYlqYZWFXe3HGENpnd6rhSpDhWl4Tdy8Hnea0+dLCAlN1HPjJ548ZQt9jy9qu0RIgG7J0dhbxH+ph3SN0pK0y6ctcsm/vSfe5pF4MtU+1Pmznkxz/HwJIaGHem78yJOnbGfviZBw6BgfibQopd3+HeLUorW5wQhcErgIZY/kGEtbeqfFuu356Z4SY7kR812CwdF+5mnztsNa5qn2BdVaXscORbYzwnwV2DTXz5cQElqo58bPPHnKNr/nRHkNTl+qRYORocHIcLy8Fn9Xaq2emguqtcitEPcm42zmkjNFtXpLexy13czREz/fJRhs13fiO9U+kAXtQhl9voSQUELBTQB4ksRaWKNzWBCuaQFAAD5ZbkCoputFAabr7ZigQYf4KLdBnac5N/5ORG5u6PMlhIQSCm58zJN1pBwdw91T86ELlUCQPDE3XS/KzGg01b6pbWhEZIQU6VGOh048zbkRIxGZOEefLyEklFBw40OOki/lEg4Z0SqkaZS8Ax0+T826RgYgeGat5F6qRYf4KHAchyMllXZDW0dKq9GuhRqdk2KsXve0dosYBe3ECETDFRUMJISEEgpufMTZmlD1RobcijrkVtTxnmUSik/D9UZToFBco3NZbRiAVYDjae0Wbwva0Swg16hgICEklNBjlg/wXROK7yyTUH0arqs3uE1GPnWpDkaj9Q3Tk1llfNalclbQjmYBuefN50sIIf5GPTc+IHRNKHezTPg8NSulHMBxLnt5TFOyGeo96AiSSzhIOPPwFz8Xec58yq2oQ7u4KKvXPJ1V5mq9KEdBEc0C4s+Tz5cQQgKBghsfEDqMxGeWSesYFY6XO19aoes/tWVcDedkxqiQX6WFJ7k52S3UuCwuCuW6BmgbGnGwpAoGF+tZCalO7GwdKk9mlQkNimgWkDD+LBhICCGeouDGBzwZRnIWEDnKBWnK9qnZ2ZN1S41ScL2apprW1GkVo4ZUwrnNi6njuXim0HWo3BESFNEsIOFCeT0uQkjzQMGND/AZRrLlKCBylpRs1jE+yjIjyczRk3W8MgKbcksEXYMjTWvq8BmiMBqNOFJa7fa42bHiVVQWimYBEUJI+An4X+wlS5YgKysLSqUSPXv2xI4dO5zuu379elx77bVITExEdHQ0+vbtiy1btvixtfzwSb5sytEsEz65IH9XOu6JsS3FX6atR72LISShDpdUgTGGdI0KI9okYWBGHK5MjcXAjDiMaJNk6UWSSCRo18J14NKuhRoSSeB+DD1Z0JQQQkhwC2hws3btWsyePRtPPvkkDhw4gIEDB2LkyJHIz893uP8vv/yCa6+9Fps2bcL+/fsxaNAgjBkzBgcOHPBzy91zNuPHEUezTITkgrhTWud+HwBIjVSgc6LG7X5Nz+tuTaPOSTFOAxxHdW78jWYBEUJI+OEYYwGr/Na7d2/06NED7777ruW1jh07Yty4cVi0aBGvY1xxxRWYMGECnn76aV77V1VVISYmBpWVlYiO5t+74ilzYbjCah3OVWtR3+h6bSWzc1Va/F5U4fb4V6bGIiPa9SyVP0urcPKi82Rks8viIhGtiOB13uxYtSWJmQ/bCsXZsYHtsbFFdW4IISS4Cbl/Byznpr6+Hvv378fjjz9u9fqwYcOwa9cuXscwGo2orq5GXFycL5oomLMKt4lqBRLVCnRJiuY9y0TMXJBEtZxXcJOo5j/rJbeiDglqOe+ZMxKJxG66dzChWUCEEBI+AhbclJWVobGxEcnJyVavJycno7i4mNcxXnnlFdTW1mL8+PFO99Hr9dDr9ZZ/V1W5L67nCT5P/kJmmYhZETZRrYBcwrnMu5FL/m0b32ToA8WVOCSpsppJFMq9HTQLiBBCwkPAxwVsn4wZY7yeltesWYNnn30Wa9euRVJSktP9Fi1ahJiYGMtXRkaG12225YsKt2LmgnAch8wY18FG95QYcBwnKBm63sjspkhTVV9CCCGBFrDgJiEhAVKp1K6XpqSkxK43x9batWsxffp0rFu3DkOHDnW577x581BZWWn5OnfunNdtb4pvhVtPUpvMSclKqXUAo5RyTpchcKSgWuuyxk1qpNzqWOkaldfTsz29ZkIIIcRbAQtu5HI5evbsia1bt1q9vnXrVvTr18/p+9asWYNp06Zh9erVGDVqlNvzKBQKREdHW32JScxZTU7Z9s4IyANhjOHQhUqX+xTV1uN8lXVPS5pGyfscjmgNRpy+VItzVVqU1ukp0CGEEOI3AS3iN3fuXEyePBk5OTno27cvPvjgA+Tn52PmzJkATL0uBQUFWLFiBQBTYDNlyhS88cYb6NOnj6XXR6VSISYmMFOKfVnh1lkRP51NMT1XTpTX8FoP6lBJJdI1/66f5EkhQltNC/iFci4OIYSQ0BLQnJsJEybg9ddfx3PPPYdu3brhl19+waZNm5CZmQkAKCoqsqp58/7778NgMOD+++9Hamqq5euhhx4K1CX4rMKtGMNdBdVaHC+v4XU+fSOz6l0SWojQHcrFIYQQ4i8BrXMTCGLXuWGMYfOZErezmka0SRI0rbi0To8d5y663W9gRpzDGT582mXLUc0cd2tbCeXJZ0EIIYSERJ2bcGHu4XC3iKTQm7m3w118coFsOepdsq3/ojM08lovyhlaYZsQQoivBXwqeDhwttSCSiYRNKupKW+Hu4Tm+LiqmdN0iQWlzPsVvGmFbUIIIb5EPTciEbvCrbdF/ITm+PDtXRJjdWxaYZsQQogv0V1GRO4WkRR6LG+K+PFZ7RoQXjOH73GdoRW2CSGE+BoFN0HMm+EuPsFRx/gojMxOFjRs5u0sKlphmxBCiK/RsFSQ82a4yxQcQfTVrt0dFxD/nIQQQghfFNyEAG8WdPTVatfujksrbBNCCAkUCm6aAV+tdu3quLTCNiGEkEChnBtCCCGEhBUKbgghhBASVii4IYQQQkhYoeCGEEIIIWGFghtCCCGEhBUKbgghhBASVii4IYQQQkhYoeCGEEIIIWGFghtCCCGEhJVmV6GYMQYAqKqqCnBLCCGEEMKX+b5tvo+70uyCm+rqagBARkZGgFtCCCGEEKGqq6sRExPjch+O8QmBwojRaERhYSE0Gk1AF3KsqqpCRkYGzp07h+jo6IC1w9/ouum6mwO6brru5sDf180YQ3V1NdLS0iCRuM6qaXY9NxKJBC1btgx0Myyio6Ob1S+DGV1380LX3bzQdTcv/rxudz02ZpRQTAghhJCwQsENIYQQQsIKBTcBolAo8Mwzz0ChUAS6KX5F103X3RzQddN1NwfBfN3NLqGYEEIIIeGNem4IIYQQElYouCGEEEJIWKHghhBCCCFhhYIbQgghhIQVCm48tGjRIlx55ZXQaDRISkrCuHHjcPLkSat9GGN49tlnkZaWBpVKhWuuuQZ//vmn1T56vR4PPvggEhISEBkZibFjx+L8+fNW+1y6dAmTJ09GTEwMYmJiMHnyZFRUVPj6EnlZtGgROI7D7NmzLa+F63UXFBTg9ttvR3x8PNRqNbp164b9+/dbtofjdRsMBjz11FPIysqCSqVCmzZt8Nxzz8FoNFr2CYfr/uWXXzBmzBikpaWB4zhs3LjRars/rzE/Px9jxoxBZGQkEhISMGvWLNTX1/visl1ed0NDAx577DF07twZkZGRSEtLw5QpU1BYWBjW123rnnvuAcdxeP31161eD9frPn78OMaOHYuYmBhoNBr06dMH+fn5lu0hc92MeGT48OFs2bJl7OjRo+zgwYNs1KhRrFWrVqympsayz+LFi5lGo2FffPEFO3LkCJswYQJLTU1lVVVVln1mzpzJ0tPT2datW9kff/zBBg0axLp27coMBoNlnxEjRrBOnTqxXbt2sV27drFOnTqx0aNH+/V6Hdm7dy9r3bo169KlC3vooYcsr4fjdV+8eJFlZmayadOmsT179rC8vDz2ww8/sNOnT1v2CcfrXrhwIYuPj2fffPMNy8vLY5999hmLiopir7/+umWfcLjuTZs2sSeffJJ98cUXDADbsGGD1XZ/XaPBYGCdOnVigwYNYn/88QfbunUrS0tLYw888IDfr7uiooINHTqUrV27lp04cYLt3r2b9e7dm/Xs2dPqGOF23U1t2LCBde3alaWlpbHXXnvNals4Xvfp06dZXFwce/TRR9kff/zBcnNz2TfffMMuXLgQctdNwY1ISkpKGAD2888/M8YYMxqNLCUlhS1evNiyj06nYzExMey9995jjJn+eERERLBPP/3Usk9BQQGTSCRs8+bNjDHGjh07xgCw3377zbLP7t27GQB24sQJf1yaQ9XV1axdu3Zs69at7Oqrr7YEN+F63Y899hgbMGCA0+3het2jRo1id955p9VrN954I7v99tsZY+F53bZ/9P15jZs2bWISiYQVFBRY9lmzZg1TKBSssrLSJ9dr5uomb7Z3714GgJ09e5YxFt7Xff78eZaens6OHj3KMjMzrYKbcL3uCRMmWH63HQml66ZhKZFUVlYCAOLi4gAAeXl5KC4uxrBhwyz7KBQKXH311di1axcAYP/+/WhoaLDaJy0tDZ06dbLss3v3bsTExKB3796Wffr06YOYmBjLPoFw//33Y9SoURg6dKjV6+F63V999RVycnJwyy23ICkpCd27d8f//d//WbaH63UPGDAAP/74I/766y8AwKFDh7Bz505cd911AML3upvy5zXu3r0bnTp1QlpammWf4cOHQ6/XWw2BBkplZSU4jkNsbCyA8L1uo9GIyZMn49FHH8UVV1xhtz0cr9toNOLbb79F+/btMXz4cCQlJaF3795WQ1ehdN0U3IiAMYa5c+diwIAB6NSpEwCguLgYAJCcnGy1b3JysmVbcXEx5HI5WrRo4XKfpKQku3MmJSVZ9vG3Tz/9FH/88QcWLVpkty1cr/vMmTN499130a5dO2zZsgUzZ87ErFmzsGLFCgDhe92PPfYYJk6ciA4dOiAiIgLdu3fH7NmzMXHiRADhe91N+fMai4uL7c7TokULyOXygH8OOp0Ojz/+OG677TbLIonhet0vvPACZDIZZs2a5XB7OF53SUkJampqsHjxYowYMQLff/89brjhBtx44434+eefLe0NletudquC+8IDDzyAw4cPY+fOnXbbOI6z+jdjzO41W7b7ONqfz3F84dy5c3jooYfw/fffQ6lUOt0v3K7baDQiJycHzz//PACge/fu+PPPP/Huu+9iypQplv3C7brXrl2LlStXYvXq1bjiiitw8OBBzJ49G2lpaZg6daplv3C7bkf8dY3B+Dk0NDTg1ltvhdFoxJIlS9zuH8rXvX//frzxxhv4448/BJ87lK/bPEng+uuvx5w5cwAA3bp1w65du/Dee+/h6quvdvreYLxu6rnx0oMPPoivvvoK27ZtQ8uWLS2vp6SkAIBdFFpSUmKJWFNSUlBfX49Lly653OfChQt25y0tLbWLfP1h//79KCkpQc+ePSGTySCTyfDzzz/jzTffhEwms7Qp3K47NTUVl19+udVrHTt2tMwiCNfv96OPPorHH38ct956Kzp37ozJkydjzpw5ll67cL3upvx5jSkpKXbnuXTpEhoaGgL2OTQ0NGD8+PHIy8vD1q1bLb02QHhe944dO1BSUoJWrVpZ/sadPXsWDz/8MFq3bm1pb7hdd0JCAmQymdu/c6Fy3RTceIgxhgceeADr16/HTz/9hKysLKvtWVlZSElJwdatWy2v1dfX4+eff0a/fv0AAD179kRERITVPkVFRTh69Khln759+6KyshJ79+617LNnzx5UVlZa9vGnIUOG4MiRIzh48KDlKycnB5MmTcLBgwfRpk2bsLzu/v372031/+uvv5CZmQkgfL/fdXV1kEis/0xIpVLLU164XndT/rzGvn374ujRoygqKrLs8/3330OhUKBnz54+vU5HzIHNqVOn8MMPPyA+Pt5qezhe9+TJk3H48GGrv3Fp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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plotting the results\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.scatter(X[:, 0], y, color='lightblue', label='Data')\n", + "plt.plot(X[:, 0], predictions, color='red', label='Linear Regression Line')\n", + "plt.xlabel(\"Area (in sq. ft.)\")\n", + "plt.ylabel(\"Price\")\n", + "plt.title(\"House Price Prediction\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c3f4acac-2313-42a2-ac22-722b3a4ba2c7", + "metadata": {}, + "source": [ + "# What's Next?" + ] + }, + { + "cell_type": "raw", + "id": "d142896b-757b-4023-be92-a68f2aace2bf", + "metadata": {}, + "source": [ + "Pandas → Tabular data manipulation\n", + "\n", + "Matplotlib/Seaborn → Data visualization\n", + "\n", + "Scikit-learn → Machine Learning models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcec5b54-7173-4636-a31c-7a443526db93", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}