{ "nbformat": 4, "nbformat_minor": 5, "metadata": { "celltoolbar": "Slideshow", "interpreter": { "hash": "eea1ea0ecaf1b6c9c37bdc3c13e4a6538d638c8bc02013b9db3c15d084051188" }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "papermill": { "default_parameters": {}, "duration": 81.080472, "end_time": "2021-05-19T14:09:09.666398", "environment_variables": {}, "exception": null, "input_path": "__notebook__.ipynb", "output_path": "__notebook__.ipynb", "parameters": {}, "start_time": "2021-05-19T14:07:48.585926", "version": "2.3.3" }, "colab": { "name": "FDP_DataScience.ipynb", "provenance": [], "collapsed_sections": [ "nxvEkGXPM3Xh", "2Mekz3nHf5AA", "67VQ-Sec5DPV", "w727DnK4-DOc", "v7kM_SZV-DOc", "RkjWVTHu-DO6", "iHMqBtt8-DPd", "bccDMQzq-DPo", "hto5zbSL-DPr", "tEINf4bEL9jR", "U5Z_oMoLL9jV", "R5IeAY03L9ja", "oQOE7l55CjDb", "9XgpYu5cCDpw", "RWMXqvfuCDp5", "oHSffVJsCDp_", "lJYf3H2NKXWi", "D33vfL2xKXWn", "260I-jj1N_jN" ] }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "a34617df" }, "source": [ "

Practical Introduction to Data Science with Python



\n", "
\n", "

\n", "

Anand S Menon, Kannan K, 06-07-2021

" ], "id": "a34617df" }, { "cell_type": "markdown", "metadata": { "id": "42f285bf" }, "source": [ " " ], "id": "42f285bf" }, { "cell_type": "markdown", "metadata": { "id": "891471fd" }, "source": [ " " ], "id": "891471fd" }, { "cell_type": "markdown", "metadata": { "id": "6d884875" }, "source": [ "

Agenda

\n", "\n", "### 1. Introduction to Data Science\n", " 1.1 What is Data Science ?\n", " 1.2 Why do we need Data Science ?\n", " 1.3 Brief Overview of Topics\n", " 1.3.1 Big Data Analytics\n", " 1.3.2 Machine Learning & Deep Learning\n" ], "id": "6d884875" }, { "cell_type": "markdown", "metadata": { "id": "e9f055e9" }, "source": [ "### 2. Pythonic way of Data Science\n", " 2.1 Brief Intro to Python Programming Language\n", " 2.2 Python for Data Science\n", " 2.3 Data Processing, Statistical analysis and Visualization\n", " 2.3.1 numpy, pandas, scipy\n", " 2.3.2 matplotlib, seaborn, plotly\n", " 2.4 Model Building Frameworks\n", " 2.4.1 Scikit Learn, Tensorflow, Pytorch" ], "id": "e9f055e9" }, { "cell_type": "markdown", "metadata": { "id": "b553ff9b" }, "source": [ "### 3. Approaching a Tabular(Structured) Problem \n", " 3.1 Understanding the Problem\n", " 3.2 Exploratory Data Analysis\n", " 3.2 Data Preprocessing\n", " 3.3 Feature Engineering\n", " 3.4 Model Building and Inference\n" ], "id": "b553ff9b" }, { "cell_type": "markdown", "metadata": { "id": "6c5cc163" }, "source": [ "### 4. Approaching a Text (NLP) Problem \n", " 4.1 Importance of solving NLP problems\n", " 4.2 Applications of NLP\n", " 4.3 Intro to NLP\n", " 4.4 NLP using python\n", " 4.5 Approaching real life NLP problem" ], "id": "6c5cc163" }, { "cell_type": "markdown", "metadata": { "id": "okrqD3E-hjpi" }, "source": [ "#### 5. Approaching a Vision Problem (Hands On)\n", " 4.1 An introduction to computer vision\n", " 4.1.1 What is Computer Vision?\n", " 4.1.2 How is computer vision used today?\n", " 4.2 Image Processing\n", " 4.2.1 Point Operators\n", " 4.2.1.1 Pixel Transforms\n", " 4.2.1.2 Color Transforms\n", " 4.2.1.3 Compositing and matting\n", " 4.2.1.4 Histogram Equalization\n", " 4.2.2 Linear Filtering\n", " 4.2.2.1 Separable Filtering\n", " 4.2.2.2 Band Pass and Steerable Filters\n", " 4.2.3 More neighborhood operators\n", " 4.2.3.1 Non-linear filtering\n", " 4.2.3.2 Bilateral filtering\n", " 4.2.3.3 Binary Image processing\n", " 4.2.4 Fourier Transforms\n", " 4.2.4.1 Two-dimensional Fourier Transforms\n", " 4.2.5 Pyramid and wavelets\n", " 4.2.5.1 Interpolation\n", " 4.2.5.2 Decimation\n", " 4.2.5.3 Multi-resolution representations\n", " 4.2.5.4 Wavelts\n", " 4.2.6 Geometrics transformations\n", " 4.2.6.1 Parametric transformations\n", " 4.2.6.2 Mesh-based warping\n", " 4.3 OpenCV Library [Hands On]\n", " 4.3.1 Introduction\n", " 4.3.2 Changing colorspaces\n", " 4.3.3 Geometric transformations of Images\n", " 4.3.4 Image thresholding\n", " 4.3.5 Smoothing Images\n", " 4.3.6 Morphological Transformations\n", " 4.3.7 Image Gradients\n", " 4.3.8 Canny Edge Detection\n", " 4.3.9 Image Pyramids\n", " 4.3.10 Contours\n", " 4.3.11 Histograms\n", " 4.3.12 Image Transforms" ], "id": "okrqD3E-hjpi" }, { "cell_type": "markdown", "metadata": { "id": "82d016bb" }, "source": [ "

1. Introduction to Data Science


\n" ], "id": "82d016bb" }, { "cell_type": "markdown", "metadata": { "id": "4c9f5e7f" }, "source": [ "

1.1 What is Data Science?


\n", "\n", "\n", "

\"Data science is an interdisciplinary field that uses scientific methods, processes, algorithms and systems to extract knowledge and insights from structured and unstructured data, and apply knowledge and actionable insights from data across a broad range of application domains. Data science is related to data mining, machine learning and big data.\" - Wikipedia


\n" ], "id": "4c9f5e7f" }, { "cell_type": "markdown", "metadata": { "id": "b1474823" }, "source": [ "

In layman's terms it is a combination of different fields like computer science, maths, statistics, domain knowledge ..etc, which works together in order to fetch, process and evaluate huge amount of data for better bussiness decision making.


\n", "\n", "" ], "id": "b1474823" }, { "cell_type": "markdown", "metadata": { "id": "15402732" }, "source": [ "

1.2 Why do we need Data Science?


\n", "\n", "\n", "

The answer to that question is pretty simple. Data Science would help companies in saving billions of dollar by taking right decisions at right time with assistance of data.
Data Science is all about data and making sense of data will reduce the horrors of uncertainties for any organizations.


\n", "\n", "

Did you know that Southwest Airlines, at one point, was able to save $100 million by leveraging data? They could reduce their planes’ idle time that waited at the tarmac and drive a change in utilizing their resources. In short, today, it is not possible for any business to imagine a world without data.


\n", "\n", "\n" ], "id": "15402732" }, { "cell_type": "markdown", "metadata": { "id": "6da530e8" }, "source": [ "

Data statistics facts as of 2021

\n", "\n", "

\"Google gets over 3.5 billion searches daily.\"

\n", "\n", "

\"WhatsApp users exchange up to 65 billion messages daily.\"

\n", "\n", "

\"In 2020, every person generated 1.7 megabytes per second\"

\n", "\n", "

\"80-90% of the data we generate today is unstructured.\"

\n", "\n", "

\"On average, 500 million tweets are shared every day. This can be further broken down to 6,000 tweets per second, 350,000 tweets per minute, and around 200 billion tweets every year.\"


\n", "\n", "\n", "

The above facts clearly indicates the data explosion and we need ways to ingest, validate, process and infer data which all comes under the Data Science umbrella

\n" ], "id": "6da530e8" }, { "cell_type": "markdown", "metadata": { "id": "86b00819" }, "source": [ "

Data Explosion


\n", "

Data is growing at pace we have not imagined before

\n", "\n", " " ], "id": "86b00819" }, { "cell_type": "markdown", "metadata": { "id": "e03cd9e6" }, "source": [ "

1.3.1 Big Data Analytics


\n", "

What is big data exactly? It can be defined as data sets whose size or type is beyond the ability of traditional relational databases to capture, manage and process the data with low latency. Characteristics of big data include high volume, high velocity and high variety

Big data analytics is the use of advanced analytic techniques against very large, diverse big data sets that include structured, semi-structured and unstructured data, from different sources, and in different sizes from terabytes to zettabytes.

With big data analytics, you can ultimately fuel better and faster decision-making, modelling and predicting of future outcomes and enhanced business intelligence.

Some of commonly used frameworks are Apache Hadpoop, Apacha spark, Cassandra ..etc

" ], "id": "e03cd9e6" }, { "cell_type": "markdown", "metadata": { "id": "7d2f77c2" }, "source": [ "

1.3.2 Machine Learning & Deep Learning


\n", "\n", "

* Machine Learning involves algorithms that learn from patterns of data and then apply it to decision making.

\n", "\n", "

* Deep Learning is able to learn through processing data on its own using neural nets and is quite similar to the human brain where it identifies something, analyse it, and makes a decision.


\n", "\n", "\n", "\n" ], "id": "7d2f77c2" }, { "cell_type": "markdown", "metadata": { "id": "562393ef" }, "source": [ "## 2.Pythonic way of data science" ], "id": "562393ef" }, { "cell_type": "markdown", "metadata": { "id": "kK70oQo5Uzqx" }, "source": [ "## **Brief Intro to Python Programming Language**\n", "\n", "**Python is an interpreted, high-level, general-purpose programming language. It is is dynamically typed and garbage-collected.**\n", "\n", "One of the most popular and fastest growing languages programming language in the world (StackOverflow survey 2020). First appeared on February 1991 (30 years ago). \n", "\n", "Highly efficient to write and programs tends to be clear and readable. well suitble for application like data science and web development.\n", "\n", "Used in a wide range of fields, from healthcare to finance to VFX and AI.\n", "\n", "The Father Of Python: Guido Van Rossum \n", "\n" ], "id": "kK70oQo5Uzqx" }, { "cell_type": "markdown", "metadata": { "id": "BrQydd54dW33" }, "source": [ "\n", "**Internal working of Python**\n", "
" ], "id": "BrQydd54dW33" }, { "cell_type": "markdown", "metadata": { "id": "gyWhzgOCuhDK" }, "source": [ "## **Python way of Data Science**\n", "\n", "Python provide great functionality to deal with mathematics, statistics and scientific function. It provides great libraries to deals with data science application.\n", "\n" ], "id": "gyWhzgOCuhDK" }, { "cell_type": "markdown", "metadata": { "id": "tB-mDACGumyw" }, "source": [ "* It’s Flexible\n", "* It’s Easy to Learn\n", "* It’s Open Source\n", "* It’s Well-Supported\n", "\n", "\n", "As is the case with many other programming languages, it’s the available libraries that lead to Python’s success: some 72,000 of them in the Python Package Index (PyPI) and growing constantly.\n" ], "id": "tB-mDACGumyw" }, { "cell_type": "markdown", "metadata": { "id": "XLOAbjeFXIir" }, "source": [ "### **Who is it for?**\n", "\n", "#### **Students**\n", "The use of short English keyword instead of symbols makes Python particularly friend;y for begineers.\n" ], "id": "XLOAbjeFXIir" }, { "cell_type": "markdown", "metadata": { "id": "CNvhRs9RYJLk" }, "source": [ "Python\n", "\n", "```python\n", "# print the integers from 1 to 0\n", "for i in range(1,10):\n", " print(i)\n", "```\n", "\n", "Java\n", "\n", "```java\n", "// print the integers from 1 to 9\n", "for(int i=1;i<10;i++){\n", " System.out.println(i);\n", "}\n", "```\n", "\n", "Python hanldes a lot of memory automatically, so less time dealing with pointers and references and so on than other lanugages like C++.\n", "\n" ], "id": "CNvhRs9RYJLk" }, { "cell_type": "markdown", "metadata": { "id": "ut1XxPaOYzFz" }, "source": [ "### **Developer who prioritize ease of programming over execution speed**\n", "\n", "Python runs slower than other common languages like C++ or Java, but its often quick to write (three to five times faster than Java and five to ten times than C++).\n", "\n", "One of the main reason why Python is used in \"support language\" for tasks like build control, automated testing and bug tracking. For the same reason used for prototyping." ], "id": "ut1XxPaOYzFz" }, { "cell_type": "markdown", "metadata": { "id": "OejznBlDZ6m-" }, "source": [ "### **Developers working with text and numbers**\n", "\n", "Python have powerful String anf List manipulation functions. Its a great tool for exploring data.\n", "\n", "Avoiding the need for compiling makes it easier to run interactively on data for step by step manipulation. (Just like this Jupyter notebook).\n", "\n", "Python's core functionality can be extended with a huge number of packages which are mainly used by the scientific and academic communities as well as engineering. \n", "\n", "These include the Numpy(mathematical function and data analysis), SciPy(for science), Matplotlib(data visualization) and Pandas(data analysis) and more recently Tensorflow and Keras (Machine Learning/AI) and tens of thousands of others." ], "id": "OejznBlDZ6m-" }, { "cell_type": "markdown", "metadata": { "id": "oB2GwvzWbdi3" }, "source": [ "### **Web Developers**\n", "\n", "Backend development for webapplication. Python has built-in supports for common protocols like HTML, XML and Json.\n", "\n", "Frameworks like Django, Flask and Bottle are widely used. For example Reddit's backend in Python.\n" ], "id": "oB2GwvzWbdi3" }, { "cell_type": "markdown", "metadata": { "id": "v7YzmVyPcIKf" }, "source": [ "### **The Zen of Python**\n", "\n", "The Zen of Python is a collection of 19 \"guiding principles\".\n", "\n", "Some are:\n", "* Readability counts.\n", "* Simple is better than complex\n", "* There should be one - and preferably only one - obvious way to do it.\n", "\n", "\n", "\n", "\n" ], "id": "v7YzmVyPcIKf" }, { "cell_type": "markdown", "metadata": { "id": "S7ZPPpOLfu3s" }, "source": [ "## **Intro to Data Processing, Statistical analysis and Visualization libraries**" ], "id": "S7ZPPpOLfu3s" }, { "cell_type": "markdown", "metadata": { "id": "nxvEkGXPM3Xh" }, "source": [ "## A Brief Note on Python Versions\n", "\n", "As of Janurary 1, 2020, Python has [officially dropped support](https://www.python.org/doc/sunset-python-2/) for `python2`. " ], "id": "nxvEkGXPM3Xh" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "1L4Am0QATgOc", "outputId": "4c759d29-d563-44e4-f2b0-ae1eabc5f90c" }, "source": [ "!python --version" ], "id": "1L4Am0QATgOc", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Python 3.7.11\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "2Mekz3nHf5AA" }, "source": [ "## **Numpy**\n", "\n", "Reference: [Stanford CS class CS231n: Convolutional Neural Networks for Visual Recognition Python Numpy Tutorial](https://colab.research.google.com/github/cs231n/cs231n.github.io/blob/master/python-colab.ipynb)" ], "id": "2Mekz3nHf5AA" }, { "cell_type": "markdown", "metadata": { "id": "fY12nHhyL9hX" }, "source": [ "Numpy is the core library for scientific computing in Python. It provides a high-performance multidimensional array object, and tools for working with these arrays." ], "id": "fY12nHhyL9hX" }, { "cell_type": "markdown", "metadata": { "id": "lZMyAdqhL9hY" }, "source": [ "To use Numpy, we first need to import the `numpy` package:" ], "id": "lZMyAdqhL9hY" }, { "cell_type": "code", "metadata": { "id": "58QdX8BLL9hZ" }, "source": [ "import numpy as np" ], "id": "58QdX8BLL9hZ", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "DDx6v1EdL9hb" }, "source": [ "###Arrays" ], "id": "DDx6v1EdL9hb" }, { "cell_type": "markdown", "metadata": { "id": "f-Zv3f7LL9hc" }, "source": [ "A numpy array is a grid of values, all of the same type, and is indexed by a tuple of nonnegative integers. The number of dimensions is the rank of the array; the shape of an array is a tuple of integers giving the size of the array along each dimension." ], "id": "f-Zv3f7LL9hc" }, { "cell_type": "markdown", "metadata": { "id": "_eMTRnZRL9hc" }, "source": [ "We can initialize numpy arrays from nested Python lists, and access elements using square brackets:" ], "id": "_eMTRnZRL9hc" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "-l3JrGxCL9hc", "outputId": "a556282a-2546-4fbc-94f6-606e124600e0" }, "source": [ "a = np.array([1, 2, 3]) # Create a rank 1 array\n", "print(type(a), a.shape, a[0], a[1], a[2])\n", "a[0] = 5 # Change an element of the array\n", "print(a) " ], "id": "-l3JrGxCL9hc", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ " (3,) 1 2 3\n", "[5 2 3]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ma6mk-kdL9hh", "outputId": "027b98e9-06d9-4cbb-efe7-62a618069d55" }, "source": [ "b = np.array([[1,2,3],[4,5,6]]) # Create a rank 2 array\n", "print(b)" ], "id": "ma6mk-kdL9hh", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[1 2 3]\n", " [4 5 6]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ymfSHAwtL9hj", "outputId": "16b5f2d4-a194-4921-8e63-096335b4a8ed" }, "source": [ "print(b.shape)\n", "print(b[0, 0], b[0, 1], b[1, 0])" ], "id": "ymfSHAwtL9hj", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "(2, 3)\n", "1 2 4\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "F2qwdyvuL9hn" }, "source": [ "Numpy also provides many functions to create arrays:" ], "id": "F2qwdyvuL9hn" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "mVTN_EBqL9hn", "outputId": "7c57cf44-1dc4-4994-f292-b0042afd3549" }, "source": [ "a = np.zeros((2,2)) # Create an array of all zeros\n", "print(a)" ], "id": "mVTN_EBqL9hn", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[0. 0.]\n", " [0. 0.]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "skiKlNmlL9h5", "outputId": "e0d7e060-31c5-4366-938e-9db4da1ceeab" }, "source": [ "b = np.ones((1,2)) # Create an array of all ones\n", "print(b)" ], "id": "skiKlNmlL9h5", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[1. 1.]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "HtFsr03bL9h7", "outputId": "3a894ac6-3fcd-448d-86ac-8ffba2dddc6b" }, "source": [ "c = np.full((2,2), 7) # Create a constant array\n", "print(c)" ], "id": "HtFsr03bL9h7", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[7 7]\n", " [7 7]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "-QcALHvkL9h9", "outputId": "6f0d66c7-6916-4020-d796-015b6102fc3f" }, "source": [ "d = np.eye(2) # Create a 2x2 identity matrix\n", "print(d)" ], "id": "-QcALHvkL9h9", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[1. 0.]\n", " [0. 1.]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "RCpaYg9qL9iA", "outputId": "4f6fe37c-9471-4803-e28d-578e6e75b8a0" }, "source": [ "e = np.random.random((2,2)) # Create an array filled with random values\n", "print(e)" ], "id": "RCpaYg9qL9iA", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[0.18008187 0.07831069]\n", " [0.3829961 0.69663983]]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "jI5qcSDfL9iC" }, "source": [ "###Array indexing" ], "id": "jI5qcSDfL9iC" }, { "cell_type": "markdown", "metadata": { "id": "M-E4MUeVL9iC" }, "source": [ "Numpy offers several ways to index into arrays." ], "id": "M-E4MUeVL9iC" }, { "cell_type": "markdown", "metadata": { "id": "QYv4JyIEL9iD" }, "source": [ "Slicing: Similar to Python lists, numpy arrays can be sliced. Since arrays may be multidimensional, you must specify a slice for each dimension of the array:" ], "id": "QYv4JyIEL9iD" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "wLWA0udwL9iD", "outputId": "db217881-ea42-4666-8f94-d18362543b48" }, "source": [ "import numpy as np\n", "\n", "# Create the following rank 2 array with shape (3, 4)\n", "# [[ 1 2 3 4]\n", "# [ 5 6 7 8]\n", "# [ 9 10 11 12]]\n", "a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])\n", "\n", "# Use slicing to pull out the subarray consisting of the first 2 rows\n", "# and columns 1 and 2; b is the following array of shape (2, 2):\n", "# [[2 3]\n", "# [6 7]]\n", "b = a[:2, 1:3]\n", "print(b)" ], "id": "wLWA0udwL9iD", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[2 3]\n", " [6 7]]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "KahhtZKYL9iF" }, "source": [ "A slice of an array is a view into the same data, so modifying it will modify the original array." ], "id": "KahhtZKYL9iF" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "1kmtaFHuL9iG", "outputId": "a1904677-760b-4b06-98a4-e40c1166a4f9" }, "source": [ "print(a[0, 1])\n", "b[0, 0] = 77 # b[0, 0] is the same piece of data as a[0, 1]\n", "print(a[0, 1]) " ], "id": "1kmtaFHuL9iG", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "2\n", "77\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "_Zcf3zi-L9iI" }, "source": [ "You can also mix integer indexing with slice indexing. However, doing so will yield an array of lower rank than the original array." ], "id": "_Zcf3zi-L9iI" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "G6lfbPuxL9iJ", "outputId": "ed574c53-725e-4aab-ea77-559030ca0c4f" }, "source": [ "# Create the following rank 2 array with shape (3, 4)\n", "a = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])\n", "print(a)" ], "id": "G6lfbPuxL9iJ", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[ 1 2 3 4]\n", " [ 5 6 7 8]\n", " [ 9 10 11 12]]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "NCye3NXhL9iL" }, "source": [ "Two ways of accessing the data in the middle row of the array.\n", "Mixing integer indexing with slices yields an array of lower rank,\n", "while using only slices yields an array of the same rank as the\n", "original array:" ], "id": "NCye3NXhL9iL" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "EOiEMsmNL9iL", "outputId": "14aeab20-fc8c-4d3b-9d82-02867f7b94b7" }, "source": [ "row_r1 = a[1, :] # Rank 1 view of the second row of a \n", "row_r2 = a[1:2, :] # Rank 2 view of the second row of a\n", "row_r3 = a[[1], :] # Rank 2 view of the second row of a\n", "print(row_r1, row_r1.shape)\n", "print(row_r2, row_r2.shape)\n", "print(row_r3, row_r3.shape)" ], "id": "EOiEMsmNL9iL", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[5 6 7 8] (4,)\n", "[[5 6 7 8]] (1, 4)\n", "[[5 6 7 8]] (1, 4)\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "JXu73pfDL9iN", "outputId": "13f315ca-dc9d-416a-d2d7-781a2d981417" }, "source": [ "# We can make the same distinction when accessing columns of an array:\n", "col_r1 = a[:, 1]\n", "col_r2 = a[:, 1:2]\n", "print(col_r1, col_r1.shape)\n", "print()\n", "print(col_r2, col_r2.shape)" ], "id": "JXu73pfDL9iN", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[ 2 6 10] (3,)\n", "\n", "[[ 2]\n", " [ 6]\n", " [10]] (3, 1)\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "kaE8dBGgL9id" }, "source": [ "Boolean array indexing: Boolean array indexing lets you pick out arbitrary elements of an array. Frequently this type of indexing is used to select the elements of an array that satisfy some condition. Here is an example:" ], "id": "kaE8dBGgL9id" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "32PusjtKL9id", "outputId": "62637a7f-9cea-4f78-c304-72299fb0354f" }, "source": [ "import numpy as np\n", "\n", "a = np.array([[1,2], [3, 4], [5, 6]])\n", "\n", "bool_idx = (a > 2) # Find the elements of a that are bigger than 2;\n", " # this returns a numpy array of Booleans of the same\n", " # shape as a, where each slot of bool_idx tells\n", " # whether that element of a is > 2.\n", "\n", "print(bool_idx)" ], "id": "32PusjtKL9id", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[False False]\n", " [ True True]\n", " [ True True]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "cb2IRMXaL9if", "outputId": "15622e1e-987e-4471-c11b-0dd9d2cff1a7" }, "source": [ "# We use boolean array indexing to construct a rank 1 array\n", "# consisting of the elements of a corresponding to the True values\n", "# of bool_idx\n", "print(a[bool_idx])\n", "\n", "# We can do all of the above in a single concise statement:\n", "print(a[a > 2])" ], "id": "cb2IRMXaL9if", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[3 4 5 6]\n", "[3 4 5 6]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "jTctwqdQL9ih" }, "source": [ "###Datatypes" ], "id": "jTctwqdQL9ih" }, { "cell_type": "markdown", "metadata": { "id": "kSZQ1WkIL9ih" }, "source": [ "Every numpy array is a grid of elements of the same type. Numpy provides a large set of numeric datatypes that you can use to construct arrays. Numpy tries to guess a datatype when you create an array, but functions that construct arrays usually also include an optional argument to explicitly specify the datatype. Here is an example:" ], "id": "kSZQ1WkIL9ih" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "4za4O0m5L9ih", "outputId": "6a16e744-fd97-4fb4-c87e-13680897b8f9" }, "source": [ "x = np.array([1, 2]) # Let numpy choose the datatype\n", "y = np.array([1.0, 2.0]) # Let numpy choose the datatype\n", "z = np.array([1, 2], dtype=np.int64) # Force a particular datatype\n", "\n", "print(x.dtype, y.dtype, z.dtype)" ], "id": "4za4O0m5L9ih", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "int64 float64 int64\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "RLVIsZQpL9ik" }, "source": [ "You can read all about numpy datatypes in the [documentation](http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html)." ], "id": "RLVIsZQpL9ik" }, { "cell_type": "markdown", "metadata": { "id": "TuB-fdhIL9ik" }, "source": [ "###Array math" ], "id": "TuB-fdhIL9ik" }, { "cell_type": "markdown", "metadata": { "id": "18e8V8elL9ik" }, "source": [ "Basic mathematical functions operate elementwise on arrays, and are available both as operator overloads and as functions in the numpy module:" ], "id": "18e8V8elL9ik" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "gHKvBrSKL9il", "outputId": "520eb959-d29f-4999-e556-2df5637dfcb5" }, "source": [ "x = np.array([[1,2],[3,4]], dtype=np.float64)\n", "y = np.array([[5,6],[7,8]], dtype=np.float64)\n", "\n", "# Elementwise sum; both produce the array\n", "print(x + y)\n", "print(np.add(x, y))" ], "id": "gHKvBrSKL9il", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[ 6. 8.]\n", " [10. 12.]]\n", "[[ 6. 8.]\n", " [10. 12.]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "1fZtIAMxL9in", "outputId": "b0733bb2-e448-4ca5-c184-8c7d6edf6847" }, "source": [ "# Elementwise difference; both produce the array\n", "print(x - y)\n", "print(np.subtract(x, y))" ], "id": "1fZtIAMxL9in", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[-4. -4.]\n", " [-4. -4.]]\n", "[[-4. -4.]\n", " [-4. -4.]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "nil4AScML9io", "outputId": "86bb0e16-18f5-4be1-800e-1faa84beaa0d" }, "source": [ "# Elementwise product; both produce the array\n", "print(x * y)\n", "print(np.multiply(x, y))" ], "id": "nil4AScML9io", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[ 5. 12.]\n", " [21. 32.]]\n", "[[ 5. 12.]\n", " [21. 32.]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "0JoA4lH6L9ip", "outputId": "3d391d97-d00c-43b7-8a75-519a01213eb0" }, "source": [ "# Elementwise division; both produce the array\n", "# [[ 0.2 0.33333333]\n", "# [ 0.42857143 0.5 ]]\n", "print(x / y)\n", "print(np.divide(x, y))" ], "id": "0JoA4lH6L9ip", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[0.2 0.33333333]\n", " [0.42857143 0.5 ]]\n", "[[0.2 0.33333333]\n", " [0.42857143 0.5 ]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "g0iZuA6bL9ir", "outputId": "39e81644-613d-45e7-d682-c57a4a0ddd60" }, "source": [ "# Elementwise square root; produces the array\n", "# [[ 1. 1.41421356]\n", "# [ 1.73205081 2. ]]\n", "print(np.sqrt(x))" ], "id": "g0iZuA6bL9ir", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[1. 1.41421356]\n", " [1.73205081 2. ]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "I3FnmoSeL9iu", "outputId": "de799a09-265c-4368-f2e4-51d87c204069" }, "source": [ "x = np.array([[1,2],[3,4]])\n", "y = np.array([[5,6],[7,8]])\n", "\n", "v = np.array([9,10])\n", "w = np.array([11, 12])\n", "\n", "# Inner product of vectors; both produce 219\n", "print(v.dot(w))\n", "print(np.dot(v, w))" ], "id": "I3FnmoSeL9iu", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "219\n", "219\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "vmxPbrHASVeA" }, "source": [ "You can also use the `@` operator which is equivalent to numpy's `dot` operator." ], "id": "vmxPbrHASVeA" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vyrWA-mXSdtt", "outputId": "bb77dbb2-4820-4cab-d451-a1235b74d949" }, "source": [ "print(v @ w)" ], "id": "vyrWA-mXSdtt", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "219\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "zvUODeTxL9iw", "outputId": "c030a4a6-2950-437e-da51-2b8cd334a73c" }, "source": [ "# Matrix / vector product; both produce the rank 1 array [29 67]\n", "print(x.dot(v))\n", "print(np.dot(x, v))\n", "print(x @ v)" ], "id": "zvUODeTxL9iw", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[29 67]\n", "[29 67]\n", "[29 67]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "3V_3NzNEL9iy", "outputId": "8c37cf2b-5d13-411e-ba89-5257a7bbdfdf" }, "source": [ "# Matrix / matrix product; both produce the rank 2 array\n", "# [[19 22]\n", "# [43 50]]\n", "print(x.dot(y))\n", "print(np.dot(x, y))\n", "print(x @ y)" ], "id": "3V_3NzNEL9iy", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[19 22]\n", " [43 50]]\n", "[[19 22]\n", " [43 50]]\n", "[[19 22]\n", " [43 50]]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "FbE-1If_L9i0" }, "source": [ "Numpy provides many useful functions for performing computations on arrays; one of the most useful is `sum`:" ], "id": "FbE-1If_L9i0" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "DZUdZvPrL9i0", "outputId": "9a865f4e-2b61-48cf-8496-db5ce6be11f4" }, "source": [ "x = np.array([[1,2],[3,4]])\n", "\n", "print(np.sum(x)) # Compute sum of all elements; prints \"10\"\n", "print(np.sum(x, axis=0)) # Compute sum of each column; prints \"[4 6]\"\n", "print(np.sum(x, axis=1)) # Compute sum of each row; prints \"[3 7]\"" ], "id": "DZUdZvPrL9i0", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "10\n", "[4 6]\n", "[3 7]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "ahdVW4iUL9i3" }, "source": [ "You can find the full list of mathematical functions provided by numpy in the [documentation](http://docs.scipy.org/doc/numpy/reference/routines.math.html).\n", "\n", "Apart from computing mathematical functions using arrays, we frequently need to reshape or otherwise manipulate data in arrays. The simplest example of this type of operation is transposing a matrix; to transpose a matrix, simply use the T attribute of an array object:" ], "id": "ahdVW4iUL9i3" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "63Yl1f3oL9i3", "outputId": "d0f5fa2c-b40d-4acf-afcd-e37fadf366eb" }, "source": [ "print(x)\n", "print(\"transpose\\n\", x.T)" ], "id": "63Yl1f3oL9i3", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[1 2]\n", " [3 4]]\n", "transpose\n", " [[1 3]\n", " [2 4]]\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "mkk03eNIL9i4", "outputId": "37577142-1e97-4606-96b8-d26d42418406" }, "source": [ "v = np.array([[1,2,3]])\n", "print(v )\n", "print(\"transpose\\n\", v.T)" ], "id": "mkk03eNIL9i4", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "[[1 2 3]]\n", "transpose\n", " [[1]\n", " [2]\n", " [3]]\n" ], "name": "stdout" } ] }, { "cell_type": "markdown", "metadata": { "id": "67VQ-Sec5DPV" }, "source": [ "## Pandas\n", "\n", "[Reference: Google Machine learning Crash Course Colab Notebooks](https://colab.research.google.com/notebooks/mlcc/intro_to_pandas.ipynb)" ], "id": "67VQ-Sec5DPV" }, { "cell_type": "markdown", "metadata": { "id": "TIFJ83ZTBctl" }, "source": [ "[*pandas*](http://pandas.pydata.org/) is a column-oriented data analysis API. It's a great tool for handling and analyzing input data, and many ML frameworks support *pandas* data structures as inputs. For a more complete reference, the [*pandas* docs site](http://pandas.pydata.org/pandas-docs/stable/index.html) contains extensive documentation and many tutorials." ], "id": "TIFJ83ZTBctl" }, { "cell_type": "markdown", "metadata": { "id": "s_JOISVgmn9v" }, "source": [ "### Basic Concepts\n", "\n", "The following line imports the *pandas* API and prints the API version:" ], "id": "s_JOISVgmn9v" }, { "cell_type": "code", "metadata": { "id": "aSRYu62xUi3g", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "496bebc9-7df0-4521-812d-33a40fc6cf0c" }, "source": [ "from __future__ import print_function\n", "\n", "import pandas as pd\n", "pd.__version__" ], "id": "aSRYu62xUi3g", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" }, "text/plain": [ "'1.1.5'" ] }, "metadata": { "tags": [] }, "execution_count": 31 } ] }, { "cell_type": "markdown", "metadata": { "id": "daQreKXIUslr" }, "source": [ "The primary data structures in *pandas* are implemented as two classes:\n", "\n", " * **`DataFrame`**, which you can imagine as a relational data table, with rows and named columns.\n", " * **`Series`**, which is a single column. A `DataFrame` contains one or more `Series` and a name for each `Series`.\n", "\n", "The data frame is a commonly used abstraction for data manipulation. Similar implementations exist in [Spark](https://spark.apache.org/) and [R](https://www.r-project.org/about.html)." ], "id": "daQreKXIUslr" }, { "cell_type": "markdown", "metadata": { "id": "fjnAk1xcU0yc" }, "source": [ "One way to create a `Series` is to construct a `Series` object. For example:" ], "id": "fjnAk1xcU0yc" }, { "cell_type": "code", "metadata": { "id": "DFZ42Uq7UFDj", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "79a0d94d-bd1f-4fe5-a63e-2ace0a24202f" }, "source": [ "pd.Series(['San Francisco', 'San Jose', 'Sacramento'])" ], "id": "DFZ42Uq7UFDj", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "0 San Francisco\n", "1 San Jose\n", "2 Sacramento\n", "dtype: object" ] }, "metadata": { "tags": [] }, "execution_count": 32 } ] }, { "cell_type": "markdown", "metadata": { "id": "U5ouUp1cU6pC" }, "source": [ "`DataFrame` objects can be created by passing a `dict` mapping `string` column names to their respective `Series`. If the `Series` don't match in length, missing values are filled with special [NA/NaN](http://pandas.pydata.org/pandas-docs/stable/missing_data.html) values. Example:" ], "id": "U5ouUp1cU6pC" }, { "cell_type": "code", "metadata": { "id": "avgr6GfiUh8t", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "93460921-0d30-4a2d-b218-a044231d0d7f" }, "source": [ "city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])\n", "population = pd.Series([852469, 1015785, 485199])\n", "\n", "pd.DataFrame({ 'City name': city_names, 'Population': population })" ], "id": "avgr6GfiUh8t", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
City namePopulation
0San Francisco852469
1San Jose1015785
2Sacramento485199
\n", "
" ], "text/plain": [ " City name Population\n", "0 San Francisco 852469\n", "1 San Jose 1015785\n", "2 Sacramento 485199" ] }, "metadata": { "tags": [] }, "execution_count": 33 } ] }, { "cell_type": "markdown", "metadata": { "id": "oa5wfZT7VHJl" }, "source": [ "But most of the time, you load an entire file into a `DataFrame`. The following example loads a file with California housing data. Run the following cell to load the data and create feature definitions:" ], "id": "oa5wfZT7VHJl" }, { "cell_type": "markdown", "metadata": { "id": "COMLdx9d6kxM" }, "source": [ "One way to create a `Series` is to construct a `Series` object. For example:" ], "id": "COMLdx9d6kxM" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "472nlbEX6kxN", "outputId": "6ef1a358-35fd-4df5-d2af-ce8e2c00d546" }, "source": [ "pd.Series(['San Francisco', 'San Jose', 'Sacramento'])" ], "id": "472nlbEX6kxN", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "0 San Francisco\n", "1 San Jose\n", "2 Sacramento\n", "dtype: object" ] }, "metadata": { "tags": [] }, "execution_count": 34 } ] }, { "cell_type": "markdown", "metadata": { "id": "Ijfpm53w6kxO" }, "source": [ "`DataFrame` objects can be created by passing a `dict` mapping `string` column names to their respective `Series`. If the `Series` don't match in length, missing values are filled with special [NA/NaN](http://pandas.pydata.org/pandas-docs/stable/missing_data.html) values. Example:" ], "id": "Ijfpm53w6kxO" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "mAgvwbWu6kxO", "outputId": "e64edd06-0506-476b-94ef-3c3030b74219" }, "source": [ "city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])\n", "population = pd.Series([852469, 1015785, 485199])\n", "\n", "pd.DataFrame({ 'City name': city_names, 'Population': population })" ], "id": "mAgvwbWu6kxO", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
City namePopulation
0San Francisco852469
1San Jose1015785
2Sacramento485199
\n", "
" ], "text/plain": [ " City name Population\n", "0 San Francisco 852469\n", "1 San Jose 1015785\n", "2 Sacramento 485199" ] }, "metadata": { "tags": [] }, "execution_count": 35 } ] }, { "cell_type": "markdown", "metadata": { "id": "L82F07sU6kxP" }, "source": [ "But most of the time, you load an entire file into a `DataFrame`. The following example loads a file with California housing data." ], "id": "L82F07sU6kxP" }, { "cell_type": "code", "metadata": { "id": "av6RYOraVG1V", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "714a3628-1a54-45fb-fb0b-4a7faedc3643" }, "source": [ "california_housing_dataframe = pd.read_csv(\"https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv\", sep=\",\")\n", "california_housing_dataframe.describe()" ], "id": "av6RYOraVG1V", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomemedian_house_value
count17000.00000017000.00000017000.00000017000.00000017000.00000017000.00000017000.00000017000.00000017000.000000
mean-119.56210835.62522528.5893532643.664412539.4108241429.573941501.2219413.883578207300.912353
std2.0051662.13734012.5869372179.947071421.4994521147.852959384.5208411.908157115983.764387
min-124.35000032.5400001.0000002.0000001.0000003.0000001.0000000.49990014999.000000
25%-121.79000033.93000018.0000001462.000000297.000000790.000000282.0000002.566375119400.000000
50%-118.49000034.25000029.0000002127.000000434.0000001167.000000409.0000003.544600180400.000000
75%-118.00000037.72000037.0000003151.250000648.2500001721.000000605.2500004.767000265000.000000
max-114.31000041.95000052.00000037937.0000006445.00000035682.0000006082.00000015.000100500001.000000
\n", "
" ], "text/plain": [ " longitude latitude ... median_income median_house_value\n", "count 17000.000000 17000.000000 ... 17000.000000 17000.000000\n", "mean -119.562108 35.625225 ... 3.883578 207300.912353\n", "std 2.005166 2.137340 ... 1.908157 115983.764387\n", "min -124.350000 32.540000 ... 0.499900 14999.000000\n", "25% -121.790000 33.930000 ... 2.566375 119400.000000\n", "50% -118.490000 34.250000 ... 3.544600 180400.000000\n", "75% -118.000000 37.720000 ... 4.767000 265000.000000\n", "max -114.310000 41.950000 ... 15.000100 500001.000000\n", "\n", "[8 rows x 9 columns]" ] }, "metadata": { "tags": [] }, "execution_count": 36 } ] }, { "cell_type": "markdown", "metadata": { "id": "WrkBjfz5kEQu" }, "source": [ "The example above used `DataFrame.describe` to show interesting statistics about a `DataFrame`. Another useful function is `DataFrame.head`, which displays the first few records of a `DataFrame`:" ], "id": "WrkBjfz5kEQu" }, { "cell_type": "code", "metadata": { "id": "s3ND3bgOkB5k", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "0c9393a4-6514-40c4-b49a-30167cd9f39b" }, "source": [ "california_housing_dataframe.head()" ], "id": "s3ND3bgOkB5k", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
longitudelatitudehousing_median_agetotal_roomstotal_bedroomspopulationhouseholdsmedian_incomemedian_house_value
0-114.3134.1915.05612.01283.01015.0472.01.493666900.0
1-114.4734.4019.07650.01901.01129.0463.01.820080100.0
2-114.5633.6917.0720.0174.0333.0117.01.650985700.0
3-114.5733.6414.01501.0337.0515.0226.03.191773400.0
4-114.5733.5720.01454.0326.0624.0262.01.925065500.0
\n", "
" ], "text/plain": [ " longitude latitude ... median_income median_house_value\n", "0 -114.31 34.19 ... 1.4936 66900.0\n", "1 -114.47 34.40 ... 1.8200 80100.0\n", "2 -114.56 33.69 ... 1.6509 85700.0\n", "3 -114.57 33.64 ... 3.1917 73400.0\n", "4 -114.57 33.57 ... 1.9250 65500.0\n", "\n", "[5 rows x 9 columns]" ] }, "metadata": { "tags": [] }, "execution_count": 37 } ] }, { "cell_type": "markdown", "metadata": { "id": "w9-Es5Y6laGd" }, "source": [ "Another powerful feature of *pandas* is graphing. For example, `DataFrame.hist` lets you quickly study the distribution of values in a column:" ], "id": "w9-Es5Y6laGd" }, { "cell_type": "code", "metadata": { "id": "nqndFVXVlbPN", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "2f7aaae6-d2e5-4426-afd1-2f75e86cbcfd" }, "source": [ "california_housing_dataframe.hist('housing_median_age')" ], "id": "nqndFVXVlbPN", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "array([[]],\n", " dtype=object)" ] }, "metadata": { "tags": [] }, "execution_count": 38 }, { "output_type": "display_data", "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [], "needs_background": "light" } } ] }, { "cell_type": "markdown", "metadata": { "id": "XtYZ7114n3b-" }, "source": [ "### Accessing Data\n", "\n", "You can access `DataFrame` data using familiar Python dict/list operations:" ], "id": "XtYZ7114n3b-" }, { "cell_type": "code", "metadata": { "id": "_TFm7-looBFF", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "92873b3d-7aee-4b88-c480-345700a69497" }, "source": [ "cities = pd.DataFrame({ 'City name': city_names, 'Population': population })\n", "print(type(cities['City name']))\n", "cities['City name']" ], "id": "_TFm7-looBFF", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "\n" ], "name": "stdout" }, { "output_type": "execute_result", "data": { "text/plain": [ "0 San Francisco\n", "1 San Jose\n", "2 Sacramento\n", "Name: City name, dtype: object" ] }, "metadata": { "tags": [] }, "execution_count": 39 } ] }, { "cell_type": "code", "metadata": { "id": "V5L6xacLoxyv", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "0960eed7-a819-4601-b589-05a0d7f57f83" }, "source": [ "print(type(cities['City name'][1]))\n", "cities['City name'][1]" ], "id": "V5L6xacLoxyv", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "\n" ], "name": "stdout" }, { "output_type": "execute_result", "data": { "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" }, "text/plain": [ "'San Jose'" ] }, "metadata": { "tags": [] }, "execution_count": 40 } ] }, { "cell_type": "code", "metadata": { "id": "gcYX1tBPugZl", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "4f1a671f-fa08-4a8e-f40d-fbbc9da5cbd3" }, "source": [ "print(type(cities[0:2]))\n", "cities[0:2]" ], "id": "gcYX1tBPugZl", "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "\n" ], "name": "stdout" }, { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
City namePopulation
0San Francisco852469
1San Jose1015785
\n", "
" ], "text/plain": [ " City name Population\n", "0 San Francisco 852469\n", "1 San Jose 1015785" ] }, "metadata": { "tags": [] }, "execution_count": 41 } ] }, { "cell_type": "markdown", "metadata": { "id": "65g1ZdGVjXsQ" }, "source": [ "In addition, *pandas* provides an extremely rich API for advanced [indexing and selection](http://pandas.pydata.org/pandas-docs/stable/indexing.html) that is too extensive to be covered here." ], "id": "65g1ZdGVjXsQ" }, { "cell_type": "markdown", "metadata": { "id": "RM1iaD-ka3Y1" }, "source": [ "### Manipulating Data\n", "\n", "You may apply Python's basic arithmetic operations to `Series`. For example:" ], "id": "RM1iaD-ka3Y1" }, { "cell_type": "code", "metadata": { "id": "XWmyCFJ5bOv-", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "c9beb5c6-5a6b-4c28-dd32-a25db90718df" }, "source": [ "population / 1000." ], "id": "XWmyCFJ5bOv-", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "0 852.469\n", "1 1015.785\n", "2 485.199\n", "dtype: float64" ] }, "metadata": { "tags": [] }, "execution_count": 42 } ] }, { "cell_type": "markdown", "metadata": { "id": "ZeYYLoV9b9fB" }, "source": [ "\n", "Modifying `DataFrames` is also straightforward. For example, the following code adds two `Series` to an existing `DataFrame`:" ], "id": "ZeYYLoV9b9fB" }, { "cell_type": "code", "metadata": { "id": "0gCEX99Hb8LR", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "d4bbdcf1-e2d6-412c-bbed-19ff303e1119" }, "source": [ "cities['Area square miles'] = pd.Series([46.87, 176.53, 97.92])\n", "cities['Population density'] = cities['Population'] / cities['Area square miles']\n", "cities" ], "id": "0gCEX99Hb8LR", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
City namePopulationArea square milesPopulation density
0San Francisco85246946.8718187.945381
1San Jose1015785176.535754.177760
2Sacramento48519997.924955.055147
\n", "
" ], "text/plain": [ " City name Population Area square miles Population density\n", "0 San Francisco 852469 46.87 18187.945381\n", "1 San Jose 1015785 176.53 5754.177760\n", "2 Sacramento 485199 97.92 4955.055147" ] }, "metadata": { "tags": [] }, "execution_count": 43 } ] }, { "cell_type": "markdown", "metadata": { "id": "s6HmFYSM8_tP" }, "source": [ "### Indexes\n", "Both `Series` and `DataFrame` objects also define an `index` property that assigns an identifier value to each `Series` item or `DataFrame` row. \n", "\n", "By default, at construction, *pandas* assigns index values that reflect the ordering of the source data. Once created, the index values are stable; that is, they do not change when data is reordered." ], "id": "s6HmFYSM8_tP" }, { "cell_type": "code", "metadata": { "id": "2684gsWNinq9", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "f0a638fe-de3e-4072-b43b-d70919d99f9a" }, "source": [ "city_names.index" ], "id": "2684gsWNinq9", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "RangeIndex(start=0, stop=3, step=1)" ] }, "metadata": { "tags": [] }, "execution_count": 44 } ] }, { "cell_type": "code", "metadata": { "id": "F_qPe2TBjfWd", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "f7320007-88c8-4484-8e6f-4c544d7a430a" }, "source": [ "cities.index" ], "id": "F_qPe2TBjfWd", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "RangeIndex(start=0, stop=3, step=1)" ] }, "metadata": { "tags": [] }, "execution_count": 45 } ] }, { "cell_type": "markdown", "metadata": { "id": "hp2oWY9Slo_h" }, "source": [ "Call `DataFrame.reindex` to manually reorder the rows. For example, the following has the same effect as sorting by city name:" ], "id": "hp2oWY9Slo_h" }, { "cell_type": "code", "metadata": { "id": "sN0zUzSAj-U1", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "66ff6fbe-5891-4e46-d18e-f862a64d718c" }, "source": [ "cities.reindex([2, 0, 1])" ], "id": "sN0zUzSAj-U1", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
City namePopulationArea square milesPopulation density
2Sacramento48519997.924955.055147
0San Francisco85246946.8718187.945381
1San Jose1015785176.535754.177760
\n", "
" ], "text/plain": [ " City name Population Area square miles Population density\n", "2 Sacramento 485199 97.92 4955.055147\n", "0 San Francisco 852469 46.87 18187.945381\n", "1 San Jose 1015785 176.53 5754.177760" ] }, "metadata": { "tags": [] }, "execution_count": 46 } ] }, { "cell_type": "markdown", "metadata": { "id": "U0ynHLEv-PfU" }, "source": [ "## **SciPy**\n", "\n", "Reference [J.R. Johansson scientific-python-lectures](https://colab.research.google.com/github/jrjohansson/scientific-python-lectures/blob/master/Lecture-3-Scipy.ipynb#scrollTo=UNBywy94-DOE)" ], "id": "U0ynHLEv-PfU" }, { "cell_type": "code", "metadata": { "id": "ot1L-Sn9-DOD" }, "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "from IPython.display import Image" ], "id": "ot1L-Sn9-DOD", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "lhheU7xc-DOF" }, "source": [ "The SciPy framework builds on top of the low-level NumPy framework for multidimensional arrays, and provides a large number of higher-level scientific algorithms. Some of the topics that SciPy covers are:\n", "\n", "* Special functions ([scipy.special](http://docs.scipy.org/doc/scipy/reference/special.html))\n", "* Integration ([scipy.integrate](http://docs.scipy.org/doc/scipy/reference/integrate.html))\n", "* Optimization ([scipy.optimize](http://docs.scipy.org/doc/scipy/reference/optimize.html))\n", "* Interpolation ([scipy.interpolate](http://docs.scipy.org/doc/scipy/reference/interpolate.html))\n", "* Fourier Transforms ([scipy.fftpack](http://docs.scipy.org/doc/scipy/reference/fftpack.html))\n", "* Signal Processing ([scipy.signal](http://docs.scipy.org/doc/scipy/reference/signal.html))\n", "* Linear Algebra ([scipy.linalg](http://docs.scipy.org/doc/scipy/reference/linalg.html))\n", "* Sparse Eigenvalue Problems ([scipy.sparse](http://docs.scipy.org/doc/scipy/reference/sparse.html))\n", "* Statistics ([scipy.stats](http://docs.scipy.org/doc/scipy/reference/stats.html))\n", "* Multi-dimensional image processing ([scipy.ndimage](http://docs.scipy.org/doc/scipy/reference/ndimage.html))\n", "* File IO ([scipy.io](http://docs.scipy.org/doc/scipy/reference/io.html))\n", "\n", "Each of these submodules provides a number of functions and classes that can be used to solve problems in their respective topics.\n", "\n", "To access the SciPy package in a Python program, we start by importing everything from the `scipy` module." ], "id": "lhheU7xc-DOF" }, { "cell_type": "code", "metadata": { "id": "t3Ooh82T-DOQ" }, "source": [ "from scipy import *" ], "id": "t3Ooh82T-DOQ", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "C-FKfjEv-DOR" }, "source": [ "If we only need to use part of the SciPy framework we can selectively include only those modules we are interested in. For example, to include the linear algebra package under the name `la`, we can do:" ], "id": "C-FKfjEv-DOR" }, { "cell_type": "code", "metadata": { "id": "hQb054p4-DOS" }, "source": [ "import scipy.linalg as la" ], "id": "hQb054p4-DOS", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "8Or8fRUY_gPI" }, "source": [ "### Special functions" ], "id": "8Or8fRUY_gPI" }, { "cell_type": "markdown", "metadata": { "id": "_p-FPWMR_gPN" }, "source": [ "A large number of mathematical special functions are important for many computional physics problems. SciPy provides implementations of a very extensive set of special functions. For details, see the list of functions in the reference documention at http://docs.scipy.org/doc/scipy/reference/special.html#module-scipy.special. \n", "\n", "To demonstrate the typical usage of special functions we will look in more detail at the Bessel functions:" ], "id": "_p-FPWMR_gPN" }, { "cell_type": "markdown", "metadata": { "id": "w727DnK4-DOc" }, "source": [ "### Integration" ], "id": "w727DnK4-DOc" }, { "cell_type": "markdown", "metadata": { "id": "v7kM_SZV-DOc" }, "source": [ "#### Numerical integration: quadrature" ], "id": "v7kM_SZV-DOc" }, { "cell_type": "markdown", "metadata": { "id": "SSPId-X3-DOd" }, "source": [ "Numerical evaluation of a function of the type\n", "\n", "$\\displaystyle \\int_a^b f(x) dx$\n", "\n", "is called *numerical quadrature*, or simply *quadature*. SciPy provides a series of functions for different kind of quadrature, for example the `quad`, `dblquad` and `tplquad` for single, double and triple integrals, respectively.\n", "\n" ], "id": "SSPId-X3-DOd" }, { "cell_type": "code", "metadata": { "id": "MhE0AQil-DOd" }, "source": [ "from scipy.integrate import quad, dblquad, tplquad" ], "id": "MhE0AQil-DOd", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "F-qZ5KpI-DOn" }, "source": [ "### Ordinary differential equations (ODEs)" ], "id": "F-qZ5KpI-DOn" }, { "cell_type": "markdown", "metadata": { "id": "6RoNvCaY-DOo" }, "source": [ "SciPy provides two different ways to solve ODEs: An API based on the function `odeint`, and object-oriented API based on the class `ode`. Usually `odeint` is easier to get started with, but the `ode` class offers some finer level of control.\n", "\n", "Here we will use the `odeint` functions. For more information about the class `ode`, try `help(ode)`. It does pretty much the same thing as `odeint`, but in an object-oriented fashion.\n", "\n", "For example, to use `odeint`, import it from the `scipy.integrate` module" ], "id": "6RoNvCaY-DOo" }, { "cell_type": "code", "metadata": { "id": "W3dCJkmt-DOo" }, "source": [ "from scipy.integrate import odeint, ode" ], "id": "W3dCJkmt-DOo", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "jF4qyrWx-DO1" }, "source": [ "### Fourier transform" ], "id": "jF4qyrWx-DO1" }, { "cell_type": "markdown", "metadata": { "id": "S6n0KaUp-DO1" }, "source": [ "Fourier transforms are one of the universal tools in computational physics, which appear over and over again in different contexts. SciPy provides functions for accessing the classic [FFTPACK](http://www.netlib.org/fftpack/) library from NetLib, which is an efficient and well tested FFT library written in FORTRAN. The SciPy API has a few additional convenience functions, but overall the API is closely related to the original FORTRAN library.\n", "\n", "To use the `fftpack` module in a python program, include it using:" ], "id": "S6n0KaUp-DO1" }, { "cell_type": "code", "metadata": { "id": "QNDx_xga-DO2" }, "source": [ "from numpy.fft import fftfreq\n", "from scipy.fftpack import *" ], "id": "QNDx_xga-DO2", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "RkjWVTHu-DO6" }, "source": [ "### Linear algebra" ], "id": "RkjWVTHu-DO6" }, { "cell_type": "markdown", "metadata": { "id": "zgYRfZTS-DO6" }, "source": [ "The linear algebra module contains a lot of matrix related functions, including linear equation solving, eigenvalue solvers, matrix functions (for example matrix-exponentiation), a number of different decompositions (SVD, LU, cholesky), etc. \n", "\n", "Detailed documetation is available at: http://docs.scipy.org/doc/scipy/reference/linalg.html\n", "\n" ], "id": "zgYRfZTS-DO6" }, { "cell_type": "markdown", "metadata": { "id": "iHMqBtt8-DPd" }, "source": [ "### Optimization" ], "id": "iHMqBtt8-DPd" }, { "cell_type": "markdown", "metadata": { "id": "Mrytp8a--DPd" }, "source": [ "Optimization (finding minima or maxima of a function) is a large field in mathematics, and optimization of complicated functions or in many variables can be rather involved. Here we will only look at a few very simple cases. For a more detailed introduction to optimization with SciPy see: http://scipy-lectures.github.com/advanced/mathematical_optimization/index.html\n", "\n", "To use the optimization module in scipy first include the `optimize` module:" ], "id": "Mrytp8a--DPd" }, { "cell_type": "code", "metadata": { "id": "WFMKeEXI-DPe" }, "source": [ "from scipy import optimize" ], "id": "WFMKeEXI-DPe", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "bccDMQzq-DPo" }, "source": [ "### Interpolation" ], "id": "bccDMQzq-DPo" }, { "cell_type": "markdown", "metadata": { "id": "VANZkmg6-DPp" }, "source": [ "Interpolation is simple and convenient in scipy: The `interp1d` function, when given arrays describing X and Y data, returns and object that behaves like a function that can be called for an arbitrary value of x (in the range covered by X), and it returns the corresponding interpolated y value:" ], "id": "VANZkmg6-DPp" }, { "cell_type": "markdown", "metadata": { "id": "hto5zbSL-DPr" }, "source": [ "### Statistics" ], "id": "hto5zbSL-DPr" }, { "cell_type": "markdown", "metadata": { "id": "DLxyl93J-DPr" }, "source": [ "The `scipy.stats` module contains a large number of statistical distributions, statistical functions and tests. For a complete documentation of its features, see http://docs.scipy.org/doc/scipy/reference/stats.html.\n", "\n", "There is also a very powerful python package for statistical modelling called statsmodels. See http://statsmodels.sourceforge.net for more details." ], "id": "DLxyl93J-DPr" }, { "cell_type": "markdown", "metadata": { "id": "tEINf4bEL9jR" }, "source": [ "##Matplotlib" ], "id": "tEINf4bEL9jR" }, { "cell_type": "markdown", "metadata": { "id": "0hgVWLaXL9jR" }, "source": [ "Matplotlib is a plotting library. In this section give a brief introduction to the `matplotlib.pyplot` module, which provides a plotting system similar to that of MATLAB." ], "id": "0hgVWLaXL9jR" }, { "cell_type": "code", "metadata": { "id": "cmh_7c6KL9jR" }, "source": [ "import matplotlib.pyplot as plt" ], "id": "cmh_7c6KL9jR", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "jOsaA5hGL9jS" }, "source": [ "By running this special iPython command, we will be displaying plots inline:" ], "id": "jOsaA5hGL9jS" }, { "cell_type": "code", "metadata": { "id": "ijpsmwGnL9jT" }, "source": [ "%matplotlib inline" ], "id": "ijpsmwGnL9jT", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "U5Z_oMoLL9jV" }, "source": [ "###Plotting" ], "id": "U5Z_oMoLL9jV" }, { "cell_type": "markdown", "metadata": { "id": "6QyFJ7dhL9jV" }, "source": [ "The most important function in `matplotlib` is plot, which allows you to plot 2D data. Here is a simple example:" ], "id": "6QyFJ7dhL9jV" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "pua52BGeL9jW", "outputId": "02d2486a-b148-4557-84a3-e558c0c2e7b3" }, "source": [ "# Compute the x and y coordinates for points on a sine curve\n", "x = np.arange(0, 3 * np.pi, 0.1)\n", "y = np.sin(x)\n", "\n", "# Plot the points using matplotlib\n", "plt.plot(x, y)" ], "id": "pua52BGeL9jW", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[]" ] }, "metadata": { "tags": [] }, "execution_count": 56 }, { "output_type": "display_data", "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [], "needs_background": "light" } } ] }, { "cell_type": "markdown", "metadata": { "id": "9W2VAcLiL9jX" }, "source": [ "With just a little bit of extra work we can easily plot multiple lines at once, and add a title, legend, and axis labels:" ], "id": "9W2VAcLiL9jX" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TfCQHJ5AL9jY", "outputId": "33fea410-20b1-4612-bba5-07e4f93d8dde" }, "source": [ "y_sin = np.sin(x)\n", "y_cos = np.cos(x)\n", "\n", "# Plot the points using matplotlib\n", "plt.plot(x, y_sin)\n", "plt.plot(x, y_cos)\n", "plt.xlabel('x axis label')\n", "plt.ylabel('y axis label')\n", "plt.title('Sine and Cosine')\n", "plt.legend(['Sine', 'Cosine'])" ], "id": "TfCQHJ5AL9jY", "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 57 }, { "output_type": "display_data", "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [], "needs_background": "light" } } ] }, { "cell_type": "markdown", "metadata": { "id": "R5IeAY03L9ja" }, "source": [ "###Subplots " ], "id": "R5IeAY03L9ja" }, { "cell_type": "markdown", "metadata": { "id": "CfUzwJg0L9ja" }, "source": [ "You can plot different things in the same figure using the subplot function. Here is an example:" ], "id": "CfUzwJg0L9ja" }, { "cell_type": "code", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "dM23yGH9L9ja", "outputId": "d864071a-f186-4b19-aec6-90d0b801ad2a" }, "source": [ "# Compute the x and y coordinates for points on sine and cosine curves\n", "x = np.arange(0, 3 * np.pi, 0.1)\n", "y_sin = np.sin(x)\n", "y_cos = np.cos(x)\n", "\n", "# Set up a subplot grid that has height 2 and width 1,\n", "# and set the first such subplot as active.\n", "plt.subplot(2, 1, 1)\n", "\n", "# Make the first plot\n", "plt.plot(x, y_sin)\n", "plt.title('Sine')\n", "\n", "# Set the second subplot as active, and make the second plot.\n", "plt.subplot(2, 1, 2)\n", "plt.plot(x, y_cos)\n", "plt.title('Cosine')\n", "\n", "# Show the figure.\n", "plt.show()" ], "id": "dM23yGH9L9ja", "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAEICAYAAABCnX+uAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3deVzU1f7H8ddh2HcFFAVZVBQ3ZHPNbLG6mpZmmkuall3LVtv35bbdbrua3XJLM9MsM8tKK7PScgMRRVFBcEFUQARk387vD/D+bLFchvnO8nk+Hj4eMsLMe0bn7fme+Z7zVVprhBBC2C4nowMIIYS4MFLkQghh46TIhRDCxkmRCyGEjZMiF0IIGydFLoQQNk6KXDgspdSNSqlvjc4hxIVSch65sHdKqX7AK0AXoA5IB6ZqrbcYGkwIM3E2OoAQTUkp5QusBKYASwFX4GKgyshcQpiTTK0Ie9cBQGu9WGtdp7Wu0Fp/q7XerpSaqJRaf+oblVJaKXW7UipDKVWklJqplFKn/fktSql0pdQJpdRqpVS4EU9IiN+TIhf2bi9Qp5RaoJQapJRq9jffPwToAcQANwD/AFBKDQUeB4YDQcA6YHGTpRbiHEiRC7umtS4B+gEamA3kK6W+UEq1PMOPvKy1LtJaHwTWArGNt98O/Ftrna61rgVeAmJlVC6sgRS5sHuN5TtRax0KdAVaA2+d4duPnvb7csC78ffhwLTGKZcioBBQQEgTxRbirEmRC4eitd4NzKeh0M/FIeA2rbX/ab88tNa/mj2kEOdIilzYNaVUtFLqAaVUaOPXbYAxwMZzvKt3gceUUl0a78dPKTXSvGmFOD9S5MLenQR6AZuUUmU0FHga8MC53InWejnwH2CJUqqk8T4GmTmrEOdFFgQJIYSNkxG5EELYOClyIYSwcVLkQghh46TIhRDCxhmyaVZgYKCOiIgw4qGFEMJmJScnF2itg35/uyFFHhERQVJSkhEPLYQQNkspdeDPbjfL1IpSap5SKk8plWaO+xNCCHH2zDVHPh8YaKb7EkIIcQ7MMrWitf5ZKRVhjvuyNxXVdSQdKGTP0ZNkHCslM7+Ukooaquvqqa6tx8PFRLCfO8F+7kQEeNEjojlxYf64u5iMji6ExR04XsaGfcfJKigju6CMg8fLqaytQ2vQaHzcXAhr7klYgCftg7zp2z6A0GaeRsc2nMXmyJVSk4HJAGFhYZZ6WEMUV9SwKu0I3+3KY31mPpU19QAEeLnSvoU37YK8cXNxwtXkRHl1HUeKK9i47zjLUw6jNbg6OxEf5s/Q2BCGxLTCx93F4GckRNPQWpN84ARfpuby09589h8vBxreA+HNPQkP8MLLzYQClFIUlVeTkXeSH/bkUV3b8L5qG+hF/w5BjEgIpWuIn4HPxjhmW6LfOCJfqbX+213lEhMTtT1+2Lkvv5T5v+xn2dYcyqvrCPH34IpOLbgsugXdQvwI8Hb7y58vLq9hy/5CNmUfZ+2efDLzSnF3ceLqbq2Y1C+SLq0d8x+psD/l1bV8npLLwo0HSD9SgoeLiT7tArikQxD9ogKJCPDC5KTO+PP19ZrM/FLWZRSwPiOfX/cdp6q2nu6hfoztFcbQ2BC7PKpVSiVrrRP/cLsU+YXbX1DGy9/sZtXOo7ianLg2tjUT+kTQNcSX064Udk601qTmFLM06RBfbMultKqWq7sFc98VHYhq6WPmZyCEZdTU1bNk80GmrcmgoLSaTq18ualPOENjW+Ppev4TBMUVNSzfmsNHmw+y91gprfzcue+KDgyPD8HZZD/LZaTIm0BxRQ0z1mSwYMN+XE1OTOoXyfg+EQT5/PXI+3weZ+66LOb9sp+y6lpGJoTy+NWd8Pd0NevjCNGUVu88ysvf7Ca7oIyekc158KqO9Ihodt6DnT+jtebXfcd5ZfUeUg8V0b6FN08O7sSlHVuY7TGM1KRFrpRaDFwKBALHgGe01nPP9P32UOTf7TrGY59t53hZNSMTQnnwqo608HVv0sc8UVbNOz9mMu+X/TTzdOHZa7swuFsrs74RhDC346VVPLUija93HCWqhTePDorm8ugWTfrvVmvNqrSjvLp6D1kFZVwfH8rTQzrj52nbnzc1+Yj8XNhykZ+srOH5lbtYmpRD51a+vDIixuIfsOzMLebRZTvYcbiYKzu35JXrY2jmJaNzYX2+2XGEJz9P42RlLVOvjGLyxW0tOtVRVVvHjDWZ/PenfQR4ufLv4d0Y0OlMl2u1flLkZrAjp5gpi5LJLapgyqXtuHdAB1ydjZl/q62rZ94v2by6eg8tfNx5e2wccWF/d4F4ISyjurae51bu5MONB+kW4sfrN3Sng4Gf7aQdLubBT1LZffQkt13Sloeu6miTc+dS5BdoeUoOjy7bQaC3G9PHxJIQ3tzoSACkHirijkVbyTtZyeNXd2Ji3wiZahGGOlpcyZRFyaQcLLKq0qyqreO5L3exaNNB+rQNYPqYOLN/ntXUpMjPU21dPS9/s5s567Pp3bY5M8fG/+1phJZWXF7DA59s4/v0PMb0bMPzQ7taxRtHOJ7kAye4bWESFdV1vDqyO1d3a2V0pD9YlpzD48t30MzTlbkTE23qtN4zFbm82/9CZU0dt3+YzJz12UzsG8HCSb2srsQB/DxdmH1TIndd1p7Fmw8xaUESpVW1RscSDua7XccYO3sj3m7OfH7nRVZZ4gDXJ4Ty2R19UQpGvbeRXzILjI50waTIz6C4oobxczexZncezw/twrPXdsHFike5Sike/EdH/j28G+szCxj57gbySiqNjiUcxOLNB7ltYRLRwT4sm9LX6tc6dGntx2d39CXE34OJ72/m85TDRke6INbbTAbKK6lk1Hsb2HaoiBlj4hjfJ8LoSGdtTM8w5k5I5MDxMkbN2siR4gqjIwk79/YPGTz22Q76dwhi8eTeVnnU+mda+Xmw9PY+JIQ3Y+rH21jw636jI503KfLfOVZSyahZGzlYWM68iT0YEtPa6Ejn7NKOLVg4qScFJ6u44b0NHCosNzqSsFNvfb+X177dy/C4EGbflHhBqzON4OfhwoJbenJV55Y888VO5q3PNjrSeZEiP03eyUrGzN5IXkklCyf15OKoP1yIw2YkhDfnw1t7UVxew+hZGzlwvMzoSMLOvPX9Xt76PoMRCaG8OrK7VU89/hU3ZxMzb4xnYJdgnlu5iznrsoyOdM5s85VvAvknqxg7exNHiyuZf0tPqzm98EJ0b+PPR//sTXl1LWNnb5JpFmE2077P+F+J/+f6mL/c4MoWuJicmDE2jqu7BfPCV+k2V+ZS5DScvjduziYOn6hg3sQe9Iiw/RI/pWuIHwsn9aKkouE5Hi+tMjqSsHHz1mfz5vd7uT7ePkr8FBeTE9NG/3+Zf5J0yOhIZ83hi7yypo5JC7aQXVDGnAmJ9G4bYHQks+sa4secCYnknKhgwvubKamsMTqSsFGfpxzmuZW7GNglmFdG2E+Jn+JicuLNUbFcHBXIo5/t4NudR42OdFYcushr6+q566MUkg+e4I1R3bmofaDRkZpMr7YBvDsugd1HTvLPBUlU1dYZHUnYmLV78njwk1T6tA3grdGxdlfip7g5m3h3XAJdQ/y4a3EKG/YdNzrS33LYItda8+TnaXyffoxnr+lik2ennKvLolvw+g3d2ZRdyMOfbqe+3vKreoVtSj1UxJQPk4lu5cOsmxLs8qINp/Nyc2b+xB6EN/dk8gdJ7D120uhIf8lhi/ydH/exZMsh7rqsPRP6Rhgdx2KGxobw8MCOrNiWyxvf7TU6jrABOSfKmbQgiSAfN+bf3NNhLj3YzMuV+bf0xN3VxM3vbyH/pPV+vuSQRf7V9iO8unoPw2Jb88BVHYyOY3FTLmnHmJ5teHttJks2HzQ6jrBiJZU1TJrfMBX3/sQeBNrIYh9zCfH3YO6ERI6XVXHrBw17yFgjhyvy1ENF3L90GwnhzXj5+hiH3ClQKcVzQ7vSv0MQT3yexq92sNeEML9TnyHtyy/l3XEJtG9h3cvum0pMqD/TRsexPaeI+z7eZpVTkg5V5LlFFdz6QcMh4nvj7X+e76+4mJyYOTaOtoFe3PHRVg4el9Wf4rde+Cqdn/fm88KwrnZ9IsDZ+EeXYJ64uhOrdh7lrTUZRsf5A4cp8sqaOm5bmExFdR3zHPAQ8c/4uDfsmqg1/PMD2TFR/L+lSYeY/+t+JvWLZHTPMKPjWIVJ/SIZkRDK9DUZrEo7YnSc33CIItda88TyNHYcLubNUbGGXqnE2kQEevH22Dgy8k5a7WGjsKyUgyd4cnkaF7UP4LFB0UbHsRpKKV4Y1pXYNv7cvzSV3UdLjI70Pw5R5At+3c+yrTncOyCKKzvb7vX6msrFUUE8Mbgz3+06xvQfrO+wUVhOXkklt3+YTEs/N94eEy8XKPkddxcT741PwNvNmX9+kMSJsmqjIwEOUOSbso7z/FfpXNGpJfcOiDI6jtW65aIIrosLYdqaDH7ck2d0HGGAmrp67li0lZKKWmaNT5QLep9BS1933h2fwLHiKqZayVGsXRd5Xkkld36UQniAJ2+M6o6Tna5EMwelFC9d142OLX24d8k22frWAb38zW6SDpzgPyNi6NTK1+g4Vi0+rBlPX9OZn/bmW8VRrN0W+alTp8qqanl3XAK+DrKI4UJ4uDYsTa7XmimLkqmssc5zZoX5fbX9CHMbL2l4bXf7X+VsDjf2CmN4vHUcxdptkb/67R427y/k38O7yYeb5yAi0Is3b4gl7XAJz36x0+g4wgL25Zfy8KepxIX58/jVnYyOYzOUUrw4rOEodurH28g5YdxRrF0W+bc7j/LeT1mM6x3GsLgQo+PYnCs6t+SOS9uxZMshlqfkGB1HNKGK6jqmfJiMm4uJd26Mx9XZLiuhyZw6iq2r09z5UQrVtfWG5LC7v7VDheU88EkqMaF+PDWks9FxbNb9V3agZ2RznlieRmZeqdFxRBN55os0MvJKeWtULK38PIyOY5MiAr14ZUQMqYeK+M+q3YZksKsir66t567FKQDMHBuPm7Pjrty8UM4mJ2aMicPDxcSdi7Za7R4T4vx9tjWHpUk53HVZe/p3sN3LGlqDQd1aMbFvBHPXZxuyh7ldFfkrq3aTeqiIV0fE0Ka5p9FxbF5LX3feHBXL3ryTPPNFmtFxhBll5p3kieVp9IxsLqflmsljV0fTLcSPBz9JtfhZX3ZT5N/vOsac9dlM6BPOwK6tjI5jN/p3COLOS9uzNCmHFdsOGx1HmEFlTR13LkrBw9XE9NFxsujHTNycTcwcG4/WcPfiFGrqLDdfbhd/g7lFFTz4aSpdWvvymHzqbnZTr4giMbwZTyxPY39BmdFxxAV6buUu9hw7yRs3dCfYz93oOHYlLMCTl6+PYduhIl7/1nL7/dt8kdfVa6Yu2UZNbT1vj4136B0Nm4qzyYlpY+IwOSnuXmzcJ/Piwn2z4wgfbTrIbf3bcmnHFkbHsUuDY1oxpmcY7/60j5/35lvkMW2+yGf8kMHm/YU8P6wrkYFeRsexWyH+HrwyIoYdh4sN+2ReXJicE+U8smw73UP9eOCqjkbHsWtPD+lMh5be3L801SJXFrLpIt+cXcj0NRkMjwtheHyo0XHs3j+6BDOhTzhz12ezVvZjsSm1dfVMXbKNeg0zxsj54k3Nw9XEjDHxnKys4f6lTb8fi83+bRaVVzN1SQphzT15blhXo+M4jMeu7kR0sA8PLk0l72Sl0XHEWZq+JoOkAyd48bquhAXIGV2W0DHYh6ev6cy6jALmrM9q0seyySLXWvPosh3kl1YxY0w83m7ORkdyGO4uJmaMiaOsupYHlqZaxc5v4q9tzDrO22szGZkQytBYWelsSWN7hjGwSzCvrt7DjpziJnscmyzyxZsPsWrnUR7+RzTdQv2MjuNwolr68NQQy4w0xIUpKq/mvo+3ER7gxbPXdjE6jsNRSvHy9d0I9HbjniUNm/g1BbMUuVJqoFJqj1IqUyn1qDnu80wyjp3kuZU7uTgqkEn9IpvyocRfODXSeGXVHrbnFBkdR/wJrTWPLNtOQWkV00fH4SVHrobw93TlzVGx7D9exjNNtBHdBRe5UsoEzAQGAZ2BMUqpJtnkpLKmjrsXp+Dl6szrN8j+4kY6NdII8nHjnsUpcr1PK/TR5oOs3nmMh/7RUY5cDda7bQB3XdaeT5NzWN0ES/jNMSLvCWRqrbO01tXAEmCoGe73D15ZtYfdR0/y2sjutPCRhQxGOzXSOFBYLlveWpmMYyd5fuUuLo4K5NZ+bY2OI4B7B0TxyMBoLo4KNPt9m6PIQ4BDp32d03jbbyilJiulkpRSSfn553eS/NXdgnnoHx25LFoWMliL00caX6TmGh1H8P9Hrp6uzrw+Uo5crYWzyYkpl7bD09X8U1wW+7BTaz1La52otU4MCjq/ndYSI5pz52XtzZxMXKh7BkQRF+bPE5/tkEvEWYH/rNrdeOQaQwtfOXJ1BOYo8sNAm9O+Dm28TTgIF5MT00fHAXDvkhRqLbhZkPittbvzeP+X/UzsG8Hl0S2NjiMsxBxFvgWIUkpFKqVcgdHAF2a4X2FD2jT35IXrurL1YBHT1hh/MVpHlFdSyYOfpBId7MOjg6KNjiMs6IKLXGtdC9wFrAbSgaVaa/nkywENjQ1hREIob6/NZMO+40bHcSj19Zr7l6ZSVl3LjDFxsnmcgzHLHLnW+mutdQetdTut9YvmuE9hm/51bRciAry47+NtnCirNjqOw3jv5yzWZxbwzDVdiJKLjTscm1zZKayXl5szM8bEcbysioeXbUdrWcLf1FIOnuD1b/cwuFsrRvdo8/c/IOyOFLkwu64hfjwyMJrvdh3jgw0HjI5j10oqa7hnSQotfd15aXg3lJJTDR2RFLloEpP6RXJ5dAte/CqdtMNNt1mQI9Na89hnO8gtqmT6mFj8PFyMjiQMIkUumoRSitdGdqeZlwt3yxL+JvHR5oN8tf0ID1zVgYTw5kbHEQaSIhdNprmXK9NGx3HgeBlPfZ4m8+VmlH6khOe+bFiCf3v/dkbHEQaTIhdNqnfbAO4ZEMXylMN8kpRjdBy7UF5dy10fbcXXw4U3R8XKEnwhRS6a3t2XR3FR+wCeWpFG+pESo+PYNK01TyxPI6ugjGmjYgn0djM6krACUuSiyZmcFG+NisPPw4U7F22V+fILsHjzIZanHGbqgA70bW/+XfSEbZIiFxYR5OPG9DFx7D9exmOf7ZD58vOQdriYZ7/YSf8OQdx9uWweJ/6fFLmwmN5tA3jgqo58mZrLgl/3Gx3HphRX1DBlUTIB3q68JfPi4nekyIVFTbmkHVd0asELX6WzZX+h0XFsQn295oGl2zhSVMnbY+Np7uVqdCRhZaTIhUU5OSlevyGW0GYe3LFoK3kllUZHsnozfsjk+/Q8nhzciYTwZkbHEVZIilxYnJ+HC++OT6C0spY7P9pKjexffkZr0o/x5vd7GR4fwoS+EUbHEVZKilwYIjrYl5ev78aW/Sd4fuUuo+NYpeyCMqZ+vI2uIb68dJ3soyLOzPwXjxPiLA2NDSHtcDGz12XTMdiHG3uFGx3JapRU1jD5gyScnRTvjkuQ/cXFX5IRuTDUo4M6cWnHIJ5ZsVMuRtGotq6euz9KIbugjHduTCC0mafRkYSVkyIXhjI5KaaPiSM8wJM7FiVz8LhcvPnFr9P5aW8+LwzrSp92AUbHETZAilwYztfdhTkTelCv4ZYFWygurzE6kmEWbTrA+7/sZ1K/SEb3DDM6jrARUuTCKkQGevHuuAQOHi9n8sIkqmrrjI5kcWv35PH0ip1c1jGIx6/uZHQcYUOkyIXV6NMugFdHxrApu5AHP9lOfb3jLONPPVTEHR9uJTrYh+lj4jDJyk1xDuSsFWFVhsaGkFtUyX9W7aa1nzuPOcDIdH9BGbfM30Kgjyvv39wDH3e50o84N1LkwurcfklbcosqeO/nLPw9XZlyqf1eOCHvZCU3zduMBhbc3JMWPu5GRxI2SIpcWB2lFM9e24WSyhr+s2o3Xm4mbuoTYXQsszteWsWNszdRUFrFolt70TbI2+hIwkZJkQurZHJquOZneXUdT6/YiaerMyMSQo2OZTZF5dWMm7uZg4XlzL+5J3FhsoeKOH/yYaewWi4mJ2aMiaNf+0Ae/jSV5Sn2cam4ksoabpq3mX15pcy+KVHOFRcXTIpcWDV3FxOzbkqgd9sA7l+aykebDhod6YIcL61i7OyNpB8p4b/j4unfIcjoSMIOSJELq+fp6sy8iT24tEMQjy/fwdz12UZHOi9Hiiu44b0NZBwr5b3xCQzo1NLoSMJOSJELm+DuYuK98YkM6hrM8yt38drqPTZ1nnl2QRkj/ruBvJIqFk7qxeXRUuLCfKTIhc1wdW6YMx+V2Ia312Zyz5IUKmusfwXohn3HGf7OL1TU1LF4cm96RjY3OpKwM3LWirApziYnXr6+G5FBXrz8zW4OF1Uw+6ZEAr3djI72pxZtOsAzK3YSHuDJ3Ak9iAj0MjqSsEMyIhc2RynF7Ze04783xpN+pIQh09ezKcu6tsCtrKnjqc/TeGJ5Gv2iAll+50VS4qLJSJELmzWoWys+vb0vHq4mxszeyIw1GdRZwbz5nqMnGTbzFxZuPMDk/m2ZO6EHvrLsXjQhKXJh07qG+PHl3f24pntrXv9uLzfO2UhWfqkhWerrNfN/yeaat9dTUFrF+xN78PjVnWQDLNHklNaWH8EkJibqpKQkiz+usF9aaz5JzuH5lbuoqqlnyqXtmHJpO4tdIm3boSKe+WInqYeKuKxjEK+M6E6Qj3XO2wvbpZRK1lon/v52+bBT2AWlFDcktuHSjkG8sDKdaWsyWLHtMFOv6MCQmFY4m5rm4PNIcQVvfLuXT5JzCPJx4/WR3RkeHyIXShYWJSNyYZfWZeTzwsp09hw7SWSgF3dc2o5rY1vj5myeEfruoyXM+jmLL7blohTcclEkdw+IwttNxkai6ZxpRH5BRa6UGgk8C3QCemqtz6qdpciFJdTXa77ddYzpazLYdaQEX3dnBse0Znh8CAlhzXA6x7nrYyWVrEo7ytc7jrApuxAPFxOjerRhUr9I2jSXCySLptdURd4JqAfeAx6UIhfWSGvNuowClqccZlXaUSpq6vDzcCEuzJ+EsGZ0auVLoI8bAV6u+Lq7UF5TS1lVLUXlNew+epJdR0pIO1zM9pxiAKJaeDM0tjU39gqnmZerwc9OOJImmSPXWqc33vmF3I0QTUopRf8OQfTvEMQLw2r5Pv0YG/YdJ/nACX7ck/+3P+/r7kzn1r7cf2UHBnUNJqqljwVSC3H2LDahp5SaDEwGCAuTq4MLY3i5OTM0NoShsSEAFJfXkH28jMKyKgpKqzlZWYunqwkvN2d83J1pH+RNaDMPGawIq/a3Ra6U+h4I/pM/ekJrveJsH0hrPQuYBQ1TK2edUIgm5OfpQqynv9ExhLggf1vkWusrLBFECCHE+ZGVnUIIYeMu9KyV64AZQBBQBGzTWv/jLH4uHzhwng8bCBSc58/aC3kN5DVw9OcPjvkahGut/3BZKUMWBF0IpVTSn51+40jkNZDXwNGfP8hrcDqZWhFCCBsnRS6EEDbOFot8ltEBrIC8BvIaOPrzB3kN/sfm5siFsBSl1E7gTq31j0ZnEeKv2OKIXIg/pZQaq5RKUkqVKqWOKKW+UUr1O9/701p3kRIXtkCKXNgFpdT9wFvAS0BLIAx4BxhqZC4hLMGmilwpNVAptUcplamUetToPJaklGqjlFqrlNqllNqplLrX6ExGUUqZlFIpSqmVjV/7Ac/RMA3ymda6TGtdo7X+Umv9kFLKTSn1llIqt/HXW0opt8afDVRKrVRKFSmlCpVS65RSTo1/tl8pdUXj759VSi1VSn2glDrZ+HeQeFqm1kqpZUqpfKVUtlLqniZ8/v5KqU+VUruVUulKqT5N9VjWSil1X+PfQZpSarFSyt3oTEaymSJXSpmAmcAgoDMwRinV2dhUFlULPKC17gz0Bu50sOd/unuB9NO+7gO4A8vP8P1P0PCaxQLdgZ7Ak41/9gCQQ8OitpbA48CZPji6FlgC+ANfAG8DNBb/l0AqEAIMAKYqpf52cdx5mgas0lpH0/B80v/m++2KUioEuAdI1Fp3BUzAaGNTGctmipyGN1+m1jpLa11NwxvKYQ6btdZHtNZbG39/koY3b4ixqSxPKRUKDAbmnHZzAFCgta49w4/dCDyntc7TWucD/wLGN/5ZDdCKhhVzNVrrdfrMZwCs11p/rbWuAxbSUKIAPYAgrfVzWutqrXUWMJsmKJfGo4/+wFyAxscrMvfj2ABnwEMp5Qx4ArkG5zGULRV5CHDotK9zcMAiA1BKRQBxwCZjkxjiLeBhGi5ocspxILDxTf1nWvPbLSEONN4G8CqQCXyrlMr6mym7o6f9vhxwb3zMcKB14/RMkVKqiIaRfcuzfVLnIBLIB95vnF6ao5TyaoLHsVpa68PAa8BB4AhQrLX+1thUxrKlIheAUsobWAZM1VqXGJ3HkpRSQ4A8rXXy7/5oA1AFDDvDj+bSULanhDXehtb6pNb6Aa11WxqmTu5XSg04x2iHgGyttf9pv3y01lef4/2cDWcgHviv1joOKAMc7fOiZjQcjUfS8B+yl1JqnLGpjGVLRX4YaHPa16GNtzkMpZQLDSW+SGv9mdF5DHARcK1Saj8NU2uXK6U+1FoXA08DM5VSw5RSnkopF6XUIKXUK8Bi4EmlVJBSKrDxez+Ehv8clFLtVcOVI4qBOn472j8bm4GTSqlHlFIejR/GdlVK9TDLs/6tHCBHa33qaOxTGordkVxBw3+c+VrrGuAzoK/BmQxlS0W+BYhSSkUqpVxpmH/8wuBMFtNYNHOBdK31G0bnMYLW+jGtdajWOoKGv/8ftNbjGv/sdeB+Gj7EzKdhlHwX8DnwApAEbAd2AFsbbwOIAr4HSmkY2b+jtV57jrnqgCE0fJiaTcOOfHMAv/N9rn/xWEeBQ0qpjo03DQB2mftxrNxBoHfjf9iKhtfAoT7w/T2bWtmplLqahjlSEzBPa/2iwZEspnFhyzoaiujUiPFxrfXXxqUyjlLqUhou+MbigIgAABwhSURBVD3E6CyWppSKpeE/ClcgC7hZa33C2FSWpZT6FzCKhrO5UoBbtdZVxqYyjk0VuRBCiD+ypakVIYQQf0KKXAghbJwUuRBC2LgzLaBoUoGBgToiIsKIhxZCCJuVnJxc8GfX7DRLkSul5tFw+lVe494HfykiIoKkpCRzPLQQQjgMpdSfXrTeXFMr84GBZrovIYQQ58AsI3Kt9c+N+380qfQjJeSfrMLf0wU/Dxeae7ni4+7S1A8rhM04UVZNfmkVZVW1VFTXARDo40aQtxt+Hi44OSmDE4qmYLE5cqXUZGAyQFhY2Hndx4cbD7Bo08Hf3BbW3JOYUD9i2/hzWXQL2gV5X3BWIWyB1podh4v5ftcxth8uJv1ICcdKzrwmxs3Zie6h/iRENKNHRDP6tgvE3cVkwcSiqZhtQVDjiHzl2cyRJyYm6vOZI88tqiC3qIKi8hqKK2o4WlLJjpxitucUkVtcCUCX1r5c070118WF0NLXofeaF3YqM+8kizYdZHXaUXKLKzE5KaJaeNOplS+dWvnQys8DbzdnPF1N1GsoKK2ioLSKg4XlbD1wgp25JdTWa3zcnLm6Wyuuiw+hZ0RzGa3bAKVUstY68Q+321KR/5Xcogq+STvKl6m5bDtUhKvJiesTQritfzsiAh1ql09hh7TWbMouZPbPWazZnYersxP9o4IY2DWYAdEtaObletb3VVFdx5b9hazYlss3aUcor66jQ0tv7hkQxdVdW0mhWzG7L/LT7S8oY876LJYm5VBbV8+w2BAeHRRNCxmhCxuUfqSE577cxYas4zT3cuWmPuGM7x1OgLfbBd93eXUt3+w4yn9/2kdmXilRLby5/8oODOwaTMN+VMKaNGmRK6UWA5cCgcAx4Bmt9dwzfX9TF/kpeSWVzFmfzfxf9+NqcuK+KzswoU84ziZZByWs34myat74bi+LNh3A18OFqQOiGN0zrEnmtevqNV/vOML0NRlk5JVySYcgXhjWlTbNPc3+WOL8NfmI/FxYqshP2V9QxjNf7OSnvflEB/vw1uhYooN9Lfb4QpyrtbvzeOjTVE6U1zCuVxj3XdkBf8+znz45X3X1mg827Oe11Xuo05p7B3Rgcv+2mGS6xSo4dJFDwxzj6p3HeGpFGsUVNTw5uBPje4fL4aOwKhXVdbz0dToLNx4gOtiHN0fF0qmV5QcdR4oreGbFTr7ddYzebZszbXScnDxgBRy+yE8pKK3iwU9S+XFPPld0aslrI2MsMtIR4u9k5ZcyeWEymXml3NovkocGdsTN2bjTA7XWfJqcw9MrduLpauLNUbH07/CH1eHCgs5U5A43WRzo7ca8CT14akhnftqbx3Xv/EpWfqnRsYSD+2lvPkNn/kJhWTUfTurFk0M6G1riAEopRia24Yu7LiLA25UJ729m5tpM5BoG1sfhihzAyUkxqV8kH/2zN8UVNVz3zq/8mllgdCzhgLTWzFmXxc3vbybE34MVd15Ev6hAo2P9RlRLH1bc2Y9rYlrz6uo9PLJsOzV153pZU9GUHLLIT+kR0ZwVd15ES183bpq3maVbDhkdSTiQ+nrNv77cxQtfpXNV52CWTelrtWeJeLiamDY6lnsub8/SpBwmvr+Z4ooao2OJRg5d5ABtmnuybEpf+rQL4OFl25m7PtvoSMIB1NTV88Anqcz/dT+T+kXyzo3xeLkZsqv0WVNKcf9VHXltZHc2ZxcyetZGjpc67GUyrYrDFzmAj7sLcyYkMqhrMM+v3MX0NRkyDyiaTGVNHVM+TGZ5ymEevKoDTw7uZFOrKUckhDJnQg+y8ksZNWsjeSWVRkdyeFLkjdycTcwYE8f18aG88d1eXv5mt5S5MLvKmjr++UESa3bn8fywrtx1eZRNngJ7SYcg5t/ck9yiCm54bwOHiyqMjuTQpMhP42xy4tURMYzvHc57P2fxxnd7jY4k7Eh1bT13LNrKuowC/nN9w78zW9anXQALJ/XieGk1o2dt4GixjMyNIkX+O05Oin9d24VRiW2Y8UMmM9dmGh1J2IGaunru+mgrP+zO46XrunFDYhujI5lFQngzFt7ai8LSasbN3SRz5gaRIv8TTk6Kl4Z3Y2hsw+lW8+QDUHEB6us1DyxN5dtdx/jXtV0Y2+v89uO3VrFt/Jk7sQeHCsu5ad5mSirlbBZLkyI/A5OT4vWR3RnYJZjnVu5ixbbDRkcSNkhrzfNf7eKL1FweGRjNhL4RRkdqEr3bBvDu+AT2HjvJpPlbqKypMzqSQ5Ei/wvOJiemjYmld9vmPPhJKr/IoiFxjmb9nMX7v+znlosiuf2StkbHaVKXdWzBW6PiSDpwgqlLtlFXLycLWIoU+d9wczbx3vhE2gZ6c/vCZNKPlBgdSdiI5Sk5/Pub3QyJacWTgzvZ5Nkp52pwTCueGtyZVTuP8uJX6UbHcRhS5GfBz8OF92/ugZebMxPf30yunGol/sbGrOM89Ml2+rQN4PUbutvUeeIX6pZ+kdxyUSTzfsmWBXYWIkV+llr7ezD/lh6UVdVx64IkyqtrjY4krNT+gjJu/zCZ8ABP3h2fYPjmV0Z4YnAnBnYJ5oWvdvHtzqNGx7F7UuTnIDrYl+ljYkk/WsIDS1OplzlA8TvFFTVMWrAFgLkTeuDn4WJwImOYnBRvjY4lJtSfqR9vY/dRmZJsSlLk5+jy6JY8PqgT36Qd5a3vZcGQ+H+1jeeKHyws591xCQ5/0W93FxOzxifg7ebMrQuS5BzzJiRFfh5uvTiSkQmhTP8hky9Tc42OI6zEy9/sZl1GAS8M60rvtgFGx7EKLX3dmXVTInknq5iyaCvVtbL9bVOQIj8PSilevK4bieHNePjT7XLYKFix7TBz1mczoU84o3rY14KfCxXbxp9Xro9hc3Yhz6/cZXQcuyRFfp5cnZ1458Z4fNyduW1hsuzN7MB25ZbwyLLt9IxozpNDOhsdxyoNiwthcv+2LNx4gGXJOUbHsTtS5Begha87/x0XT25RBfd9vE0+/HRAReXV3PZhEv4ersy8MR4Xk7ylzuThf3SkT9sAHl++g525xUbHsSvyr+4CJYQ35+khnflhdx7T1mQYHUdYUH29ZurH2zhWXMV/x8UT5ONmdCSr5mxyYsbYOJp5unL7h8kUlVcbHcluSJGbwbje4QyPD2H6Dxn8vDff6DjCQt75MZMf9+Tz1DWdiQtrZnQcmxDo7cY74+I5WlwpR7FmJEVuBkopXhzWjQ4tfJj68TaOFMvKT3v3a2YBb3y3l6GxrRlnZ7sZNrX4sGY8fU0X1u7J592f9xkdxy5IkZuJh6uJmTfGU1VTx90fpchVxu3YsZJK7lmSQtsgb166rptD7KFibuN6hTEkphWvrd7DpqzjRsexeVLkZtS+hTcvDe9G0oETvLp6j9FxRBOoravn7sUplFXV8V8buGCytVJK8e/h3QgP8OLuxSkUyGKhCyJFbmZDY0MY1zuMWT9n8cPuY0bHEWY2fU0Gm7MLefG6rkS19DE6jk3zcXdh5th4iitqZL78AkmRN4EnB3emcytfHliaKvPlduSXzAJmrM1kZEIow+NDjY5jFzq39uVf13ZhXUYB//1J5svPlxR5E3B3MfH22Diqauu5d/E2amW+3Obln6zi3iXbaBfkzb+GdjE6jl0Z1aMN13RvzRvf7SVpf6HRcWySFHkTaRvkzQvDurJ5fyHT5fxym1Zfr7l/6TZOVtbw9tg4PF1lXtyclFK8dF1XQvw9uGdxipxffh6kyJvQ8PhQRiSEMmNtJr/uk8vE2apZ67JYl1HAM9d0ITrY1+g4dsnH3YW3x8aRX1rFw59uR2uZLz8XUuRN7LmhXYgM9OK+j7dRWCYjDVuTcvAEr63ew+BurRjTs43RcexaTKg/jwyM5ttdx1i48YDRcWyKFHkT83R1ZvroOE6U1fDwp6ky0rAhJZU13LMkhZa+7rw0XM4Xt4RbLork0o5BvPBVuuwqeg6kyC2ga4gfjw6K5vv0PD7YICMNW6C15snlaeQWVTJ9TKzDXunH0pycFK+N7I6vuwv3LE6horrO6Eg2QYrcQm6+KILLo1vw4tfppB+RkYa1W7b1MF+k5nLvgCgSwpsbHcehBHq78cYN3dl7rJQXvpL9y8+GWYpcKTVQKbVHKZWplHrUHPdpb5RSvDoiBj8PF+6WkYZVyy4o4+kVafSKbM6dl7U3Oo5D6t8hiNv6t2XRpoOsSpOLN/+dCy5ypZQJmAkMAjoDY5RSsrv+nwhoHGlk5slIw1pV19Zz75IUXExOvDkqFpOTzIsb5YGrOhIT6sejn22XhXV/wxwj8p5AptY6S2tdDSwBhprhfu3SxVEy0rBmr3+3h+05xfzn+hha+3sYHcehuTo7MW10HNW19Uxdso06WcJ/RuYo8hDg0Glf5zTe9htKqclKqSSlVFJ+vmPv2f3AVR3pFiIjDWuzPqOA937KYmyvMAZ2DTY6jgAiA73417Vd2JRdyLuyhP+MLPZhp9Z6ltY6UWudGBQUZKmHtUquzk5MH9Mw0rjvYxlpWIPjpVXct3Qb7Vt489RgmRm0JiMSQv+3hH/rwRNGx7FK5ijyw8DpKyVCG28Tf+HUSGNjlow0jKa15qFPt1NcUcOMMXF4uJqMjiROo5TihWFdCfZ1594lKZRUyoXOf88cRb4FiFJKRSqlXIHRwBdmuF+7JyMN67Dg1/38sDuPxwZF06mVLMG3Rn4eLkwfE0tuUSVPLk+ThXW/c8FFrrWuBe4CVgPpwFKt9c4LvV9HoJTixeu60srPnXsWy0jDCOlHSnjpm91cHt2CiX0jjI4j/kJCeHOmDojii9Rclm2Vg/7TmWWOXGv9tda6g9a6ndb6RXPcp6PwdXdh2ug4jhTLSMPSyqtruXtxCn4eLrw6IkaW4NuAOy5rT6/I5jy9Io2s/FKj41gNWdlpBRLCm/1vpPFJco7RcRzGc1/uYl9+KW/eEEuAt5vRccRZMDkp3hwVi4vJiXuWpFBdK3v9gxS51bjjsvb0btucZ1bsJDNPRhpN7cvUXJZsOcTtl7SjX1Sg0XHEOWjt78ErI2JIO1zCK6t2Gx3HKkiRWwmTk2La6IYzJu76aCuVNbKEv6kcKizn8c92EBfmz/1XdjA6jjgP/+gSzE19wpmzPluujYsUuVVp6evOayNj2H30JP/+Ot3oOHappq6euxengILpo+NwMclbwFY9fnUnOjVeG/docaXRcQwl/4qtzOXRLZnUL5IFGw6wKu2I0XHszqur97DtUBEvD4+hTXNPo+OIC/Cba+MuSXHohXVS5FbokYHRdA/146FPt3OosNzoOHbjh93HmPVzFuN6hzE4ppXRcYQZtAvy5rmhXdmUXcg0B742rhS5FXJ1duLtsfEA3PXRVvlk3gxyiyq4f2kqnVv58qQswbcrIxJCuT4+lBk/ZLAuwzH3cZIit1Jtmnvy6ojupOYU8+9vZL78QtTU1XPP4hRqauuZeWM87i6yBN/ePD+sC+2DvJm6ZBvHShxvvlyK3IoN7BrMzRdF8P4v+/lmh8yXn69XVu0m6cAJXhrejchAL6PjiCbg6erMOzfGU15dx92LU6itc6yjWClyK/fYoE50b+PPQ59ul5Vs52FV2hFmr8tmfO9whsb+YXdlYUeiWvrw4nVd2ZxdyGvf7jU6jkVJkVs5V2cn3rkxHheTYsqHWymvrjU6ks3Iyi/lwU+2072NP08O6WR0HGEBw+NDGdMzjHd/2udQF26RIrcBIf4eTB8Tx968kzz22Q7Zj+UsVFTXcceirbiYFO/cGI+bs8yLO4pnr+1M91A/Hvwk1WGOYqXIbcTFUUE8cGUHVmzLZcGv+42OY9W01jyybDt7jp3krdFxhMgl2xyKm7OJd8Yl4GJS3P5hMmVV9n8UK0VuQ+64tD1XdGrBC1+ls2HfcaPjWK1ZP2fxRWouD17VkUs6OPbVqBxViL8HM8bEk5lXysPLttv9UawUuQ1xclK8MSqW8ABP7vxoqywW+hM/7c3nP6t2c3W3YO64tJ3RcYSB+kUF8vDAaL7afoSZazONjtOkpMhtjK+7C7NvSqSmrp7JC5Plw8/T7C8o4+6PttKhpQ+vjugu+4sLbuvflmGxrXnt2718u9N+P/yUIrdBbYO8mTEmjj1HS3jok+3UO/AeE6cUV9QwacEWnJwUs8Yn4uXmbHQkYQWUUrx8fQwxoX7c9/E29hw9aXSkJiFFbqMu7diCRwdF89WOI7z+3R6j4xiqpq6eOxYlc7CwnHfHJRAWIJthif/n7mJi1vhEPN2cmbRgC/knq4yOZHZS5Dbsnxe3ZUzPMGau3cfSLYeMjmMIrTVPfZ7GL5nH+ffwGHq3DTA6krBCwX7uzLkpkYLSKm79IImKavva71+K3IYppXhuaBcujgrk8eU7WJ9RYHQki3vv5yyWbDnEXZe1Z0RCqNFxhBXr3saf6aPj2J5TxD12tu2tFLmNczE1rPxs38KbKR8msyu3xOhIFvPZ1hxe/mY3g2NayZV+xFm5qkswzwzpzHe7jvH8yl12c1qiFLkd8HF3Yd7EHni7O3PTvM3sLygzOlKT+2H3MR76dDt92wXwxg3dcXKSM1TE2Zl4USS39otk/q/7eefHfUbHMQspcjvR2t+DhZN6Uldfz7i5m+z60lfJBwq5Y9FWOrXy4b3xCbL8Xpyzx6/uxHVxIby6eo9drJSWIrcj7Vv4sOCWnpwoq+ameZs4UVZtdCSzSztczC3zk2jl58H8m3vi4+5idCRhg5ycFK+OiOHKzi155oudLEvOMTrSBZEitzMxof7MnpDI/uPl3DhnE4V2VOZph4u5cc4mvN2c+eCWngR6uxkdSdgwZ5MTM8bEcVH7AB5etp2vttvunv9S5Haob7tAZt+UyL78UsbO3sjxUts/b3ZnbjHj5jaU+JLJveXCycIsTp1jHh/mz92Lt7I8xTZH5lLkduqSDkHMndCD7IIyxszeaNOLIHbkNIzEPV1MLP6nlLgwLy83Z+bf3JNekQHcvzTVJtdkSJHbsX5Rgbw/sQcHC8u54b0NHDhue2ez/Lw3n1GzNuDl6sySyX1k1aZoEl5uzsyb2IN+7QN5eNl25v+SbXSkcyJFbuf6tg9k0a29OFFezfB3fiX1UJHRkc7a5ymHuWX+FsIDvFh+R18pcdGkPFxNzL4pkSs7t+TZL3fxwspdNrOPkRS5A0gIb86yKX3xcDUxetZG1qQfMzrSX9JaM3NtJlM/3kZiRDM+vq03LXzdjY4lHIC7i4l3xyUwsW8Ec9Znc8eirTaxnF+K3EG0C/Lmszv60r6FN7d+kMS07zOscrRRWlXLlA+38urqPVzbvTXzb+6Jr5xiKCzI5KR49touPDWkM6t3HWXUrA1Wv/e/FLkDaeHjzse39WZYbAhvfr+XWxZsoajcek5P3JdfyrCZv/Bd+jGeHNyJaaNjcXeRxT7CGJP6RfLeuASy88sYPH2dVe9nLkXuYDxdnXnjhu68MKwrv2YeZ/D09fySaexmW/X1mgW/7mfI9PUUllWzcFJPbr24rVwYQhjuqi7BrLynH2EBnkxemMzzK3dRWWN9Uy3KiE1jEhMTdVJSksUfV/zWtkNF3PfxtoZTFHu24bGrO1l8GuNQYTmPLNvOr/uO079DEP+5vhut/ORiycK6VNXW8eJX6Xyw4QCRgV68dF03+rSz/JbJSqlkrXXiH26XIndslTV1vPndXmavy6KFjzuPDOrItd1DMDXxJlRlVbXM+jmL2euyUMCTQzozukcbGYULq7Y+o4DHl+9oOKU3MZSH/hFNkI/lVhhLkYu/lHqoiMeX72BnbgnRwT48eFVHBnRqYfZiraqtY1nyYd78fi/5J6sY3K0Vjw6KlkU+wmZUVNcxbU0Gs9dl4WJSTOgTweT+bQmwwJYRTVLkSqmRwLNAJ6Cn1vqs2lmK3DrV12u+2nGEN77bS3ZBGdHBPoztFcbQ2BD8PC5syuVIcQUfbTrI4s0HKSitJjG8GY8P7kR8WDMzpRfCsrILypixJoPPtx3G3cXEiIRQRia0oWuIb5MdWTZVkXcC6oH3gAelyO1DTV09y7ceZsGG/ezMLcHdxYkrOwdzcftA+rYPILTZ34+etdZk5pXy45581u7JY1N2IfVaMyC6BTf1ieDiqECZRhF2ITOvlJlrM/lqxxGqa+uJDvZhSEwrerUNICbUz6zbLDfp1IpS6kekyO3SjpxiPtp8kO92HaOgcfOtEH8PwgM8adPMk1b+7piUol5DXX09h4sqyS4oJaugjKLyGgA6tPTmik4tGdMzTKZQhN0qLq/hy+25fJKc878V1K7OTnRp7Utrfw+Cfd1p6evGoK6tzvt9YHiRK6UmA5MBwsLCEg4cOHDBjyssR2vN3mOl/JJZQMqhIg4VlpNzouJ/5X5KsK87kYFeRAZ50aW1L5d2bEGIv5yFIhxLYVk1SfsL2ZxdSFpuMcdKqjhaXElFTR0fTupFv6jA87rf8y5ypdT3QPCf/NETWusVjd/zIzIid0g1dfUAmJRCKWS6RIgz0FpTWlWLq7PTeU+3nKnInc/iwa84r0cUDsHFJGvKhDgbSqkmu6KVvAuFEMLGXVCRK6WuU0rlAH2Ar5RSq80TSwghxNkyZEGQUiofON9POwMBYzcHMZ68BvIaOPrzB8d8DcK11kG/v9GQIr8QSqmkP5vsdyTyGshr4OjPH+Q1OJ3MkQshhI2TIhdCCBtni0U+y+gAVkBeA3kNHP35g7wG/2Nzc+RCCCF+yxZH5EIIIU4jRS6EEDbOpopcKTVQKbVHKZWplHrU6DyWpJRqo5Raq5TapZTaqZS61+hMRlFKmZRSKUqplUZnMYJSyl8p9alSardSKl0p1cfoTJamlLqv8X2QppRarJRyNzqTkWymyJVSJmAmMAjoDIxRSnU2NpVF1QIPaK07A72BOx3s+Z/uXiDd6BAGmgas0lpHA91xsNdCKRUC3AMkaq27AiZgtLGpjGUzRQ70BDK11lla62pgCTDU4EwWo7U+orXe2vj7kzS8eUOMTWV5SqlQYDAwx+gsRlBK+QH9gbkAWutqrXWRsakM4Qx4KKWcAU8g1+A8hrKlIg8BDp32dQ4OWGQASqkIIA7YZGwSQ7wFPEzDlakcUSSQD7zfOL00RynlZXQoS9JaHwZeAw4CR4BirfW3xqYyli0VuQCUUt7AMmCq1rrE6DyWpJQaAuRprZONzmIgZyAe+K/WOg4oAxzt86JmNByNRwKtAS+l1DhjUxnLlor8MNDmtK9DG29zGEopFxpKfJHW+jOj8xjgIuBapdR+GqbWLldKfWhsJIvLAXK01qeOxj6lodgdyRVAttY6X2tdA3wG9DU4k6Fsqci3AFFKqUillCsNH258YXAmi1ENl96ZC6Rrrd8wOo8RtNaPaa1DtdYRNPz9/6C1dqiRmNb6KHBIKdWx8aYBwC4DIxnhINBbKeXZ+L4YgIN94Pt7f3uFIGuhta5VSt0FrKbhU+p5WuudBseypIuA8cAOpdS2xtse11p/bWAmYYy7gUWNA5os4GaD81iU1nqTUupTYCsNZ3Ol4ODL9WWJvhBC2DhbmloRQgjxJ6TIhRDCxkmRCyGEjZMiF0IIGydFLoQQNk6KXAghbJwUuRBC2Lj/AyMO7/scLs1PAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "tags": [], "needs_background": "light" } } ] }, { "cell_type": "markdown", "metadata": { "id": "gLtsST5SL9jc" }, "source": [ "You can read much more about the `subplot` function in the [documentation](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.subplot)." ], "id": "gLtsST5SL9jc" }, { "cell_type": "markdown", "metadata": { "id": "oQOE7l55CjDb" }, "source": [ "## Seaborn\n", "\n", "Reference: [Visualization-With-Seaborn ](https://colab.research.google.com/github/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/04.14-Visualization-With-Seaborn.ipynb)\n", "\n", "[Seaborn](http://seaborn.pydata.org/). Seaborn provides an API on top of Matplotlib that offers sane choices for plot style and color defaults, defines simple high-level functions for common statistical plot types, and integrates with the functionality provided by Pandas ``DataFrame``s." ], "id": "oQOE7l55CjDb" }, { "cell_type": "markdown", "metadata": { "id": "9XgpYu5cCDpw" }, "source": [ "### Seaborn Versus Matplotlib\n", "\n", "Here is an example of a simple random-walk plot in Matplotlib, using its classic plot formatting and colors.\n", "We start with the typical imports:" ], "id": "9XgpYu5cCDpw" }, { "cell_type": "code", "metadata": { "collapsed": true, "id": "9EI1yPflCDpx" }, "source": [ "import matplotlib.pyplot as plt\n", "plt.style.use('classic')\n", "%matplotlib inline\n", "import numpy as np\n", "import pandas as pd" ], "id": "9EI1yPflCDpx", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "3LtrvkccCDpy" }, "source": [ "Now we create some random walk data:" ], "id": "3LtrvkccCDpy" }, { "cell_type": "code", "metadata": { "collapsed": true, "id": "PqcvRhU0CDpy" }, "source": [ "# Create some data\n", "rng = np.random.RandomState(0)\n", "x = np.linspace(0, 10, 500)\n", "y = np.cumsum(rng.randn(500, 6), 0)" ], "id": "PqcvRhU0CDpy", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "mfLYZpVBCDpz" }, "source": [ "And do a simple plot:" ], "id": "mfLYZpVBCDpz" }, { "cell_type": "code", "metadata": { "id": "1_9vMwoHCDp0", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "2302119d-54da-41e4-ead5-041be7de8da9" }, "source": [ "# Plot the data with Matplotlib defaults\n", "plt.plot(x, y)\n", "plt.legend('ABCDEF', ncol=2, loc='upper left');" ], "id": "1_9vMwoHCDp0", "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAEACAYAAACwB81wAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOydd3hb1dnAf1eSLVmSLe+9shxnOjsh0w0JOxAoTdmblllmKdA27K8FCoVCKNACTRiFspIQRlgJSSDD2YlHHMd7W7YlWduS7vfHdRw7lhMPJXbI/T2PHkt3nPPeK/m957znHYIoisjIyMjI/PxRDLQAMjIyMjInB1nhy8jIyJwmyApfRkZG5jRBVvgyMjIypwmywpeRkZE5TZAVvoyMjMxpQsAUviAISkEQdgmCsKbt8xBBELYKglAkCMIHgiAEB6ovGRkZGZneE8gR/l1AfofPTwN/F0VxONAM3BjAvmRkZGRkeklAFL4gCMnA+cC/2z4LwHzgo7ZDlgOLA9GXjIyMjEzfCNQI/wXgAcDX9jkKMImi6Gn7XAkkBagvGRkZGZk+0G+FLwjCBUC9KIo7AiCPjIyMjMwJQhWANmYBFwqCcB6gAcKAF4FwQRBUbaP8ZKDK38mCIMjJfGRkZGT6gCiKQm+O7/cIXxTFh0RRTBZFMR24DPheFMUrgXXApW2HXQusOkYb8ksUeeSRRwZchsHyku+FfC/ke3HsV184kX74fwDuFQShCMmm/8YJ7EtGRkZG5jgEwqTTjiiK64H1be+LgWmBbF9GRkZGpu/IkbaDiOzs7IEWYdAg34sjyPfiCPK96B9CX21BARNAEMSBlkFGRkbmVEMQBMReLtoG1KQTSNLT0ykrKxtoMWROAGlpaZSWlg60GDIypx2DdoTf9vQaAIlkTjTydysj03/6MsKXbfgyMjIypwmywpeRkZE5TZAVvoyMjMxpgqzwZWRkZE4TZIXfT7Kzs4mIiMDlcg20KANCeno6ISEhhIaGEh4ezsyZM3n11Vfx+XzHP1lGRuakIiv8flBaWsrGjRsRBIHVq1cPtDgDxmeffUZLSwtlZWU8+OCDPP3009x4o1zvRkZmsCEr/H6wYsUKZsyYwXXXXcfy5csHWpwBx2AwcOGFF/LBBx+wfPly9u/fP9AiycjIdEBW+P1gxYoVXHnllVx55ZWsXbuWurq6gRZpUDBt2jSSk5PZuHHjQIsiI/OzoKm1lcbW1n63M2gjbXuC0KuQA//0Nf5n06ZNlJWVsWTJEqKjoxk2bBjvvfce99xzT/+F6iXC+vX9bkMMcI6SxMREmpqaAtqmjMyJ5rmKCv5w6BCuefNQBkLBBIhf5+WxvaWFdVlZTAgN7XM7p7TCH8hgzeXLl3PWWWcRHR0NwBVXXMHy5csHROEHWlkHgqqqKiIjIwdaDBmZHvNpQwOvVlcTrlLxk9nMnPDwgRYJgFeqqqh0uXh+2DCuKyhg15QpCH18GJ3SCn+gcDgc/O9//8Pr9RIfHw+Ay+XCZDKxZ88esrKyBljCgSUnJ4eqqipmz5490KLIyPSYVUYj96ekUO92c/+hQ1wdF8cdycknpK87Dx5kWmgoV7fpj454RZESh4PhWi05FguPlZayedIkhmg0vFhZyadGI5fExPSpX9mG3wdWrlyJUqkkLy+P3bt3s3v3bvLz85kzZw4rVqwYaPEGDIvFwpo1a7jsssu46qqrGDdu3ECLJCPTY7ZYLMwIC+MPqanckZTE74uLcXq9Ae/HK4q8X1/PF92YPNc2NbFw716+b27mybIyHktPZ2hICIIg8OKIEdx84ABlTmef+g5EEXONIAjbBEHYIwhCriAIj7VtHyIIwlZBEIoEQfhAEITg/vY1WFi+fDnXX389qampxMfHt7/uuOMO3n33XTwez0CLeFJZtGgRoaGhpKSk8NRTT3Hvvffy1ltvDbRYMjI9prG1lSq3mzFaLcEKBVfHxzNMoyHfbg9YH6bWVlYZjfxoNqMEfjCZMPvRFTktLZQ6nZy5Zw+fNzbyq9jY9n3zwsOZHxHBJrO5TzIEwqTjAuaLomgVBCEI2CQIwpfAvcDfRVF8XxCEV4EbgX8GoL8B56uvvvK7fcmSJSxZsuQkSzOwyGmOZU51fKLIN01NzDMYUCmOjIHH6fXss9mY2I9FUoB8m4336+tJUqv5bWEhkSoVD6SmkmOxkPTTT7yVmdlJqedYLMQHBzM1NJTJoaFEBQV1am9qaCg5FkufZAlEEXNRFEVr28egtpcIzAc+atu+HFjc375kZGRkAkm504nyhx94rrKS86OiOu0bp9Oxx2rt5sye83FDA4+XlfFFYyN/TksjTaPhmrg4Pho7lueGD+fzxsb2Y0VRZHtLC99nZfHRmDE8kp7epb1poaF83dzcJ1kCYsMXBEEpCMJuoB74BjgEmERRPDxfqQSSAtGXjIyMTKDIs9nQKBTMNhi49KiF0AUREaxpbOx37YbmNrPNqsZGLoyKYueUKSSo1QBMDw1la0sLAEV2OxUuFyKQ2WZa8sec8HDO7qMHXEC8dERR9AITBEEIBz4FMntz/qOPPtr+Pjs7W65bKSMjc1I45HRybVwcfx8+vMu+qaGhuHw+v14xTq8XjVLZoz6KHA5WZGay2WJhvF7fad9YnY4ql4s6t5sR27ZxfmQkU0JD/bpdrl+/nvVtMTeGHl7f0QS84pUgCEsBB/AHIF4URY8gCGcAj4qieLaf4+WKV6cZ8ncrM1i4p6iIpOBg7k9N9bt/g8nE+fv2YZk9u10Ji6LI9J07yQ4PZ67BwAVtsTjdMWbbNt4fPZpxRyn7w1yWm0uCWs0LlZUAPJSayv8NHXpc2Qek4pUgCDFtI3sEQQgBFgL5wDrg0rbDrgVW9bcvGRkZmUByyOFgWEhIt/vnhoejFgQa2tIatPp83H7wIHusVp6tqGDRcfJF2b1eypzOY/ZxVVwcL1RWcmFUFNVnnMGD3Tx8AkEgbPgJwDpBEPYCOcA3oiiuQRrh3ysIQhEQBbwRgL5kZGRkAoJPFNnZ0sJYne6Yx6VrNJS2+b1/1dTEBpOJL8aP54rYWIYfQ5Eb3W6+aGxkpsGA9hjmn3PbFovVCgUJajVhqhMXD9vvlkVR3AtM9LO9GJjW3/ZlZGRkTgTbW1oIVakYodUe87jDCn9KaCgvVVVxV3IyZ0ZEMCMsjOgff0QUxXZzj8fn48+lpfwuKYm0LVsYr9NxS2LiMdtXCgL7p04l8gQq+sPIqRVkZGROS/5dU8Ove5CiIE2j4d6iIvbZbLR4vVzTlg5Bp1QSqlRS53YT3+Z1s85k4q/l5Zg8HjyiSInTyeVxccftY8xxZhmBQlb4MjIypx0VTiefNDRQOH36cY9N1Wiocrt5sqyML8aNQ93BXfLw6L/S5SI6KIj/NTQQqVLxanU1zwwdyuTQUHQ99OY5Gci5dPrI4dJ+er2eiIgIzj//fCoqKgZarJNOx/tw+HXHHXcMtFgyMt2ytqmJGw4c4FexsUQeFcXqjzuTkqidOZNLY2JYGBHRad94nY5NZjPn7tvHkK1b+bqpidcyMnhm6FDuS0lh/lHHDzSywu8Hn332GVarlZqaGuLi4rjzzjsHWqQB4fB9OPx6+eWXB1okGZlueby0lL1WKzf6yVTpD4UgEBcczIdjxnRKvQBwfUICT5WX0+LxMNdgoNnj4eKYGH6fmopiEOXTP4ys8AOARqPh0ksvJS8vb6BFkZGROQbFDgcHHQ4qzziDKWFh/W5vZlgYI0NCGBYSwiUxMZwRFjaoCqccjWzDDwB2u50PPviAGTNmDLQoMjIyx+DjhgYuiY4mqJu0Bb1FEAR+mjQJl8+HQhB6tAg8kJzSCl94rP9PUvGRvkd8Ll68GJVKhc1mIyYmhrVr1/Zbnr6wXljf7zayxew+n3v4Phzm2Wef5eabb+63TDIygeYTo5HH/CQk6w8KQSCkbWH2sLfOYOWUVvj9UdaBYOXKlSxYsACv18uqVauYN28eeXl57VWwThb9UdaB4PB9kJEZLJyzZw+vjRxJmkbTvq3K5eKA3U72ICldOBDINvwAoFQqueSSS1AqlWzatGmgxZGROa0xezysbW7miw5ph6tdLn6Vm8sFUVHdZqE8HTh9rzyAiKLIqlWraG5uZtSoUQMtjozMac1eqxUB+KZDzvhlVVWkqtU83YOkZD9nTmmTzkCzaNEilEolgiCQlpbG8uXLGTNmzECLddI5fB8Os3DhQj799NMBlEjmdOLq/HzCVSpeGjECgE1mMxdFR7POZMLj8/FlUxOv19Tw08SJ7XnoT1cCnh651wLI6ZFPO+TvViaQhG3cSIvXi2/ePPbabJy5ezefjx/PTQcOcE9yMg8WF7Nm3DimBcANczAxIOmRZWRkZAYSg0qFAOyz2djR0sJ5UVFMDwvjnMhIHjh0iKvi4n52yr6vyApfRkbmlMTm9ZKxdSuVLhdXxMay1WIhz2ZjdFv2y7uTk3H4fFx0nAIlpxOyDV9GRuaU5Nnyckra8tTPCAvjN4WFAKweOxaAJLWa4hkziAsOHjAZBxuywpeRkTnl2Gax8K+aGoqnTyc6KIhyl4uxOh37bTYmdiglKCv7zvR70VYQhBRgBRAHiMDroii+KAhCJPABkA6UAktEUWz2c768aHuaIX+3vUMURUQYlMm4BoLnKyp4rqKC25KS+GNa2kCLM2AM1KKtB7hPFMXRwAzgdkEQRgMPAt+JojgC+K7ts4yMTC9ZXlvLNfn5A9K3zesdkH6PxWvV1VS73VwWGzvQopxy9Fvhi6JYI4rizrb3LUgFzJOAi4DlbYctBxb3ty8ZmdOR5XV1bLFYTnq/P5rNjM/JOen99oR9U6YcszC4jH8C6qUjCEI6Un3brUCcKIo1bbtqkUw+MjIyvcDs8ZBjsVDjdmPxePrczueNjeT04qGx12rl4eJiip1OGtzuPvcbaLyiSJnTeczi4TLdE7BFW0EQ9MDHwN2iKFqEDvZGURRFQRC6Ndo++uij7e+zs7PJzs4OlFgyMqc0e63W9nqnO1pa+EUfKyi9WVPD0JAQpvbQH/2lqirKXS4m6PXstFo5OzKyT/36wyeKLC0p4fEhQ3q9LlHhdBIbHIxmEJUNPFmsX7+e9evX968RURT7/QKCgLXAvR22HQAS2t4nAAe6OVf0R3fbBxPvvvuuOHnyZFGn04nx8fHiOeecI27cuHGgxTqppKWliRqNRtTpdO2vqqqqY55zKny3g4WXKirE3xQUiG9WV4uGDRvE6/PzRZ/P1+t2xmzdKp6zZ0+Pj5++fbu4oblZvL+oSLyhj312R5HdLrJunbjTYum03efziasaGkSfzyf6fD7R7fV2OfezhgZxwe7dAZPlVKbt/6hXurrfJh1BGsq/AeSLovh8h12rgWvb3l8LrOpvX4OJ559/nrvvvpuHH36Yuro6ysvLue2221i16md1mT3i6BKHiYmJAy3SzwJRFNloNpOl13N9QgK7pkzhJ7OZdSZTr9q5LDeXXLudfVZrj473iiL7bTbG6/Xcn5LCj2Zzp0Rk/SXPZgOk2rKVbX70AP+uqeGi/fupcbv5uKGBC/bt63LudybTaZ3euL8EwoY/C7gamC8Iwu6213nAX4GFgiAcBBa0ff5ZYDabWbp0KcuWLeOSSy5Bp9MRFBTEokWLePbZZwdaPJlTCJ8ostpo9Lvvn9XV5NntXNwWKTokJIRF0dFs7YUt3u3z8UFDAwAWr5c6t/u4LrFrGhuJDQ7GoFIRFxzMDQkJrOxGxr6Qa7Mx22Dgr+XlpGzZwlaLBZvXy7KqKkBaLP5PbS0bzGZcPh9unw9vm8zfNDV1KSQu03P6bcMXRXET0J0h7sz+tj8Y2bx5M06nk4svvnigRZE5hThgt2PyeJjewY6eZ7Nx0f79fDluHOdERbVvd/l8PFdRwTujRnXK8DhKq+WHXozwK10uAH4VE4NKEBiXk8P9KSk8kJrq9/hVRiO/PXCAj9uiVQEuio7mzN27eXnEiH7HAnzR2MhDJSW8MXIk50VG8onRyNxduwgSBNQKBdfHx3N5Xh5RQUFEBwVxyf79RAYFMVmv54KoKIytrUwODe2XDKczp3akbSACUfoQANTY2Eh0dHSnsn4Dyfr1/b8P2dn9L/UotZPNypUr+y3Pz5F36+rIs9n4qIMy3d1mZlleV9eu8I1uNzceOMBEvZ4zDIZObWRqtbxaXd3jPkudTuYZDPxvzBg2mkxstVh4p66OBRERpKjVxHSIRK10OnmitJQ3MjOZ1aHfkVoteqWSHS0tPV707Y6ny8t5PD2dJTEx6FUqbktK4tbERNY2NbHDakUtCAQpFBTPmMHapiZ+mZtLsCBQ7HDg8Pm4JCZmUBcJH+wMDo3VVwYoWjMqKgqj0YjH4xkUSr8/yjoQyCUOe0ap08kWi4UnS0v5Y1oagiCwy2plcXQ0B+329uP+UVVFkCCw3E8xnUytllybjc8bG7F7vVwaE0NHj7ix27ZxT0oKNyYkAFDmdLaX+ZsTHk7h9OmM2baNyTt28MfUVJ7sUBAkZcsWAM72YzJZHB3Nhw0N/VL4lU4nuTYb32ZldSoiLggC50RFcU5UFJtMJq6Oi0OnVHJJTAx3JCXxZk0NBXY7pU4nH52G9SYCiZwtsw+cccYZqNVqeSQr0yvKnE6q3G7+XFrKl01NlDocfGI0cnVcHIUOR7ttfbXRyF3Jyej8uB5GBgXxjxEjWJKby5K8PAodjvZ9u1tayLXbyW1bFAXpIZPeoa6rUhAomD6d90aN4kDbuWaPhzq3m3CViuZZs1D5KQF4Y0IC/6mtxdGPyNtvm5s5MyKik7I/mtnh4bw+cmT75/nh4UwLC+P/hg4lSa1mhpzmuF/ICr8PGAwGHn/8cW6//XZWrlyJ3W6ntbWVL7/8kgceeGCgxZMZpJQ6ncwMC+Pu5GQeLS3l4ZISroiN5ZKYGIIEgfrWVvJsNmrdbs44hmK7MSGB10eOJEunY2MHe/6HDQ0kq9XUdgiUKrDbGeEnSGmUVkuB3Y7V4yF9yxbeqKkhS6cjPCjIb58jtFrG6nR82w9vna+amljQywXXxdHRfDZ2LL9NTOSniRM7zWZOJqIo0rKrZUD6DiSywu8j9913H88//zxPPvkkMTExpKSk8PLLL7N4sZxBQqYrHp+PWrebdRMm8NywYYhINVcPL56O1GpZWlLCktxcbk9K8jvK7siVcXHclpTE+g4K/7PGRu5MSqK0g6vjFovFb/GPDK2WIoeDd+vrsXg8/LGkhPEdskz6Y354OD+YTOTZbO1eM4cRRRHfMUys1S4XXzc388uYmGP2cTSCIKBvM5se756cSMwbzeyYtIP6j+p7fE7de3U0ft54/ANPInKJQ5mTzun43V6bn0+B3c7WyZMB6QHQKoqEtJltciwWXqis5KLoaC6MiupRJGmty8WonBzKZ8xgl9XK9QUFrJswgek7d1Izcya1Lhejc3JonDXL78h49LZtNLa2cndyMg+XlLBnypRjKv1NJhPz9+yhVRT53+jR/KpD8rJ/VlWxzmTif2029g0mE+/U1fGP4cPRKJU8XFxMi9fbXnf2VCPvqjych5yoU9SM+Z//dYSmb5poWtuEs9TJ8OeGs2XoFkKGhTC9cPoJkakv2TIHfsVRRuZnjtXj4ROjkdqZM9u3qRSKTv98U8PCeHf06F61G69WMz88nPfq69lgMnFfSgrJajUmjweH18vWlhamh4V1awZ5LD2dP5eU8IfUVB7qQZrhmQYDa8eP54Ddzgf19Z0U/ueNjXzV1MR2i4UpYWGsNBr5V00NBpWKR9PT+VdNDVsmTerV9Q0WRJ9I01dNjHhpBFUvV3V7XO2btTR81IDoEdGO1BJ9UTSWzRYcxQ5Chg6O3D+ySUdG5gSzwWxmSmio30XY/vKbxERer65me0sLcw0GFIJAqlrd7hE0/Rg+67+KjWXf1Kk99q1XCAK/iIjg17Gx/GA2t0fuenw+NpnN3JSQwIdtQV5FDgevZmTwz6oqXqmqYp7BcMpmt7TttxEUEUTYtDDc1W6s+61dZqjuBjeNXzai1EvfceXzlYRnhxP+i3DMG80DIbZfZIUvI3OCWV5by4UdgqoCycKICBpbW6lwuchsq+U6RKOhxOlkq8XSKcjLH8fymOmOiKAg7k9J4R9tkbGbLRbSNRpuTkxkVVtEbq7NRnZ4OGN0Oh4pLeV3ycm97mew0Ph5IxELIghODMZZ5mT7uO3sW7QPr0PyWHLVuth/0X4Sf5NI/PXx6Mbr8Dl9xPwqBs0QDc4y53F6OHnICl9G5gSyuS33zU1tfvGBRiEI3JyYyDidrn1RM12j4YDdzvaWFr8LtoFgnsHAzhbJa2WV0chF0dFM1OupcrspcTiodrsZptEwy2AgJiiI2UcFkJ0qiF6RmtdriL8xHmWIElWEishzI/HZfDR8JM1mGv7XQHBCMEP+bwjDnx9OxisZxF8XjzpBjSZVg6vCNcBXcQTZhi8jc4IQRZEbDhzgtYwMQk9ggN69ycks6eD9MiQkhHfq6sjUaonqxs2yv4zX68mz2ylxOPiqqYm3MjNRCALjdTquKyjg8thYVAoFV8bFMTk09JQtz+godiD6RMKmSA9OdZKasDPCUOqVWLZYiL86Hnu+nfDscBQq6YFrmGXAMEt6wKlT1DR82jBg8h+NPMKXkTlB7LXZcHi9LG5LfnaiCFEqGdFmzgHJpLPTauXcAOawPxqtUolPFBm6dSsVLld74fAJej2bLRaWti0CTw4N5cq4U7f2keOgA23mkXsbNiOMiDMjCJsRRs2/arDusWLLt6EdpfV7vjpFjatcHuEfl7S20HOZnx9pp0nh6WfLy7kqLu6k/47PiojgzZEj+VUvfd57S8UZZ3DTgQO4fb52c9KF0dEkBAeTfoou0B7GuMpI+PxwHAcdhIw4ci0jX5eigH0uH+G/CGfPwj20NrSie1fnt53DJh1RFHv9O2htbCUoKrAztEHrh3+yeGfvO3yY9yE/lP5AwR0FxOvjB0wWmZ8PeTYbC/fsoXD69BPinTNYyLPZaGptZfbPKEd95cuVFN1ZxJiPxtC8rhlthpbk3/lfdK59pxbjSiNjPhzTrULfMnwLmW9mEj732PdI9IkICgFHsYOK5yqof6+eSdsmoR3hf/bQFz/8096ks7VyK7NTZnNR5kX8ZeNfaPW2BryPvXV7KWkuIbc+N+BtywxOtlksZIeH/6yVPcBone5npexbdrdQ9mQZMUtisBfasefZCcnofrYSf1U8Yz8ae8zRe/oj6Ry67xA+j6/bY0RR5AflDzR+2UjTV01Uv1KNx+TBlmvr9py+cNor/E0Vm5iTNoelc5eys3Ynv1nzm4D3cf/X9zP7rdnM+888bO7AfoEyg48iu51/1dQwRc7bfspR+XwlKfekELEwAssWCy07WjDM7p+HUdxVcajCVVS9XIX9gB13Q9ei8O5aaduh3x/CssVC2Blh6MbpsBfYuxzbHwKi8AVBeFMQhHpBEPZ32BYpCMI3giAcbPt7QsrUbK7YzI7qHQC8veft4yrUkuYSsl7Nori5GIvLQlFTEZMSJjEschhrr1rLR3kfYXL2roTcsXB5XGwq30R1i5TD/P397wesbZneY9lqYVf2rhOa2uHxsjJ+sljkzI6nGK2mVoyrjCTclIB2pJbG1Y1EnBmBSt+/pU5BEBj23DDK/1pOwY0FFFxX0OX3Z8u1YZhjwFnipO7tOjJeyyD5rmSav26mtTlwVodAjfD/A5xz1LYHge9EURwBfNf2OeC8tuM1/rHtH5SaSrl25bVsLN/Y7bH/2PoPrll5DXvr9rKvbh+7anYxLnYcwUqpCIQ2SMuUxCncsOoGKi2VAZFvW9U2RseM5rYpt/HQ7IfYUL4hIO2ejgRCSVtyLJh/MNP0ZVMAJPJPhcvFh6NHdyleIjO4MX5qJOLMCIKigtCNlhZhhz4z9Dhn9Qz9eD26UTosmy20GlvZv3g/Va9UIXql37Q9145urI60P6aReFsiurE69JP0mNaZKP+/8oDIAAFS+KIobgCO/g+6CFje9n45cELSSBYYC1hXso4nNzxJsDKYnTU72/fdfDNcfz34fOD2urnrq7vYXLGZy8deTq21ll21u5gYP7FTe+nh6Xxa8Cnv7H2n37L5RB851TnMSJ7BsvOXcdaws/ip4qd+t3u6sv/C/ey/dD9lfylr3+Y2dp0ed0erqRV3tRttppZ9F+5jz8I9ALTsasFdf6SdqlerqP9fz7MidkQURfZZradsoNHpguOQg7wr8/CYPRTcVMC2UduofauW8PnSekRQVBDZYjba4f4XTPtC7GWxhE4OZeLGieiz9By8/WC7yaZlVwu68TrSHk4jY1kGgiAQOjGUsSvHYt3Xs+LzPeFE2vDjRFGsaXtfCwTcGVcURfKN+VRYKsipzuGFc17gnb3vUGAs4N41S/nwQ8jNhb+9dRD1k2rSw9OxPWxjeORwaqw17KrdxYT4CZ3afPIXT3L71Nv5OP9jNpVv6rNsa4vWonxcyZ++/xOTE6QMiaNjRlNnraPZ0fec4qcrnhYPTV81YfzYSMWzFQC4691sSd+Cz+1/Mcxd78bX6kP0iRg/M/JjxI9UvlBJ8t3JRJ4TSfO3zTR908SOSTuo+FtF+3kHbz1I3q/zEH29n1HUuN0IgkBch9KBMiceT4sH4+qeF1qvfLGS+vfqqXu3joYPGvCYPZg3mtFPOHaK6P6QcHMC49aMQxGsYMjjQ4hZEoN1t5SXx/SdiYj5Xa3e+kl6rLs65+6xbLew9/y9fZLhpPjhi6IoCoLQ7X/Po48+2v4+Ozub7OzsY7ZntBu57+v7OGvoWQQrg7E9bCNEFUJVSxW3fn4rC99eSH1LIxcseJRZV/7AQ5tuhTA4a+hZqFVqEvQJ7K7dza6aXdw57c5ObSeFJXHntDtZtmwZSz5cwvrr1pMSlsKNq2/k3UvebV+Nf3//+6wvXc+rF7zqV8bP929kuGYGRc4tVO+cyB4FZGUpSQtPo9xcTkTICVnS+Nli3mAmbGYY0RdHU/O6NI6w7bfhs/mw59vRZ3X+R7UV2Ng1cxfBCcGoDCqcJU5iL4ul/v16NOkaxn02jp9if6Lg2ur6WRMAACAASURBVAISb0/EtF5atxF9IsowJQqNgpbtLYRN650dfp/NxjidTo4hOck0fdFE0d1FRC2KOu6997l91L1bR8jIEEofLWXoM0MJigoi79d56MefOIUvKASCY48MBPRZeqy7rYROCQUFnfz9D6NOVoMP3DVuNhduZv369dQur8VZ2rf8PCdS4dcJgpAgimKNIAgJQLdz5I4Kvyf8VPETK/as4Kuir5gQPwFtkJbXX4fg4GS23LiFGW/MAGD2xXm8WHY9rboa3py2i+vPlUbzCaEJrDywkoNNBxkbO7ZL+xlRGWy9aSsf533M9H9P55bJt/Df/f/l2YXPsql8E/nGfFYfWI02qPvp3tr9OTR98TDP/l8Nvz9zLAeuhBUrICk0iaqWKrLis3p1zac7DZ80ELUoioQbEyh5uARRFNtd1qy7rV0UfuOaRiIWRuCqcOFt8RJ3VRzRl0RT/3496hQ1giDgc/rwGr0M+9swfor7CVeNC6/ViypCRcwvY2j6uqn3Ct9qZbzOfxCOzInDvNmMu9aNq8pF7Ru1OA45yFye6Vf5N3/XjHakFv1EPdWvVBN9cTSCSiDu6jhUYScvFlU/SU/5U+VoR2sxzDb4lVUQBPQT9bTsbCH7gmzmzZvHpuc3ETwymOUHlvtp9dicyKtbDVwL/LXt76pANPrO3ndYsWcFF2RcwJrCNZw7/FwA3nsPgoLg86snolZqcDkFXm/+FXPS5hBd9ja1u+EfB+Hjj+GZdxP45tA3jI0di0al6dKHIAhMS5rGtKRpzE6dzYXvXwhATnUOt3x+C45WBxqVhsgQKXQ9JwdSU+FwBLnT46TEtY3WfVMp+TAedRAcLjOaFJpElaX7nNqnM3X/raP4oWIy/5NJRHYEoihi3W1FO0qL8RMjU3OnogpVodAqaK1vxbbPhjpNjWWbhfhrOwfM2XPtRCyIIPHmRLwOLwqNAp/Dh0KjkEZNwMQfJxIUHYRSoyTuijhKHylF9IlEXRBF5FmRlD1VRvqf0nsku+gTsefb2auwMe9n5Jd+qmD5yUJQdBAtOS3UvFmDq9xFyu9T0I09MtsSRRFEKHuyjIQbE4hZEsOQJ4YQFClFs45a0bVo/Ikk4hcR5F+VT8PHDUQs6H7Gr5+kx7rTStR5UbQ2tiKoBKLOj4IDve8zUG6Z/wU2AyMFQagUBOFGJEW/UBCEg8CCts/95i+b/sI3xd9w7vBzGRMzhgsyLsDphO3bYcsW8LUG85uUF0luvAalQsmr57/K7NmwcSMsXQobNkBs8BBEREbFHP8LXjRyETk353BN1jW8uetNxsSMYVbqLB6e8zA11hpEUeSmm+CNN46c8++db0D5HC45K55XX4WHHoL8fGlfUpg0wpfpSukjpYTPC6f2P7UA2PPt7Ji0g5acFoLjg1EnSoo6ZEgIZf9XhvFTIxmvZlD/QT2W7ZZObdlybejGSCNtZYgSQRBQapXMKJ3RPorTj9e3t5nyhxRaja04S52k/SkNwxwD1l1WPBZPj2Q3bzKz56w97SYdf7SaWnHVDp68KoMRy3ZLr72xGlY24DF5SH0olbIny/DavEQvjmb33N1sHb61PeCp8NZCNmg34HP4iL8+HlWoql3ZDwQKtYL4a+Jp+rxJMut0g36CntJHSilZWoLjkIOQYSGkPpDapz4DMsIXRfHybnadGYj2D5PfkN/uLpkRlcH+2/bj8vnYshFGj4b4eHjqKRiV+BssYjPPXedDF6xj3jy4+mpITIQhQ8BYFkvpXaXt7pjHY0riFKYnTef2L27ngZkP8NgvHkOtVPPkhifZlW9m734dB84bxgO+YlQKFf/Z8R6h+Ut542vQauH22+Gvf4XWVmmEv716eyBvy88CX6sPZ5mT4f8Y3u6GZi+UPBgq/laBOkXdfmzYGWFULasi8z+ZRJ0TReYbmexduJfke5Nx17oZ/uJwbHk2tKO7mtyC4/x/5yHpIYz9pLN5L2xGGKZ1JqIvOn7yM8tWC+5qNyEbWondYcGU6SN8XueRfvlfymltaCXzzczjtnc6YtpoYvfc3Yx6bxS2/TbS/pyGUnP8SOWql6oY+tehRF0Yhb3ATvoj6bTsaMG40ojH5KFuRR0JNyRg2WJBdIkk35OMoBgcayxDnhqCz+0jdHL3Cj9iQQQJv02g/Klyql6qIvLsyG5/x8djUEXaiiJYLN3v31q1lfNHnM/UxKmMjR3LFrOZrJwc1v8gEnxTKdl/reHll+HQIRiZFkGUVio6ER0NF18Ms2fD2LGwfz+khaeRENrzHOVXjruSuWlzuXjUxWhUGgRBICE0gXdWVTH3xq9waSrYV1aJ2Wkm17iHSdFzCQ+Ht9+W+o+Lg8pKaYRf2FR42tV0PR7OUifqRDW60TochxwAOA44CI4PpnFNI+rUIwo/5lcx4IXIsySTWvRF0UzaOonSR0qpfqWa+v/Wox2hJSi8f6O3yPMiaVzTsyLUlq3SD/f/7hMpva2I4oeLuxzTtLYJ237/gYGHHjjE1oytOIodfRf4FKfq5Spir4yl8DeFVL1URdHdRbjr3F0iUx0ljvZtHrOHlm0tRJ4diUKlYOTrI4m+MBrdaB2CSmD81+Mpe7IMn8eHq8zFhPUTiLti8GTvVIYoyXg5A2VI9w+24OhgRr46kqTfJeG1eNuravWFQaXwV6+WFHN37KndQ1ZcFttu3ka8Pp7vTCYOOBy811LN7hGVvNpSTly8yGefwdEJGZctg2eegTFjJFfN3mLQGPjhuh+YkTyjfZvJaeLv7rFsSJJs/I/+vYwfyn4gSZzBxLGdV9xTUqC8HOamzcXkNPHmrjd7L8TPGMdBByEZIaiT1LQ2tuJ1eLEfsBPz6xjwSVkHD2OYZSBrXVYnjwdthpawGWEIaoGSP5cQ/cv+pySOXhxNzb9rKH2sFI+1e9OOq9qFab0J9+QQmoYqmWOZg22fjYIbC6h+TYqwdhvdOIoc2PPtXdw9HcUOat6sIfriaEofK+233Kcq9nw7KfelMKtpFpN3TsZZ7GRb5ja2T9iOx3zk/pcuLaXyeWmm3/RVE4a5BpS6zkow7Iww4q6NI3JhJKpIFUV3F6GKUBE+LxxBOThG971lxIsjmOucy4hX+l4IflAp/E2bYO8x3Ev31u/t5N2ywWRinFZH4fxD3JGQhFcUGb/ITmFhV4UfHg6xsTBiBBQVBUbem8fch/b7f1L+uxoWj1jC+j2lvL/tW1TlCzi6XnNKClRUQJg6jL+c+Rf+tO5PfHnwy8AI8jPAXmgnZEQIglJAk6bBWeLEusdK9OJoBLXQaYTvRuS/I+xdZknj144n4YYEXGUu4q7q/yguZEgIaY+kYVpvouof3a+7VDxbQcL1CVTNCcaarUWpUzL878Ox7bVRsrSE/OvyKXuiDN1oHUqDkup/VlP97+r280sfKyXxlkSiF0djzw9s7pRAknd5Hgd/dxBbXuDzQYk+EUeRlIpYEaRAO1zL2FVjiTwvEt04HXXv1LUf6yhy0LyuGZ/bh/EzI1GLupaP1KRqyPy3ZDob9rdhuMpcjHrn5C7KnggUakWPzFzdnh9AWfrN5s1gNEKDnwIxXp+XXTVHImP3Wq3sslr5k3Msaquaq1NiGanVMu6GJh5938K0af77GD5cUvj19VBcDN9+K9nW+0Ja5QNclHQLKRHxjIkfQcb0Yr4+tJbKHxZy/vmdjz2s8AHWvbGQWmst5713Xt86/hniOOhoTwOrH6+n/v16XJUuDLMNhE4KJWT4kRnTWzU13H7wIF83dw5gU4WqMMwxoJ+oJyQ9MPnYhzw6hPgb4ttNMbY8G/nX5LfvF0UR42ojcVfFseE6Nd6lkpkw4cYEJm2dhCJYgfFjI1UvVaFJ12CYaeDQA4cwfiIFCbmNbowrjaT+IRVthhb7ga4PssGA6JOuExH2LNhD8R+L8XptlJQ8AoDb3fOgJ3+4KlyoIlSd8tYoQ5SMfnc0URdE0fBJAyWPlGDLs+EoctCytYVto7bR9GUT0YuOPZuLyI5g3GfjMMyUo58HjcJ/4y9Oag62Mm4c5OV13Z9TnUNyWDJxemnk9lhpKb/VpvHreSHcsW0aY3Q60jQaltYe4tG4nSyrrejaCDBsmKTor7kGZsyAhQvhgQf6JvPnn8MFF0jvUw2pbNc9jrUmmV+MmsjRiRIPm3QAPv0oiMuLnKiVajw+/6YCm9fLitraQfnPfyLoWGjCMMdA+TPlxF0Rh0KlIOvbrPaScQDv1NVxfXw8j5WWdrk/cZfHMWXnlIDKphutw5Znw/iZkYLrCqh7uw7zZjMgmSHEVhHdeB0lPhdpYUceNIJCYFLOJCb8MAFE0KRrSPxtIj67r30k7yp3oRmikTxGooIQggRa6wOfors7Gj5pwLLtGAtnbTjLnARFBjHipRFM2TsF4yoj+x59m7KyJ7DbD7J161B8vr57IJl/NKMd6T+uRTdah+l7E2VPlJEzJodWYyvTCqYRMiyE2CWxqJPUfs+T6cqgUfjRj+/i300/MX2a6Ffhf33oa84edjYAVS4X35tMjCqLJysLHlkq2eTS1NIXvzwzk0dLSzH5Gbrr9eBwSG6aANddB+vW9V5ep1M675y2lHFnDTuLy2P+guuNLxg3tuttTU2VFH5JCZSVwe4dahJDEyk1lfpt/7vmZq4tKOCdujq/+39udFT44fPCQYTke6WiE0qtslNQSpHDwWPp6TS2tvKj2XzCZdNmanEUOij5YwmtDa0M+/sw9l2wD2e5k8Y1jURdIEV3ljqdpGs6x3Wo49VSiTxBUvjh88OZXjQdd60br92Lq9LVHhcAoB2pxZbfP5OJKIrSGkihHbdR6scflhwLub/MpWrZ8d2Ebbk2tGMkhRwcHczEHyZiU/0IiDQ2fobX24LFsgVR9OL19m7h2bLVQtFdRaQt9V8J7bC3Vezlse256bUjJZPP8JeG96qv051Bo/BbvQKCR2Sywdrus96RPXV7mJo0FYC7i4q4PTGRxgoVM2fSPppOa/tnOy8ykoWRkbxf7z+498orYdUqySf/pZegsPBIYBRAU2sr3uOMrNevh/Hj4XDZ0PTwdO6c+CB41WT68bobMwb27ZNMSJdcIin9IYYRFDYWdjnWK4rsbGlBr1Sy+VhuSwGmpaX7fZs3Q2mp9LB09zxfWY/wuXy4alxo0qXvT5+lZ0bxDDQpXYPirB4PFq+XJLWay2NjWdXYMy+a/qDUKQmOD8a2z8bU/Kmk3J1C1PlRNH3ZhHGlZEP2+HzUuN0kq7uONpVaJZo0DZo0ybsrZFgIIcNDsBfYcVW5Oo1Qw7PDe53Js7W5td1LCCRvl22Z29g2ahvbMrdR/nTXbIs+j4+ql6oImxGGp0maZZY8UsKPMT/67cOea0c7TkVNzVuAlFyMCdsJcgylsfEzAJqbv6Wy8gX27TuvVzPT0idKGfr0UCKy/QcfBccFE7EwghHLRjBpyyTGrx0PSCafw4XDZXrGoLhbnhYPoa1uoq5NYJjF5HeEf8B4gIyoDJxeL583NvJQWhoVFZKp5DDpGg3xwcFEBwdzb3IyT5aV8WpVFduPUprvvAMLFkBmpjTiHzcOduw4sj9582bm7dp1TJnXrDlizjlMalsshD+FP3So9FB5+20491yYPx/2fJfJW2vysLo7Z8O7+cABHisr4+LoaCpcJzZQx+GQzFz33w9hYZ2VvscjralYrZL31FlnQUYGjBp17IdDbyl/ppzQiaEogo78HLubphc7nQzRaFAIAudERvLlSVD4AFnfZZH5n8z2BbOIBRGUP1uOu9ZN3mSBt+vqiAkKIljh/19q2HPDOhXSMMwx0PR1U5cRfsylMdT8u4bd83f3KEuiq9pF4W8K2T1/N7vn72b7pO2U/LmE4IRgoi+KRjtCS/PXXZP1lfyxBMtWC+lPpOMocuDz+Ch7ooxWY6vfpHG2XBuKiQcpLLwFn8+N01mBT1+Pet8vMZnWo3ZkUbNrDU1NX2M2/4jZfCRNuSiKtLTs7vYa7Pn2YxYZEQSBrK+zCAoPIigiqN0dV6b3DAqFX7vFTqVCS9z5EYQdau6i8L0+L4eaDxEXNIL/+8CGuiGEhgplF4U/NTSUVWOl4JkzDAb+mJbGdyYTV+bn0+rrvrzY9OmwdSs0t7YybMsWROBHiwWLx799XRQlhX/0wmx8vORzP3Jk13MEQXogbNworRvceCNY8s7gI8vvCf1LZ4N/qVNKjHR5bCzlTv9Jkg7a7ZyxcycWj4d8m38TwCefwIsv+r/mhx6Czz6TopOLiyUTVXi4NAs5zMcfw+LFUhvZ2fDkk9IDa+hQ+MMf4Pvvu7b77LOdo46Ph8/lo+JvFYz5eEyPji9yOBjWViB7SmgoRQ4HDq9/k0UgCRkS0il9Q/RF0USeHUnmW5m8WF/NDQcOcGF094uHMZfEoDIcWZCM+WUMxk+MXRS+foKeUW+PQggWaNl2/Kdq7i9z8dq8ZLyWQeItiWS8msHMmplM3DSRMR+PIWtdFrZcWxdfdvNGMyNfH4lhpgFHiUNKYZGpJTgxGFdF10GGLdcGaYcQRTfbt0+iuPgBwoIW4tskOVEoty3EHbmb5uavSUq6g4aGT46ca8tlx46JmM1dU4N7nV5cVdI6hsyJZ1Ao/MpVzVSEGzDMMtC614LLKfKbDpUGM6aVE6GM5asfVTyh24srT88999BF4asUCqZ1qDJ0a1IS/xs9Gq1SyVY/phFRFGnxeMg8w83WrdLosdjpRK9UMj00lL1W/yOs+nopQGzMUTpKqZSCq7pLhf7ii5LraVKSpEj3rJ7j97gSp5MD06YxLSyM8g4j/K+bmviibUT7WnU1BXY7U3fsYHROTpc2WlvhnnukPlevlhT8YUwmeOUVuOEG+OgjePBBePlluPRS2N1hILZ3r/RA+PvfJWW/ZImk+M8/H/71L7jsMvjxR+lefP21dM6XX8ILL0gPxePR2tRK45pGdGN17SkOjsehDgpfpVCQqtFQ0s1D8USiMqjIWJaBYa6BjWYztycm8qejfYGPgWGuAUexA/NGc7spC6TRbNR5UYRNDcNZcezrsh+04yhxMHb1WOKviid2SSxh08LaTR2CIKDUKIn5ZQy1b9a2n+fz+LDutaKfoEepVaJOUnPonkMYZhkIGRGC/aC0oOy1e2n+rlnKE1Rgp9VQgCCosdsLqK9/n7TMe3BuSATAmauA1ZeQnvYY8fHXYTR+jMNxiL17z6Oh4QPU6mTKy5/pcg2OQgchQ0M6ze5kThwnLzXcMaj8tI4dV2pxxypRhijZ/6mDcedoefBByT5fbN3HY68+hM23G1Z4+ODGOK5/UTI5DD/Omo0gCIzWail2OpndYbsoikzfuROb10t4ejDlWydQ1qY4Rmu1jNJq2W21+i3QXFAgmTX8ZWH1Y8JtZ+7czp9HJSURPPYD3PbvubWwELPHQ4XLRanTyVCNBqUg4Pb5sHg8hKlU/KumBo1CwXlRUXze1MTHY8Zwedt0yOb1diqYnZsrpXWw2yVlf+AA3HSTJN/SpdJic0SENBrf0FaEa8KEzgp//35QqSRF3/E+X3ON9Nnjkcw8s2dLM4OqKul8UYSDByXzT3eIXpGc8Tng611VoUMOB2M75KoZFhLCIYeD0b3MULl7tzSDeeKJXp3WhWKnk2CFgpePdbF+UAQpiL4wmubvmzHM6TpCUKeou3jPiKKIz+Gj8fNGmr9pxpZnI/ay2OPasZPvTmbPgj00f9+Mfrye+OviUSeq22cc49eOp/79eqIviqbqpSochQ7sKXZKHy+l4aMGsr7OIjguGLs7l/T0R9FqpcCf8JjpRJ2Vi/nvL6JjKs7PFMTcNBatTktk5Lls3z4Jn89Oc/P3jBnzEfn5V+LzuVAojvyT2PJsaEcFrsiIzLEZFApf32Bnw2IHK+rqyJ4RhrjbxKWXaln9gYdx01Qkn7GZEd+eQUKlg6Wr0lh4r5Y77wSFQhotH48hfkaBXzc3U+504vT5qBPcOGdX8165mWEaDWdFRhIXFMTHRiMeUeTujtMIJIXvz06/vLaWdI2mx9kSK51O3FGxEL6YOqedWo+PcocLhSiNXkFalyh2OMjS69loMpGi0SCKIqVOJ9NCQymeMYNJ27dT5nSSa7NR4XJxb0oKe/dKCnz6dGmkf/310qg+J0eaFb3+uiTDnXcemalMmADL2zKums2wZw+sXSu10ZHIyCPrF//9L1x0Uds9/Vp6oMyaJSWzi4uTZkMj/AQGmtabCI4N7rELZWkp1NXBIY2TxR1MJ8PbFH5v+eQT+Oc/4bHHpN9RXylxOBgR0jef/9SHUom/Nt6vwlYnq3F90tm0UvNGDYU3F4ICVOEqPE0ehr9wfC8VfZaeSVsmYcu3kX9lPsFJwegnHUknrR2ubc8Kqs/SU/G3CoruKiI4PpiIBRHk/jqXpN8lUmHbx7hxnxMUdGRxNe2PaZg2XELCTQkU/qYQ849mdKN1ZGS8isEwC41mKB5PM9HRF6DTjaG5+Xuios5tP9+6w3rMPDIygWVQzKPK0+G29BQO2u0k/DaBiqcrmDyilZF/2sxvF7TgidlKlCeU7RMg+8Uyih8q5pFH4M9/7ln7Q0NCKD5KKWw0mbglMZHqmTN5Z9QofL8u5+OWOm5JTOSPaWlcERfHPquVew4d4ovGRoT16/GKksvoLbdIo1efKLK2qandI+G6ggKyd+/usYfCRrOZucGRKH54ik/nh/B21ASeqZiB78x5fPihdMxEvZ6dViubzGZEoMBup9btJlSpRK9SoVMqSdNoKHM6WWcy8VVTE5s2wbXXQlaWpNC/+Qb+9CcpH/+uXZLiNxikV0ez1Pjx0szAZJKU/PjxkvLWHMO8evbZ0prF2WdLM4gLLoApU6SZQ0aG1E5JSdfzGr9oJOaXMT26TwC33SYttOc2O4huDWlPjzFMo+FgLxR+pdPJpw0N/PADNDXh1yOsN1S6XCQda1p3DLQjtF0SrB1GnazuYku3F9hRaBRkfZfF9MLpTFg/ocfKMmRYCNEXRBOxMIKSh0u6PS9iQQTOEieGuQZGLBvB8BeHk3J/CtG/bUWlCu+k7EF6QCTfmYwyREn4/HCav5UWiAVBQXz8tYSHzyEqYhGHHjxEpO8Kqqtf6XR+y/YWWeGfRAaFwtcOMzAtNJRDTieRCyIJigtixIf5hPi8vMJOztsxjOBmBe9frCQoPYTGNY04Djkofqj4mDlODuNvhF/idDI0JAStUsk5UVH8fr8UmutzSmYRrVLJjilTuDAqikvbtEuRw9G+CDp/vsjdRUWcs3dve9sj20Z6efaehcd/29zMxSkRJKt8ELuf19d9ys7tAmfMEHjuOck2Pl4dxjqTiesLCvhnRgZ6pZJNZnMnf+/DCj/fbme/zcabbWl6srKkdYUFC6SF1qFDpYXl7tYYQkOlGdPSpVL20dWrJZPOsVCrJaX5/vvSQ+aZZ2DaNGlBd9kyuPVWyfX1aKw7rcdMCduRgwelB9Wy13zUuF3cfKGGCRMkD6OxOh372hatRVF6sPnr7zB/q6jgxgMH2LbLx+WXS4vQtbXdH388qtxukk5AOUN1iqTwOw4ebHttjPloDBHZUqHt8Hnhva6slXhLIj6HD/1E/5WdQjJC0AzVMOzZYURfGI12uJbU+1NxivnodOOP2XbEmRE0f9eMz9XZQcJ+0E7F0xW0/G06zc3rqK5+Hbu9ENEn0rKzBf3kE1dlSqYzg0Lhz14Q026LBUi+JxnFQTMvR3lRRCs5b/s5kBSEuFDHzMKphM8LZ8fUHZT/tRzLT8f3Ux+j05Frs3Vyzyxus5Mf5r7fKRj3yWjKVsSwqa2UbVxwMAsjIhij07EoKoo9FisrV8LneVYeUe9jk9nM+ZGRrDNJ5fGq3W6ujotjlfH4YeYun4+VRiOXxsQweWQc2iW38mrZXWzbJincigrJVPL5c6G8U1fH/IgILomJYZRWyxdNTQzpIHu6RkOp00m+zUZDayt1jlb+858jQWGHWbKEblNOHGbqVGl0fqwkdkcjCJKHz5NPSusCv/iFpEQvvRQWLerqzXO4sEl3SsdqldYHXnsNmpslU9E558C4RTZSg0PYnaPA45FMTpNDQ9lpsTJ2vMj+/VJ67KVL4Y47oKPzzhqjkUv27+fd+noiCCZ0YSNPPQVPP911baU3VLlcfn3v+0tQRBAKtQJ3zRHvGts+G7px/aumFXFmBIm3JhI23X8lL0EQmJo7ldBJnR/GVute9PpjK3xNqobQSaHUf1DfnoP+sNxBsUG0likJDZ1MYeEtlJT8kYJtd6E8czuqSFi/XqCi4oV+XZvM8TnhCl8QhHMEQTggCEKRIAgP+jsm+eZ4hraNwlcWrOZ28Xcs+sMVfHznAvb8Zy9Rligs2VpGh4egUCmIvz4ehVpB4u2JtGz377omimL7jy42OJg/paWxrPpIwqoSh4OhHWyvYWFw57hYXnkqmDkdnGduTUpiXVYW43U6Ps+3kZwMD1vzmWswsHnSJC6KjmZdczNmjwcR+G1iIq9XV3NbYSGPlZbS2E2inpVGI+P1epI1GuJ0cUSpE7B6TGwzPMj4qS1ceKEU3LXhn2FwzVSWDZUWBUdptXzR2CiZqYolpTdJr+erpiZsPh/TQ0PJdVmZMKHrovJddx2x3fvaRo52r7fTKPKee6SR8/z5fsXuwhOlpXxwVICbIByp/jVlimR/7/gMdBY7UegUnbJddmTePClW4ZZbJNfRT7fZmb/Qxyazmfkxhvbr2r4dwlVBYArigM3OI49ID6rQUGl2sWXLkTbXNDbyqdFIuFPNrJZ41PMasURb2bxZmgX1lf6YdI6HbqyuvYyj/YAdBDrVBegLgiCQ8UoGqtDup27+knPZbHvR6cYdt/3E3yZS/tdytqRtwWuTnri2/TYiz47EVeXCYJiFWp1KU+066syv4btmGc3N3wJ0MffIBJ4TqvAFQVACy4BzgdHA5YIgl4gdMwAAIABJREFUjD76OJUhiGBbJfcEr+TW3M18pJqOZdoK0sPT+bBmBd88+A1b7tMzrG1UG7M4hqn5UwmfE07z912nkD63j81Jmym85UgU60yDgRyLhdVGIzavF7PXS/xRU/HZbW48HU0eSkFAr1IxTq9nY6WN7Kvt1Le28vvUVNQKBb8ID+d7k4kKp5MUtZpZBgOLo6MJUSj4wWTiXx0eMof5b10d9xUV8bu2Feffz/o9/7toDYqmTFxTnibfvI0LLoCYGKisFMiK1LFli6TlMrVa6ltbmRkWxn33SQuto8Qw9tpsnBMZySyDgcpIk1/vJdX/s3fecVFe2f9/P1NgZpgBhg7SpIgo2DX2kqgxJsaY3pPdtE1PNsluEtM3veymJ2u66aYY0ztijYqgSJEiiPTOwDRgZu7vj6sgAgqWxO9v83m9eOk8c597n3lm5sy553zO52jAxwdWNTTgvWYND5SW4rN2La9XV3eN2WegB5IMB0hraWHdQeQNNBoZWtpfBbXx28Z+W7o5HPJH7OefJXV17VpIm5lHx6QGfmhqYk6gH3FxMHWq/EG8/nrQ55tZ+GQ9K1fKvMPjj8vdxZdfds/7S0sLQSovit8JIu0FXypG1TA6I4OEBJkMPlxUHmuDv1e0rf7TeoLPCv7DmqNbrTsOGdIBCFgQgLPUSUdVBw1fNtDZLKm3AScH0FHTQUjwBURo70N8vhjyRmBkDjk5iwkOPoeOjlo6OvpQTjwABQXXsGvXP2lv/7Nz3GBxrD38SUCxEKJECNEBfAQs7mtg8c4bme/8Dz5+QwgOGo+XouLOaXexqXITzpONvNZcy9nB3Uk+rb8W83wzHqeHHQ+9jcfT7Um3pLXQUduBZU23IUrx8SHPbmdxTg6X5Ocz32xGdcCXJzlZ0vWczp4Kmu+/D9UbfShT28icWsQ1ERGo954br9dT3dFBakYGQVrZcOPZxESeSUjgktBQcg+I53uEYFl1NQsDA7sKdeLMcUweEcV5J8nfwuzabE45RdIlhwyRNMj582Vjl2SDAQWY4edHdbX8Ufj1Sy1TfX25KjycqEZ/1NMbqVf1n8jcZrWyKDCQ5ysrmeLry9cHVKtGD6J7Wp7N1m/h1z6MGNFTEK9hVQNBZ/RdpLRjh2QBxcVJUbvX3xCISDsP1O+ivL2dxUFB3HQTPP+89P43boTv/hLFxrBKrv27ixPP7uDCC2Vo5403pH5RZVsnxfUdjH5tLCc1RlHxS3e4IiBAVg0fjlyExeWi0OFghOHY0Ap9Unxo/U2GIS3rLZjn9d/39FjC7bbT3l6GwdBHReEBUPuoibk3htgHYqn4TwWVL1Wii9ERfK4sPPPsjKHt8UnERN/D1Gt+IXXRS2g0/hiNY/H1PYHW1t8OuUZt7XLKy59k48bIo/Hy/qdwrA3+EGB/2cqKvcd6YOu/fGizb8NgGMG/3LfxXVQBJrWayXELuHXyrfhHnsapAQGkGHvGfLVmLaO+GUXz5Fsp/+pHqt+oZsfpO2hJbyH6rmhcFheOUmn4fNRqhhsMXBgSgkGlYllf5bBIbzQioptZ4nLBxRfDA1foUcKclHkcLN3PIiqKwj0xMfwrNpblB3A1U3x82LFf8ZYQAnV6OussFh4ZOrTrR2Mfrp94LddOuJbsumxUqm7q59//LhOvr7wCbVtNLI2JwSC07NgB998P334Lq8eMYX5AAPkr/Ij18WZaVha1/VixUoeDUwMDaZw2jW9SU/m5uRnXAZXIZ+zYwfkH6RTT6nJR29FBQ2dnryR1hdPZI0w0YoRsMp+fL/u6tm1uI2Be3+XxmZmysCtnp4eWxCYu+IcdvDxUtLfz9vDh+Gk03HQTjB8v78tLL8GEUAMn+vtTfGkuJzVupLajg/A4F6dfa+fWW+HKx61QYsTYpuedZWpEu5qGadPQqVQoiiA4WNJH98dBCrMBWFlfz6qGBmb4+WE8VGb7MBF8XjCWNRZat7RKzZ0jDOccLmy2PPT6YahUA+sgFnN3DDH3xqCoFcqfLCf00lBUWhWdDZ1kTsrEus1K5I3ReBkCUKu9GDr0MQIDT8PXdwqtrRt7zVdQcDU7d14BQGvrJoQQpKZ+jVr9J7tnsDguePht023gcWL2O49w+zMY7OsYbhhPHUZOnngPp2Rn80Fy7+YFTU0/4+OTAqZWSp/7Fb7wAUW2y4v/dzzte9pp+q6JIdfJ35gfUlOJ1OkOuS0eO1ZKBwQFweuvSy+7okLF6C0G5pnNXRz5ffjX0KF9zjPCx4cChwOXx4NGpaJib9VspLc3wX0wO6ZETUGlqPjbN3/rOlbaXMry7csJHnYZ//5PDCtWaBk1aigb75D9ec89V9JT62pUBAXBJ8s1bPhbKne35/J9UxOXhYX1Wme308mle++DWaslSKulrL29q4IVYI3FQrPLxTN9hCzKnU6mZmXho1IIdDfS0hnAjqYyUszRNLlcDN+8mVujojgrKIgxJhPJydITv+oq+PTaJvxn+/fqUASSZfPLL9Lg37+nlP9WVRFwhpaZ3n48EBvL2AM0px97rPv/T8XH82xFBTa3my8bGtAoCmnzytn98EQ4x8r44T588UX3+ECtFp1KRZPLRWioltpaiNzPYUxKklXR+3IR+8PqcnFeXh5+Gg2P9PPeHw1o/bXEPRFH0fVFvUTWfk/YbDsOmbA9EIpKIeaeGHactgP/GZJ66pPiQ+DpgUTeFIna0P3+R0RcCUBHRxVlZY9htWbj7R2JVhuAx9NJU9P3tLeX43Zbqa9fgZdXBAEBCwGFzs7mXlTRP9E/jrXBrwT2r1qK3HusB955zITbzwHBuUTF6Fg0cz0p5ruZn52N796M2vyAbo/Q7XbS2PgleXkXEBS0N0I0tISEZxOofqMa5x4n/jP86azvJP/CfFBAG6Cl7j/l+D6bgN/kgzdCuPRSGT9WFMkr38f3vyUykqn9cRr7gI9aTahWy6U7d7IwIACzVsskk4lH4/qvLB0dNprCxkLsnXYMWgM3fHcD3xZ9i8nvZUTse+jU82hqggsvlGGe4GBpSOfNkxTI4cNlFewJ5b5sbWvrZfDXtLTwW2trD1pnksFAgd3eZfDrOjoQwDyzmcy2ti6Dv95i4dHdpcTrdQTai9jZkEt7+ScQfwOnfPUyF01/nBcrK3F6PDxcVkaOzcbKlBRmz4YNG+Cmm2DFNXVM+kdv/r3VKndXJXYHlz3RwRtV1aweM4ZFO3YwxmhktvngX+oonY5nEhJYUVfHWzU1nGAysdtj58GvWvhOa2FxeO8dxRAvLyra2zGbtVx/Pfx9ZT0+XiqcnYLiZhPFxd59Gvz0vTURFperR5jxaKO9vZKWSfdh14zHYxmJNujIevQeLmTCdnAGHyDglADGpI+RyprAhOwJB3W2TKYTsFq3kp29ALN5Lr6+J1BUdAOK4sXUqbUUFV1PUtIbhIScL1VH9XE4HLvQao9u/4PjFatXr2b16tVHNMexNvhbgERFUYYiDf35wIUHDrpv5kVsLfqAovNnUm8fjsfzEY+FNmDSRLGsqgrnzJldKoQ2204qK1+gqupVgoIW09CwErXKiNeCaiLnR+Lc7cTV6kLlrSLw1EAib4uk6PZsmJOGsu1UqpdV92vwPS4PKo2KhQvh7bdlYc4pp3SHVv4SPvCm5/sQr9fzaX09261W8ux2bhoyhJMOYrx0Gh0pISlsqdxCUlASa8rWEOUbRXlrOVw6nzMm383lUY8wcmQ3xfKxxyR98dZbJZURYLzRyIe1tbR7PHjvtyOZtVc7IWo/rz3JYKDQbmdhoGwVt6m1lYkmExNMJjKtVhbtzTW8VV3NL7XFfOsVxOT67yk65Ql0mtsJ+eR2KrWRvFhZQYqPkcm+vsTqdHy2t3WZRgNTpsC6bzpYG9PCKznJdHcGlonaxYtlg3nzOeWcVS5zHGNNJiqmTh3U/T4lIIArCwrQq1RMNJn4QF+I3ePhuhG9S6OHeHtT2d5Oe7uRTZsEtxXuosrdzhCNN1xqZvfuJKZN63lOrs3G43v2cF9MDMkGAwHaY2OEOzrq2bQpAYNhOMrlhXhVPImi+v0TtnV1n1JR8SypqYNvx6moFPxndheWHWpnrdX6k5DwPLW171Jb+y51dSsAMBiG4+UVwsiRn/QYr9PF43SW4Ov7v2HwZ8+ezezZs7seP/jgg4Oe45jG8IUQLuAG4AcgH1ghhOgVGI6In8HZ77TS9P1KpkZNIzHxFQp2XsJfvX7lhYShPSRni4quo6rqZSZMyGTEiI8B8DGm0mkowuEoIfaBWOKeiaGhYRWKTwcJTycQ/N4muP0pxm0eS8PKBjqbujOyrlYXBVcXUHRjEb/F/kb9F/VotbKI6NZb+5ZQAFlM4na6yT41m9oPetI8PC5Pl8RsvF5PpxAU2O2cFxzM+SEhh7xvs2JmcdqHpxH+TDh+3n4sSFjA+BBpIndZdjJihIzpz5q191pcVq66SlbEnn22PDZhb/jj/Lw8pmZm8lJlJSUOB6FaLdkTJqDd754m6fUU7Fet+lNzMyf6+zPJZCKtuZnXq6q4obCQLxoaCCx6hKE7/8nK0/5NlF8UwT7BPD/5MlQRpxKlcrIqJYWHhg7lsrAwtttsXfRPAEe+HZ8UI9+na3rEyL/6SuZKlq9w4xzWQoxOx22Rh5eQM2k0zPL3Z2VDA3dFRxOj0/FlSgq+fcTZY/bKVixeXsHYJ8ppa1PwLJxOzPNjYFY9eRU9KbUdHg/Ts7KY7e/P3TExnD2A9/JwYbGswd//RMaO3YCIKEM5/ftjttbBUFf3Af7+J+HvP+t3WS88/C+kpKxEUeQPaUDAQpKSXutzrMk0lpaW1b/Ldf1/AyHEH/oHCNHWJl6bbhDLRyEqLBVCCCGKi/8h1q8PEzU174v29hpRXb1cuFx2sWaNSXR0NIl9cDj2CKezWpSU3CvS0hA2W6Gorl4u1qwxim3b5gm3u11s2jRSpKUh7PZSkXdZnij+Z7EQQghnhVOkkSbWBa8TpQ+Vivy/5ottJ28Th0JHc4dII038lvib2Bi7UawPWy+q360WHrdHCCFEznk5ovK1SiGEEI+XlQnTmjWi0ukUHo/nkHMLIYSj0yG+KvhKhDwVIha8t0DsatolihqLxJc7vxQL31/YY2xBQ4EIfSpUuNwu0dTUc55Kp1OQliYmZmSIwLVrxbUFBeIv+fm91ttssYik334Tn9fVCY/HIxJ/+01ktbaKdrdbxG7cKIZu3Ci8Vq8WEzO2CN3DOuHsdPY43+PxiKQ134nEVQ/3OB6zYZ24c/0rXa+75r0asXLuShF+ylvijTeE2Hc7zj1XiGXLhCAtTZCWJjrd7gHdp/7wckWFIC1N7LTZDjrutcpKMSEjQ2hWr5ZrX1YiZs4UAoTgqmIR+nFm17UX2Gzi5G3bxPgtW47o2gaKwsKbRFnZ40IIIUre/16kfxkuPJ4juy8DgdvtFG539/u7YUOMsNkKjvm6B6K9vUZs375AlJU90e8Yh6NcrF1rFp2dLb/jlR0/kOZ7cPb2uKi0xWjkq9OHs7hEyxAvGVaIj3+CxMRXqKpaRmXlK5SU3EVb22YMhhFoMXbx/HS6KLy9wxg69CGCgpZgtWZRVfUqw4e/jdttIzNzCjpdFGbzXOz2fOIei6Pu/Tos6y04SqRXG/tALLH3xpLwXAItq1t6aeFsjN1I43fd1EXbDpusEhVSidArwoudl+ykbkUdboebxq8aZTekLxtIKlVIMhjwLegkc3Im7ZWHbmii0+g4bdhpLBm+hNSQVOLMcSQEJBBuCqfGKnUAyi3lPLr2UT7c8SG1tlq21WzjwEhRhLc303x9uTs6mvNDQthpt/NUfHyv9cabTLS63ZyZm0v23mrdUUYjXioV36SmsnHcOE4PCmK23kVCQALemp7JQ0VRSJ8wjbqdz1Jv6+ZRR7jreTz3B97IkgL5VUVVrG5fjXv2PTz8WAfP7i2sXL8ehs92EKDRkDNxYq+k+GBx8t58T9Qh+PFT/fzIaGvjtshI/FUa5mtCu7T8fT+No7a9A9UYC9XVsCQnhx+am5k9QGG8I4XVmonJJGN2MefOQxccSH39J3R29m5m0hfq6j6htXVzr+MHfrb3h8fTTnb2QoqKbgIkKcLttqDX//5tBL28QklIeIHw8Kv6HaPTRRIUtJiyssf6HfMneuL4MPjAZ7dtwrRgcQ9FND+/6Vit26itXU5nZx0tLWvx8UmWAfapU2Wmbz/4+KTQ1PQddnsBgYGnk5z8PibTRJKTP8BgSMZuz8M73Juof0Sx58k9tJWWEXxBEEOuG4IQAo1Rg3La9+RnX0HVF9vYcfFGyrd9QHtZO5Z1Flo3teLp8ND8czOmCSbG/TaOITcMYeSKkQx/dziVL1TS8msLuhgdLatl0+XkL528mZRE03dNtG1uI3NyJrvu2DWge/L43MdZOmNp1+MwYxjVbdXsbtnN4o8Wk16WzgPpD+Dr7cvPJT/3OUfamDGcERzMi8OG8euYMQT2EXNWKQqPxcVh1mh4q7qaCSZTV43CCB8fQr28eD85meHOfEaHju5znVC9iZSgJPLquwn37Q0biIw5o8vg7yncQ2h8KOG+QVx1bxarV0tVzsaYZmZWbuLEvTIWR4o4vZ7N48ZhOEQJ7XCDgdMCA/l7VBQ106fyw+uGroK1hKEKVw4JJ/Csen7d2kFlezuuWbN4oo8fzGMBu72wi/eu0qgYNvxF8vLO77d9oM22k/b2blGgiornqKl5q8eY3bsfZsOGvsNQQnjIyBiHSqWnvn4FVmsOeXnnMXz4chTljzETBkPCIRk4UVG3U1//6e90Rf/3cdwYfI1Kg/LSS/Dmm7IFE+DlFYRarcfjacfffxb19R+j1w6V6li+vvDxxz3m8PEZSU3N2wQFnY5KpUWvjyUp6VW0WjP+/nOoq/sEIQRhl4XRWd/Jrs4zEeM30N5eSXq6itbWLYjzl9PY8gWFvhNpnHMxu1ouQjPMwp4n9pA5OZOdl++k7KEy9MP0aAO1KGrZozT47GBsO2w0fNFAyPkh6BP1tGW0YdvYRqrRSOtvrSS/n4z/if5UPFcxoHvir/PHT9edYA71CaXaWs3wF4ezq3kXX13wFWadmdum3MZ3xd/xa2nvFlTaAXrLl4WFcVFoKG/V1HTF//eHl0pFdt12RoX2z9YYFjiMgsYCrB1WntnwDGWlK2nVD6WoqYTP8j5j9849RCRGMCxwGN5hJWzfLrn5yvXFvJKYyHOHam4wCEz07VsrZn+oFIWvUlMJ8fLqkdj+6ScpE33tRDOWqdX821bCWJMJtaL0qp04FnC5LLjdNry8ukkC/v6zmD69FZsth/R0FW53z9qHnTsvJTPzBBoaVuHxdNDWlkFzc1rX8x5PO2Vl/6KzswGXq7f+VEtLOoqiZdSor/HxGUVBwRWEhV1OUNCiY/dCjwL0+iQ6OqpxuQ7dDvJPHEcGH4CQENmGab8eeT4+qZjNJ+LnNx2bLQf911mSd/jPf/YUS0HSutRqE7GxvbPXQUGn09lZj9WahcZXQ9zLIRBfgj3pA/bseRyAnJwlKB4DURlrUG8+GW2UG1/PqQQ/tgfckkdc92Ed0XdHE3ZJT7qjWqfGb6Yf1a9X4zfTj8ibI/Gb7od1uxWX1YVlgwW/aVJyVlErg2ryvA9atfTO293tTIuahpfai6Z/NnHzCTeTXpbOSctPwu3pu91fa3srE5ZNoMXZQvru9D7XT/HxodXt5uR+WETba7f36+EDJAUm8Vrmazyz4Rlu/+l2Xpn/KNE6PRNHXsc5Ky9H60zFmJhIvDkeq9cuGhrg53VuOkIc/DU8nIhjJFEwWMydKxlDY4xGXBoPmaE1tJVp6UcW6ajDbi9Cr0/oxWrRaEz4+k4BwGrt7lTjcOzC6Sxj2LD/Ulh4LSUld2MwJNLZ2YDTKRuYt7Zuwmgchb//bCyW3o3KGxq+ICTkfEB+59raNmM2D1BQ6Q+ESqXBYEjGZsv5oy/l/wSOL4MPUv1q5couLz8k5AJCQy8hJOQCAHTfZsLSpbI6avv2Hqfq9bHMmNGKThfVa1pFUePvP6crrumOzAGLL3ZjOpWVLzJ06CN0dtahq53F7tsbcD9zGSNSPiBs+CKcgbL6L+7JOMKvDif2wVi8QnsXTiU+l4hxrBHfE3wJvSCU0WmjMZ8o9cc1vhp0MTo0Rg0qvaw6PFwsnbGUWyff2vXYT+fHJaMuAaC0pQ/xeSCvPo+t1Vu58ssrmf3ObN7NfrfXmItDQ8mdOLFPzrtHeNhes53RYf0b/DhzHBlVGTyQ/gAPz3mYc0aew+mBgXxvmIVywsdEVoBzeDTxAfGUWnZxwQXwr/etxKoM/Tb//iOhUhT2JMxi4ecTKFsa36Mb2LGE3b6zq6vUgRg58jPCw6+mrS2j61hLy1rM5rkEBi4gNfUramvfZejQRwkMPJWGhi/3jknD338Ovr5T+5QvaGvLwNdXMsF8fGRfaKNx3NF+accEPj7JZGVNoaOj7tCD/8dx/H3LTjhByh2ecgoIQXj4XwgImI/BMIx481J8ch3S2KekyG4dg2hgbTKNw2rNBMDSuo4gr8tJSfiZYcNeJSzsr0RE/A1dyUIARrw0C3PEVIzGsbRrZZcM00QTSf9N6relnD5ez4TMCaj1Mnas0qgYctMQKl+oxP+k7mTfPq3zvAvyqHx1cAJQnvs8PHziw8yLn9fj+PIly1mQsKBHDH1/5NXnccKQE/g8/3Pmxs3lzp/vxNHZU2/HR63ut1Xg1qqthBnDCDP2rtzdh4WJC3njdLk7mzREJhwfiYvDM2sWO4Im4OWrZpuhHbNvHBvbddz2rJ0nV1qZFXH86qFHRSp887yR+aN17K804XQeWn7hcNHc/BP+/rP7fE56+SdQXHwzmZlTcDrL9/5AyEp0k2k8U6fWEBS0iODgM6mpeWNviCcTX98TMBpHYbPt6DGnEG6s1u2YTLIhudGYipdXGN7eg687+SMQH/803t5R2O07/+hLOe5x/Bl8tVqGaux22UtwP0TtGod63GTZk87XV4reHETv5UAYjeNobv6ZoqKbqK5+jSGTFxMUeRIREdfg7R1GYuLzeLVKAbOQc2Vyy2AYgdNVQMxDUXgFDb7RRcC8AEb9OIrof3br73gP8abskTLqPqqj5deWQc13sOKVEUEjyK/vu4VTfn0+i5MW88iJj/D8gudJDU3lq8KvBrRmk6OJlTtXsjBx4UHH+Xj58Nexf+Wh2Q8xJWpKj2sO2OFBM8FIeksL19V6UWKayt+y08lttzLWePwa/H0YObLnRy00FBYvLsBuLx7UPI2N35Gbez55eRfQ1ra11/NCuGlq+pbAwP5j56GhFzFpUiFBQUvYtm0OLS2/SDLDXuz7jAQGLkKrDaWqahk2Ww4+Pin4+PQ2+DZbPt7e4Wg0Ml/k6zuF0aN/GdTr+iPh5RWKv/8snM6+d7d/ohvHn8EHqWlwxhkytAOyA/eMGbJbxf4NUhcskMphA4TJNIGIiGvp6Kils7MOX9/eVZyJLyQytb77uEZjxMsrjNDbBr6TOBAB8wLQD+3WqfGO9May1kLI+SFdmuFHA5MjJ/NzaU+2zh7LHlweF9trtzMieAR3zbiL5OBkZkbPJKMqo5+ZuuERHgKfDOSNrDe4evzVA7qOa9uuJW9MHhljM+iolwJubVvbiJliRqMoXBASykOhXmy1tZNltfbSyDkesb/Bb22VCpupqY9RWblsUPMUF99Cc/PP1NV91FVJuj+czt2oVAZ0uv4lS1UqbwyGRKKj/0FIyLm0tWVgMPSuEFQUFeHhV1Bf/ykdHdXodPHo9Ym0t1f2SHJaLOn4+c3ocZ6PTy8V8+MaOt1QHI6SrsednS3U1n74h12PEIKKiud6Jdf/aByfBh+kwV+6VCZo58yR37DNm3sa/NNOg1WrBjylSqUhOvoORoz4kEmTClCrezdrVRvUvTx5o3F0n97Y/vB4Bq6vO+y/w5hWN42Ye2NwFA++AXd/WJS0iKzqLG79/lZanHLnsOTjJTy69lG2Vm9lbtzcrrHjI8YPyOA32hvx1/lTfms5wwKHDeg6Cq8qJP6peHTxOhpWys4nthwbAaNNbBg3jmcTE1kSPRab4k1GWxsq2+/jmTXYG+hwD14HWQhBYuJmcnJkojs3F8aNg9TU32hsrEQIN0Ic+ofb7bbR3l5OYuKLBAefQ1PTt7jddrKzT6G5+Vfcbic2W+6AZIj3ISZmKb6+U9Dr+475m80nYrGk71W71KBSafDzm05j46q91y1obpbx/f/L0OmG9vDwi4tvIT+/l4rL74aOjhqKi29h1647/rBr6AvHr8GfORPi46UIvE7XTcHc3+CfeCIUF3cleAcKRVFhMAzMeAH4+8+mpaU35XEfPJ5ONmyIoKPj0K0N5fpyy60bqsNZ5kS4B8/Y6Qs6jY5PzvmEKmsVS39ZitPlJLs2mwfTH+TKsVfi49Udnx8fPp4N5Ru4L+2+g85Zba1miGkIXuqBhbOEW9DZ0In5ZDMh54ZQ91EdQghsOTZ8UrrXHxaYiK7iQ7Du4i+fn394L3iQiH8+nhPfGTzzZM+eR6mrO4GAgAzuvFMqek6blkd4eAFtbZWUlt7Lxo0xVFe/3eM8my2ftrbMrsdW6w4MhmRCQ88nOfkDHI4SWlt/o6npe0pL72PdOj9ychaj1w/8s6lW+zBu3AZUqr7fH602kBEjPmLkyG4Kc2jopeTnX8yOHYvJzJxES0saZvNJg7spxxkMhmE0N/9CW9tWhPDQ2CjDlR5Pd89ri2UDJSVL2bgxlrKyR3C7D97H4Uhgs+WgVpuwWNYeszUOB8evwddqpTEfNw6mTYNhw6S3P2JEzzHnngsrem+NjybM5rlUV79OXt7FXcdstnysVkkFa28vx+VqpKVlcHFPtV6NYZiB3Q/s7tLeOVLMGTqHa8ZfQ059DttqtjHUfygXpl7IfbN6GvajShrWAAAgAElEQVRgn2C+u+g73tr21kEpotVt1YSbBp6862zqRO2nRqVREXh6IB01HVS/UY3H6ekh76soCq+NPZlhu5/E6HXsY/jtrnZa21vJqska0M5mf1gsG/HyCmfOnHW8/HITev08Tj99DpWVV9DZWUlT03dER99JcfFNXRx3j6ediorn2L37fkAWUmVlTeliwKhUGozGUdTVfYjROJbW1vVdXrq39wDbjQ0QISHn9dg1hIZeQELCczQ1fUNbW8betoMRR3XN3xu+vlMZMuRGSkqWYrVmo9UG4uUVQUeH7OYmhKCo6Aaam38kKuo2qqvfxGLprb1/OLDZJFGivb2yq/bBZsshKOhMHI6iAe3+fi8cvwZ/H84/X0opKgoUFcGBipUnnkhX1/ED0dAA11xzxJdgMIxg2LBl1Nd/gsvVhtttY8uWEezceTlAV+ywqWnwAlejfxpN049NAy7GGgiGBQ6jsLGQVTtXMT9+Pu8uebeHd78Ps2Nno6BQ2FjYxywS1dZqwo2DMPh1nV29atU6NTFLYyj5Rwm+k3x7JZwvGnURW67awtbqrazMXzngNQaLnQ07Of+z85kQMYEn5j7BUxueGtT5DkcB4eFXcMIJX/Doo/cyZpyZqTN3ERLyLAZDMQ7HLiIirkGvT2DDhlBqat5j8+YR1Na+R2Pj12Rlzaai4jmGDLmBhIT/dM1rNI6ntvY9AgNPQ1G8MJtPwmgcg8k08Wjfgh5QFDUREdegUvkwbNgy4uP/fUzX+z2gKAqRkTfT1pZBdfVrmM3z0Omiu5g7bW1bcbvbGDduE5GRN2I2n4jDUXTE63Z01LJly0ja2rbS0LCK4mIpS2GxrMfXdzJabTBOZ9kRr3O0cPwb/Dvu6JaA7AtTp8ruGn1x5HJyZBGX09nzuNUq5x0gFEUhIuIqzOa5NDV9i91eiJdXGE7nLoQQOJ0lmM3zaWj4ErfbeegJ94NXqBexD8bS8PnAwkEDwRDTEGqsNTyz8RnunnF3v+MURWHRsEV8sOMDQHpBBxZuVbcNzuB31HegDemWbwhYGICr1UXoxX0IywO+3rIi9swVZ9LuOrTO0OHgyi+vZGTwSN5e/DYLEhawqWLTgM/1eDpwOvcQFn4d/uGBjBmzmmf2FDDy5VFMny53Jj4+sylvrcJsnoePTyqFhdfQ2VkHCIKCzsJqzaKm5i2io+9Eq+3W5Q8MPAWPx4nBMAKDYThG41gmTMgiIGAujzzSsw/w0YZK5c2ECVmEh1+JwfD7a+UcC6jVeoKCFlFV9QrBwWeiVvuRnT2ftrZtNDf/TEDAKV0yEXp94lEx+DabzORXVDyLw7ELmy2HqqrXsNmy9+6shh9XdNHj3+AfCuHhsut4enrvPnVlZZKnn3cANz03F559VmryDgJm80m0tKRjt+/Ez28mKpUPbW0Z1NQsx99/FkbjaJqavhl0bNB/lj/W7VY6W45OKec+T/rC1AuJMB18q379pOtZlrkMl8fFsq3L0Pyrp4xwVVvV4EI6dZ14BXfHk7VmLSM/HUnwOf03Cim6sYiUkBRWFazCIzxc/PnFB911DAa11lpy6nK4d+a9jAwZSZw5jiZHE82OgYmQ5eaei6JoWFmUxtnrfyRxzK/stnZS2lLKNd9fSmXlJKqqHmLGWzMo9sxg7Nj1jB79C6NH/8rYsetISfmUyMhbCQ4+q1eoJjDwVEaPTkOrXURQ0FMEBZ0OyM5fzz8Pv/afNjoqMBgS/7Cm6McKQUFn4uUVhr//bFQqGUJsa9tCU9O3PfIUR9PgBwScSlPT9zgchWg0gZSU/JOIiL+h1ZoxmSb0aNu4f+/tPwL/9w0+SC//9NPhttt6Ht8jy8rJzpbdTPahuFga+4rBhVH8/GZSVfVfqqpexmAYjq/vRLKyZmC378RkmkRo6MXk5p7N+vV9N+juD2q9GuMYI9atR08PpPjGYpYtOjRlcETwCCJMEawtW0tlmywCK27q5paXWcqI8u1dudwfOup6evgAwWcEdxWj9YWEgATGhY/jvE/P466f7+KjnI/IrM7sMcbR6Riwkd4fXxd+zckJJ3cpfKoUFaNCR7G9dvshzoTOzmYaG1eRnPwuK3euxNfbl7e2vUWNtYb3lrzHu9nvUla+ji9+HEJ5azmf5H+BSqXFz28yvr4TuwqZYmPvIympp5DZ0xueZk3ZGiyW2QQG+nDllfO7vP+CAum7HOin/IlDIzDwNCZM2I6iqBk5cgVxcU9RWHg1QnRiNncXK/r4jKSlZQ35+ZfhclkOez2bLYfAwFPQaoNobPyamJi7cbmaMZslI85snktZ2cPs2LGY/PzL2bHj1CN+jUeC/z8M/rRpMkzz1Vc9wzdlZZLp8/HHEBgITz8Nn30mmT8waHaP0TiG8PC/YrGsw8cnheHDlzN58m6mT28gIGAuwcFnARxWksY41khbZtugz+sP8QHxA2bWnJF0Bssyl7HHsge1ouargu6CrPyGfEYED4yT7XF5aNvU1hXDHwweP+lxVl+2mmWZy3ALN7uauhVFbR02zE+YWfTh4IW8vij4gsVJi3scGx06mm013ToJTmc5Llfve9/Wthl//9kEB5/J6t2ruXbCtfxS+gsalYaLRl1EclAysZNy+XFHFjF+Mfyw64c+r0FRVKhUPXdOd/x0B7PensU997m54QbYuhU6O6Vf8ve/9y70+hMDg6IoeHnJ3aRK5Y2vr8yHjBz5GWq1oWucwTCMsWPXY7fn9aktNFA4HEXo9UkMGXIDAOHhV5KY+DI+PqkA+PlN2/vvDJqavsViWf+HcvOPyOArinKOoii5iqJ4FEWZcMBzdymKUqwoSoGiKCcf2WUeAnPnSk7++PHw5Zfdx8vKYNEi+OEHiImBO++USeAdOyTV8513ZEVvZye8+67cSx8EKpWGpKTXmDnTSXDw2Wg0Jry9u6UGNBo/pk6tRa0efCGRaZwJa9Yfo/h33cTrKGos4p3t73DZ6MvYULEBAKfLSbmlnISAgcV4q5dV05bZRsDC3v1jD4VwUzizYmex4uwVnJl8JsXNcpchhGDRh4uI9oseNIfe1mEjfXc6CxMX9qDMjgkb08Pg5+Wdz9vpJ/eSmrBYNqDWpWDrsGHtsDIrdhZppWmMDZee+/iI8WzpWE759MXMjVyCxWmh0d7IoeD2uPFSexFnjuPnnCwWXp5L4ORv2bIFLrkEYmPhxx/lj8DTT0vuwe8B+0HskBAweTIsXHjIr8lxBT+/mUyevKdPFpLRmILJNLFHwVZfsFqzqa/vm1TgdJah08UQEXEd48dvRaPxZciQa7tCZSqVN7NnC6Kjb2fq1BqMxjFYLPL71ZeTcaxxpB5+DnAmsGb/g4qijED2rx0JLABeVhTl4OLkR4LEROndX3klvP46VFdLI56dDeedJz+hV14pjb6XF3zzjaR7Ll8upZY/+0x2Ln/rLbDZ5PkHgUrl3W/sU6sNxu22Djp56zPaB2v2H2PwAw2B3Dn9TgAuGX0J6/esRwhB8kvJdHo6u1Q6+4PwCNqr26l8oZJhrwzDd+KhpYn7w7z4edww8QZ2NcmE+MqdK2lxtrDur+sobCwcsMpoZnUm096cxgmRJ+Cjho0bI7qE8/Y3+Jt3f4ql9TfabRtZX97t6a3PvYuMwoc465sX+ankJ6L9okkKTMIt3MyIllWpF6dezHObniW5+XbGWu7rFSqqaK0gu7Z35rWitYIQnxBOjl1Ek/lHNrd9Rsu4e8jPl81gHnxQqoZ8/DE8+SRERfXmHRwLjB4tfaEDIQR8+CE0Nsoo6RH20f5doShKn2KK+6DTxeF0Hrw/RWnpveTmnokQkhjS0dFAY+M3eDzttLdX4O0dhaIomEwHF5tTFBX+/nNoaUlDCMGmTXG0tx/c1vQFl8tCY+M3gz4PjtDgCyHyhRAFfTy1GPhICNEuhCgFioFJR7LWgLBkCWRlyR+As86C6GgpxhYcLD/N27fD1VdLPv/XX8vdwJ13wgUXyONPPy2/beeee9iXILeU4XR0VPU4npt7Lps3p9DS0nchhmGYAecuJx7XMVLkOgQWJi7ES+3F9Ojp2DvtFDcVs7tlN7eccMshz63/pJ6NERtxWVz4zei7QfxgkBSURG59LnPemcPN39/MbVNuI8QnBI1KQ62t9tATAD8U/8D22u0sGb6E1taNCOGhvPxpNm1KJMqrkaKmIj7L+4w31pzDmiYzQ30UynffR0PDKhobv8dW/wq7O+OJDj6JFbkriPGLIcQnBF9vX6ZHTwfg5IST2XLVFm4adT8bfjUzOnQ0WdVZXdfw9IanGf1qz/CR1QqnXVqClzWOMPs8vEf+SFFTIU3eWaz8pYKAAPlxBbk5FUIa+zff7M1JOJpoaZGprbV9fDw3b4brr4dHHpHq5R/+cYoFRx16fTw1NctpaUnvd8y+UFBt7fvU1n5AVtY0Cguvp6joRjQac58V+/3BbJYG3+VqprOzoUvXSAg39fWf09l58B2i221n69aJlJU9MuA198exiuEPAcr3e1yx99ixhU4HV1whwzsaDfzjH5K//8ILMHs2GI2SjvnZZ2A2y29UUxNUVsIrr8hdwVNPyR+GnMPX1/b2jqC9XRp8qzWH6uo3aW7+iZiYpeTlncvmzcldsrX7oDao8Qrzwrm725XzdHgourEIx66jJ7/QHwxaA+33tKNRaYgzx5Felk5qSCrPnPzMIc/d17bRPN98VFgfEaYIhpiGsLFiIy3OFhYPlzH44UHD+1UDPRCt7a38ffLfuXLMRdTWvk9g4Cm0tKThcBRTVf4w75/5Pud+eg7zQuHN4kY8vpfi785g584rKC65mx+bYgiPfoAFCQv4LP8zYvxiUBSFFWevYFrUtK51xkeM5+R5Gn7+GRYnncGb297Es9cTrGyrJMo3is/zP+8aX1gIudVFVOYO5f7LZuE0Z5BVk4W/OpxvsjcwaT+3SKWCG2+E4cOlwf3ooyO+tf1iX75gYx+1SGlpcNll0g+aNQs2bDh21/F7Q6+Pw+Vqori427Gpqnqd0tIHuh7b7UXExz/Drl23U1v7PkOHPsKYMaupqXkHnS5mUOv5+k7FZtuB1Sodg306/qWl97Nr1x1s3z73oLvYiornMRrHMm7c4b0JhzT4iqL8rChKTh9/iw917h+CRx+VLsjnn8M558hj550nJZdB7pX3l2cwm+UxlUqGhR5/XHr5t97ae+4BwssrAqezFI/HRUZGKgUFV2A0jt1b4SjfsObm3pw7fZKePY/twdUm6aLlT5XT8FUDeRfmHTX5hYEgzhzH6t2rifIbGDvHvtNO4KJAwm706VMQ7HBwzohzuGTUJey5ZU9XJe648HFkVmcy/935fF/8PfaO/oPOVdYqUkJSaGz4lNbW34iOvpPOzoa9IlsFnDH8DG4ZdzY6rQ/DwueycMKb3FUQTYf3BKzWLF7Ny2ZixERmxsykw91BtJ8UMzs54eReIa6hQ+XHK9Q2F7Wi5o3MN/B+2Ju00jRumXwL3xR1b7937wbzrPfQlC7kjpuNzI6fRl59HtOCT4WAIub1VL3mvvu6P8Zbtgz+Ptrt9Knj/8QTctcA8OKLMnw0c6bMHbQe0BBr9WopZwUwapRMjbUMTuT1uIWPzyji4p5Are7emdbXf9wliSCEwOEoJCzsL0ybVsuoUd8QEnI2en0sQ4Zch14/uJaXarWekJDz2bXrdoAuD99iSScpaRmdnU1Yrf0zyOrqPupKEB8ONIcaIISYe6gxfaAS2N9aRO491iceeOCBrv/Pnj2b2bNnH8aSe3EkjTSGD5d/LpcM7+zcKR8PEmFhl1FUdAN6fTx6vSxpNxploi8k5BxUKm+qql7tdZ4uSkf169U0/dBEwr8TaPy6kaTXkyh7sIzqN6uJuCqCzs4WKiufJzb24Bo4R4I4cxwf7PiARcMGxoqx77QT+2AszohVFBfcRFDQ4i4O9OFACDdLZy7F5XH1YBpNiJjAt0Xf8lPJT/xU8hMrp5uYOPxJhgz5W685qtqqiDBFYLN9T0TENfj5TUOrDcbPbzoNDavo7Gzk9vELaW5W8/3s91ApKh4+8XFu/OF8rk+OI/v6n4gzxwFQeEMhwT791xGA5A388ovCvJHzuP7b6+n0dNLoaGRx/Pn888e7cXlcvJn1Js/mfUmnoYxXb1nC3DlQy5NMfmMNM+Im803gGk45pffc06bJgvLNvXuS94tbbpGb3JoaeP99GZsvK5Ob4JNOkiKzZrPc+N57L6SmyiZyH38sfwDu3q9eLztbprxAqplMmQLvvScTuHFxA7+m4xGKohAcfBZVVa8A4HJZsVjWodHIJkAtLelotYF99taNi3sKt3vwebfIyFvYsiUFf/8TaWz8hsrKV7Fat2M0jiUk5ALq6z/BZBrT67wff/yIjz4qIirqJxTlMOWrhRBH/AesBibs93gksB3wBoYCJYC6n3PFcYm//12Ie+897NNzcy8U69eHi7y8i0VV1RuiuXlt13M2206RlobYtetO4fF4uo67O9zC4/KIpl+bRLohXaSRJlx2l7BssogNURuE2+kWtbUrRFqaWrhc1iN6eQfDK1teETyAeHTNo4cc2/B1g1jjt0a017aL/Py/iLQ0RFoaor299qDnud3twuNx9TjmdFaJ7OzTxKZNI0VLy0bR2PiDcDjKRUdHkxBCiNy6XBH3XJxI+TfC8C+5ztq1/r3m8Xg8YsRLI8SO2h1i27a5oqHhWyGEEFu3ThalpQ+KjIyJoqVlvcjNvVCUl7/Q49zS5tIe78lA8emnQsyfL8TK/JVCeUAR725/V5gfN4uvvxaCW2JEZmW2CHkqRJjujRWXP7mi1/nppWtE0lNT+p3f5RLC31+I6mohBnJ5kZFCgBAGgxBnninE0KFCpKQIERYmxC+/CGE0ChEYKMSNNwpx003d56WlCTFmjBB79gjxn/8I8cUXQuj1Qrjd3WOWLZNzn3SSECtXCrFt2yBu1HEIt7tdrF7tJdzuTlFf/6XIypot1qwxiurqt8XatYGivv6ro75mWhoiO3uxsNkKRFqaItLSpB1sbl4ttmwZ3+c59fVfie3bF3Q93ms7B2Wrj5SWuURRlApgCvCNoig/7LXgucAKIA/4HrheHE8KQgPB7NmQMTiRrf0RF/coHR3VmEwTCA//K/7+07ue0+mkW1Re/gzV1W90aW2otCoUtYJ5jpkRH40g4YUE1Ho1vpN80cXoyD0nl8L/fgK4aW09jP098ge+pSW9i3HQF+bFybjCwfj3QnjY8tNsdj74C+FfbEETpNDSkkZi4osAWCz96BsBDkcpv/02lK1bT+hRldzc/CONjV9jt+dSV/cROTmL+e23KHJylgCyZ26ttZY7ktRkX/wEdpeCxjuB6uo3e8w/ftl48urzCDeGdzX+APDzm4XJNAmDYQT19Z/R3PwToaEX9zg31j/2sPIQ8+fLQilt+Uk8u+BZLki5gFXnryItDWhI4pnVy0gKTGJaxi7OGHZOr/OTgoZR7ynoN36rVsO8eZKEFhfXs47wQLhc8vm335YtIy6/XCZ+16yBf/1L5gVCQ6UO4RdfwD33dJ87fboMAUVHy6jmGWfI9fbfOJ9zjqwV2LJFSlXdfnv/15KfD98PXmLqd4VK5YVWG0xHRxVNTd8TEHAKPj4pFBb+jdTUrwkKOu2or5mc/B6xsfdjMAxj4sS8rsI8X98pOBxFfSrvOp270OkGF0LqhcH+QhztP45XDz8/X4j4eCE2bBiYS9UH3G5nL+9zH6qq3hL19au6POJDoeyJMpFGmljz8XCx7sMxIjNzpnA4dnc9X7uiVnQ0dxx0jpaW9SIjY5JYvdpbFBffPqDX4PG4xcaN8cLhKOs5V/MGkZaG2PBLokhLQ5SWPiQ2bowXHo9HlJY+JIqL7+hzvpqaD8XGjUNFScm9YuvWKXu9+DJRXv6c2LbtZFFa+oAoK3tCpKUhdu68WhQUXNvD45nz1lTx86+IjIxJ4v0fAsQHGQ+JdetChMNZJ1ocLcLj8QjlAUWM/+94UVn5uti8eXQvj72y8lWxerVGFBbePKB7MFC8+64Q8+b1PDZunBD6M28UPIAYc/NDIjZWiLq63ud6PB4R8lSIKLeU9zv/Rx8JoVIJodEIccklQjz7rPS8KyuF+Pzz7nE5OUIkJOw/txCtrfL/jY1CaLXSe/d4hOjs7L1OSYncTURGCpGaKsTixX1fz+rVcidgMgmRlyfEli29x9x8sxALF/b7ko4bZGXNFg0N34nNm0cLi2WT6OhoEA7Hnj/kWrZtO1nU1a3sdbyw8AaxZ89/uh7ze3v4/18jLk5m2KZOlXH8zMxDnnIgJF+/7/KD8PDLCQw8jdDQSwH6VdQTez2+oDOC8JvphxJei/u+OzB5TWZr2jxK7pd6ICX/LMGSfvAS8ZaWNDweO6NGfdfFEmpry6Szs/8MnMNRjNO5i23vX8fuh3fvnWcNZRvfAaBdVURo6MXs3n0fYWGXoCgKfn5Tqap6hbq6j3vNV129DJ0uhqio2zGZJmC1bmPbtlm0tv6GSqUlLOwKQkMvRa32Iz7+SeLiHsNu34kQHlyuNmZHRKMgq2CNxnFc+PV9bG3145O1J3DGx2dQ1VZFoCGQLVdtobb2HeLjn+jlsfv5TUcIFwEBR6Ee8IMPoLQULBbOOksWS5XtfSubmyUr59S4M8EWTEjT6WRkdNMu94eiKIwLH8fWqq2s29P37ujcc+XHcNkyWSf473/LJOopp8Df/tbd3jkrS7Z97p67m7MQECBzATfeKI9r+sjiDR0q2cyjR8sdQn9prFmzZI3AkiWy5nHWLLmL2B8//ijvwfEOP78ZWCxrcDp3odcnotUGHpS/fyzh7z+jl46+EAK7vWDQSeID8afB7w9eXrJQ6+KLISiob4LyEUJRVCQnv0Nw8LnU1r7f63m320lGxijZBWmYgdRfEvEIJ5qOWKqmnEpniZ6Gtk9wO9w4dzuxFx68ZNvpLCMi4jr8/WfS0VFLR0cd+fmXUFPzdr/ntLVtxWgcjzNgI7vf/47cHRexbdssmpTlmMWFgEJc3JMkJ79PdPRdAPj7n0h09N00Nn7XYy6Px0VbWwYjRnyCRuOLj08qe/Y8iVYbzIgRH5Ca+hU6XSTe3mHMmN6MRu2LptaKx+Ng586/sHXreGYZN9PpLYu6JyY/hU6jo9AzBzOlFNVncE/aPUyIkM9brdkYjb2LYQyGZIKDz8Xff9ZB79chsWuXLI2Ni4Nbb0WvlyUdb70lmcCffiqrUx//22xeiavj81dGExjY/3TjwsbxWf5nzHhrBnW23qR7RZFG+C9/keKw110nFcOzs2VDuG+/lUSzzMyeBv9AjBkjQ0QHw4wZ8toffFAyhQ6GF1+UHIc77pDlLe3tcNddkvNQVQXl5ZLxfDzD338W9fWfoCjaPhO0v++1zKGhYRUtLWvJy7sIIQS5uWfR3PzTEbeePCRL538ac+bIL/SOHUfEyz8UYmLuZceOU/Dzm9ZlhOz2YrZsGYkQLhoaVuHjM5L29nK8vSMxjDHR+GUjrDgTx9lf4Ci+CQTYCw5t8IOCzkBR1Pj7z2b37oew2/Nobv6ZqKi+C6ys1kyCg5dgrXoG1V8/pb7xV3SOiRi+X0ro3TYcpZvw9g4nNLS7nZyiKJjN86ir+xiPp6OrG5PNloO39xC8vKS4nNE4CperkdjYd2RtREuLrKMASRN56y3w9yfhjHiqz5KCWD5evkxOXYVG449abcCx1IEQgnVbMnl13imkV1Vz24zLsdvzUav1Xboq+0NRVD06QB023n1XWt3Q0C5py6uukp6uxSIZLS+8IOWc4gfgmE2JmsJTH0ut/ohnIrh18q08Nb9v7X5FgZtvlq0inn1WGvC77urm0w+i1XOfWLp04GNNJnkb1q+XX5fQUMlufvddye4pK5OboGEDb+T1u8PXdyoORzE63dA/+lLw9Z2C0TiKHTtOxeNxEhJyLg5HMdOnt6HRHFmzoD89/IPh9dfltzc5WWafjhGMxhRiYu6lvLy7EYXFsg6TaRzJye91lVFLgx+F7yRfIq6JYMTfL0fE59K8ZQ+aAA2OgoMXaO3T/QCIi3uc+vpPiY5eisWytku21eWydom/dXY27vXwx0H+cDwTf0VpCEOTM4OA+PH4+88hJuaePtfy8RmJzbadzZuHd4Wl7PZ8DIaRXWNMpklMnJhLYOCpUkNg6VLpHoJ0W2trYfFiIv9bz5jg9xiVuIoJEzLw9o7oIYSlKAoRgSeR4qfh6gQ9DSXns2XLyC5q3TFDXp4M+V15pSzWE4LRo2WS8scfpXjrVVcNfLoTh56IRqUh2BCMW7h5OeNlWttb+x2v08lwy6uvSq89N1dWwkI3jfL3xOTJckN8++0y/FRZKXcaI0YMjlL6R0Ct1gP06Iv7R0FRFJKT3yc29n50ujhKSu4iPPyKIzb2wJ9J2wGhslKIoCAhHA4hpkyRHLmjjPb2erFmja/weCT/TSZonhZud7tYvz5MWK25oqrqdZGff7nwuDzC3SnHrX15llh/6f2i8KZCke6TLvKvyO9zfqezSqSn60VnZ1uv57ZsGS+am9cIIYTIyTlbVFcvFy6XXaSn60V6ul5YqyrEmuEfi4qKl0TOVWtFmvd3onlt8yFfU2npA2L9+iHCYpHZvJKS+8WuXUt7D9yzR97fceNkJlAImTEsKpL/v/ZaySE8++x+12ppWS/WrQsWW7aMF2lpiN27HxF2e8khr/GIMHp0d6YyJESI8v4TrgPF7T/cLrZWbRUdrg4x+pXRIqMyY0DnrV0rqZIrVgjhdB7xZRw2PB75FfnlFyESE4VYvlyIVauEGN830/C4Ql3dZ6Kq6s0/+jJ6IC/vYpGWhnA6q3o9x59J22OE8HDQ62X17saN3Tr7RxFeXkFotYE0Nn6FEB5aW7dgNI5HpfIiNPRS6uo+wm4vQKeLQ1ErqDTyrQuLO4eOpF+JucegPh0AACAASURBVDeGabXTaNqaT9ZLd7Jjxxls2BBJRcULAOTlnYe//+w+vYSAgHk0N/8EyIYObW2bsdly8XgcaLVBeKp90XkPZciQ60h6ajLBp0dhGntoRdDY2PuJjLyJwsKraWr6CYejoEdv1S5s3y6zfqmpMihdWytF7PbFQf75T5md/PFHGSvpA35+U4mMvBmrdSvjxv1GTMzd6PVHeXuemysD00VFMkNaXNxdtT16tLy+BQtk/OIw8dT8pxgXPg6tWktiYOKAG8HsS6wmJ4P34de8HTEURXYcPfFEWfC+ZIlUOsnLk2/p8Yzg4DMJD//LH30ZPZCY+BLTp1vw9h54E6KD4U+DPxAoiiRaP/mkfFzQl17ckUOrDSQn5wy2bz8Jl6sRX9/JgNTht9t30tT0Q4+uPQDRc85FPX0b2kA1NncmnuevprX1N/xVi4iPf5rq6jcA2Wg5KenNXmsCBAWdRU3N27jdNhyOYtrasrDZtqPXDyMoaAmOEge6WCkQpfHTMHLFSNQ+AxM/jYq6gyFDbiIv71za2rL6Nvh5eVIAPjFRGtEdO6QB3ceuiYmRMfLx4/sWe9mLkJALURSvLi3yo4a8PEk/ufRSaVFTUiQR3WCQ3dZAxlSuu05Kcb/77lFZdljAsAEb/KAgufwh4+TXXitJ9gfTQj5C7OPsjx8v5atUKin5fAS/g/+z0Gh80WgOX332QPxp8AeKs86SnigcM4OfmPgKo0f/QmDgIlJSvuxS4dPrE7BY1tPeXoGv7wk9zvHyCkbrFYLdXkBFxbPEDr2PkLxXUK1dQHDwWTidpdjthQjhwsurn76yvhP4f+2deXxMZxfHf08ispBIIgkREYnYQmLfaWPft/b1VimlqxfVTVtKCdXadUFpqaWqWkuVKq2lYgmJPUIEEbKKCBKSyDrn/ePMZCYySWYyk0zI8/185mPmzr3Pfea6Ofd5znPO79jatseZMy0hRFWkp4fi3r19qFPnLTRs+A3SzqehesvS+Q+FEHB1HYfq1VsCoHyJCQAcuvHf//II3seHVUyvX+cF8mbNCjfm7l6sdLW1tSc6drxVwL9vFGbO5LWcuDh2ym/YAKxZU9BR3qIFrz+sWcML0EbAx9kHpxJ0d36vXMnBZcVy6BDrLQQHG9Y5PfHyUtcdkpgOafB1pX9/YO5cYPJkntaXAXZ2beHg0APu7h+gWrWm+dutrRsiOzsBjo59tMb129m1x4MHh3D//l64uPwX9j3tkXosFWZmFnBw6I3IyHdhY9Ok2AxSH59foVBkQqFIh7v7R8jMvAln55cAAGnn0nRy4RSHp+d8NG36M8zMNITHgoLUYSV+fjzCv3SJH6zNmxduxNW1xFoFxpr6FuDSJV59TEpijeD+/Tl1ddgw9T4dOvCQ9tVXeZbyyPDiFsObDsfZhLM4FW+kFc+EBBa1nzgRCNG9kHs+RWQB64KXF18u1SRZV3JySi5Md/iwrA6mM/o6/Y39wtOwaKvJoUNEXbuW+2mPH3ei27c3av3u9u1NFBhYha5e/R8REaWeSqXTLXkx8fHjaDp8GHT58qgSz5GTk0rp6dcKbnuYQ8edjtPj6McG/gItfPkl0fvv82I4d4AFWszMiIKDC+//9ddEkycbvx/FkZJCVK0a0bffEjk6qrOup07VnjJLRNSxo3rx2UB2hO8g50XOtCVsi+GN/fwz0fDhnLIL8PXUlY8/Jpo0Sf1ZzwXqd9/lU1pbE2Vk6H7cli28Hp5WhHRUeDiRlRXRkCF6deeZAKVYtJUGX1+SkzmXXFNNqhyIj/+BcnJSivw+IyMqX8Yh51EOBVoE0t0/7hIRkUKRS3l5xcsuEBE9CHxA195hg591N4suDr5I554/RxFvRhjhF2hh8GAOK9EkN5fo7l3t+//2G9GLL5ZNX4pi3z5+wEdGFjR4xTF5MtG8eWo9AwM5cusIuS9zpzxFKe652FjWXVAoiF57jWj5cqJHj1gDYvRo3do4dYqoVi22vNevs+wIwBoMOhIfT3TyJEs1nDnD70vi3j3WMKxalWj+/ILfbd1K1LYtkZcXP0zs7YkSE3XuzjOBNPjlhRB86cogPNNYHMZhOozDlHFT9+FU7DexdMTmCOWm51LEGxF0tuNZCu0fmh8CanRcXYlu3tR9/6NHiTp3Lpu+FMXrrxMtWaLfMaGhPEsx4r3t+50vHbl1RP8D+/Xjfty4QVS/PtHly7z99GkOK9UFf3+i9etZDnTvXo6zBHiWoyejRnF0LVD8JCEjg6hxY95v4UKO2lVF6T54wGqfW7awCui1a9zmhg16d+eppjQGX/rwS4NqsS4hofj9TEiDZQ1g38MeYQPCcOOTgqtlj849QvKuwmp8WfFZUGQocP+f+0g5moJGaxrBb69ffgio0Rg2jJVI09M5AkdXdPDhG4W//mJBGoWCy2C++KJ+x/v58cK+lRX7zPWBCNi6VZ2ApmSU7yj8EvaLfm09fMjrJK1bs6P78WOOMgJ4kfzatZI1DxQKlsV84QW+/omJvEZRp07JznUtNGzIkhMNGvD6cVFs26YOL/3Pf3j5bNgwjoY9cIClH0aO5IXghg1Zm3/PHr27U+mQBr80nD7NGZYVOM7M/X13NN3UFJ5feCJ+eTxy07iKliJbgSujryBhTeGHVVZ8Fmzb2yJxXSKy4rNg08TI0S4AC9Lt2gV89RXH3esjRVyPq04ZKwpGKwoFK4utWMGLtTVqcEyhvnh780KvvpIcoaFcoe3tt9XbHj/Gy81fxvbw7chTcBb0W3++heUhy4tva9cuLmPl68syFd27q6+3jQ33sU0b4Jtvim4jOpoV1+zsgNq1eZBz/ToH2sfGFn1cEYwfD+zcyYFZBw4Uvd/Bg7y2fOoUi7lNmMAPADs7vnUGKRWLVT9nyBB+gJTHeOBpRhr80iAE34UV2OADgGUdSzgPd4Z1I2tkRGQgKyEL196+BsojZCdkF9o/OyEbbpPdcG/PPdg0tjH+yB5gkZfq1Vll0lfPePmqVVmkZcMG/nz3Lhefnz3boAiSAuzfzxE4Bw6wBenRo/Rt+fqywb95U/fR8P79PJretYtH+ampgI0NPC7FwtbSFjce8Gxt7bm1mPL3lGKlF/DDDyz74OHBI/2eBXM4MGIE5zwEBhbdxuXLPBsAeIQ/cyZrOXTvXqoERA8PHqn36sVGPbnwRBNE/F2vXkC7dvznJgQ/58eMYdWNUaMKHlOzJk/ENAus793L7WgjNJTrBlQ2pMEvLU+BwVdRrWk1RH0UhQvdL4DyCD6/+uQXHtckKz4Ltm1t0eZcGzTZoH9pR50ICuL6ezVqAFOm6H9827bsbsnLYwOWns4B6M2asftk2DBOKjp9Wv1g0JW8PGDePI4drFmTZSLHjtW/jyqaN2dj6uWlFoUrjtxctljjx/Pv+ftvdUHaTz5By9otcSHxAhSkgI2FDfo06INNoZzkRUQYsW0EMnMzef/kZLaMAwcCbm68beDAgud77TXO1LIrJrHn4EF1iGzt2urjRo7kKitZhe8jXfD05IRqZ2f+t0MH9UQjPJy9YU8KztWvz1Gxv/0G2NsXbtPPr+BzdetW9shpY+DAfL27SoWhFa8WCyEihBAXhRA7hRD2Gt9NF0JECiGuCiGMIDxewfDyKl7om4iny5sLyx6XN+Y1zJESmALPzz3RZGMTVG9RHbkPcqHIVle9IiJkxWfB0s0Stq1sUd3XCEJN2jhzhl0WKSlqf7I+eHqyKleVKkC1alxctUYNFrebM4dHxhs3ApMm8QhXVxISuNyTlRUbs4MH2Vp06aJ/H1U0b84lpfz81EPKqVNZpkMbGzdyodmBA9mfsXQpP9zeeAO4dg3+Cg9cSLyAuIdxqGFVAzO6zcCK0ytAREhKT8L28O2ISI7g86xfz7/HwkLtCnN9IkfBzY39I0kFpZgVpEDk/UgknDuCtI1r1CWtVAZ/1ix2Cbm68v9FKfn1V77c8+bxssDPP/N21eheGzVrFn5uqahXr+CkIzJSXZtAxS+/sEcqPr50qQhPPfqu8mq+APQBUEX5fiGAhcr3PihY0/YGnraatiWRkEDk4EB0p4jarf/+yyEGuobylSEZURl0/9D9AttO1D1Bj2+pY+vTwtPohPuJsunAvXtEy5apY9q1lVnSBw8Prjd8X/mbjh4lWrSIr3fXrlzI1cuLQzt0ZcMGLs1kTOWxpCTu0yefcETMsWNENWrwNm0xhP36qcNUc3K4uGzt2kTff080YQKFfzCWev3Ui/Zd30c9N/YkhUJBvt/50r9R/9LRW0cJAaDNFzcT7djB51i6VN12UVXbTp9m0ToNfg79mSzmWtCn/2tMOxuDktOT+YvLl7ldVVvPP0+0f79Bl2jSJG5y8WIOrVQoiNq351q6+nLuHId9qqJ5XFz4EmpSpw7RhAlElpZEAwca1HWTg/KO0iGi/USk8oQFA6irfD8UwK9ElEVENwFEAmhvyLkqHK6uvFKkchru3w98/736+xs3WOAkMtI0/dPA2tMaDuunFPDVVnWriqwE9XT83l/3UHNgMdU5DOH0ada3V42YtZVZ0oebNzlsw0Epf9ytGxdgrVOHReHffZdHuFlZxRd/1eTqVdb3NabymLMz4OLCi7d9+rDPwsmJF1KfXMzNyGDVsX79+HOVKny9ZswARo8GRo9Go9+PwObwcRy4cQCd6naCEAL9vfvjZNxJXL3Hch/hd8N5VvDxx3wdVBS1OO7iwj4VDVYcW4q1ZsPRI6sOMhq44/Ctw/yFjw/PzFRttWypdjmVEtUk7913eb180SIgLQ0YPFj/turV4yWJ3r05QOnePfUIPzubb4eEBJ50jx0LnDihW2GW2Fj2kNWvz318mjGmD/81AKoSR24ANJfw45Tbni0GDmSxrEuX+I/y00/VgiG3b/O8NDy8/PuluotVK2IZGezQ1BD1svKwQubNzPzP9/feh+MAx7Lpz61bvBA6fjxX1DYUbcarShWepw8YAHz5JRvVRo14/q4LERFAYy3CboYyZw6vNfTty/GInTuzq+eXXwquGt64wVpBthoSFu7uLOVRrRrQuTPMx43Huh25SPp+Gfo06ANs3gyfmk2QFBYCsfMPtKzdEpeSLrHvvl27kstaAWzwk5LYv7JmDW4+uIm6F6IwduZW9HxcGzVadEBQTJB6f5VYHMAPslKU/tRk4kQ28BYWbJSnTePFWbNSWCZH5e176xYv2Pr58W2n8h7u2sXfP3rEXjIvL15SKo7Tp/lB0r8/Pzxu3dK/XxWKkqYAAA4CuKTlNVRjnxkAdgIQys8rALyi8f2PAP5TRPs0e/bs/Nfhw4fLdh5kTO7fJ7Kz41T6lSuJ3nyTU/CJeN747bfswkgpOkO2SBISiGbO1G3fqVOJunfn95cvEzk7c3alyn3y999ckN3JiSgriyg9naLev0BRMzlTMic1h45WP0q5aWWUSPbJJzxvd3MrX7H2V14hWru25P1iYjiD58KFsutLdja7ZhIT2V9RpUrByuM7dxINGlRiM6f/XkcZVuaUffECEUDX1i2hh1VBBNCuiF1UZ2kdUnh4EF27RnfS7tDuiN35Rdz3XttLO6/spBMxJygpTUMWws6O/2+qVaNfNn5E30zvwf9fAB3/bQn1/qm39s5cvEjUqJEBF6UgrVoZrpzxzjtEPj7c/VWrOBv39df5c/fuXGbBy4sT5WfN4luzOMaP5z9l5eWgnYVri5cbhw8fLmArYYpMWwDjAJwEYKOxbTqA6Rqf/wHQqYjjy/IalT2ffcaOwpwc9iMPGsR32pAh/AfdqRORtodYXh7RkSNsbLSxZAnnlGdmcl56QuECCPm0bcv/lenpfIfWrMnHApz12acPP5C6d2ef8PPP0x3vNyhseBgRESXtTKILvcvQ2L30ElfCKMqPXFZ8+y0bVj+/4vcbOZKoWze1pk95sGABW6esLP68eDHRe+/pdmzXrkQffkgEkMLSko67g3ItqpAiJ4eazKpJeXYs/fHO3nfIfoE9+az0oYi7EWQ9z5oG/TKIOqzpQC6LXej6PaWze/BgIm9vos8+oz0DGtLJz8bnW7ibyZHkttRNez9yc/leM0LhFyLjqZVs2EA0Zgz/dx49yoVYJk7kn/TSS+oE+T//5D+NolD9vJgYXq5o145ozhy9FCXKlHI3+AD6AQgH4PzE9mYouGgbhWdt0VaFQqEetaoWy1TVm4KD+Y/6jTeInnuONUxUTJ/Of2QODkQjRvDDQrPNNm14ZSkggNvs1Yu/mz278ILfoEG8z9ChRB068B0ZHMztBgTwiD87m4XfOnYkAuiR8KKjNocoa+g4ujHhLN2cc7PsrlGHDkRBQWXXflGEhKiHZsXRqhXrxZQnJ09yv0aM4M8TJrDOjS6oxGOmTiWKiaHgqGOkqFePKDKS5nzUgaLbNqKUxylUY34NarKiCVnPs6bBvwwml8Uu+U1MOzCNPtr/EX9Q3cNhYRTlVIXufvIOaxaFhVGeIo+qfVGNUh4XMUsdOVK3WZSJycriNfkff1Rvi45miaCiCAkhataM3ysUvD6tWnPPLlmaqswxhcGPBPvqLyhfqzW+mwGOzrkKoH8xbZTtVSlPzp9XGxiA76iNG1lXxdZW7TJQKNjdkpDAqlA2NqzEqIr4Wb2aozqGDuV23N3V031nZ6Lt2wuet1MnHrl36FBQVnDZMp7DDhig3hYfT1S3LuWNepWCzTbRRYtFdF4so+Ru+uuiaCUrq2AUTlISG6cHJZdENDpZWRy2oYqUychgETRNbRzV/0V59y83l4eiDg5EcXE8w/jnH92O3byZf9OePept/foRbdxI9wf3pi+6gep9VY8cFzrShdsX6N+ofwkBoE5rO+Xvvu/6PvLf4F+g2QsxpymjCkgxfnyBh0/bH9rSydgi1M42bybq3581dp40/FlZRCtWlP/MTkcUiuJF1+bOZfE2TWbP5kuvigQyJSZx6Rj6eqYMfmoqX9IlS4jGjWPDl55OdOIEG91du3i/xESeKxLxvPPUKaIXXmADcOgQG+nQUFbmvHuXHxTNmvEMAWA3kqZwm7u7dhGy8HDef8YTdWRzc4l++40yUIuCHA/TsRpHKMu2Hp/PEBQK/p2NG/MMIy2NVSNff92wdg2lbl2+DlOm8IwLYBVMIn4AurgUf3xZMnYsWxF7e911g3NzC/9f/fwz/65Bgyjp0ilCAKjtD22JiEihUFDNhTVp9A61Oubd9LtkN98uX4EzLSuNnBc50/36tdkHsm2buos7x9Las0WM4tPT+aHVujU/dDTZto37dP68br/LBBQXWdqli/ZncK9e6tvHlJTG4MtMW2NiZ8ehd++9x2GBVapwgkqnTpxTrooRu3FDnUZoZcURFQMGcATLpEkcWujnx1kmTk4cThAVpQ4R2L4dqFWLy+7l5bGgVZ06hfvTtCkXO9WszARw9EaPHrByyIGCzGHlZYOqAzqxYpVCwUIneXnafyMpxb20xbOFhHAy2v/+xyGOs2YBq1aVLqPWmDRqxMlUoaHA8uUcJaMKiwwPL5voHF3p148jeQYPLjoh60nMzfne0GT0aBZI27ULTj5t4WjtCC8HLwBcdaxj3Y5o4KBOXXWycULt6rVxJuEMriZfRc1FNdHZvTMcWnXiyCZVkhUAHycfXL57GTfu31AN0vAo6xGSM5L5/p4xg6/tlSv5x2wJ24Kbyz/n++/XX0t5ccqeFi34dj57tuD2u3c52Klbt8LHeHurE8UmTOCon6ck6V6O8MuNBQt4oU3lux05suD3Dx6wH3/OHO066io3UePGHHEzejQv0JbkiIyOLjrRKSuLLvS9QHEr43gFq0sXTsQBiC5d0n7MDz+wPPQXXxT+bvVq9Wj+yBFeg/D3L7xfeXPkCLtNUlLYTTZnjnqu/t57/NlUJCfz6L64RflS0HVdV5p2YFr+56CYIIq4W7CuwdzAuTThzwm08tRKarW6FcWmxnJRGiurAjOIP6/+SQgAIQB0LPoYERGN+X0MIQCUkZ3BM7v4eK5ukpxMvb9qTQgA3XIw48gkLQWDvj75NXX+sTNtvbS10HflyY8/8u3+ZBLWRx/xsoo2lizhiWKvXkRNmvACsQlqIkmXToVmyxZeCFMtwvbtq9/xqjuzaVP+HB/PhmLpUt0LWWgh+0E2KXIVvArl5MRFMoCCq1uaDBnCRsHFpfCD5N131f7xrCxem9AMPawobNpE9PLLbKjq1SMKCzN1j4zO3MC5tPNK8TGEUfejyGWxC728/WX68VwR/99ElPAwgVyXuNLI7SNp5amVtP78eqq1uBZV/bwqbb+ssZ7k60vUujX95WNB+z55kTKrCHZJVq9ewAUZnRJNNRfWpB3hO8h+gT3FP4w3+PeWljNnOKDNzk69ELt7N98WRQUfnT/PS2mOjvyMc3bmn2ikejc6Iw1+RSY6mhdW3dzYQIeE6N/G+fNEZ8+qP3ftyv+FqrUBQ5k4kReYR48meust7fs0a8ZrCq1bEx08WPC73r158U5FeHi5VwbTiQMHOET1zBn2V1fQRcXyoOG3Dcl6njVdTb5a4r7fBH9DHdZ0IPM55oQA0Lwj8+idve+od9ixg3K9G+TPRiOcBOcAeHlxlSwlP577kUZu5xnuqztfpXlH5uXnCpREyuMUOnjjYMk76ohCwX9SnTuzzENeHgc/PVlh60kGDuQxUZs2PBvo2ZMrR27axHEB5YE0+BWd8HCOxrh3zzjtBQYSffON8YxqZCTfscHB2qshKRQ8an/4kGcqmlkreXnsWrp1yzh9KUsuXeK5+PTpJWfePON8+M+H9ObuN3Xa91DUIUIAaMP5DRSbGkvBscHkt6pgjsPJ2JMU61SVqEYN2tvUgu6k3SF64QVK27iWtoRtodWnV1OtxbXyZxSn4k5RrcW16OfQn0s8f/zDeJobOJeqf1mdHjw2blTVgwcc5PbRR+z1e7Ly5pOonk8nT7L3a88eLigG8ET3yBGje+kKIQ2+xDhkZrJhf7Jy9O3bPH8l4miiDh3Uvt7Dh3UvmWdq7t3jUMw6dThbtBKTm5er8+j6YeZD+vTgp/mRPZk5mWT5uSVl5qizp9efX0/zAnoShYTQWx80ouDYYIqYMIICnkf+OsCq06soO1cdyL727Fp6efvLBfrUdEXTArH/CoWC2v3QjhAAcl/mTitCVhj60wuRlMTjAKDgRFoXFAqOwJ40iUf9Dg6659GVltIYfBmlIymMpSVHsvz+e0HR8MhIlicGWMA8JIQFwgAOdXj55fLva2lwcGAd/SZN9C/C8oxhbmYOoWPVMVtLW3zR8wuYCTYbllUs4engiWv31DLh52+fh2XvfkD79sh5rgtOJ5zGL3kX4HMXqF61Or7t9y3eavMWLMwt8o/p6dUTh24eQlp2Gib9NQlhSWG4knwFwXHB+fscjzmO1KxUfNnjS8ztPhf7o/Yb6QqocXZmqX+gsBZ/SQjBgmwBARzx06cP6yqmpRm9mwYhDb5EO2++yXfw2LFqicCdO9UVoKpVY7nB6tX5oRAYyDKFTwNCsJziP/+YuidPPc2cm+Hy3cv5n8/cPoM2rm0AAP28++Gv63/hH8tYPJfhjOc9nsc7Hd7Jf2CoqG9fHzWta2Lor0Px3Znv8ks3nog9kb/PtvBtGOM3BtO7TceAhgNw5NYR5OQVDA1WkELlNSg1fZWVOzQ14vTByYlDNH/9lcswqkoJVBSkwZdo5403WDLY0RH47jvOAfjpp4KVmzp14nyBnj05BrtFC5N1V29sbQ2XaZagmXMzhN0JAwDkKfIQmhiK1q6c99Hbqzf+jvwbkc7mcM6tit+C6vDsUYvG8AtNX8Cp+FOY3G4y1l1Yh451OyI4Xj3C3311N4Y1GQYAcKnmglaurbDl0pYCbUw/OB2LTyw26Pf4+ZW6iFc+qhLIc+YUnbJiKqTBlxRNo0acODV3LicGffABZ51o8vvvXP919mzd5HglzxT9vPthW/g2EBEuJV1CXbu6qGHFw2MHawd0r98d1rYOMAs5hWpRsayBHBenbkBZdXxSu0nY57sI05u8gWldpmF+z/m4eOciACApPQkpmSlo5tws/7CZ3WZiYdBCEBEUxA+QHVd2YMOFDQb/pqpVDW4CABcU8/TkevQrVxqnTUORQxxJ8XTpwnVi16/ntMIncXFh3f+iatJJnmk61u2IquZVERQbhLA7Yejs3rnA9/vH7EdGTgZgaQfs28fFz69fZ5H5x485Q3zFCrhOmADXKYuBxo0xf+9eEIDHOY+RnJGMsDth8KvlV2CtoYdnD+Qp8hAUG4Q/Iv5AwqMEpOekIzsvG5eT2MU0ed9k/Dv23+LXKM6c4dleGWVbr1rFVSQ/+YS1/3VcLikzpMGXlMxLL3H+uKrClESiRAiBF5u+iF0Ru5CYnojnPZ4v8H0Vsyqws9Qokt6oEctv9OzJBWuE4NlhVhY7wK9fB4KDIVavRssuPriUdAkX71yEXy2/Qucd7Tsaf0T8gd1Xd+P6/ev4qu9XiEmNwbbwbbCztEPgrUCYzTVDxKQINHZ6wqDfu8frU2fO8Kxj+HCuvtKokVGvT/v2XOvGzY3VUbQtBmdnG29WURLSpSMpmZ491aUcJZInGNx4MP689idOxJ4oNMIvRMOGXKU8MpJdO9268crmhx/y63//A+bPB376Cd3z6iHsThhOJ5xGq9qtCjXlX98f6y+sR0pmCq5MuoIpHaZghM8IbAvfhmMxx/L7svTkUiw7uQzZedlcS9HbG1ixgvWUgoI44mz9+jLT/BGCF4NbtixcAO/771mCKyKCK3IZun5QIvrGcRr7BRmHL5E81eQp8sh1iSvZL7DPj9Evkhs3WBm2enUuFDRqFMtwzJnD2gahoUTm5kQA/bXoTXrtj9fIcaEj6/w8QWZOJiEANOvfWQX6UndZXbL90pYSHibQxgsbCQEg50XOdCDkV5YpX7CAg+2XLeODTp7kjKt27Yx5pEB1aQAAE3hJREFUWQqgUPDpXnyx4PZOnTidZdw4lqjSR9YJpYjDV5UkNBlCCDJ1HyQSiWG89edbiHsYh72j9+p2QIcOrPjp6wssXKjenpXFIb95eYia9jYaWH2PVrVb4dzb2mvnRt6PhJeDV4FQzzmBcwAAs/1nIzkjGStOrUCeIg++By7iv+HgQIN+/Vg5tUkTPignh9ejwsMBV9fSXIISiY7mwLaEBNaemDaNi7aHhrKgbZMmwP37ak9XSQghQER6rQpIl45EIjGYqZ2n4rPnPtP9gHbteBG3bt2C2y0teQHV2RluCZy1NKGtlmABJd6O3oXi+mf7z8bsdlOBzZvhZOmAgPiGeG/mHtTdd5x9K+bmHFmmMvYAV1Hv2xf46y/+nJwMfKbH79GBevX4eZaYyC6cRYt4u58fK4h/8gmrTYeFGfW0BTBohC+E+BzAUAAKAEkAxhFRguBl8W8ADACQodyu9REtR/gSSSXkt9+4RkFwMI/2NRk/no3yjRv4btEIvN7qdVhWsdSv/eXLua6EszOP3E+dAgBknzuDqq3aaD9mzRpOIGzThh8If//NFtqIK6q9enF08+XLnOYSEFDwmTd5Mj8YPv645LZMMcJfTER+RNQSwB4As5Tb+wNoqHy9BWCVgeeRSCTPEi+9BOTmFjb2APDjj2wVb9/GxHYT9Tf2ALBhA6+CdunCxn7JEjy0MccZx8dFH+PjA/zxBy8et23LUUOqokNGomtXTkzftImVSJ6c4PTtW7YJ4AYZfCJ6qPGxGgDVUH0ogJ+UawvBAOyFEGXjGJNIJE8nRSXqmZmxH12ZlKUT164B337L73NzeQjduTMnDNrbA1Om4Mcv/oP1oRuLll9o2hTIyOAs888/57CaGzf0+00lMHgwsHQpn6Z798Lfd+/Oz6fERGXROVU0k5Ew2IcvhPhCCBELYDTUI3w3cHFzFXHKbRKJRFIy9vYcoJ6ertv+//kPh1zm5QExMezGsbZmC7txI2BhgdFvfouDNw/iVPwp7W04OvJxbZQunwYNOHjeiLRuzR6rrVv5ufYk1auzO6d1a1b+SPtgFoeQGokSE6+EEAcB1Nby1Qwi2kVEMwDMEEJMBzAZwGx9OxEQEJD/3t/fH/7+/vo2IZFIniWEUI/yn5TzeJKcHB7he3jwyP72bXUClY0NMGQIANbg8a/vj9A7oehQV4srCQDGjFFnjTdowCNsIyIEsG5dEV/Onw+8/z4++8wKw4ZxZi5OngDMMgAAgYGBCAwMNOj8JRp8ItI1Z34zgL1ggx8PwF3ju7rKbVrRNPgSiUQCgGUXEhJKNvhRUZzK+txzrOCak8MJXlrwdfHNF3vTypIl6vfNmvHibXmQkgJ8+iknonXtCl9fYEyPeFgfj0XeZSuYo/BgeM6cOXqfxiCXjhBC86oOBRChfL8bwFjBdASQSkR6OOQkEkmlp0oV4PnnWcq6OK5e5VDOVq2ACxeA48f5vRZ8XXwRlqRj3KOfHwfJlwdnz/K/p0/zv8uX4+U/X8ZveAmKm7fYvWUEDPXhLxBCXBJCXATQB8C7yu17AUQBiASwBsBEA88jkUgqG8uXs0vm6tXi97t6lV04vr4cVrl/P0cBaaGdWzuE3gnF7Uc6jD/d3Hi2kJjIC8FlyalTvH5w9Cjw4AEQEADbjCS4rpiBBCsvowXnGxql8yIRNVeGZg4monjldiKiSUTUgIh8ieiMUXorkUgqD76+vOh67ZoyZAWcohoTw/pOqpBJ1Qjf15ffv/hikRVM7K3sMar5KKw5twZ5ijxk5mYWfX4hOKyzQwdeyL1/37i/T5Pjx4FZs/i39unD8ZsREWg4tBn2KfqC9v1tlNPITFuJRFJxadSIVS2trVmee9MmXpwNCVFLMqgMvpMTC9Brk/HWoJ93PwTFBuH13a9j1uFZxe6LX37hEM0mTTjapyzIyWGD/9JLwDffsIKnsvSWmxtw2LI/Hm7dh8Dh3yDjpIEuJn3Fd4z9ghRPk0gkRbFqFQudRUYSeXgQubkRLV1KdOwYUb16vI+zM1FcHL/PzCyyKRW3H90m63nWZDbHjAZuHqhbP/76i6hLl9L9hpLYv5+oRQt+n51NVKcO/14le397SGmwIQIow8KWTv92g0ihkOJpEonkGePRI+D8eY7ACQ3lRc3x49nd4uDAI/vTp7lsoh7VRTy+9kB/7/7YdXUX+jboiw3DNhR/QFYWULs2cPEiyzuPH2/Y71KhUPAC84wZwH//y9tycljbR3O3Jj64m26N3XGt8RrWwXzpYogPP9RbWkEafIlE8nTi7w8cOcLv9bQh0SnRcLR2hN0CLs6SOi21YKGWJ1CQAmZjXwWSknhRODWVhewN5fffgQUL2EVV3ANr7Fikm9miXfBydK0ThR/Ot4NISZFqmRKJpJLwwgtcmOdxMfo4ReBh7wFbS1vUsa0DAAhNLN433n9zf5wa2o4NM1C4kklpWb2atXtKmp1Mm4ZqM9/Hlt/McPKud8m5CUUgDb5EInk6mTKFFTetrErdxNROUzG8yXCcTzxf5D5EhJC4EOxyuMOJYGPGsLB9UJB6px07WHfn0SPdT37vHj9ABg0qeV8fH8DbGx4eHJxEg4fofh4NpMGXSCSVlvc7vY9BjQYhJD6kyH2iU6ORmpWKk3EnsfHaNtxRSh1g9271TjNmsPLZc8+xX744kpM5+icwkMM+q1XTub/29pyPdn/iTJ2P0UQafIlEUqnpWq8rjsccz/+85MQSfPDPB9h6eSsA4Pzt8+js3hmhd0Ix9cBUdHLfD1q2jJOkAB71X78OxMZyjkBJ+sbLl3Oo6fvvsyKnnrRpA3w+Ty/XfT4laulIJBLJs0xDx4Z4nPMYMakxsLO0w5fHvkRKZgoIhM7unbH05FKMazEOnd07IyUzBUN+HYK7rwyHy2efAWlpvHA8YABr6FetypW8+vfXfjIi1ur/6CNgwQIkernAIuMeatrU1Lm/b74JjB5dut8qDb5EIqnUCCHQ06sn1p1fBycbJ/jV8oNLNRdEJEdg1I5RyM7LxmutXoO5Gev3e9TwQExOMlxatOCELAsL4L33WJ75zBku3zh4MNC7d+GTRUQgD4RN/g4YtwCYFLsa7kdv4et+X+vc3xEjWObHx0f/3ypdOhKJpNIzuNFgzDkyB+/sewdt67TF1hFbsWn4JtSqXgtrh6zNN/YAUK9GPcSkxrC/PiGBV1FVKpatWvFI/+23tZ/o8GHcaFkPiyI34IsJzRBolYjfr/xedFEWLZiZca2W0iANvkQiqfQMajQIH3f+GNWrVkcbVy6A0qJ2C2wbsQ1+tfwK7OtRwwPRKdHIfvUVpKz7DjGjB7GOD8BVvE6cAO7eZRG0J/n3XxxrYIGJ7Sbi4xXncXHSZeRRHqJTo5GdZxxFzOKQBl8ikVR67CztsLD3Qvw07Cf0b1iE/11JvRr1EJ0ajZmxG+EYPREeDffgRspN9Q4WFkDHjqyPo4lCgccH/8YsEYiOdTvCwtwCbnZu8LT3RHRKNBp82wCJaYll8OvUSIMvkUgkSoY3HQ57K/ti92lbpy3+uv4X1p5bi5A3QvCK3yvwXeWLiOQI9U5t2rA2vwah+zchoWomXun3MVrUapG/3cPeA8FxwYh7GIcb941bQ/dJpMGXSCQSPejm0Q3Zedl4tcWraOfWDlPaT8Hj3Mc4GHVQvZOfH/D998Dbb0NxIgiZ772DUz/MRlbP7ljYeyEszNVaOfVr1MfeyL0AwGsDZYiM0pFIJBI9MBNm+PPlP+Fp7wmAi6os7LUQNx9ouHV8fYH4eGDfPkQlXIb3niAMsq8Cl+1rC7XnYe+BL49/CYCTvMq078ZoRAjxoRCChBBOys9CCPGtECJSCHFRCNHaGOeRSCSSioBfLT/YWtrmf27g0AA3Hmi4Yxo3Zt3+2Fh47wnCn42Bqs18Yf68f6G2vB1ZF6dbvW6ITqngBl8I4Q4ub6g5F+kPoKHy9RaAVYaeRyKRSCoqXg5eiHoQpd5QtSroJo/4k2paI3fn76h5/BzrIjxB9/rdkfxRMqZ2nvpUjPC/AvAxAM1A0qEAflLq9wcDsBdCuBrhXBKJRFLhUBn8jBzW2UnPTkedZXXw8PDfGDjJvlBopyZCCNS0qQlvR29cv3+9TPtpkMEXQgwFEE9ET2qLugGI1fgcp9wmkUgkzxw1rGpgaJOh+SUTj8UcQ2JaIt5MXofM2jXh6eBZYhsNHRsi7mEcHufoL/esKyUu2gohDgKoreWrGQA+BbtzDCIgICD/vb+/P/xVWWsSiUTylPBV36/QdGVTvNvhXey/sR+O1o7YenkrQt4IgZkoeWxtYW4Bb0dvXEm+gtauhZc9AwMDERgYaFAfS13xSgjhC+AQAKVWKOoCSADQHsAcAIFEtEW571UA/kR0W0s7suKVRCJ5Jnhz95vwcvDCV8FfYc+oPVCQAh3rdtT5+JHbR6Jvg74Y36rkEopCiPKreEVEYUTkQkT1iag+2G3TmogSAewGMFYZrdMRQKo2Yy+RSCTPEp3cO2H+8fno7N4Z7d3a62XsAWC072jMOzYPmbmZZdK/skq82gsgCkAkgDUAJpbReSQSiaTC0N6tPR5lP8K4luNKdfzgxoNR07omQuKKLshiCEZLvFKO8lXvCcAkY7UtkUgkTwNNnZpibIux6O9dvB5PcXSt1xVBsUFwruaMpk5NIUqqd6sHUlpBIpFIjIS5mTk2DtsIyyqWpW6ji3sX/BHxB1qubolT8aeM2Dtp8CUSiaRC0c+7H+49voccRQ6CYoNKPkAPpMGXSCSSCkS1qtVwcMxBLO2ztJDBf5j1EGnZaaVuWxp8iUQiqWB4OnhiaOOh+S6dtOw0pGen49NDn+Ynd5UGqZYpkUgkFRBPB088zHqI5IxkjN81HpeSLiFPkYcaVjWw8PjCUrUpR/gSiURSATETZmhZuyV+OPsDwu6EYXiT4XiU/QhxD+NwJPpI6do0ch8lEolEYiQ6uHXAF8e+wNtt3sa8HvOwe+RutHZtXbDYih5Igy+RSCQVlJnPzcSQxkMwruU42FjYoJtHNzR3bo4cRU6p2pM+fIlEIqmg2FnaYcuLWwpsa+7SHObCHHnI07s9OcKXSCSSpwjfWr6ob1+/VMeWWi3TWEi1TIlEItEdIkJMagzqO9TXWy1TGnyJRCJ5CilXeWSJRCKRPF1Igy+RSCSVBGnwJRKJpJJgaBHzACFEvBDigvI1QOO76UKISCHEVSFEX8O7KpFIJBJDMMYI/ysiaql87QUAIYQPgJEAmgHoB+A7IYS5Ec71TGNogeJnCXkt1MhroUZeC8MoK5fOUAC/ElEWEd0ElzpsX0bnemaQN7MaeS3UyGuhRl4LwzCGwZ8shLgohFgnhHBQbnMDEKuxT5xym0QikUhMRIkGXwhxUAhxSctrKIBVABoAaAngNoClZdxfiUQikZQSoyVeCSHqA9hDRM2FENMBgIjmK7/7B0AAEZ3UcpzMupJIJJJSoG/ilUHiaUIIVyK6rfw4HMAl5fvdAH4RQiwDUAdAQwBaq/Hq22GJRCKRlA5D1TIXCSFaAiAAtwC8DQBEdFkIsRVAOIBcAJOISH9pN4lEIpEYDZNr6UgkEomkfDBppq0Qop8yMStSCDHNlH0xJUIIdyHEYSFEuBDishDiXVP3yZQIIcyFEOeFEHtM3RdTI4SwF0JsF0JECCGuCCE6mbpPpkII8b7y7+OSEGKLEMLK1H0qL5RRkElCiEsa2xyFEAeEENeV/zoU1wZgQoOvTMRaCaA/AB8ALysTtiojuQA+JCIfAB0BTKrE1wIA3gVwxdSdqCB8A+BvImoCoAUq6XURQrgBmAKgLRE1B2AOTu6sLGwAJ7FqMg3AISJqCOCQ8nOxmHKE3x5AJBFFEVE2gF/BCVuVDiK6TUTnlO8fgf+oK2XeghCiLoCBANaaui+mRghRA8BzAH4EACLKJqIU0/bKpFQBYC2EqALABkCCiftTbhDRUQD3n9g8FMBG5fuNAIaV1I4pDb5MztKCMry1FYAQ0/bEZHwN4GMAClN3pALgCeAugPVKF9daIUQ1U3fKFBBRPIAlAGLAOT+pRLTftL0yObU0oiQTAdQq6QCpllmBEEJUB7ADwHtE9NDU/SlvhBCDACQR0VlT96WCUAVAawCriKgVgHToMG1/FlH6p4eCH4J1AFQTQrxi2l5VHJRVpEqMwDGlwY8H4K7xua5yW6VECGEBNvabieh3U/fHRHQBMEQIcQvs4ushhPjZtF0yKXEA4ohINdvbDn4AVEZ6AbhJRHeJKAfA7wA6m7hPpuaOEMIV4JwoAEklHWBKg38aQEMhhKcQoip4AWa3CftjMoQQAuynvUJEy0zdH1NBRNOJqC4R1QffD/8SUaUdxRFRIoBYIURj5aae4NyWykgMgI5CCBvl30tPVNIFbA12A3hV+f5VALtKOsDQxKtSQ0S5QojJAP4Br7ivI6LLpuqPiekCYAyAMCHEBeW2T1Vy05JKzTsANisHRVEAxpu4PyaBiEKEENsBnANHtZ0H8INpe1V+CCG2APAH4CSEiAMwG8ACAFuFEK8DiAbw3xLbkYlXEolEUjmQi7YSiURSSZAGXyKRSCoJ0uBLJBJJJUEafIlEIqkkSIMvkUgklQRp8CUSiaSSIA2+RCKRVBKkwZdIJJJKwv8BFtmAeQQan+gAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": { "tags": [], "needs_background": "light" } } ] }, { "cell_type": "markdown", "metadata": { "id": "233BUwDDCDp2" }, "source": [ "Although the result contains all the information we'd like it to convey, it does so in a way that is not all that aesthetically pleasing, and even looks a bit old-fashioned in the context of 21st-century data visualization.\n", "\n", "Now let's take a look at how it works with Seaborn.\n", "As we will see, Seaborn has many of its own high-level plotting routines, but it can also overwrite Matplotlib's default parameters and in turn get even simple Matplotlib scripts to produce vastly superior output.\n", "We can set the style by calling Seaborn's ``set()`` method.\n", "By convention, Seaborn is imported as ``sns``:" ], "id": "233BUwDDCDp2" }, { "cell_type": "code", "metadata": { "id": "8xeodUGhCDp2" }, "source": [ "import seaborn as sns\n", "sns.set()" ], "id": "8xeodUGhCDp2", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Pqu1xVyZCDp3" }, "source": [ "Now let's rerun the same two lines as before:" ], "id": "Pqu1xVyZCDp3" }, { "cell_type": "code", "metadata": { "id": "wF3FRlJXCDp4", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "47df5495-5119-47f1-b5d9-024db8c51d48" }, "source": [ "# same plotting code as above!\n", "plt.plot(x, y)\n", "plt.legend('ABCDEF', ncol=2, loc='upper left');" ], "id": "wF3FRlJXCDp4", "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEGCAYAAACKB4k+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOydZ2Ac1dWwn9mq3VVf9WpbluXeu8GADcaAwUAIOBAgCeFNAklIQkjIG0IJJMTAS8oHBBKHFnoxxhiwsTHuvci2LFuyei/btLvavjvfj5FWEpKbvLYsM88frabce2Z2ds69554iiKIoIiMjIyMjAygGWgAZGRkZmfMHWSnIyMjIyISRlYKMjIyMTBhZKcjIyMjIhJGVgoyMjIxMGFkpyMjIyMiEibhSeO655ygoKKC0tBSAwsJCrrvuOq688kp+8IMfYDabI92ljIyMjEyEiKhSOHz4MIWFhWRmZgIQCoV44IEHePjhh1mzZg1Tp07lmWeeiWSXMjIyMjIRJGJKwefz8cc//pFHH300vK2oqAitVsvUqVMBWLJkCatXr45UlzIyMjIyESZiSuHvf/871113HVlZWeFtjY2NZGRkhP9PTEwkFAphs9ki1a2MjIyMTASJiFLYv38/RUVF3HrrrZFoTkZGRkZmgFBFopHdu3dTXl7O/PnzAWhqauKuu+7i9ttvp6GhIXycxWJBoVAQHx8fiW5lZGRkZCKMcDYS4s2bN48XX3yR4cOHs2DBAv7yl78wdepUXnjhBWpra3nyySdPqz2rtZ1QSM7bZzRGYzY7B1qM8wL5XnQh34su5HshoVAIJCQY+nVuRGYKx0OhUPDUU0/xyCOP4PV6yczM5Omnnz7tdkIhUVYKHcj3oQv5XnQh34su5HtxZpyVmUKkMZud8hcNJCfH0NrqGGgxzgvke9GFfC+6kO+FhEIhYDRG9+/cCMsiIyMjIzOIOavmo7NJYqIBpfKbp9OSk2MGWoR+EQyGsFjaB1oMGRmZkzBolYJSqZCniYOIwarMZGS+aXzzhtoyMjIyMsdFVgoyMjIyMmFkpSAjIyMjE2bQrimcj9jtdq6//iquu+4GfvGLXw+0OGedm266Fo1Gg1qtweNxM3ToMG677U7GjZsw0KLJyMj0E3mmEEHWrl3NmDFjWbduDX6/f6DFOSc88cRSXnvtbd59dwVXXbWIBx64j8OHiwZaLBkZmX4iK4UI8umnK7nzzrvIy8tn8+aNAy3OOeeSS+axePG3ePvt/w60KDIyMv3kgjQfbT3UyJaDjf0+/6Lx6cwZl35a55SVHcNub2PKlGlYLGY+/XQl8+Zd3m8ZTgf7tq20bdnU7/PjLppL7Ow5EZFl9OixbN3af1lkZGQGFnmmECFWrfqYhQuvQRAELrnkMoqLi2htbRlosQYAOR2JjMxg5oKcKcwZd/oj/TPB7/ezbt1q1GoNq1d/CkAgEOCzzz7hzjvvOuv9x86eE7GR/ply5EgxQ4fmDbQYMjJnDV9zE+rkFATF+TOm9ptacZUcJWb6TBRq9Rm1dUEqhXPN5s0byc7O5Z///E94W1HRQZ544pFzohTOFzZv3sCKFR/wf//33ECLIiMTcURRpPXdt7Gt+4KUW79L/DkyD58MURRp/PdLeMrLcO7ZTcbPfwkI/W5PVgoR4NNPV7JgwVU9to0dO55QKMT+/XuZNGnKAEl29nnood+GXVKHDBnK00//nTFjxg60WDIyEcexawe2dV9In3fvOqtKIeT1EvK4UcWdvCCZeeUKPOVlaDIyaD90EMeuncTPmtXvvgdt6mw5Re7gItLfl/z9dyHfiy7O5r1o+s+/aS86RNwll2JZtZKoYcNIue0OonKHRLyvmj8/jqeinPx/v4Ig9Bz1i4EAto1fEXfxJXgqK6h7+i/Ezp5D6vfuovqRh0CAIb9/mOSs5H71ff4YxWRkZGTOY9wV5UTl5ZG48GriL1+Ap6IC5949Ee9HDAbxVJQD4Gvs7UXp2LWT1rffpPyXP6Pu2adRxsWRctsdCAoFSd/6Nr7mZsyrPul3/xEzH91zzz3U1dWhUCjQ6/X84Q9/YNSoUVRWVvLggw9is9mIj49n6dKlDBkyJFLdysjIyJx1gg4H/uYm4uZchCIqipQlt+IqLsJbXxe5PpxOTB99gDopJbytbdNXJC68BlW3uva+lmYARK8XAMP4CSi0WgCiJ04iesJEPJXl/ZYjYkph6dKlxMRI6ZHXrVvH//7v//LRRx/xyCOPcOutt7J48WI+/vhjHn74YV5//fVIdSsjIyNzVnFXlONrqAdAN3JUeLs2Mwt3ZcUZt++tr6f+//2VmElTaNu4IbxdnZyMbd1anHv3MPSpZ8NmJG91VY/z4y+Z1+P/qKHDcGzeQH+JmPmoUyEAOJ1OBEHAbDZTXFzMokWLAFi0aBHFxcVYLJZIdSsjIyNz1vCbWqn98+M0v/oyypgYooYMDe/TZGYRMJkItp9Z8Sjn/r0ETCasa9eEt8VeNJchT/yFpBtvImC14je1ApKnkaeqEv2o0WT+4n5GLHuVqK9ZXrrL2B8i6n30+9//nq1btyKKIsuWLaOxsZHU1FSUSiUASqWSlJQUGhsbSUxMjGTXMjIyMhHH19Rl00+48qoesQn6UaMxr1hO+8EDxM6a3e8+uisVTWYWWb96AIVeh6BUoh87DpZ/gKeiAk9lBQq1hqDDQfTkKRjGjuuzPV3+CPRjx/dbnogqhT/96U8ArFixgqeeeor77rsvIu32twC1zPlFpKuvydXcupDvRReRvBcBVxsA015dhiYhocc+0TiBZqMR+7rV5FwyE3VcXHifu7EJ0e9Hn5N90j5abWY0iYmo4+NIu2ohacOzuvpIHEWdRoNYU0Hzmi/C29MmjiHmBNeZct89p3yNX+esxClcf/31PPzww6SlpdHc3EwwGESpVBIMBmlpaSE9/fSijY/nkiozuJBdUs8O8r3oItL3wlJRi6DVYvMrEfpo13jzEhr/+TwVH68m8aqrAcnEc+zH9wIQNTyfrF/cjyIq6rh9tNc1oMkdSsa9PwN6/07048b3UAgAruhEPCe4ToVC6PdgOiJKob29HbvdHn7Zr1+/nri4OIxGI6NGjWLVqlUsXryYVatWMWrUqAvSdNRZW0Cj0eLzeZkwYRL33/8gKtWFGx/Y/Zo7efLJZ0hPzxhAqWRkIoe/tQV1UnKvWIFOYqZMozk6OmzzD/n91DzxWHi/p+wY3vo6dHnD+zw/5PHga2nGMGHicWWInTkr7Pqacvv3UMXHo1Br+ntJJyUibyy32819992H2+1GoVAQFxfHiy++iCAIPProozz44IO88MILxMbGsnTp0kh0eV7yxBNLGTZsOMFgkHvvvZuNG9czf/6CgRbrrNJ5zTIyFxpiKISnqgr9iBEnPE5tTMJvNgHQfvAAvvo6NBkZ+E0mRJ8Pv9nUp1JwFu7H19wEwSCG8ccvTGUY36UwYmfPPqsKASKkFJKSknjvvff63JeXl8f7778fiW5OGX/pVvwl/U/frC6Yi3pE/xPM+Xw+fD4vMTGx/W7jdCg51MTRg039Pn/k+DQKxqVFUCIZmcGPp7KCYJsNw8RJJzxOnZSEr74eMRDA+sVqlHFx5D7yOKLfT9lPf0zAZAofKwYC1PzlT8ROn0Hre+8AoIyLQzc8/7jtC0olmb96AG9V5VlXCCDnPoooDz30WzQaLfX1dUyfPoPp02cOtEhnnc5rBsm77D//kQvsyFwYtG34CkGtxnCS8rKqRCPOvXsou+9eRK+XtLvuRlAqEZRKlNEx4VkEgOtIMd6qSlqrKsPb4uZcfNKMq4bRYzCMHnNmF3SKXJBKQT1izhmN9PtLpynF6/Xy0EO/4b333uLmm2896/0WjBu4kb5sPpK5EPG3tmLfsY2EK65Eqdef8Fh1UhIgRRgrdDpiZ3W9e1RJSfhNJpwHCvHW1uBrbOhxbsz0GSQsWBj5CzgDLkilMNBotVpmz76Ybds2nxOlICMjEzkce/fQ+E8p/Xv8/CtOenzcxZegioun8cXnSfrWt3vs02Zk4CzcT/Mr/yHolLyF1CmpKA0Gkr59C/oRBZG/gDNEVgpngVAoRGHhXrKzcwZaFBkZmdPE/PFyQEppoTYaT3q8QqMhZuo0ol9cBh2Bup3Ezr4I+7atPbbFzb2ExIVXR07gCCMrhQjSaV8PBPwMHZrH975390CLdNbpvqYA8OCDDzFy5OgBlEhGpv/4mprwNTSQePUiEq665rTOFfpwP9cVjESTkYGvoQFt7hC81VVnJdV2JJGVQoT44IP+p6odrHwTr1nmwsa5T4oHiLv0MpQ63Rm3JwgCOb9/BE+HMnDs3tUjqd75iKwUZGRkvnEE3W4ElbKXi6dj316ihg5DnXhys9GpotBqw2sHcRddHLF2zxZykR0ZGZlvHHVP/ZmWt97osc1vNuOtqiR68oVbPvdUkJWCjIzMN4qgy4W3tpb2wv2IoRAgpciu/O39AERfwDXVTwVZKcjIyFzQhPw+Am1t4f+9tTWAVE3NW1cLgO2rLwEwXn8jmrRvdnS/rBRkZGQuaFpef42K++8j1FG+0rF7V3ifq/gwto0bsH6xhpjpMzAuum6gxDxvkBeaZWRkLmgce3aF/2qzc2jbsB7diAKCTgfWNasJOuzox44n9Y7vD7Ck5weyUogggUCAV19dxrp1X6DValAoFEyePI2f/ORnF2QK7a+nzp48eQo///n9AyyVjEwXQacThU5H0O/HWbgfOtYQUu/8AbavvsS2TqpTkH7X3SesefBN4sJ7Uw0gf/7zY3i9Hl5++b/o9QYCgQCffroSn893QSoFkHMfyZy/BN1uyn/x0/D/nmPHUKg1CBoN6uRkEuZfgW3dFyiiolDGyEW7Orkw31QDQG1tDZs2fcXy5Z+h1xsAUKlULF584wBLJiPzzcT88Ufhz7qCkbhLjuLYtQNBG4WgUKBOTibzl79G1a2MpswFqhR2Nu5le+Pufp8/K30aM9JPzy2ttLSErKwcYmPPTQ2F7lQW76KyaEe/zx86diZDR0/v17nd01z85Cc/Y8aMWf2WQ0YmEoiiiLvkKLZ1XxB36TwMY8ehTkqi+tE/AGAY05WC2jBm7ECJed4SEaVgtVr5zW9+Q01NDRqNhtzcXP74xz+SmJhIYWEhDz/8MF6vl8zMTJ5++mmMp5BkSmZwIJuPBh4xFMJTU01UTu5Ai3JeYFm1MjxLSP72LSi00qBl+HMvEvK4EbTy2sGJiIhSEASBH/7wh8yYMQOApUuX8swzz/DEE0/wwAMP8OSTTzJ16lReeOEFnnnmGZ588slIdHtcZqRPOe2R/pkyYkQBdXU12O32cz5bGDp6er9H+jKDn5o336bug+XkPvo42qzsc9avKIq4jhSjH1HQZzK4gcK+fRsAhvETwgoBQBEVJS8mnwIRiVOIj48PKwSAiRMn0tDQQFFREVqtlqlTpwKwZMkSVq9eHYkuzzuys3OYM2cuTz/9Z1yudgCCwSCffLICl8s1wNLJXMg0rVkLgLem5pz2236gkPpnn8a69otz2u9JEQQ0mVmk3fU/Ay3JoCTiwWuhUIi3336befPm0djYSEZGRnhfYmIioVAIm80W6W7PCx566DGysrL5wQ9u5/bbb+bOO5dQXV2FRnP266rKfDMJulwEHFLxls5I3f5g37aVho7CMqeC+9gxGp77OwC+psZ+9xtpxGAQv6kVw/gJKA2GgRZnUBLxOd/jjz+OXq/nu9/9LmvXro1Im0ZjdETaOduo1Wp+9KN7+dGP7h1oUc4Jp5s6Ozk5sm5/kW5vMNJ2uEsR+MpLiVX40RoTT7ud0pf/DUCCQYnqJOUnAcre3Rn+HGptiuh3IQaDHH70cdKvXohx1unVOfc0N0MwiDEvR34++klElcLSpUuprq7mxRdfRKFQkJ6eTkNDV01Si8WCQqEgPj7+tNo1m52EQmKPbfIXPvhobXVErK3k5JiItjdYsR4qASDhyquwrvmcPT+4m6wHHkRfMLJf7TUcLEGXd3LHgbayCnQjRxGVnYPtqy+p2bH/lM47FXwtLbQdPETbwUOMWPZqeLsYCtH6/rvETJ5KVF4envJydPn5Pc5VVktK0qOP/0Y/HwqF0O/BdMTMR88++yxFRUU8//zzYXPJ2LFj8Xg87NkjFa545513WLjw/CpSLSMzWBEDAZx796CKjSXppptJ//E9CBoNpg/fP612apf+OfzZW1938n5DIbz1dWizsomffznK2Fha3n7ztOU/Hr6G+vDnts0bCXSYm23r12FbuwbTx8uxrVtL7dI/0V50sMe5tgMHEdRqooYOjZg83zQiMlM4duwYL730EkOGDGHJkiUAZGVl8fzzz/PUU0/xyCOP9HBJlZGROTXEUIiWN/9L3NxLepVxtK1fh7u0hOxbvo0gCMRMnY6nsgLbl+sQQyEExcnHfGIggPtYafh/95FidMPyTujF1LZlE6LPhzY7G3VSMnGXXIb5ow8J2Kyo4hP6fa2deKoqwp+bX3uFqOH5JC68Gts6yRzta6in9egRSd7ycrTZuQSdDrSZWdgKD6IbUdCreI7MqRMRpZCfn09JSUmf+yZPnswnn8hlG2VkToanphrb+i9Jvf1OhI4C8L6Geto2fkXbxq/I/9fL4Rd9yOPBuu4LdAUjybl1SdhUoklLRwwE8JtNaJJTTtqn32IJf9akpePYvQvH7l0Mf/6lHu6cndi3b6Pl9VfRjxpDzFTJDTp60mTMH32Is3A/8ZfOO6N74NizG8uqT1AYDGTc+3NMyz/AU9a1qA0QtNvDny2ffIzlk48ByH30cdx1dSRfdMkZyfBNR06dLSNzntD8yn+wb9nUw4TT3c3U39IMQMBmpfbpvxCwWjEuvqFHG5o0ydvP19hAX4S8XgJtXd5/AbMJgKz7f0Pyku+Etzf883natm7uca6rtISm//wLTVY2mff9Mqw0NOkZqFNSce7fd9rX/HWsa9cAkHrH99CPKCD7N78j+3cPkXzzdxA0GqI7FJF+zFiSv3Nbj3Ob33gdgOgpU89Yjm8y50/EyWkSDIbkxeZBRDAYGmgRzn86Zgc1f3yEzF/+GsOYsXhqqsO7fc3NaNLSsa3/Em91FWl3/zhc+7cTTXo6AE3/fgnD2HHEXXIZ+m6F4it//1uCNlt4AdffoRTUScmok5PJ/eOfqXv2KVxFB3EVHSRm+oywKabuKSnoNO6iuT2C1QRBIHrSJKzr1hJw2FHF9C9402824ykvI+mmm4mZMk1qW6FAlzccXd5w4i+/gvaDB3Du2YVx0XXo8kfgKS/HsUtK8eIpO0bcuLGoE87chPVNZtAqBYulfaBFOOfIHjcXNt1ftA3P/Z2chx7FsXM7mvQMfI0N+JubAHAeKEQ3ooDYGb3dNZXR0cRfvgDbui9w7N6Ft6GBIY89AYCnqpJgx6JtyO9DodbgN5tBEFB1vEi1GRnk/P4R2jZ+hWXVSvwtLYRc7nD7uhEFxF96Wa9+Y+dcjHXNato2buh3oRpXcREAhnET+twvKBRET5xE3l//XziradSwYTh27UCTmYWvvo7UBVf0q2+ZLmTzkYzMeUKgm31f9PupfuT3iMEg6T+5F4XBgK+5CU9NNb76OqInTT5uOylLbiX5ZsnhI+h0IIqSO7dj756uvsxSX76GetTGpB4KSZ2QEC5e76msoO5vz1C79E8AGK+7vs+UFtqMTHT5I3Du29vfy6e96BDKuDg03QJe+6J7muv4eZeTed+vyH3kj2Q98CBJF8/pd/9ngiiKFO2tp83qPvnB5zmyUpCROQ8IeTwELGbi511O3t+fR5MmmYHi5l6KNiMTTWoabRs3UPPHRxC0UcTOueiE7SUsWEjKbXcQbGvD39SIKIq0H9gfNlH5Ta2A9NKPGjas1/maVKlOsemj5YgdZSwBtJlZx+1TP3oM3toa2jZtxF1e1vP6/D481VXHPddvNuPcv4/Y6TMRBOGE19YdQaHAMG48gkKBvmDkaZ0bSeqrbWxeW8ZbL+06oanU7wuGlfSODRV89kERXk/gXIl5SshKQUbmPKDhxRcAiMobjtJgIPu3/0vGT+/DeO1iAIyLb0AZE4Nh3HjSfnAXSv3JUzgYJkwEQcC+fRuu4sP4GhpIvHoRICmFgM1KwGIhamhvpaDQalGnphJss6HsWCPQjxl7wmI0uoKRIIo0v/4KTS8vC7/8ACyrPqHm8UfD7q+uI8WU/+Jn4XrJnRXQ4i9fcNLrOh85cqAr1cfxZgulh5tZ9uwWXly6iY1rStm/o5bqMjNVZeZzJeYpISsFGZkBJuRx4zpymJiZs4iZLiWWVMbEED1xUtjDxzBmLMOe/QeZ9/0qvAh7MtSJiRjGT6Bt62baNm1AGRtL4lXXIKhU+E0m3BVSPEDUsLw+z0+44koAEhddy9Annybzvl+dsD/d8HyM199I3CWX4W9u6pGLyV0quaybV60EJFNR0OmgcdlLeGqqadu8kZip01EPwrT6oihSW2klNl7KwNru8PZ5XOHO2vDn4v1dSqS20tLX4QOGrBRkZAYYV0kJBIPEzbn4hOaP/phG4uZeSrCtDee+veiG56PQaFAZjfhNrXgqykGpRJuT0/e5l1xG1v2/If6y+aiTk08aDCcoFBgXXUfS9TciaDSYVywHpFxGng7XWteRYoLt7fgaG1DoDSCK1D75BCGPh4QFgzPbgbmlHa8nwIgxqQDs215LS2NPhxCn3dtrBqGNUpE3Mpmm2rZzJuupICsFGZkBpm3LJhR6A1HDI1+syDB2HKqERBBFtB1FeNRJyfhNJjyVFWizc44b/SsIAvpRo08pMro7ypgYEq+6hvaDB/BbzLiPlSJ6PSQsvBpCIdoPHcDX0IBhzBi0ObmIfj+6/BFEDRlyppc7IHSaf/LHSMGCDTU2PnxtH6WHpbgSp93Lx28VAqCP7rrXOXmJxCXocDq8vXK7DSSyUjgFPDvexbPjnYEWQ+YCpP1wEe379xE///KzkppBUCqJvehigG5KIQl/UyOeygp0xzEdnSn60VLJS29NDc7C/QgqFYnXXIug1dJ+6KAUcZ2RiVInZWSNmTY4i0SFQiLFhY1kDUkgPrEru6wgwK5NVQBUlLRit3lYsHg03/3xDK7+tlQCdOL0bKJjtYgiuJy+gRC/TwZtnMK5xH/wcwCiZi4ZYElkLiREUaTlzf+iycgkceHVZ62fhI7FW/2o0YA0Uwh5PNK2s1SjuDN3Uuu7byEGAuhGjkKp06HNysaxcwcIAtFTpqEfNZpgu5OY6aeXIvt8wW5z0+7wMu2inqVQp88dys6NlbicPqxmF9ooFTl5iQiCQG6ekR//di6CINDulNYfnA4P0bG904oMBPJM4SSI/q5Fo5DrwiwOJDMweGuq8bc0k3DFgj7zDEUKpcFA0uIbUKjVgKQUOuke7RxJFFotyrh4/K2tBKzWcCpvbba0fhEzcxbajAx0w/PJffgxlNGDo2bK12mzSOsECUZpltC52JyRHQfA8tf30drkIMGo77Em1Pk5Okb63p32vhenBwJ5pnASQrYuL4FQaxWK3IkDKI3MhYIoipg/+RhBpSJ60rmtJ64fNZrYWXMwTJx4VpVR5n2/pOaPjwCgyx8h9V0wEvv2bRivvf6s9Xu2EUWRfdtr0OnV+P1STEJcog6Am++aihgSUSgVROnVOOxeHHYvI8en9dlWdKykRE5XKYiiSHODHb1BG1ZEkUJWCichZO3K7R6oPYhKVgoyEcDXUE974X6SbrzpnI+SldHRpN1191nvJyonl+wHf491zWqihkj1DaKnTiNv4qTwrGUwsntzFXu31ZCYbCA9Kw6NVkmUrmMWplaGj/vez2ZRuLOWfdtryBrSdz4mbZSKmFgtFaUmJkzPOqGHmdXUjs6gwdXu491lUnS6IUbDHffOiuDVyUrhpARbykGlRZUzAf/RjaiGTEaVFVk7bKDhCMGmUggG0Ey4GkGji2j7MucfnspKgHA6iQsV3fB8dMO7qqMJgoAwiBVCS6Odvdtq0GiVtFlcaLRK4hP1fb7MBUFg0swcJs3s2+W3k8mzc9i4+hjHilvCbq1fRxRF3ulQBDMu6Sog1O6I/AK1vKbQB6IoIoakaWGw6RjK1OFoJl+HoI/HvfpvBFsrI9qfe9VSfHs+wrf/E7x7V0S0bZnTJ+APntX2vfX1NL/6HxAE1Cl9vwRkzk8O7KpDo1UydU4uwaBIU52dzCGnV17464wcn05qZixb15VRdczEoT31vY7p7p20a1PX+0ehECLuzhoxpbB06VLmzZtHQUEBpaVdlZwqKyu55ZZbuPLKK7nllluoqqqKVJenRKD2EJ6N/0EURTzb38Z3dOMJjw9a6nC+fDfeza8getsJWepQpo1AmZiF4Vt/BEGB/9j2iMknBnpq+kD5zh7pAWTOLU31bfz7/7ZQfRZTD3TWDNBmZZ12DIDMwOFx+6koNVEwNo3ktK50H3kFySc46+QoFAIXXZ6Hxx3g8w8Ps2VdGTUVPaOcLaaurNCiCEOGG7l4QT6hkMiB3ScvoXpa8kSqofnz5/Pmm2+SmZnZY/sjjzzCrbfeypo1a7j11lt5+OGHI9XlKeFe/Tf8JZsJ1hXhP7QG76ZXwvtCokigW/Iqz5bXcX3wEAQDBGoOEjTXACLKFGm6JmgNKFOG4i/6Au/O9yIiX/dZh6CLQ3TZEB2tEWn7m4ar3YfL6TujkVNzgxSJ+uWqo9htZyfjpb+lGUGjIeOen5+V9mUiQ/fkdcFgiPKjrYSCIgXjUklIkryN0rPjSEo98zWhlPRYUtK7FM3nHxSx4o3CsHKwmFwAjJ6YjlKlYPLsnPDxO76qwO+L3Ow2Ykph6tSppHcU+OjEbDZTXFzMokVSEq5FixZRXFyMxXIOc32opIAg9+f/F94khqSshM++W8g9z26isMxEyNaIv3h9txNFQiYpNF9h7PJBVsRK033fgc8Qxf4XjhFFkWDTMYINUq1Zw+3/QHf1rwEINped6FSZ4/Dusj289tx2XnpqUzjzZE2FBa/Hf0rntzQ6qKu0AuD1BHjzxV20NjkoP9pKcbeEZ4U7a3nzxZ39KhwkiiLe+jpiZ85CnXxmI0yZs4e5xcmyZ1Wyn8cAACAASURBVLdw5EAj7yzbzb+e3symNcfQRqlISo1Gp9dw24+nc913JkQsM2tmrmSGunjBcIYVJNFY10ZxofTcmZqc6PRq5l6Zz12/nENqhqREpsyW1ivMrc4ebYVC/X83ndW5a2NjI6mpqSg70vUqlUpSUlJobGw8yZmRQdLyXaNGhVG6gZ71/8L+6j246koIBEOs+WIH7e/9DgD1+IVoJi5CdDsImioR9PEo9HHhNtQTFoIg3Tb3qqU94hi+3nfI3nJc2YK1B3Gt/BO+vSsQdHEodLEoEjJBHUWwpeK458n0jc8bwOPuevm3WV24nD4+fe8Qaz8+0uc5pmYnDTU2LKZ2Sg838+Fr+6ipsKDTqzF0pCPYsaGSL1YUs/HzUnxeSdFs/6oCu83DseLjf7/HI2CzEWpvR3OCFNQykcfrCfDlJ0dOOXL40F7Jrr9x9TGsHaN0gMQkQ1gJxMbrUCgil6p76pxc5l6Zz+iJGVyxeDR5I5MxtzgRRZH6aiuZufEIgoBS2fXaHj1RGoibmruUQmNdG2+9tLvfcgwK7yOj8fSmZwGnjca3HoOgH/weYidfiWHCfIiKpvGf9xCo2IUAzI86TPS4ZIaVfBA+N2P+EtqPbMWESKj+MFEZ+T3LfiYX4Pufv1L30n0EG0sIbnyBpKt+TO3zPyHpqh8RO3kBoYAPZ9FmTJ/+k+x7nkOd0NtHuXZPeZe8Pg8vf36U394xDW98Cmp/23FLjcolSLvofi9KOvLMCAoBMSRCSCDolwYEzQ32XvetucHO+6/0XRBGo1Xx89/P4+k/fEFdlTW8vd3uI2NkPBqtEp83SE2ZhYvn5ffZxvGw1kqzwNSxBcRF8LuUn4su+roXOzZWUHq4hQSjgQXXjsbvD6LR9v36CwSCVJSYwv/rozUsuG40K94qRKVWntV7nZ7RtWidMzSR8qOt+D1B2p0+Ro5N79V3UlI0Or0ap80b3rfqnYNnJMNZVQrp6ek0NzcTDAZRKpUEg0FaWlp6mZlOhtnsPC07sb+qEH9rt4LnWVN4ZYudz3cW8c/v/hbfZ0sBiNGIDK2SMjnWKHMZddNPaLQGUQSlYJCQ20EwNrNXCUxRjEUz9UYC5TtxVxyg9vmfSHJuWY7D2oZ35/sIhnhAxFRViSrQO/d9ffFBfIEExLgsvmhK5GBrA7c32ghpY/FYTX2W3ZTLcUq56mPiokhNje1xL/bvktwEb/3RdF79x3bqa60oVdKIKhgI9bpvB/b1Xpybd00B6z8twWn3YDI56bQKzL92JBtXl3JgTx0owOcNotYoqSo309hgQ9XNN/1kWA5LThhuQyK+CH2X8nPRxfHuRWO9lInUanGxeuVhdm+u4qbvTe6xYNxJ1TETXk+AuEQdbRY3+aNTiE2Q3gkF41LP2b3WRUuuuxu/kJ6Z6Hhtn30bUwzUVltpbZWq7DU12NEb+p9H66wqBaPRyKhRo1i1ahWLFy9m1apVjBo1isTExLPWp+/AZ70WgZUpw/j81U0AVAeS6VRJQ6kDn5JtabeyqlRg7n4nn+8o5oHL4+ic3HeanLojCALaydehmXQt/pJN4cVrQanC25E4T3RK3iuiq40DZSbMdg/zJkutin4v8Z4Gdgdyea92UrjdJoubZEMCAUtkvQkuFIr21bP5izLSs+K4676LEEWRA7vqGFaQREVJK/mjU9HpNUTpVNhtnvBCcTAo4nR4wykFAKyt7egNGr7zP9Pw+YJ43X5i43Ws/7SEBKOkxOdfO5KWRgcjxqRSX23jWHEzdpsbhUJg5qVD2fxFGY11bWQPPfnzHAqJHNpTR0JNPaqEhEGb1mEwIooijXWSUmhtcoTrF+zYUMGsy/LCC8V+fxBHm4fdm6sxRGtYfOsE6qts5I9JQRCEcL6ic0VmjjQrLTvS2hEP0Xf8UlJqDIU7azG3ONEZNPh9QRQx/ZczYmsKTzzxBHPnzqWpqYnvf//7XHPNNQA8+uijvPHGG1x55ZW88cYbPPbYY5HqsheiKPZSCIr4dPzBrhtUXGvHlnsZXlHSh9qZN2McMZH2gJLPd0izixJr18hPmdQz0VV3BEFAM/ISoi6/FyEqpkdKDDSSd0LIZeP9DeW8v/4Y/oC0+OM78hUafOzy9UyV3GhuRzAkILrbEENn11d+MHJgl6QsG+vasNvcWM0utn9VwTvL9hDwh8ILdTFxURQXNlJXZSM5LRqlUuCrT0t6rDlYTC4Sk/VotCqiY7QYU6JRa5Rcu2Q8V98sBSdmD01kymzp+580Mxu9QUN9tY2xUzIoGJuGQiFQW2nlVKguM7NtfQWHW6KOu57gcvp6VPCS6c3h/Q093DNPhWPFLZiancTERWE1uXC3S89BXZWN91/ZS2WpZCravr6Cd5ftwdTiZNa8PAzRWkaMTQ0rgnNd6lOlVpI/WnJs0Uapj9t/p1J77+W9tDTYAZg1r3c1vVPut99nfo2HHnqIhx56qNf2vLw83n///Uh1c0JC1oYe/xtufRZPTR3l9VIiO0EMsXZPLf5Jc9jZlsBDV+hRj53PCFdP75RKq4Du2t8RsjUixJzcQ0Q9bBqiqw3vtjcA0H/rcUDEtfLPOC0mmk0ank18k5bNJjIv+zaeYzupDRiZOHM6joONXDQ+nZVbKmkwtTPRmACiiOhqQ4g+ezOqwUYwGMLR5iEtM5amejsWkyucjCzYoWxjOrJMpmbE0trkRK1RcuMdkyk51MSGz0v57/M7CIVE5i0aidXczqgJvc2Yx0tHEJ+o57YfzyAYDKFQCAiCQFpW3CkrBatZWqysUWQQUidTtPwwC64f3WOh8vMPi2hpdJCZm9Arn40oitgsbuISIru4OZiorbSwac0xAFLSYxg7JZOCsScP/jtyoJF4o55vf28yn7xzEEEhkJhk4PB+6X2xee0xsocl0lDblfAyb+T54Rk245KhtDY5GHGC68zI6XKE+fzDwwAkJp+8XOvxGFSRM6GQSGGZ6bjBXaHWLq8dZXoB7uoG6p59FtPnn3F1yzZ+a/qUgNvD6p012IUYYifORxAEYg0a4jq8TfIyYqk3OVGlF6AZdekpjw5UedMRYpJRZo1FacxGacxB0MfjaqjgOv0+ALTlXyH6XAjmKo76Mxg9NJGnfjKLxRcNJTFWS6vNg8IgvZRClpoTdfeNw9HmQRQhe5ikKG2WdmwWV49jOlMPD+sIJopP1KNQCIyakM7CG8cQCIQIhUTWrTxCwB8KZ7I8HZRKRfiZyMlLxNLajt3mOel5nZW4QgoVNQ4dlaUmmuraejzLncdY+xgJr/+0hHf+vZuvPis5bZkvFI4caAp/bml0sH7VUfZurabsSEsP9+DWJgdHD0rHej1+GmvbGDrCiEqt5IbbJ3H9bRPDPv4qtYJ2h4+SQ020O7xE6dRc950J543i1Uap+Nadkxk3JfO4xxiitfzkwUvIGdY1iOxuKj1dBpVSWLenln98cJC9JX0HdwXNtaDUYPju39Fd9Sup3CCQuvdLxtvLoM3KdLEJUQRjbBSKbi/83393CrddMYKJ+UlY7F7cHe6Hp4pCF4thyVPoFv6ya2PAR7yrmkujJJfIBp+B1qOFCIiU+DPI7ObelhAbhcXuQZmWjxCTjGfjK+F4ChmwdcwKMnPiUSgELCZXePTdiT5a+iGkZ8cxcUYW8xYVhPcNHZHU41iVWhFWMP1lWEebb764M1xlqy/sNje13SJU58xIQqkU+PitA7z+3A583gBuV5er5NfNI1azi9Iiqf3SouazFlR3pgT8QQKB/vvHnwyr2UXucCO3/HAql11dgEIpsGtzFWs/PsLebV2DqI/+u5+vPivB5w1QXW7piADu+f2nZsYCsGDxaIzJBjatOYbPG2TWZcPCZsjBxjU3j+OOn87kmpvH9XBbPV0GlVKobJJGUmZ73yOzkKUWRWIWCn0cgkobLhbeiTI6htFeyf/YGNdzep4Ur2P+lCxSE6S1gBbr6f/wBEFAUHStRwSyJtMW0lGS/S1Eg5F4hZvK/Tvxo8YVk42um0tcYowWi8MjRU1PX4LobsP53/sIeWSvEujKWx9v1Em2YbMLU7OT9Kyu0X54dOf3kXNoJVGmnrOtJT+cGra/Dhlu7JHRsj/EJegYPkqalWz4rCTsIff16NKDu+sRgYQoScnnTR7K+OnSuoKr3cd//rqVV//RlTplx4ZK/vmXjeF2dm2qRKVWcOUNUjWz7n7z5xOffVDE689tZ9emyl6zuDNFFEXaLC7iE3UkJhkYOT6NH9w3JxxZXLS3vlv0sfR33/YaqsvM6PRqUjN6ehklGPX86DdzyR1uZPolQ9FGqRgy3Bj+PgcrhmhtjxlDfxg0SkEURRo7RlB9vbDFUJCgqQplkuQt5K4ox3X0CJ6xUpm/YOYQtEOGYmw8xp0Us2Bk36aDlARphf9ghZkVmytYuaWS4qr+RWAXxlzKI7abyJg2D23+LBIUTtJcZRzzpzCpoKc9OzE2CqvDS0gU+ai8o6yft51gXVG/+r7QaLO60WhVROnUGFMMHDnYiLmlvdcMAKBt80baDx6g4fl/9DDPJCQZGD9VmoaPPcF0/HS4YvFo5l1TQDAo0mZ109LoYNmzW6g6Ji1eiqJIVZmZrCEJzDJUMsO+CUOcnpmXDOPHv53bq73uHiYWUztul5+KEhPjp2aR3mHuivQLNxJ05vcXBIG922r48LX9NNWZWP3ak7TWlVNffgi/r/+FZJx2L8Gg2KPkpVqj5Ja7pnLR5cPxegK8uHQTn314KLx//45ayo60kjvc2KcZuHMQMWS4ke/fN5urbhp7Wu7FFyqDRimsXFeGq0VSCg192FyDLRXgc6PMlEZTlk8/QTBE84IrjxdybyTr579AbTSCKJJetgfDf/+B39TbDNWpFD7aVMHKrVWs2FLJM+8U0uY8/Qf6QLmZpHgd6UY9Qoz08kpUtrPdM5wpX0uilRijJRAUcbj8FJZbedR2IwAhW1OvdgGCLhf1z/0dT1VkM7aer7RZpVGiIAg9ZgfDRydz64+mc9uPu2r8OvZI0Zwht5v2gwd6tDNibCq33zOzRxtnSkKStKi3c2MlH74mrR/t2FCJ3x/EYnLhaPMwZLgRhbWV5Liul44gCNx4xyQuu7rLzDV9bldaZKvJhbNjVpycFoNOr0YbpQqb0s4FB3bVsXr54ZMe52jzEPCHmHHpUJbcPQ2NVsnq99bRZm5k+2evsuXjf1O6b0O/5agulwZmX3fLFAQhPFsA2LOtGqDH9ztpZvZJ2z/XnkXnM4NGKdQdbGIoCmZmJ9Bo7kMp1BWBIKDKHI2vtYX2gwfwjp2GT6Hm0kvHEmOMl5QCgFJJ0OnAtv7LXu1Eafp2yCrvcPU6Vbz+IMXVVibkJSF0yOWKHcIyx6Uc9OeSmdTTO8DYUYGppMaKqc2DNRSNT5tAyN63rdpdWkJ74X5qnnjsG5FVta3D8wYIj5hTMmIwRGuJS9ARG9/1svC3NBM7ew7qpGSsaz7v0Y4gCBGvhdtZirHTtTFKr8ZqdrF1XVk422puXiJ+kwlV5zPYQWpGLHndTBbDCpKYc3keINnQnQ5pMBIdKzlCxCfqzth8FAqJNNTYKClq5lhxS4/o3e401rWxbX05laVSMJfkAeXq83mztEq/ycQkPQlGPYtvnYBKkK7d45JMoM3VR/H7PDRUHD6tZ7auysqWtcdIy4oNrwV0p7tS6GTk+DS+9/NZ3HjHpB6zC5mTM2iUQqjDu0Dr8mN3+XG4euYwCVnqUMSlIWgNtL71BoJGS+vwyQDhoDFVR9Ccbng+hnHjse/YRsjfOxfKhDwjaYl6fnrjOO69YRxKhUB5Q1t4v6emGnfZsRPKe7Taij8QYkLHApciNgXHxb/kkF8yb2m+Nk3N6FASq3dKdnABsAnx+C29/daDLhdtWzZ1/e88u+sOXr+ULbKowozd1XfumMJjJlZsrqDZ6uJQhTmiiioQCOGwe8NKISk1mkXfHs813x7X69iQx03QbkeTlk7MzJm4j5USbD89v/bTRa1RYujw9pgyJ5fv/3w2Q0ckUXXMTEWJieS0aPR6FQGbFbWxt7mr+9qGIAiMn5pFQpIeq6md9g6l0Nl+enY8zQ12PG7/Kd/jdqeXHRsqcLv8BPxBdm6s4OO3DrB+1VHWrTzCmo8O91oH8bj9bPuyKxWL3eZmy9oy3v7X7l5pnaEzi2eA0t3v0WZuJDZeh17bYULrSM5maqyicOMKNq94icbK4lOSHeDA7joMMVoW3Ty+zwXUzujd/NEp3P/oFRSMTWVIvhGdXkNqRm8lInNiBkXuI583gKrDqcHb5kUAGs0uYvRdodyhtkYUcWmEfD7aiw+TcMWV7BejUCkVxOilcHFFlPRSURuTiJ09h7pnllL75J/Q5uSQevv3EDoS9/3spvEgdtkcs1OiqeyYKfiaGqn/2/8RtNvJfvD3PapKdedguRmtWsmI7C5Phs7ZQF+kJOjQa1VUNTlIjNWSkWTgSJOWOHcZnspCtLnjw8eaPnyP9sL94f8DZguqmN4Pv99iQZWQcMKpcSAYQtXHDy0UEkEAfyDEAy9sw+MLEgiGmDc5k+8uKAifu6ekhfHDjPzjQynfysqtVQDcMHcY184e0md//kCoxyL7ydi5UXI1TkqTFokFQWDyzJw+Q/79rZJJUJ2Sgio+AcuqT3AVHyZm2vRex0aSq28ay46NFYwcJ/mTDxlupLLUhNvlZ/ooLVUP/Q5CoV4zhU7mzM9DpelSDulZcRwrbgm71eo6nvW8kckU7qzllb9vI29kMguuH31CuZob7Kz5qJh2h5f9O2qPe1xDrY3cvC7Z9myppqXRQW5eItXlFqwmF0X7JL9+U7Ozx7EgudFGG+w0VhzCZTcz86o78HvMBEUdSkEyd4mhIBWHtgFwdPc6MoZJpl5RFDm650uyho8nJiGll2xWk4u0zFjUmr7t/YIgcPf9F6FQKjDEaJm3aOQJ74nMiRkUM4WWjgyA0akGgoEQeuBoTVfQkMfjJ9TWQiA6hZdf/hKCQXbZJRfPxBht+KVoGDuOxGsXk3zzEvQjR5Gw8Gq89XXYt2ymbcvmcHsKQejhpzwsI5bKJgd+u4Oqh35H0C4pCOfePX3KK4oiB8pNjBmaiFrVdYvjOkY0Q/rItyIIAukdZoixQ43MGpNGdSAJrRDAv/ZvBCp2hY/1NUg/zs5i6H5L74Iw3vp6Kh/8NZZPP8G6fh1BR+8X6No9tfzP0xuwt/tweXq6v77y+REefXkXxZUWnG5/uO5E9/WctXtq+dfKYh5/XUosN3F4EheNSyfOoOGjTRX8c0VRr7WYt9Yd496/bsIfOLWI7UAgRPH+RkaMSWHI8L5fqN3xtUiZS9XJKUQNHQZKJZ6a6lPq60xISo1m0c3jw2as3OGJJKVGk5kbT/yO5fhbJbn0o/p+iY+flsXobsF0wwqS8PuCHDnYhD5aE34ek9OimT53CInJBprqT27S3PzFMdodXmLiohiab2TaRbksuH40t98zg5vvmspdv5yDUqUI2+w7aWm0k5QazRUdSqeimxt4Wx+OHhaTi2i9tP7RZmpgzX//AoA7OL7XsQCt9RV4XA78Pi82UwMHN69kzX+X9pr9BDpST8QbT2wCUqmV501swWBnUCiFmiorIiI5HbbXBKWSFZsrKetIcvX0srX4AgqONqiZu+ddAL6oF9hztJXEbvZjQaUiafEN4bwzyTfdTP6Ly9BkZuHYvbNXv36rFU9VFaNbi/F6AzSW9xxpeWr7DjCzu/xY7F4Kcnr6OysUAo9+fxq/XjKxz/OunpXLnHFp3HRpHjNGpzJ73lzEkFRpKeRzIwaDhPw+/GYz0VOnkXHPzwAIdCgF+87tVD/2MGIoRNumDRAKYV6xnNa33sDyNdt6IBji7XWSCez5jw7x079tor4jJ7vT7WfroSbqWtv5f8sPoRAEnv/lXC4en05da3v4h2vtMG00W1xMH5XCz28azw+uGcXlUyVz3e6jLfztg4M0W128/NkRnG4/G/ZLLsH7j/Vtx+6OqdnJti/LCQRC5I9JPaXFwM6Xrzo5BUGpRG1MCm87l+j0Gr79/Slcu2Q8eKSXaMLCq9Ek9x4J90VGTjwarQqfN9BjDUQQBKbMzmVovhGX03vcRJGiKNLa5KC1ycns+Xl89yczWPitsUy9aAh5I5OJjo3CmGxAo1UxbEQSxw43h9cNQiERc0s7GTlxqNVKYuKiqDwmPWOx8VFh92CvRzIxOe0erGYXamXPgUd0QgoBoSulhy+UiVKtZ8bC7wIiRds/Z/lzD7D+nb8BEAz4sbb0/I11LqonnEQpyESOQWE+qi5tJtpjJrctRHWMjklGPXVVZtbvqyMlXke0t4VdvplU2ZKZqY5DF63GodKDKIa9iY6HIAhoM7PCgW6diKEQVf/7G0S/n2ggJ2MBjRVBOpMgqJNT8NbUIIpir5dVU8dCeIaxd6h5Turx0+5Oyk9mUn7XouOkiSMoWyYQ8oiw+zWqn30tvE978VwU0dEIGg0Bs/SDbfr3SwD4TSZch4uIyhuOr6GekFtSKKIoInq9KKKiqG/tGvEf60gWtmp7NZdPyWL5Jslck5UcTV2rk5G58ei0KnJSY9h8sBGb00dCjBZTRySvUiFww9yuXCtzxqVT1eQgNzWG5Zsq+N1LOwBIiNai0ypxe4PsK21l+qjjh+6HQiKr3juIu92PTq8+5ehjf2sLiuholHrpJaJOScHfcvpKobTWxl/e3Mfjd00nM7n/yev8Lc0EHQ5S7vge8XMvPeXzlEoFicl6murs5I/qrUiiY7WIYkft3o7bGAiEqK2w0FjXFs4TBfR5fnfGTc2k7EgLr/5jG4IAlywcQSAQIrnjWb38upHs21aDIVaLGBKpKDGxf0cN5UdbaW1y0txgJxgIIQal2XucMZ2YhBTGzLqKHZut1JdNBhT4QjkkD0sid9QY6ssOUX5giyS330usMQ27uZnGymISU7uSUHYGKMpK4dwxKJRC0C+Q2l6Df+VaUi//Gc0Ndi4en8bh4lZ2J0czRNWKrSO53J7Mq5h82Sgud3lZt7eOq2cNOWn76uRkHHt2IQaD4XWF9qKDiP6unEjXtG4j7vOuQhaxs+dg/vgjyn9+Dxn3/EyyYcfFI6hUNHY8yGlf83qwrPmc9kMHybr/N6c26jWbJYXQBzZdIkZBQG1Mwtfa0mPa7Wuox282ET9+Amnfv4vqR/9AwGrB+vmnmJZ/QN7fn6euY1Ywa0wa2w9Lbq97jrZQ0dBGq83DbVeMYOrIFDYW1nP5FGm0l50ivRxrmh34gyEKy0ykJui4+bLh4aA/gPhoLffeIC0CKxQCH2yQFO6hCjNur2Q2qmpyUG9qp6jCzNwJGb3WGOqrrbjb/UyfO4QJ07JO6j9e3+pkw/4GLmpq7jEaVyen4Ckv61N5n4jNByQT3b7S1jNTCiZpRqRJO7108QBz5g9n/44aCsb3rsfRufDc6Z0EUHq4mY2fl37tOA366BOnUU7NiOW670ygqsxM0d56tqyTvq/kjjWctMw4ru5Y1D+0t54jB5rYsaHLFbqp3o5ao8Db3kr+pEuYfNm3wvsmTjfg8wYYOzmD8hITDdVSfqGZV99B0fbPcTmshIIBJl36LbZ+8h/qyw4yZubC8PmtTQ6USuGk5iOZyDEolAJAtNeC6PWQPyJe8uhwh8gLwNENlYzVBalCmhEEVDp2ba7i5rumcv3FQ9FHqU/atjopCUIhAhZLuESip7wcFAoy7vkZAZsV3ng9fHzeP14AQcD88UeE3G4al/2LYJsN4/U30j7tMl5fI0VSx+uVmD7+iJip09BmZmF6XzJttR8oJHripN6CfA33MakdwaimIT2d8twbGB8vsnxjGU27ffx7XghtTg7u0lJch7uC3NwlRxH9ftRJSWjS0tGNKCBgNmOrkGYAxbsP858dklK4c2EBMXo1I7LjeW75IVptHq6Yms38DkVw3Zwuv/lOpXCk2sp/v5Bku2pmLpNGHD8K9IqpWWGlUNURkT57bBrbipr4wzLJZFfX4uSuRT1t7TUVFlQqBROmZ6NSndzK+ebaUo7W2BhZU4dmWB4b9tczYXgSmpQUQm43Qaejz8X4r+NraqLlzf9iT5gpydHsPMkZJyZgk0bPqvi+E+2diJT0mHAU89fpzG3T3k0pdOZMSk6LZuKMbCwmF0OGn1p0a0ZOPBk58dhtHipLTSiUAnF9uHJmdUsBkZwWzcxLh1G4s5asHBUl233EJfVUfmlZcVz3nQkAeDwByopbsJpdJCYZmHDxdYA0K/zs/UMoxFwcLZswNVSSlCE9d62NDoyp0WeUtkHm9Bg0d3ro9VcCkK73kZIeQ2uHjVOBQLF7Fq6gjmhv14Lr3q3VrP/4SDgT4olQd4wsuwez+VtbUScaiZ44ifhL53Hku/8b3qfU61HqdOQ8/BjK6BiCbdLox112jPUdxVvG5sbT8soyLJ98jOWzT6V+UqV5vn3rllO6ZlfxYRQ6HepRKWRo2xjftJx6b4imqCQQBDbsb6BFl0TAaqH+H39Fk5aOQqej/ZDkCaRKktwfVYlG/GYTQscPq/6w9JLWapRo1EqWzM9n8ojk8KJ4blrfI2OdVkVKvI4vdtdisXt58LbJzJ2QceJ7q1Lylx/P4vEfzmDs0ESWzM9n1hhp5KvXqpg+KoVtRU043T0z1ZqanSSmGE5JITRZXBytsVGQGYPB52RPc4jX15TwwopDaDKkyOXWI2UEgiH+/Ukx9z+/tdcCuCiK+JoaMa34ENeRwySUSE4Ee0tbWb2zhlA/XWwD1k6lENl8Op3rDI62rpQvltZ2ktOi+dadkxk+KoXpFw8hJf30XDJHdcxKQkGxz4XbzhG7WqPkpu9NIWtIAotuGU9SsuSoEJ90/EjxrFxJMXbGbnRis7iorbRSXZuAQqniy3f+yp517xLw+2lttpGSFoPLYTujiGiZU2dQKAWFbnXxRgAAIABJREFUAhKHS3bGgKmVcZP7nornWIuYPTGKnGFSGbvaSmvYje5EaNIzQKnEsvqzsBnGb2oNv1QBLp6cw7b06XxpnMq/PykmEAwRlZNL3KWXhY/xWyzsKzVxZYqPGw69i2PXTpSxsbhKjiCKIgGbZLtvP3yIkO/EtWJDPh/OfXuJnjyV1Iw00pRtFChrCTUcZkhaDLEGDW+uLeXDYx0v01CItLt/hDYnF1+TFNugTpJG8GqjkaDdTsAujdRVZslc9NAdU3v0OX2kpBxzT7DuMaQju2SMXk3+KUQFh/x+jJoQmUkGfnXLRBZMy2b0kATuuX4sT/1kNpdPyUZECtrrRBRFTM3OcJ6ir9Nobqe60c6ba0v5al8dhzuKptw+KQYFIiaNJFddSztRuUMAWPnhFv65oojth5uwOrz88rmtbD3UFQNi37yJqod+h7MjGjrfUc3VM6VaCu99VUZZXRv9IWC1ooiORqHpfyWsvuhM+dE9utnc2k5isuGMonNz8hJJz4pj1mV95+MXBIEld0/jO3dP67G9rbUeEIhL6m3q6iQ2PoqM7DgO72ugqb6NgF8yJVrC61sqDPHSIKP84FY+e/XPRIsrSU7T8flrf2L5cw/gbj+9IFKZ0+ecKIXKykpuueUWrrzySm655RaqqqpO6/yhk5IRkqRpsPvwDlI2/45Lg6v4TvyrXBW7MnxcvKeFUZOGkD+6y6bcZnH1SKvbic8bwNzxMKri4khafAOuw0VhTx5/a2vYlARSbqLcGxezO2E02w//f/bOOzCu8kr7vzu9SBpNU+/FkixXuXdj44ptqgMxkEYICSmQ3WyWJGxICMmGJCSQQggQSGIgISaAwQVj3Hu3ZVmyVSxZXaMykkbT2/fHHc1okGTLjfJtnn80mnvf22bmPe855znPaQ0nag1Ll5P8tW9gWL4CT2sLDoeLcZU7CPp9pHz9mxhvvhV/dzeu2lqCbhfaseMIejxUP/gVLjz+2CBWUD/6jh0h4HIRN30Ggjqy2rvRt4Pl8TVMzBcNVqMqgU0JM6i/9WuoMrNEAwcgCLT4lKzdcg5ZqljmH3SLq0pVVxsT802Dqqo/t7SA//jMeFKMGpzna/B2duDtaMfdHDGs/WGlJINmRJNPy7O/o+ZbX496TxAEJhcmoFHJyEqORSmXUnEhYhR6rE48bj/mYYzCD144xDd+tYNtxxr5x/Zqmo+VMkZqRd0qssEa1eLn7/H6QaPBro4j2dU5iPHUz74C0VD3o8+UitnTzfjKnXxxsZir6r4CmRMQw0dXEjq6FARBwGDShBVVuzrsOO3ey/YMhjruLfdMYMK04aUh9EZNOKfRj+6OZmLiTcjkF68WLy5Jwdbr5q21J9n9vvj8u9oHFhdGwr1OWyeC4KPhzBv4Ql5CW/2lpcP9Pi9u5/UtWPz/GR+JUXjsscdYs2YNW7ZsYc2aNfzwhz+8rPGbfa/zbuVGLowyUX/6NO0nQVrbjrc3iFHWyVjVSaYbWlAHHMiNRvJGJ3DjqiJuCAmV9Wur98PnC7D22YP8889Hw5LF6gKx4OXC44/hrKnGb+sNr7T7kZ8aWRm3hkTJJCoVsZMmo0hLQwgEGONqQNrRhmHJMmImTkJTUARAb6gCOW7GLGImT0EaG0vQ46Fz/Vvhis9+dO/YRuufX0CRnIK6sAh5wRy8Y1ZiC4T6xFreoyBUFJeTqqM0Lp/36/24PD4Uof7X0jgdm463suNEEzs6I6tUdVExBruFpLjBuRa5TMqYHCN9x47S8LOfUPvf36H2kf/iwg+/H/ag8tPiuX/FaL684uJFU/3oD2X5bEOv8GRSCRmJMTRYIrH7fs58aubgydTtja5v8PoCTDv8T1ace5e+kyeQmUzYZKKxCwLf+9NBamRmcl3NqPzixHLLnOzwsfrDVo66SC3DRrn4XQge3kNhQAwp9tgv7tkNB5/1+hgFEBupWDtEivD5s+J1Zo+6dC3H9UBPRwvxpouHEoEoBc+as+04+jycPd1GbEi1WJc0gzhjOi6/+BkFg1K6WiJ9UjqbL631dWz7Ot7+4/c4d3wHgX93MLxsXHej0NnZSXl5OStWrABgxYoVlJeX09V1ecqjttOldMk8WHRyPP0r1BTRhR0jPUHMiW1ox41HkMmQSATyRyeQmWskJk7JoQ+2UHlKrADu63XRdMGKJ8SC6e+cpUwTV0YBu52G/30CAE1BdGVkqlnL7fNEt7p1gFLl5kMXeO2UOKktb9qNoFQRM0m8tv48Qs/unQDIjEaSv/I1sn/xa/RLlxH0eMJVuCAmOnv2iAYk4e57EQQBaUIOhpm3EyuPfMEnF5q5a2E+/3nnBFbNyqKjx8Xf3jsXNmSyuDg6Q/HmPZWR0Idn0mxkwQCFRzcQ9A3dr8Hd3DT4vQt14dczxiRhVAn4+0aehPW0DN9mMtWkpbkjUv9QV9WB3qQJy1oMRD9rCiAvVScWcYTgPFuBfsEibihJZXyo4rajx4V+8RLkfi8rJBdYHtfF8mI9j983lWAQ3thZjcPaTaCznUptOusT59CgjlBl5R4XEkGg9wqMgt9ux93YgDL90oJsVwKDWYvH7aetxUZrcy+mhBi0MddW12kk8Hk92Kzt6MyXNgoKpYxRxQmhcQE2vF6K3eZm7pJ8VGo5led8WN3z8ElLKJ71GRbe/aPw2IS0PDpa6oY8f7++EkBtmUiBPrnzLVrrKq7q3v4v4robhZaWFhITE5GGqJ5SqZSEhARaLjJJfBhTy91oQpIGthgZZQWxCBotfo8MiSkLr6aIoNeLefWdUePUGjl3fGESWukJTmx7mb/94SBrnz3Eni1VCILYaKW/+YlEOaBASKnCeMttgyQsBEHgphlZmHQqWjrtOFw+LFYH63bUcHRAZMJ8x2pkOl14TPyiJUi0Wky33YEqOwdBIkEil6NIEUMxnmYxOR0MBql79BHc9RfQ3bAATWFR1PmTVv93+LXE0cXiKWJPhv5rOljexvMnHEj1Bgyf+SwNFhsxajlWmxvld35E9pNPcdgeg02mQVlzho633hjyefs6O5DpDWQ+/jOyn/wVAPay01H7tL3yV2oe/sawKq0eiwXb8WOR/1siIai+kydwN0SKlJJNWuwuHy9tqsDp8IQ6ZQ3WCAK4EGIwffczY/myrpGb5ZECQolajX7xEu5dXMBDq8czuTCBrKRYFiydgnZiCXk1Bxl3fAPt//w7yRqBu9T17D3ZxC9/IxIBjukKGLVsAb/81jziHvgWAIFuK7Fa+SBPwWJ1YHdFJ8cHomP9W7T++XkIBEbENLsS5BSYUChlvL/+DHab55oL/Y0UPZ0tQHBEngLAwpVFPPDduZgSYuhst6M3acjIMeByenE5vPR0OZk8O4cx02ZjTtJRPH0pWcXTMKXm0tPeNCjhfHLXW6x/7gdYLQ2UHxEXVP1hrL7uSxdJ/hvR+NRQUj+MQLKZ3kOHUd21Buumtcj0ehSJ0Umus0e3oVRH4tL99D1br5vE1Dji4lXU11rDHHbTw48gVcjRjcq96LnTE2I4XGHhcEWkKOqLK4rhGfF17JRpUfubV9+J+TN3DYrBK1PEH5GrthZ1fgEBd+TLHs4NDIAmdyKamx/Fsf4J/J31SGLNBLpbcL/3NLebi9nQp6Gs1UCZcQXTqwP4/EFWzcri9e3V7Kh1cndeBvurztCz7EFWNmzDdvQI5tV3DTqPt6MDuckUvj6ZwThopd+fkO3euYOkL0RoqwG3G19PD3Xf/27U/s7SA8RNn4ajvILmP/xOfE7TZ5D85QfCRX77TrdiCAoEg5CdPzgMYrW5OVxhwRCnJLV0By0bNtFvNlXZOSR96ctR+3/15mIIiobZfPtn8NtsuKqrcFRU0HfsKBmlOyhKnI3GJ3pU3/7mcuLMYnhDN6WEvr+p8Vqt6DQJgzyFR/50kES9mv99YMag6wy4nHS9ux4AqU4nym1cB6g1CqbNy2bP+5WAQFLaxyP+1tMhGnzdCI0CiPUrk2ZlsOWtcrShOgpTYgwdbX3MXZIf1T97zMzlALTUlhMMBik/+B6aOD35E+ZitTRQUyrqKW1//Rl8IYHLmSu+yP6NL//bKFwBrrtRSE5Opq2tDb/fj1Qqxe/3Y7FYSE6+/GKenKwczteJ8cVAsh5pTR2WV9cCkLRoIWazyIzxuJ3sePOvVJVGS1cIuAkiriBGj0smXq+m6oyF557cTcGYRM6VtWEwafjGrKFlKPqxZEZ2VNJyclEiy+fkYum6G5fFQlL2SO8tlnqTia5NG+jatIHC74uegDIhgeylC5DrBrOAEgpGUyeRouprxGCeT/P7TxHsbWM0bYzWwQ+tt9MT1HIw1B5ywbQsep0+Nuyr5Wx9N3aXj5tm52Iqb+PCyRPEK4PI4yKTiWX7TpyV5zDPnxd+npaMNHxd7ZHna7WGQ0/+5obw+z2nyyh79DEIURklclDqwO8GZ1UlHX/6Dd2lkeSu7eABih68n5kT06hptbH5QB3nytrQyaUUFScjGcBN73N4+NqvdxHfa+E26QXayirRjRtLT9kZCAQY9/gPkccNz5rCHEvqUz+nffceKp96Gtcp0Yu50V+LQ67Cq44hd3Rm1JBGswmpw0ZMnILSmk7OvL2Z+IojxBQVMqm7nWMUhe99ILoOnw2/1hUVkpB47Xo3DMSFytN01KwnTmGj1zObhMS4Ia/neuNsXwcyuYLsvCwEyciDD0ZjDH29HsaVpGI2x/KFr8/E7wuE8wsfRmzMGHa/JS72AGpO7qanS1yYFZbMorOtCVt3Bzfc8nlyiidRfnAjHmfPx/JMPs247kbBaDRSVFTEhg0buPnmm9mwYQNFRUUYDCNvGafOzaKytpu5WMnU2NgXSKRJ4SMnMRFfWxv6ZTehvnE57e02Oppr2faP3wx5nBtXJFJ2KkhLQw8pmTqUKjmxcUpsvX00nX0bCcV0dUB1pSUqnt2vL+MIMVByErVMLUqgtctBqknL/SuL6el2oJy3CCVEqXf2x8mHY+pITWYIVb2e/dmTAKT9z4/p9kjgQyqgZnMsnd0eJMYMeqtLcam24qqPliB+XP8vOlf8msf/dlI8r8/HyhkZ1DX3UFbbhUohJVWvwhWK/1a9ug7jzbeIbUQlEqqeEVfxXok8fB9BvQnHuUosFrGzVt8JsXGNOn8U9vM1tDWLITjLxvdDDyyIUgdJa25FljmBtt89Rl8TUQZBlZ2Dq/Y8TSfK0RaPYdWMTGaOMrHuhaM0e32UVVlIDnkQLo+Pn609htvj58aOI5hcFgKAbuWtmL/6TXw93XS7GfS8hoI/qwBBJqPnlJgAV3c0oQaMt9w2WHU1Voe9rR0hDggGkW5chz0YwF5znkVAlTZ90Ji+E8dof2MdAJriMcStHOK41wAuey/vvvxrQPwRy4ROEILX5VwXw/myg5zav5V4cyodQ/Q5uRTGTQnVkQy4blf78GG5MTNvomy/GO7rNwgAusQcxs1bjT5eRXePh46OPlRaA9b21o/8mXwSIJEIGI1XVon/kbCPfvSjH/HKK6+wZMkSXnnlFX784x9f1nhTbjFVGXKe1fahNSQzYf5tdLZe4FQKOPPSiZ9/Q3iFcuaASPE0peSwaM13wscQJBLOn3qfuYszWLSqgPOnttDeWM49D04nN7sLhaSZjHQLgkBUwVttZQdvv3qSv/x2P2ufPcQbfz2OVCLw1ZvH8KMvTuX+lUNXnJ4/105zfTdrnz3EP186htMR+aI7HZ5wS8WBtFcA7YSJSNUX12uSJo0iYKnBtf05GIJdkRFa+KcnxIh9o9vOsXK6mL8Yl2tEIhFQZWWBIGB9bxPVX/sKNd/+Jq5Qjwi52Yx+0ZLw8RRJSWJVcE+ozqL8DIJSiW7+AvD7aX3+Oaq//gC9B/ahTpATnwemmxainHQzUlMm2mnzw8cyfeYukr50P6kP/QcA7gGigu4+8RnZCFBeF6GonqzqoLHdzoNzEkh3iROB0mxClZ2DRKEYscgcgFStRl0oMqcUKalIdfHETJyEYenyQfvKjUa87RbucJ/mkZq1SIIBjuiKeDV1MQBTuivwD2COBbxeWl58Hm9bK6nf/g5p3/4OisTh9Z2uBu1NYgHirJX3gaBGKzuMQjmYen290XJe7MpWNHXRR3K+4ulLuPXBn0e9Z07LIzV3LIIgQa6I5FV05hRsVgsOm/XDh/k3LoKPxCjk5uaybt06tmzZwrp168jJubwYa0HqWOJ9AbrkUroSMsgqmkJm4WQ8bif1JikemcDON36PzWqhvbGGzMLJzLv9QQxJGSz53CNMuvFOcsfNxtrWwLnD7yAJ1HLu2Hb2vfMiDZUn8dpF7rPBqCS3KIHyky30WJ309bp4780ztDX14g5JSzvtXlobo+mVXo8fjzvC5HG7vGx5q5z1r53CbnPT1W7nL7/dT3O9WPm87qVj/P35IwSDQaQacTWszMgk4/GfkfqNhy75PJQlq1BMuCny/+zPo5h8G7IpqwEIOrr5/cNz+d49Jfi7GnFu/AVp9Rv56s3FfG6JyKiSqNRkPRH5cQUcDhqe/BkAqQ9/J8pYqbLEnEHdD3+As7oKR9lpNAWFxJSUINPr6TtxDEKToyreh27hSrQL7g2Pj1v2+fDr2MIM4mbOQhoTg8xgwH5gC/5W0RjZQ57YA3EfsOtoDU0hDv6xynZ0MQri1z0HgH7REiY8/dQln9NwiBknyjkLcjk5T/6KlK9/E0E22GlWZmUTsNtx74506NtjnECDOolTcXlM6amgZvt+XB4frtrzVH/tfoJuNynfeAht8Zgrvr6RoL2xBplcQUrOGFIKViEVnPRazl564FXC7eyjvSlCEe1qqyejoISMgpLrfu5+KFQaZq2K5I/m3/EN5IrBIafs0SLDrPrknkHb/o3h8amoaFZJVTzoExOPpxNECeVpy+5h+rLP4ejtYts/nqatvpITO9/E7/eSMmo8Wxp34fA6iDelkDduFuPnrEITZ8BqaaTm1D5i4s3oTCns3/ASfd0iJbS3s5Vpc7OQSgW2bzxLb3dEQuAL35rBfd+eBQSpr2kgGAyGu1WtffYg/3jxSHjfTku0G90fOdr0Rhlulw97n5gM27ethuNdIofdP2EOL/+tKqrb1XAQlFqUU1cjxIjPRJ43XTQUqWLaNejsQWlvIbD9dzjfE0Np/sYyphYlolFFJr+Bq1jtWHGiNK66ZdDqtj9RGnDYaf3Ln/G2t6MpHI1EriDpyw9gXnMP0lBeQhkXRGLMiBovCAKZj/8EfZEcf32EkaRMisfV2oN9/U8J+n30WcQwWqK0nXxXGf+79hhen5+aph5mq7oJ9PUhN5kxrFiFLObKReo0xWP7L2xIY9APdW40+6xTGc+8qeKzeM88nW5ZDA1vv8O6nTW0hBRqgUGsseuBLksD+oR0JFIpK9fciCrGREPFTmrPHBpRR7bqU3vZ986fo94LBoN0tdYPO95h62bjSz9h++tP09fdwfnTB3DYrBiSM4fc/3oiLW8cC+/6NtOW3YtkmDyGVmckvWAilSd24ey7sor0/4v41LCPkud/jdFnXmWr5ShTs+dhUhtIzBQ7gDnt4gfeUityks/5WthUvx2718FnRt0MgEyuIKtoCuWHtgAwfs7NJOeMZu/bL5A3cQ727k5qzxwiVqekZEYG+7ef59ie40gFG6vvuxG/pwdFrB6topq6429i75hMdU0cOdnd+NwG3C4du9+vomR6Bgd2iiup5avHIJdLkcoknDrcSM3Zdg7tjlA4Tx9tApSMu+lWmg25QD2njjRy6kgjd90/5ZJywZpV38ffXoegEMNNgkYsaHMffoOgx0nQHqkFCTp7CPp9CNLojzzrf38BgSByo5GAyxXuNTEQgkSC8eZb6Vz/Ft5WsRBQlS16D5qCQjQFhcSWTMJ9ejeBireQGgfz8pUp6WjyMglYI5LOCq0Pux98DvA3lWFr70EuuJHLYGFSNztqfVQ39WJurWZs604AUh/+T6TawZLklwNFYiLmNfdccjXfXwgYO3U6+kWLyTEYmaHTsfVoA0FBgnf8VNKPbaf03Hm8ljYMK1YRN206EtXwHfauFfqs7aTmisZNLpcxeeGt7F3/Aoe3vApA+qiJyOSRosX2pvN0WxrJmzAHQRA4tu2fADj7elDHiInwk7vfpvLYDubc8kC4K1o/AoEA2//5DF63KKtxas87NNecJt6cSlZRtOTFRwVTSnZYOG84jJ66iIZzx2mrP0fW6Ovbfe//F3wqPAUAiSaeNWPvJghsOC8mNFWaD7MKgkgkUvZbxUToSUtp1KpHZ4xQVtNGTUBnTOam+35IQckNmFJz8HndNJ8/Q+G4JNRaOY62t9HJtyGRONn40k/Y+sovUSAanvb6o+jk2+lsPI5KUg3AmePNrH32IJZmMbGVkWMgJSOexJQ4FtxUgFQm4czxZiTS6Obx3vFzsLTaidWpMIWUSNtbL50ck8QYkWdPCv/fL4cRsDYRtHehmHxbZGefB8fbPybQE13drTAnoEhMRJDJRHZWYOi4tHHlzehuWBg6kYAyPdobkMXrkUr7QKZAiB06xi/RJRPobiXoceDY8CRyr5hPcNvluPb8jZ66C6gFJ7KMCWj9oqE/VN7GnC4xaW6+a024GPBqoV9w4yAK84chSCTk/eFPJH35K6iyc8K1Jw/eMoaFJWlMWjYHgJtOieq3msKiIanE1xoetxO3s48YfSTEl5o7loV3PQzA4S2vUrrnnagxBza8zPEdb7D9n89E0TQtDWLozu/zUnlsR9R7A2FpqMTe08mUxWswpebQWCV+JnNv/WoU7fuThjhjEhKpjO72S2ug/RsiPjVGAUCvimdWylSOW07h9LmitvXHNANSgQ6XlfGmYno8NrrdEbdRH2rekVk4mRhdNA8+LX88mjgDNaX7UKrkLFqVF9723l8eB8QiHQEvTn8RwWDk0WnUnYiiChFMnZsVxTiSyaWkZIiTSrxBQ/HEyOTRXN9Na3Mvyek6brlHpMP29V6+1s6HvQBZ6mhivvAs2rvFEFKgswFP6XtDjg16HNhffwTPsbdwH30Tf8fgFpbKNDFZrUhMGnI1HOhsQGJIG5aWKIlPIujsoe8vX8ffXIFUo0CRkoKt3oezqQubQ4Pc6UCiS0Ti6EQpg/0n6jF5elAuWo7+xsVXJfZ2JZAolYPuZ3JhAncvHoUyI5OgJNLnodr/0UyOfVYx3BkbH01SMKXkoFSLXlTbgIndZrXgtPcQq0+g29LExpceD29rDWkJdbbUhd+zNFYPOmdTTRkyuYLMwknhIrU4U3LYy/ikQiKRojMm0XrhbLiG4d+4OD5VRgFggnks/qCfP556CW/AR/H0pai0cYyeHmLLeH0UGvJZkDEXgKa+SNFVrN7MqgeeYNqyewcdVyKRkpieH46pCoHhi15c/jx6vAtRxhUyYd6t+D02JIh5hNg4JTcsL2DSzMFx1unzcpDKJBSOTWLi9HRuXjOe9Gw9Jw424HJ4ScvSI1dIUapk9NnEVotu19BSFJeCNGkUEnMWgkKDRKtHohd/yEGva8j9A9Zm8HvxnNyE5/g7ON58bNC+sVOnY7p9NWnf/d6g8cFAAH9nPVJDxqBt/ZDE9XsQogGN+fyzaMeOh0CQrkpwyONQuXoRYs0Q8DMtS0GCx4qEIPq8ixcUfhwQJBIM3/sx27MXcsA4nu3llyfdcqXo7RK9vYGeQj8W3vVtdKYUejtbsIWMR0dIL2jOLV8JJWgFdMZkUnPH0VxTRiDgx9JQhSAI5I2fQ7elEb8vmhZqbatHn5COVCYP90xQfYI9hIHQmVPp6WimdO+7H/elfCrwqckp9CNHl4lSqqCmp44TllKmzlwernhMLpzIDkcZ84yFpMaIoYHGvhbGmCKJP7V2+KpPfUI6tWcO4ezrpiPEsDCklqCQOzEkZtB8vgxbj4OgR0EQBbNW3IskKKqqSoVegsRwz4PThz2+KTGGL//HbARBTL6mZMTjcfvC+kvp2WLSOSZWSV+vm03rTtNQa+Wr/z13xCtk7Zpfg1SGRB19n5pbHsP53q8JWIeWFwlYQ+51MEJx9ZzZjnJChKopVasxLLvpw0PF8R214HEgTSkccjuInos0uQB/S6h5kFSGcdUtxEyaTHvZaXzlSuJcHbi7suith6Wyk7QXpUMjqLOyRnL7HznM2Wl89Qf38qd3ztA0QF67pdNOn9NLXqrusr2bYDAYXtUOpFj2o7XuLEq1ljjD4PBXrD6BURPncWTr39n08k/InzgPqVSGRCpFqzMSq09g+RcfRaWNo7WugqaaUi5UHMVqaSTWkIg5LZfqU3votVrQm8UagkDAT3d7E7njZgGgiRVrjLS6j0d873Ixeuoi6s4cotvSeOmd/41Pn1GQSqT8dNajPLb/55xqP8PUpFDYKBhAM2kiljMV5MRlopapMamN1PXWX+KIEegTxfDIppeewO/3YkrJYeGdXwhvHz19CXver6L9lIVb7pmAOSkWr0dUG42PdzN5wdA1CwPx4cYlmXlG5i7Jx2n3oNGKiUGVRh7ViMTt8qFSX7qDHIAkZuiiQEGuFHWiyrcTDAQGhUT83c0glSHLnQF+D0FnL75zu6OMwnDwt1bhPr4eBAFZ2vDJW0GpRbPyezi3PYffNIrebiexOhXqnFycdi2UnyPO3U7Ly38XB7QeRV/kxKXVIruMYsePAykmLYfK23C6faiVMn7x2gmCjjZWjIEbb7r90gcIoa78MMe2rcPndRMTb+amL/1P1PZAIEBLbTkpuWOGZd1kFE7C7/NitTRQdWIXAHHGZCShUFdsyMNIzRuLOTWXk7veQiZXYkzJDnsBPe1NYaPQ29mK3+dFnyASCJKyipgw71Zyxgy/APokIVafQGbhZNqbz19653/j0xc+AlDLVJQkjqe88ywNtmaqu2t5dN9PefnMawCYNaKYWpFhFOes1Xj9w1dIDoQ+MYOMghL8of2yi8MIAAAgAElEQVQNSdGhEKlUxowb8lm4spCkVHElLleo0MYZSE2DrPyhRdwuBkEQKJ6YwuTZWeH3+pPN/eixOrkWkJqzwe/F3xrRpPdbavC1VhHouIBEl4x6/n2oF34NaUohgZ5Wgp6LnzsYDOB456f4G8uQ5c1EUF06pHDBfAuvvi/j1ecOc2RPHQDtrX3I5BLi1IBUSsKqBQA4Ks6gysj8yHMJl4u0UG+K5pBQYo/dwwz5LjrP7bqsWPaZg1vwecV8Ul93Ox6XI2q7vacTj9uBOW34cJpMriB/4lymLF4TXt0HfIN/AxKJlLzxs/G4HDhsVnTGZGLjE5BIpVGJ2f4cgzk1JzROQsGkG5ArL15k+UmCVmfEabMS8IuesMthY+/6F7H3fjQhvw8jGAyy792XqD1z6NI7f8T41HkK/RhvKmZP0wF+fuTpQdvUMjEJOsZYyJ6mA5yzVkeFkIaDVCpjxk1fYNqye6k6sYuMwsmD9lGqZIwqjmbA6MwpdLU1DNp3IKztTcTEGUb0Q5o8O5PikhT8vgCv//koPVYniSlXL3Ymy5oIchXODU+inHUviuKFON7+SXi7ouTm8GupKQsAf+s5ZBnDa0EFXaKUtTS1GNW8L43oOg7uOo8pIQav10/5qRamzMmiq8OOwaQl5xtP4u/tRSL1YtmwHQIgFXrCooXXE/6uBoJ2K7L0cZc3zucl2FFKrNBDc7ud4IcIXL1dbQT8XozJWQjC8Oswr8dNX3cHCpUmbAyaak5Ttn8TMfEmxs1eRWO1yKyL1V+ahSUIAuPmrOJ82QHyJ84bcp+EjFHh1zpTMhKpFFNKDjWl+0QKpwCW+iq0cYZPTbhoKGh1RoLBIA5bFzHxZk7s+BdNNaWY03IomLQgal+btR2tzjisJ3Yt4LL30lh1ksaqkySk5X2inu2n1ijk66OrohdlzGdr/c6o9woM+Whkao60nRiRUeiHRCId9EW5GBLS82muKYtqOD4QAb+f99c+iT4xg8V3f2eII0RDoZShUMrC7QoHFtFdDQSZEtWse/Gc2ox739pBk5+8aH74tcQs3ofzvafRrnkKSczQX9qgoyc8VhjAxBkOgUAQl8PL6PHJ6E1aPningsY6K13tdjLzjEiUSiRmM8FgAG2KDHujD5m3iWBfJ0Ls5XtilwPnpqcIOrrR3P6TIWsthkPF4a1UHXyPIpmZlzfHo9NISRQiPSmOffA6XW31xBmTmXfbV9HEirmjrtZ6zp85yKQFdyAIEpprTgNBpi5egzk9n3eee5TasoM4bFYcNisf/D1SxR1nGJm0h0KpZvVDvx7WGKk0sYyZsQyvx01SppgPyho9FUtDFVvWRirec8bOHPHz+CSin214eMtrzF/9TVpCfRYGynC31Jazf+PL+Dxi6K54xtLrVoPRrywLYLU0fqKMwqcyfAQgk8h4Yub3w//PCDXcyYiNNA6XS2SUJIyjtP0MvsCVsXhGgsR0sYhu2z9+Q0+HmMhtraugdO8GAn4/DpvoolrbRp7fAJHGGm/UcPpYE80N3dfkWuWjZqGc8VkAvDUR11U5Yw0SbaRDmEQdF65z8DVE91IYiKBDvK7+wrlLwe3yEgyKss9ZeUZidSq2rq/A6fBiGNAeVBAkmG+7g4SJoNJDoOv6JgmDPk/4XtwH/3FZY7tCn2us0I0CF2O97zFBfiRqe0y8md7OVkr3vovDZqW3s5UP/v5rak7txdJQTU9HCwc3/w2A+IQ0FEo1OnNKWOPow1CoRl7AdzHvBKB4xjImzLslXOyWUVBCcnZ0Z73s4mlDDf3UwJSSQ9boqbQ31VB9cne4CM/RK5I8gsEgJ3a9FW772dfdTvWpvdfk3JUndmGzWrD3dnFw81q8HneoB4WI3q62a3Kea4VPrVEAsW6hP9GcoDbx+Izv8a2JX4nap8CQjyfgpbFvcPGKzdPHd3Y/RpX10tISF4POlBxeSdVVHMHrcbPrzT9Scfh9OprP09cTSRr3S2qMFEtvK0apkrH5jTJ8vmvTWlASL7JWPMffBkB7929QjF08aD/FxJUIWj3+xrJhj9U/kUo0I+OrO+1ibFutlSNXSJk8OzNMuzUnR+cjFOOWorv/WXHclqcJdEcX3l0r+Nuq6Xv12wBIkwvxN53B33XxcOBA9FM/ZXiZKDuERrAzZtwsCpdHekpkF09DG6fnQsVR3n3hMTb/9WcEQ3GmnW/8nr3vvAhAyYI70MaJSXV94mBvRa5Uo9UZr2soTSqTi0J7IRROXogxOeu6ne+jgEQqZdLC1cgVKk7sfBMAbZyBpupS+no66Wqtx9bVxuRFd7H64afJGTMjXA9yNXDaezmx419sevkJzh7ZxoWKI9SdOUTbhUqUmlg0sfp/G4VrjXuLPsNv5v0UQRAwqvWoZdEx+xydWC9wvmdwMVaLvQ2nz8lZa3SxjsPr5Fs7vsfpjvJBY4aCIAhMWXQXSZmFNFWXYrNGJH2tlsYoo1B/7sSI7w3EJumzFuTicfvDvYuvFkK/R+D3IS+aH+UhRO0nCMjSx+OrLyXotou1CJZoBkfgMj2FcE9sjcimysqLuM3JaYMNi6CISH14K0Vhs6DPQ3CE5IGRwHVAZDspJtyEctY9AAQsl+4FDOD3+7D3dJI7diZxmljiJVbSYlRknN/MKEMkuZAUF4NeP3z4q6+7nZwxM8ifMDf8XmL6qEH73fzAEyz7/Pf5/Zun+fmrx0d0jVcCqUzOnFseYMm9/834uTd/4hP9I4FMriQlJA2iT0wnJt6Ex+1gx7rf0RYq4kvNHYtEIiFGb8bt7MPjvjqSR+8Aj6B/QXh6/0Za6soZVTKPWH3CJ84ofGpzCv2QCBIU0uFtW7xSh1GlZ1/TIQQEbkifHd7W5RJdx6YPeRGNfc34g3421X7AWNPIGtQDJGYWcGr3+iiZgPOn94c/dGNKNrVlB4kzJoWkfkf2Q0vJjEcqk1B91kLJzOGLw0aKgeEExZSL0yXlo2/Ae3Yn3sq9IFPi3vMXVIu/iTxLlNcIOrpBoUGQKS56nH70S4irNSH6rVrO5FmZxBs1wz4P9ZKHcG55Bs/JTciyJ+PY+As8hhSUq/5nyP0vBwFHNwFLDYrJt6EsWSWu3mVK/F0NjIQEvPWVXxIMBtBLfBRImggUTUToaYJe8L39QwLSZFyoke95nsKgQN4dj+B22mm7cA6lJoa88bOpPrmHiiMfDEoGp4+awKSFq+lzQ3u3k+xEDVKZnGAwyPFKcYIZifjdleLD+kf/PyA5q4gLFUdIyiwMT9KO3i6aqkuJN6eGpXP6q8X7rO2DWIiXg56OiHfbekFUsfW6nUilcgonL8TrdlF5bAdejxupTE57YzUJ6fkfqxH+1HsKI0G2LpNWh4U3qt6hzRFxCfuNQqOthbKOirAkRv9fgcv7YMypojTGqd1iWCYpszBsEAxJmWSPnkpfTwf73nkxSlbgUpDLpZiTYmiqvzZ5BQD18u+gXvptJKqLd6WSmjKRGNPxnT9K0CZWefvORWKtAVvHsLURQ2Fg+KgfU+ZkkT96+MSpLHMi8sK5QBDHWz8GjxNPa3TIL+Doxlu5b8TX0Q/fBVHDR5Yl9lEWBAkSYzqBzkvnf9xOezg2bHCL3qGk+TSCM6JbVZwxihMuMcSpEIKY9QYyCkqYsvizjJu9Ek2snqJpi1lw50PED2h879r7NxybfoU2eSy/3e7l78dlyBPHA9DcGaGpdvf9W7rhcpAeevajpy2hZMFqUnLFupqutnryJswJ7xcbSuTvfOP3VBz54IrP19PZgkKlISZkZCQhKRqJTCaqKGQUEAj4efP3/8W6px9m5xu/D3stHxf+TxiFrLiIpT/edir82uoSJ1mru5s/lr7MD/b9lD+c+nM4bBTk8lZh+sQ0DEliuEqp1jLpxjuZuuRubrrvMW5Y/Q3SR0UauF+ulK85MZa25t5wF7irhSxtDLKM8SPbN2sS/rbqcGLa13w2HA8PdLcgiR9Z+1Gf18/Jw2KsXqkaWTFePxQTViIbNQcGeDn94n0BZy/2Vx7GtfMFArbLiwP76o4jxJqR6NPC70mNGfg7IhLSnS0XqCs/MmhsV6sYkpx/xzeQd4TCTX4feJ0oZ92DJD6ZFEknObLINfmaBock5QoV5tTougNv+Xb8jWVUbIsI25XVdtLe7eTFDZFjNFj6Lut+ryd6HR46eq5NTc31gkQiIWfMDGRyBSpNLAUlNwCQkJZH7gCGVZwhiWlL70GpiaWh8uQVn6/PaiFWnxAu9EsfNYH0UROYsVzsMWJOzQkZisgC1NIwWHvqo8RVG4X169ezcuVKRo8ezSuvvBK1zel08vDDD7No0SKWLl3Kjh07rvZ0V4T8+Ah99VDrsfCPvcvVPcgbKO88x3GL2Kqx3tbISctpvAEfr539F7VD5CUGQiKRsmjNf7Liyz9i4V3fJkZnJLt4GjE6IzK5EoVKww2rvwlE5L5HClNiDF6P/5oVsl0O5EXzEeISCPZPuF6nqMTq8xC0tSOJH5ky6JmTLfT1usnMNQyq7L4UJHFm1PPvQzU/0lwlaO8iGAjg3Pzr8HvDyXgMhaDHib+pHFlWCc01p8MduiTGDPA6w57R8e3rOPzeWhynosUELTXiAkOnkhN02ZAOoPhKjZlITJko28q4L3ZX5PpG4IEEAwEQRHpvfE8Vy/KDPGTcSVWdhb9tOYfF6uALy0T66NPrTrH9aD02x/X3GJra+7DahhZqDAaDPPnqcb77xwMcO3f1CdqPCqbUXEoWrI5q2gNiPi1r9FQSM0Zh7x5eBw3ERcPON36P1zOYOm63WdHGGSmcspBxc1YxetoSZq74UpjdJZXJWXn/j1n98K9Z/sVHiTenYmmoBETKssvx0bcSvWqjUFRUxG9+8xtWrFgxaNuf//xnYmJi2Lp1K8899xyPPvoodvvl93G9WqTFpvDk7Mf4XNGdtDs72XJhOw22Zhr7minQ51107Atla3ml4p/saz7Er479gU5nFzsa9hL4cIXSAGjjDMTqhw6HmNNykUikgzyFQMCPvbdr2BixMUGkIHZ+DCtDiSYe5eRbo97zt1bR99JXIBi8pKcQCAQ5d7qV00caSUqLY/nqsVd8LfL8mahv+m7oGirxnvmAQEcdyhlrxHN1j8wo+Fur6PvL1yDgI5BcxN53XhTlTXxeJAaR9ePvrKf98Dt0tdUTBFr2vQGIE+CRd1/gbOkBTFIfvu1iRzhZaiQGLzFnIcuOcNzX++ciScwj0BlhNQV62nAffoOAK/qH7+3tEDWopHIyhWamufaSE6xHZz1LTVMP04uTmDs+hWXTRA/4N38/wcubrn/Xtf/582G++8f9g94PBIOsfb+SllBY671DF188fZIgkUjInzAHhWro3iX9yegPV5YPRMWRrbTVV3Kh4iggJpRP7V5PW/05nLZuNHF6BEFC0ZQbiTMMLjpUaWKRSKTE6hNIzh5NV1s9Xo+bra/9ivdf+eVl31NfdwcVh7de9rh+XLVRGDVqFHl5eUNW/23evJk777wTgKysLMaMGcPu3buv9pRXhBiFlokJ4mT07vkt/PzI0/R57cxNmxHe55EpD2FSi2yYm3OXMSlBDK8cbYu4jz888HPeqHqHiq7KK7oOQZCg0sYNMgpbX/0VG178ETvf+ANejwu/P7quIj7UcKe76+Nxz2WZYlWzYtKtoFDjb4ncvzTh4u1Vz59rZ/vGc9h63YOqwa8E/UbIteN53AdeQ9AakI+5EZTaERsFX2vk+q1+8bvr93t547f/yfubXsOPgHvv3zh/aCMCQSQEOerSsPXVX1J75iDnq8TajVEKN8FeMW8kGfAcBKkcefYkVDd8hXOF97O9Jwu3Nhl/ZwPBUF9tT8VOPCc34Nr5Ynic0+3j938VPerzMRNRCH7iPGK+Is1/AZfHT06yWN1++/xIyOlkdQfbjl2/Wg67S8wF+QPBQQuX80297DzRhCDAqllZ1DT30tV7bQouP27E6ETG2KaXnyAQ+tyCwUBU3+f+PEHVyT2c2vMOW1/7NWePbmP3W88RCPjRxg3N7hsKCen5BAOBUCEjOPvEELfP6+HkrrepPLHrouQCr9vJttefpqb08vNr4fu54pEjQHNzM6mpkWKy5ORkWluvD9d8JFBIFZQkRFz8BLWJ0YYCbsldzt2Fq0mPTeVr477A4swbuDFjHl8aczcPjr+PL47+LI9MeQijKvLh7m48cMXXoY7R4QqFj1ovnGP7P39Ld3sTKm0cloZK3vz9d9my9smoMXK5FJ1eTfeAJKPX42f9a6fYvaXqurJQAAS5ipgvPY+iZCWS2AT8LeLKVLXggQGS2EPDPiDk0K8EezWQaPUI8oh6qCx9nJggjk8m0N1MMBAgeKliRY/4HKWLHuboB/+M2mSzttNXsIiAo4dmn5xEqY/cGPGH39XWwJH3/45aCLAsSU7CjAh7SxJrQjnzHtTL/iP8njx/JhljxVxSrZAJXie+6oMEPY5wFzp/45mwxlSb1YEuIFKP19Un4g8KSL2id50lE8MY2SGjIBEEJg7Q23p1a+U1+x74/AH6nBHab1N7xMO3dEcvTM7WixPk09+czfg88XpqmqP7mH9aoY0X78ft7MNhEyfo6pN72fTSE7id4jPpC1HQeztbOXdsO4akDGatvI9gKP/Xryo7EphScpBIpFGFc8FgkDMHNnPu2HZO7PhXuMHRUKg8vhOX3cbsD4XDLgeXpKTeeuutNDcP3bVo//79SKWXljb4JOHuwtUsy7qRlJiI7PCizPnh10naRG7OXRb+v9hYEH79+dGf5d3z7yGXyCnrrOBsVxWFhug+viOBOkZHV1sDfp+XXf/6Q/j9Gcs/j6WhinPHtmPrasPjckS5tXqjhqpyC6mZ8RSOS+LEoQaa67tpru8mJUNHXtHIpA+uFP20U0mcGV/nhdDrS6/8rZ3iJJKVp6V016uULLgDTezI6hqGgyavBHvFARRT7kBRLEqSSE2ZeCv34Xj3ZwSszUhnfwFJStEQHfogYO9GiDHSYnPgsvdiTs2Nqh52xqUh16XgsfdhlrnJW3Anpr2vc94JbU4/ZqkPmSEXWfIo+qP5gkaHYsyNg86VoNdg0qnYb9VSGJ+Ma99a2PmCuFGmAJ8HX3MF8qwSOju6maKsweKPpcUfj1ufg6ZbvC6TxIZAgOQBbVq/fttYXtlaxc7jooGxWJ0kGi7exhXESd/j9ePy+Dl61sLYXCPJxkiV9EsbKzhY3sYL353PP7ZVs/tUZA44UNbKLXMiXtG5eitp5hhiNQrUShkKmYTqxh6mFF7f7+NHAZ0xGVNKDh3N57H3dBKjM9JYXYrf76W3sxVTag42azv5E+eRN24WKm1c+DebM3YGNaX70OpGbhRkcgXpBSVcqIgQG+w9HXQ012JMycbR00Vd+eEo0spANFSexJyWS3xC2pDbR3QNl9rhrbfeuuKDp6Sk0NTUhCEke9zS0sK0aZdfLm80XstmHrGkc2UaOmbzWKbnj8Xr9/Lghkc53HGUOaGOb5eD0SXTef/1U5zcES2nkDe6kOKSEjLz89m09ndIg32YzZFJNyE5lrrqTnZurqTsWDMIkJoRTyAQ5OCO80yenoXP6+StF37Owtu/RGL6xcM6V4rOxFR6asX4qSkzE1nMxWmtfb1uMnIMjBtrY+/GUmLiYlh4+5eQXMGCwuNy4nL0wew1xJUsQZ0VyU/YcopoP7ONQJvI3tj+7l+xB6U8+MSLg87V4rMh1RnxODqQyRV85sHv84cfRKp4vS4rwtTb4O2/Meq2b5I0diaxagm+9X+iDS25o8eTsuRe5IZk7IpHCAZ8xCQMX9U9qSiRPSeb0M4twXZ0Y/h9Vf5UXBV7cVnq0fi7GbX/byCDyuTlfHZaIXnFRTT/9ftIzNnI2mtJ03hISIgWR5xYYA4bhQ67lzEFF/88AJ5/+zTv7jlPXpqO6sYeqltsjMkx0tJp565FBRwsF0Nilc22cFgqKzmOVHMMW440cO9NxaiU4vTR3OlgUmEiZrN43sIsA1uPNmDUa/js4oLLJhVcLfqv41ph6We/witPPYIUJ7o4GR3NopEOeKzY2qvwed2kZ+eQWxi9QFx4693kFBaTW3B5dQfTb1wRZRT2vP0cvV3tjJtxI1JZAaf2bUUXJ0PxIXHN3q52ejpbmLPis1c1Z17X4rWlS5fy+uuvM3bsWOrq6jh9+jRPPfXUpQd+CJ2dfdeMinmtMNE0lr3Nh2hoaUclu7xG7frUYlLzxlF9WvzgMwon4ei10mvzg81GUCr+6N/44xPMv+MbJIaULOcvKSAtW09rUy/7t4lfzEkzM0jL0rP+tVPs2lqJSnaBrrYmdr37Ojes/sY1vOMIPLLI5NflEKJ4+QPh9bqpOGmh6YKV0ROSaawV2TrnTuwHiYoJ824Z9hz23i5s1naSMiOemsNm5d0XHgNEyu/YWStxnT6HJk5P9uip+JVirqHFJyMQBHtQNATH9u4ia3Qk6Rtw9uKsP4ssfSxtjfXEGZLo6IyER+IT0rA0NdLd2YlCqUGaNIaOjj5InUr6chnJci3q1AK6/UC7DfQiE8jZPjxTJCcpli0uH/U+EwMDaJZuLz6/Fn/NWTgphuScQQUly29FkMqxAdp7nsFvbcK18Rd8bYGJ9g+d54ZJ6aQbNXzv+YMcPdOCQSPDEKtCqRje6O44Kia8q0ONgY5WtHG0ImQILkQq53/5yjEEAX75tZkY4lSU13Wxr7SZn750iPgYBZZuJ1abG51GFr6u8TkGSqs7+MfWc5yubifZoOGuhfkfiXEwm2MHPZ+rhd8nBwRam5pxe4Sw/Pau9a8QDAbRmVIwpI8d8ryGtNB35zIQlEW86AWfeYjt/3wGAEWMiRidiUDgPcpPnCA1N5qw0Xy+OrRfEp2dfVdsGK46p7Bhwwbmzp3Le++9xzPPPMPcuXOprhYv7r777qO3t5dFixbxwAMP8PjjjxMT8+lo4XcpFOjz8AV8tDosl955CIybvTL8evqyz4WbrkN0R6s9b/+JiiMf4PN6UKnlJKbEMX5KGrMX5ZGQEkv+6ARSMuJJTIll//YaDm4TDY3/CmUgAgE/5YffD7dwHAqyFJFOJ9GnIQgSfF7PoFh2IBDgzd/9F6W7/oox9gTp6fZwy0eAhovERXu72tj00k/Y9a8/RMVW+ytCQSwcO/rBPyjbv5HD772CvadTTEDLFJxwaTjljoRQSve+E6WG6dr6e7GRkCChu6Ml3FjGEOrhHW9Koa3+HA2VJ8kdPyuqAlyeVYI6NWKoRoriLD0xajl/L1cgTS5Ec+tjKCau5LBiGpZAHGabeG8fKBfzkuxuBGmkjkOi0SENtVM1MJjKLAgC8TFKxmQZ2HWymR+8cIi17w9fAOUPBHB7/chCSgDzJ4jHnjchhXsWj6K2JXpyWzo1A0OcuPAZlS5OWCerO9h5spnyOjGfkKCPPO/JhQnoY8Wcz5naLj441khV49CFl/VtNp5ZdwqP99roel0PSGVy1No47L1dtNRVIJMriU9IIxgMkJCWyw2rv4lUeu3W14IgULLgDrKKp2FOy2XRmu+gT0wnKbMQU2oOMrmC1rrBbDNbqEI7dog2rZeDq76TFStWDElHBdBoNPz2t7+92lN8IpEQauTz+rm3+WzBbaTFpiC5hBrlQMQZEll5/4/xuJyDXEupVEbe+Dl4XHbqzx2ndM87yJVqklOWhvcZOymVsZMiSfysfBNtzTZkgqiz1Nlcy4ldb5M1ZhF6o5ZThxuRyiSMKRm+psDSUMWOdb8L/7/y/h+HZZ4HQhKfRMznfk8w4CMYDPDO8/9DQlo+U5Z8AaVK/Ep1NNcBIJdY8DstHNosejZTl9yNo7eLsgObcdl7UX2oPWpN6T6OfvA6ICBXqKgrP4whKZOzR7aGi4jijEn0doqEBalMgd/noaezRdTMN2RCd0RLJl8roaqvh/L9GxmdloIsdxr+EPPoQkCD22EjMUOc5Bfc+RABv48LZ49SV34YgFEl84d9XpcDjUrOHfNz+cvmszQseIACsx6pOZtTLx1G4Y+jUN5CAIHNLWaWzhwcDxbUOgRV7EXrHCaOMnEsJH+xv6yVXoeHueNS0KrlbDxQx1dWFhOnVdDS6cDrC3D/ytHkpsRh0qkZnWVgQr4Jp9vHK++Lz+fJr86gzeqgKDPyHZBJJXznrgn86h/RRj1RHwllxGoUPPX1Wew73YLD7WPdjmo2H6qntKaThZPSwgYG4Nm3yrB0O7nQZiM/7eryTNcTMfEmejtbCfh9mFNzGTd3FZ3NtaSPmjgsnfVqMFADy5CUweK7/yv8vzEle0j13L7uTuQK1WUp6A6F/xMVzdcDxhB1td7WyJNHf8ufSv962cfQxOqjpA0GYtLC1Uxf/jnijOIqtnOYlbvDZsXttJORI+ZtFHIXnkAiMnUGlce28+af1+F2+di/vYY971cNeYx+dITaFWaGmgtZGqtpqS0PT5ADIahikGjisVnb8bqdNNWU8tdnNmO39VF9ag8HNr025DnS8sZhDPWcePfFHw3yMPo9iMk33knm6Kl0W5rY8/afaKw6hUyuZNSkG5h/+9cBccJe9ZXHAejpaKG7vZnmYPQPNCPYQ5rMw9njO2je+gK+c6KonnLmPTRbe9AnppNRKOo4SWVy5Eo1pgHVxUMlqS8HG8+/z5vVGzjdUc600YmolVL2lIq02T6nlwZLH7UB8TtQ7kll9Y2F3DpncC5IEAQkpkz8HXW4T24g0DNYRG1GcRL33VTE5FCCt+x8F8++XcYv/36C8jorO0+KPR4a2sRwRkZCDAl6DRKJwOTCBGRSCbEaBV9cVsgjd5dgjlczJtuI9EN089FZgxOn5vjBzaNmjU1m0eR0JuSbKa3pZPOhep5/50zUPv1Mpo5r1DPkesGUmou1rYHu9iZi9GbiTSnkjpt1XQzCpWBOzaWnowWPSyRJAHjcTmrLDqCNN3t6SUMAACAASURBVF21btKnXhDv44JcEv3oKrtrrnl3MEGQsPRzj7B/w0s0VZdibW8FIquAvu52Nr70E/QJaSy+57vced94tq79FxJFMpaeHOJkNtTSs1haIiGHi12jvdeKUhPL1KX30Hy+jI6m82G+c0bh5CFrUawDOs6ppWVseGFw0Uxm0RQEiYSsosnIlWoSM0aRkJaHpbEae29XuAFKwO+ns7mWvPFzyB03k5rSffj9Xvx2L7NXfZmUASKC9z7yFNUdbXT6bQhyOaV736V077tR5403JaN02RitdNHik9Pik2PY/RIAElMmPZ1bySicNOh56IxJSGVyRoUkEK4UrXYLm+pE3Zxt9bv55ZwfMa0okf1lrQhAa5eDIDB67kJery1ifEEyi8YM39xHasrEc3Ijns4GvBU70a7+WZQQoSAIzBqbzMwxSTTOzOLAmVbeOxTxLI6ctdBj99Bo6UMuk5BkHHpCmzP+0hXqJaPMHK9s51cPzqS1y4FaOfxUcu/iUQSDQWx2D1VNPfT0uXn+3XKykiIGt806fHHYJwEJablUHH4fiNQufFwwp+UBQQ5uXktL7Rnm3PIAZ49uw+/zojMkXXL8pfBvo3AVSNIk0OqwsHrUzayrXE+vx4ZOefVtMwdCEAQKJi3A0lDFtjdeZN4dDwFgs1rY9PITgCjP7fd5kUlCqy1BAwi4AvnEyA5TeSrSD8FucxMTN3Ri3N7biTZWj0QiwZSaG1UAY21rwJicOWhMl6UBiVSG3y9BIYkUjnkCSeQV51BfsR9NbHxUDkUQJIybczMf/P0pqk/uCUsz93Q24/N6wr2AdabI5FSjtPGrXY/y01k/QCPXsOnCHt6qEGUnsvUB0gekdvLGzyY1dyzG5CxkQT8o1Oj/+gQ9XjfO2ADVPi0JbW143U7iTYOrsQVBwh3funxCxIdxtE2USU/QmLA4Omjqa2HO+BR2nmxmX5kY/pIIAnPGpbCg5NIUQmlCpPo+aOug75WH0Sz7DzBH0xMFQSA9IQZDXCYKmYT/x955B0ZZ33/89Ty3V5JL7rJJAgkJgQCBBJAlQwUcgLRV3FqtWmf156i2VmuHo61VO+ywjlattrZKFRVRQVkywg4rkE32uKzLJbee3x9PcknMJgkBfV7/kLtnfe/h7vt5vp/x/ny+t4Qml5eSKmeg3iAsSNdtBTAYbl4+kdqGFkKD9F3cQT1hMWq5Y9VkcorreOqNPdz7B/l7daSwowCsvPbMNgq2mI7VmzlkdLuk2WMSiYhLoSxfXnXt3/weDTVlJE6dR9rsC/s5un8U99EQeHDG3Txz7s+INMrL9XJnJc2e5j4lME4FW/RY0mZfRHlRLjVtImxVJbKrpz04WlGUE6iyDA2XA022mBQkSaS8oMMoFOX13pOhucGBsa3BS9qcixA6TRoVRXJgy9lQS11VSeCa5fmHCbHH4JW6uhTc4jymzFuKPSaRpKnz+Srtwd1juzeQf2g7AA218swe1B74jYxnyvwVLL/5Z/z7+Hu4/R62lcmB9NzaDimF/GgNTeemEzlrPpfe9gTTF19GZEIqGp0BQW9GEFXYEtNpcLkotKVTXFXF7s/+3TaOgek2nQrlzkrCDTbumfZ9AE42lTE2KohbV0wiI9lOXLiZu78zGY16YD9DVcxXZNzdzXjyugv1tWPSa7h0/jieu3s+1y7t2psh2ja0hA+dRtWlrmEgjIsOYkJc97hBUmwwecMo9jgSqDsVS5qCRtcoCILA7ItvCLg9G2rK0OqMTFv4rSG7O0ExCkNCp9KiV+uJNMlGobiphAc2/5R3T3zQz5GDZ0yK/DRYdVLO7HJUFKPW6lh0+d3oDCbysr8MVFzOPm8K375+OiuvnoGoi0bwFUOb4usX645T2ZZd0tzoCLQlrK0optFRGej6FRoxhotueIRv3fkrQsJjqSiUs1k+eOlxPn7tabweN5//5w801FYQFBpNs3cCFlsKeutcGjzzsEcFY7JYWbz6Bz0Wqqk1WmKTZBmR4rbGQ421FYAQ0LIXRVkvxqWWAoq12dVyb93ihlKSQxJ5Yu5PmBM1gz0NR/l36y4qvfU9usciE1Lx+3zkHezQ7kmbfSFhUd17ag8Xla5q7EYbQVoLZo2JkqYyJEliRqqdO741mZ/eOJMpiQN3RQgaHarIZERrDIZLfogYOgZ//cAUAjpP4I9cl8lNFw+8Z/lwoVaJPHCl/D1WdUpPXZI5hur6FvYe71t4brSZfdH1mILDMIeMrvsI5JTs2RddH2iTGj9xxrBlQCnuo2EgWBtEiC6YzW3SF1tKd/Dt8cv7OWpw6I0WLFYbB7a8jz0miZqyfKz2WNQaLfETZ3J8zxcYzCFtwlrWwBckLiWdwoNrGZekYtrcdD586zM+/udfMWjK8XlbUWt0XPK9n7J3438BurRdbP/yR8alkLPnczzu1kBguN04AehMdnyShtRzziUyNoj/vLqHmPj+M0nmrriJ7G0fcmj7Orb870W8Xjem4FBU6q6y2qVO2S0VbrRR5aqhvrUBh6ue82IXEKyzcEH8osAKYnfFfsZ06tPdTkRcMqkzl3Bk53p0RgvnX3HvsP+4jzty2VN5kISgMYQb7VQ1V5MckoggCMSao9lflc2JujySrUlcNaHv5ka9YVj+ECC7uDzBEQNuG9o5fjAuuncXZ4u3lQZ3YyC7brgRBIFn7piLJEkUVTbh9fqZnmxHrRLJLa0nI2Vo6ZQjSdyEjMDT+ZlC2pyLiRor1z0NF4pRGAYEQWBC6Hi2l8lVvlpxcL0CBoo52Eqjo5pP35T93dMWyROLtS1nuvDwTmwxiV2eGKbOOYfCg2uJiXGBrwIjnyMJfkRtLPYYM+WFR6k6eYKG2grGTprFmOT0bteNSZrM0azPOLF/c+C9/EM7An+rdJFADeYgHQajlqu/P2vAhUoTZy3F63FzbPcGQH6i74zP7+NIrZw1Ndk2kc+KNlHQIE+EMWbZzRRutPHsgl/wwv6Xyanrvd/22EkzObJzPWGR8cNqEPLqC8iq2M/+qmzqWuvZVNKxrX1yjbFEcdRxnGaviypXDd9KugS9WtfLGXunc82EGByJt2Avkq8fnSfAYtBgteg4L6Pv2MV7eev44uRWrkhZxfyY2X3ue6q01zB0jkXYQ/RU1Z3ZvRjORIyWEIyW7r/ZoaC4j4aJKZ3adnr93hERqMtctJzI+AnEjp9KcsaigK/eEiK7rzzuloBOezsGUxCm4DDqq09yYv8WtHojIWNvpN49j7krb0al1lKcsw93i7NX/7otehyR8RM4sLmj4Utxzl7UWh2rbn8Kp0tOawy1yS6KwVSuiioV6QsuRdNWst/uUgL5Pv5m9x/54qQcmIy3yBPagWo5wBZl6pAA0aq0hOqtNLT2Xs1qsYYzb+XNzFhy5YDHNxA2Fm/hi5NbAx37Osuxx5jlexpj6hrQPlw79O5aYkgUSD5ay/pvytL+hH7ROd2TBTqT3dZgamf54HqJDxV7iIHC8sbT0hdCoW8UozBMTLFN4pxIOb+/xddKg3v4+x7EJ09mwbdvZ+7ym5i2YFUgRbRzBWP02O49pUMj4zl5fB+luQeJHT+VuHERtDR7aGrwEBE3nqJjuwF61HpvZ/Yl3w1M3MFttRPjp8o69NXlTVhtRlQDDJj2xDkXXsuYlOmMTevQxsqtK6CoUdbdsepCsLc9dW8vyyJYZ8Gi7RosDdJaaHA39mmQYxInD0swrjPFjR1Lg/kxs/le2jWB12OD5USAWItsHJaPW4paUFHQ0H+znf5QJ0wHnYnaz98c0GqhPxwtddS0tajNqy8IdCYcCH7JP6j9v4o92EB1fQs/+N0WqgexYmh1+/jgywJa+6iI3n64nDc/HXkl4a8LilEYJgRB4NqJl3NX+s1Ahx/8dKDVG9HqTRiDQrH0MLG3ayf5fV4Sp84j1C4/0Tuqm0lf0NE8Jyis9xxnrc7AsuseJiXzAi645gEuvvFR0uYup9npprKsEVvE0LJZoselMefiGxDFDr2e/AY5w+iead/nnum3EmOKCsQL4kO6u0GCdUH4JB9O7+lLb3S1uYPaq9m1ogajxkhqaDLnxZ0beD/GHMVj5zzA0vjFxFiiKWoYeu8DQWtAN+tyWgqzaX7nUaTWoTWwynHIrrcl8XJ9xiPbnhhwJt3fD7/FI9ueoLSpHEmS2FKynSb3wMejVnesLr/Y37Mqc0/sPV7Ff7/IY/3O7ka21ePjSEEtf33vMJ9kFVNQfvq7mJ2NKDGFYSa2zV2QX1/IBOvg1BGHQuKUORjMwT1eb1zaOegMZvRGM1Z7DO5W+aly3TuHuPzGDFZ+/5c01VUHMo96o7ZGYsf2YOImtqLVWvjHH77E1exBrRaZNG34Uzvz6wuJMIYz3tqRI/6DabdypDaHjLGpSF+Zc4K08gqgobURs2Zopf4DJbeuAIAV45axJvdDJtvkmMid6d317MON8oou3jKGbWU7ySrfS3r4ZNTiqf8MtRMWEBRkpGrtH/Hk7kQ7cXAFd4UNxWwr28WcqBnkOHIxaYxcELeQ9YVyo59qV22/QefjjrxAI6qPCzewLOE83jz2DhuKt/DoOfcPaBznTY/F7fWTnVdDcWUT728rYNp4G7H23h82Kh3NbNgjr9LW7SxmcUYs7Wvm9TuLeG9rAc2tHSuoLQfKAr0oFHpHWSkMM2atPBl9kP8JX5R0b104UkyZt7yLXkpnBEEkNmkKtmh5ctV2qj7d+lkuOoMZW3T/qZm11U78fokTR6rYuTkfV7Msurfo4hQi+shoGSxunxuv30tRYwkJQV0rfA1qPdPDp2Azdjdg7Uah3n36GrzsrTyIQa1n4Zh5/H7RU4y3JvZ7zMzI6Xj9Xl45/CY7yncPeQzmKYsQQ6Lx5m4f9LHv5a5jS8l2NhZv5ZjjBONDEjFqDDyYKfcSL3X2n/L6Xt46wvRWEoMTqHLVUNksp5ZWNFfi8g7MFWQLMXDtkhTGRQdzILeGdzfl8frHvcddWtxeHvrLdk6U1KMSBVpavbyxPgefX6LJ5eFfG08Q27YiThsXSkaynf251YoLaQAoRmEESAyWJ9hDNSPfN/dUmZwpu2FKCut4+bmtnCzoqC49WeDgv//YE1hRtONslIOAeceqKD/ZwNhkG9ffNXvYm/s8sPmn/Crr9zS4GwMZRgMhWCcbhbrWkTUK20p38rfs1/FLfg7WHCYtLBWNqB6wIOLY4Dhunyr3bihpGpyb0S/5WXPiQ/ZVdRQkCoKAOukcfGXH8DfJgoiS34fkae3tNAA0e1yBbK0cxwkcrXWktBm1KFMEAgIljaW4+1Dc9Ut+ihtLmGpPw2YIo6G1kSpXR71B7SDjDJ0bCFXWuXqdxLOOVgX+tlp0rJw/lu2HK3hj3RF2H6tEkuA7i5J4+vuz+f6KNCYnhlHb0MrJqtPfI/5sQzEKI8DNk68lRBeMdxiCfyPFvPOTuOrWmUxMj8Ld6gtUOns8Pj7/KIfK0sZu/aCdTfIk46hupt7hwh5pxmjSdjv3UKhqrsHr9wYmy84d8vrDqrcSrLWwvnAD3rZ2nJIk0eLte3IcDH7JzxtH/8PeygMcrD6M09NMamhy/wd+hUlhKYwNiqe0aXDtaU82lfJJ0ee8ePAflDtlUbzsimO4E+SsLW9bhXPLpldpeuVWJE/vQnP7qrLxS37CjbbA6iq5LXNKq9ISYQrnw4JPufeLH/d6jhqXA4/fQ5QpgiCtBUdrHXsrDwa2Dzb4nJESzpTEMC46J566JjcVjp5XGocLagkyali9OInbLk3jkjkJRIYaefuz4/x93THMBg3jooKwhxgw6tWkJ9lQq0Q27hm5PtZfFxSjMAJYtGaSQsZS09K7pMSZQLDVwIJlyYTZTThqmnHUNPO/N/bRWC9PJJ17K8uv3ViCO3LLw6OGN4sHuq+uBrNS0IhqLk26OKAzVOGs5Pm9f+GhLT/juCMXr9+L2+cZkguhvaIaCFSuJw/AZdQT0eYISp3l5NcX8knh5wMa19HaDqXbfVXZNHua+dnnz/HowReRzGH4qmQ1XW+OXFPi3v9hYH9JkrpcY2vpDiJNEWSEywbFrDERYezIZJsc1lEz0urrOVW0vFk2TJGmCILaVmoFDUWBVZOjdXBGIcZm4p7LpnLuVPn//Y31x6hraqXF7cXj9Qc+x+GCWiYmhLJ0Zhxjo4IQBYFbVnRk3s1MDe+SGh1k0jJrYjhfHqoI3IMv9pXw4xe393jf9x6v4liRo9v73wQUozBC2PShOFrr8fl7T5Xz+L00eUZ/OWu1GSnKreWtF3dRW+UM9FxoXxm042xqJTzKwtJVk5h2zhiie9CxGSrtGUcAk22pgTjBQBkbJOfh7yrfy7N7/kxxYwkev4fn9v6Ff+es4d4vfsz/cj/iWO0JHvvy6UFlyLh9Hj4s+JQwvRWbPpQqVw2JwQlY9ad2H6JMkTg9zfxm9x9Zk/thQAajtKm8x++Nx+9lR9luYsxRJATFsbtiPyebStvG5uaELRx/TRGS5AeVvILzHN6I5JWbIDW9+F1av5QlzRvdTRQ0FDEjYlpAxDHWHN0lUWFezDmBvxt7SbE+UHUIAYEoUzjBnf6vvpd2DaIg4mjp3hRoILRLcR8qcPDPT4/zwAvbuOv5TTgaZRdQQ7OH1ISuvT4SIoN49dElZCTbuWROQrdzxkVYaPX4aHTJ7rC/rztGWU0zzpbuK/rf//cgT//z9NZqnCkM2Sg8/vjjLFu2jBUrVnDFFVdw8GDH0rG6upobb7yRpUuXsmLFCvbv3z/Uy501hBnC8Ev+Lv7VzvglP49/+St+9uWvT/PIuqM3yBXY5iAdq66dxtzzkxCEjhgCyE9nzsZWTGYt41JsnLNwHCrV8D9TFDWcZKo9jafnP8atk28Y9PFhBnmi2HhyC42eJq6buDqwbWup3Bfik6LPeSvnHapdNV1qDPqisrmaR7b9kuLGEi5Nupi7pt3Cgti53Jh29aDH2E60qatr7EhtDq8d+Te/3PlbdpTv6bb/9rJdlDdXsmLcMuZFz6LUWc66gg2B7fsMAv76cvx15eBzox43A6mlEW/eLiSX7B7yZH+C1+/l5UOycUixJhGml4P2mRFdK2NthlBum/JdoKtRkPxe3Ec+p6RwF9vKdjHfmopBbQgY8BBdMFPtaQRrgwYdU2hHEAQuWySvwLKOVuJs8eL2+CmqaORwgbwCn9RDX4ewYAN3fGsyIebu1eJhbRXUtQ1dXWo19R2v124r4P1tBYHX7auTbxJD/lWfe+65vP/++7z33nvceuut3HvvvYFtzzzzDJmZmXz88cc8+uijPPDAA9+Y6H9q6HhEQWRTSc8ZITmOXBytdTi9zQPO0BgpJqZHET/eyvLrJmGPtCCKAkaztov7qLa6Ga/HH6hxGE6a3E6e3PkcJ+ryqXRVE2+JxawxnVI6ryiIgRTKq1K+zRTbJK5LXR1Qsm0nkCHjqup2jp7IrS/A6WnmipRvydlPhlAuT15JiC64/4N7oXO8JExv5XDNMfZXydXahY3dNY32VWYTYbSTZktlRuQ0wo02jjlOYNUHMzd6Jge9dbiR8B6Xs940k85HDI7EfWRjF+G8A9WHyXHIVdBxlhhSQ5P50cx7mR09o9s12wsEmzwdRsGbs43Wza9SuOVvAGQ4ZINjbtt3YluMJcxgpdpVc4p3By6cFc+i6XJCRLuAXoXDxbbscmLspn4lu79Ku1F4e2Nul2K3mk5G4p1Neby7KS/wurhy4EWoZTVOvL6z34gM2SgsWrQIjUZ+0kxPT6e8vBy/X74x69at44orrgAgMzMTrVbbZSXxdcaqD2F6+BR2le/BL/nZVrqLp3f9jmqX/JRT29JJS955an2eh4uwcDNbI9/nJ7t+GXjPZNbhbOpYKRTlyj/u9g5vw8lRx3FONpXy7J4/AZAYMjTl0nun38Yv5/6YuTGzEASBWVEZPDTzHrSiBps+lFmRGYwPGYdepQsYh/6obK5CJaiYE9V94jxVOldkp9snk1OXS4tPnqC+GoBu9bnJqctlcpucilpUc8vk68kIn8rqycvJjJiGW/LyhzGh1Bz8EEQVKls8qvhp+KsL8Ds6VkQn62WD82DmXahEFYIg9Bq7CdR+uDsKv5zZ63nHbiE3TDa+YdVyUkCUKYLbp97E5SlyQWSsOZqSptIhSclHt6m7XrYwEYNOxVufHae4sollM+MGfa7QIHn1cKTQwdpOq4GahhZKqp09NvrpL64gSRJZRys5Wujgxy/u4K3P+u5ueDYwrMVrb7zxBgsXLkQURRwOB5IkERraMYlERUVRXl7OlCnDp+h3JjPFNpGsin1sKvmSt3P+B8DavI+5YdKV1HdKmyxzVjA2uG9NmqHg8/uoa20IuFZO1OUTZ4llf1U2qWHJaERNQLen2lWLzRCKOUhHTWWHv70otxZbuBmTZfAibv3ReaVkM4SRGJwwpPP1FIfQiGqenPcTvJIPk9qIhMSvs35PZfPAVgoVzkpshjBUnSquh4Ml8Yswqg3EmqP5rHgTABOs4znqOE5efSHj2r4X1a4a/JKfuE4KsFGmCG5Muxq73UJFZT1T7Wnsr8rmxZgQvucN5f3ctaQZ1BhUEu8Xr+cqUcDklyipKyTSFEF8UO9d3tppLwL8uGAjG4o2c8u4FZS4KtgZYwU8WEUd6rpipFYngs7EpLCUwLGxlhjcfg+VzdUBefnBcu7UKKwWHenjbfxrg7y6mZkazuxJg+8wZjZ0CFVuP9TRzrTS4eLNT3d02ddq0WExaNh3opoL+9CLyi1p4IU1HenBB/NOfWV0ptCvUVi1ahWlpT2XnW/btg2VSv6RfPDBB7z//vu88cYbwztCICxsaBIKo8XcoGm8cuhN3s75HyH6ICbYkjhUeQy73UJroQuT1ojH58Hhr8VuH1hAtX2/SmcNP/ns1/xw3u2MC+37qem1ff/l/WOf8teVT1PnauDZPX/CoNbj8rZw3rh5zIjpEKE7UH+AeSEzcJocNNT5sIYY8Xr9lJU0MHdR4oDHORhcpR3G56qpKwkPH1gh3ODH0nX/uNAYDlfm4De0EGHuXbK5wHGS/dWHmBEzddg///fslwPg8XnIbpjNOGscZq2Ro9uP81bOf3n2oscAyGuVJ8Tk6Djsod3HEBEezI8X38HurP/xzImPWB9qIat0J1sB4sIAP8XRY5hwsohSVwWp0ZOwaluo/vhvhMy7DK0thsb9G3FXF6M2WVEFhWJJW4CgtmBQ6wOZdO/k/o9pnbJ6ok02oJgg6tDbu07UUzXjef0IOKhmsv3UMrQAoqPkQH5KvJWjhQ7uvTqzywT/Vfr6P5qfHsPmfSXUNLRgtegwGzV8trt7mur5M+MQBYG3P8vBZNFj1Pd8vdc+yenyWqtRjchv5HTSr1F49913+z3JJ598wrPPPsurr76KzSYvKa1W+am0trY2sFooKysjMnLwFr6mpumM7srUF3OiZ7K1dAfLxy6j0d3Edvcebl3zMGatiWBNEDq9lpzKfIrKqtCptIFUPo/fy5aS7fglP3OiZyBJEvHREVRVycv4tbkbcLjqWXPwExbHzedIbQ7nxy3ocQz7S+U0yk3HsthUIvd8cHllN0VZXRX/qf0Qi9aMRWPmv4c/5L+HPySoPoo4aTrHj1XSUOdC8kvYIs2B6w8nJ2srCNEFc8fUm4g2Rg7oGna7ZchjidRGssW1k7s+eJQfz/y/Xmsi/pW9FoBEc+KIfP52LhvboUO1JH4R6ws3suHIDibbJpJbLk9cqhZDtzF0vhdx8YuJqz5KVn1Bt/PXpC7AWbuGWq+LcG0EpR/8DW9+Fs3Hs1BFpeCrzANJgrYaj7qcAxgW3UyYPpSTTaVYtGYONVcRaekw2ulhU4C91OQdR6vr2sdC5zdjVBvIKsom1dRdqHGwfH/lJFpavbiaWnA19Vx/0d/34rvLUoi1GfnP57msXpyEXqvm+bf303l2uf3SNKYn2zmQW4Nfgv1HKkiK7R478vsldh4qZ97kKLYclF1o5TXNvLXuCNPG2wYd8xhORFE45YfpIccUNm7cyJNPPslLL71EbGxXkbJly5bx1ltvAZCVlUVLSwtpaWlDveRZxerkS/m/6bczKzIDm0Fu4+doraO4sYRgXRBxQbEUNpzkJ9ue5IP8jqb37xxfy3+Ov8c7J9Zy/6bHeGDzT6lzyS4ev+TnQJvE8fbyLJ7Y+SzvnviAZo+LF/a/zD+P/rfLGNrdKa8ffZuixpMsiJ3DZeNXEqS1cLj2GCfq8jk/bgErEpcFjmk1yAG2v7+7ns+zsxDEkalLAKhpcRBpDB9Uodpw0FlCo7y597hOZXMVE0NTmN8pRXOkaddQ+vOBVylqOEm1qwaT2ohRY+j32M4upusnXsFdQdOwasyUN1dy0i6nG8fW1+M92eH28JUdA58H3ewrMX/vb2gmnY/3+DYkt4t7p9/GXek3c/vUG5EE2NdWsDg3eiazEuaDSovf0d2boBJVTAgdz5GanGFJMAkyagm3GvvfsR8uyBzDX+5fyMzUCKYkhvGbO+byp/sWEG0zEWzSMi3ZhijKfa4Biit7NjL5ZQ04W7ykjQvl2bvmccOFE/D5Jd74JIe1Xxb2eMzZwJCNwsMPP4zH4+Huu+9m5cqVrFy5EodDDs7cd9997Ny5kyVLlvD444/zq1/9KiD3/E1BJapIDElAEARshq5B2mBdEPGWMXj8HlxeVyC4KEkS28uzmBWZ0eXp/90jHyNJEh/lf9qjJk11Sw2Hao6ytbSrf9Tp6QigfXv8ci5PvpSFY+Z2OXdSyFgm2ybyizk/AsCtl106xrowmvM0NOvrKGkeuHplbzR5nOTWFQQmifrWBoobS4ixDLxIbbiI7TR5vpT9OltLdlDaVM4vdjwTkOyWJIkKV3WXoq7TQbxlDLMi5S5fm0u2U+osD0iH93tsm7EL0QUzM3I6EzKvJMoSw6Gao7xrcCNKApmfEQAAIABJREFUEvbt74CnBVXcVNRJHcZODApHENWox0wBJHzVBejVOiaEjie8tgJRkqgWvJg0Rq6a8B1UohrRGoW/rufvRlpYKvXuBv559D/cueGHXarLPT6P3BjJ0X8/iJHEatGh06j4+U0z+fXtc1C1zVGhQTqMOnWvGUjZ+bUIwMSEUIJNWuZPiWJumvxg42o9c9UM+mPIgebt23sX4bLb7bz66qtDvcTXhs5GwaoLIcWaxBTbRJY2L+ao4ziOtoykRk8Tbp+buKBYZkVmICFRUF/ErpL9jDWO48OCT5kePoXl45aS48glPiiOp3Y9R0mnjJVyZwUGtZFgnQVHaz3T7JNZlXRJINgMdJFniG5rAmPVh/DDGXdT0ljGv1s/JPngQlQ+Dc3menaW7yEuqO/OXX0hSRIvHvwHJ+rySQxOYGnCYvLri/BLfuZFn76n8HY0oppfz3+cBzbLfvt/Hvsv4QYbla5qXsp+g8dn/5B6dwNun3vE2lP2hkpUcd3E1XLmWplcX3HR2AsGdOz08CmoRTVjgzpiTVPtkzhce4xmYIE9HW2uvCrVTlyEasxUmk7Iv2MxSA4Ii+FyBpi/Kh+iU/E3VOHZ8DfCYsxUqcHSKZgvhkTLK40emGpPQ5vzbqBdqpxUIY9rb9VBDtUc5WRjKU/Me2Sgt2bEEAQBtUro8jo23NyHUaghISooEN8QBIGbLpmIo6mVqjoXfkmi0uEiMnToq5vTyTfrsX2UMagNzIrM4PtTbuAXc3/EzMjp6NV6ViQuI84SGyj0OdCWq27Th2JQ6/lW0iXMjJxOdXMtL+x/CaPawPUTryDcaGdezDkBY7O7Tb4Y4Bc7fsvfsl/DL/lpcDcSYbR3MQjw1c5lHYG0OEssabbUwGoBwB5rJKtiH16/l9/u/hO7K3ouRGwXbDtc032SyKsv5ERdPnqVntz6Al7Y/zKbS75kUljKaZ902+nsjlELKirbig0bWhsC1cXQIXt9upnYKZunXY6iP9SimunhU7pUWs+NnsWqpIu5L+N2Lpt8VeB9wRLepR5EMMsuTlFvQQiKwHvyED5HCc63HgCPiyirbCw6Z3iJoTFIzlrchzfgq5VXWH6nA/eRz9GptCyOnRfYt7y5Er+rgdadb7Nzt5yU0uBupNnTPR10OBoHDZUx4WZySxv46/uH8Pk7UmvrmlrJK20gbWz3FO1wq5FKh4v3tuTzo79u51iRg/e35nc5/kxGMQqnmesmrg7kmncmVB+C09vMnsoDvHnsHYBADAIgPXwy06LSSAwey4rEC7to8BvUckDrSG1HJoRZYyKvvoAcRy5+yU+IvnugTBAE7s+4g/sybu+2zaI188OZdyO2PTnNnJJCo6eJ9YUbya3P5+VDPWeZfXFyG58Ufc4f97/UTRqhvfHQj2fdy6qkiwHZnbRwzLxu5zmd3Jx2LZcmXsQv58pPqya1EbffQ6uvleyaI2hE9YimDPdF+2puVmTGKad1gvx/fX7cAsYFJ3QxAqJFNsaGZfegnXoRQqf+3pqkWfhKDuM5tkV+PXkpcTZ5PLGd6hpUIbIbrnXLP2jd+rr89653aN38Kp5Dn3HxuCV8d5JsiI47ctm06Xla931AuRosKgMSEoWNXTOAWra/RdNLN+N893H8ztHTIGqPK2w/VBFo0iNJEu9tyUdAYO7k7nGw8BADTS4P720tAODpf+7l3c35HCk8O7SUFKNwhhCql5/id5R16OuH6Tue7M0aEw+fewf/l3FbjwHPCxPOC/z9YOZdPJB5JwCvtMkZJIf0nBI4Njiecb3UBcRZYll9UybfuWE6aeGpGNWGQDBc00tjmGOOjuKdrE4rF4AyZyU6lRarLoQFMXPQiGoijeFMsI7v8Vyni/TwyVwQvxCz1sRzC5/g2+OXA3K8Y3/VISaGpqBTDa8a7ECxaM08Ne9Rrk29fFjPq4qVEz4Etfy51HHp6GZ1vYZm/FxAwnPgIwSTFd05V3B+3AJ+Mus+vpV0SWA/0dZhMH21xbTu+m9AkM9zZAOiIJIZkU60KZId5bt5W9PI0/FhODQqphllg9JZbsTfUIXnwDpUUcn4a0tw7+4/A3Kk6Nzk53C+nJa762gln+8rZfH0mB4D3ym9aIIdLlCMgsIgSAiKQyNqyK6R00enh09Bo+o9F/urXDJuKfdOv4250bOIs8QSZgglMXgsTR4n0aZIIk7xKTMk1Ig90oJGVHdZ4Xj83kDBW2eqXbVMtU0ixhzVzcVU4awkwii7KzQqDddMuIyrUy87bd3pBoJGVAcE4g5UH6auVS4KG00sWvOw3yPD0nswf/fPfe4jBkeAVp70VBHjA/9vkaaIrqsNcyjmG15AN/sqaHXi3vu+vEFQ4XeU4m/TXVrUaUVYp5Hrm+L8GsL0oRR1MgreIvlhQjfvetRjM/Ac3YSvumBAn8vnrMe18a/4m09NiO+rJERZ+M7CRMKCdKz9spAjhQ7yyxrQqEWuOL/nh5mxUUHEfEUOJsZmYm9OFe9tyee19b03DzoTUNpxniHYDKHclX4zfz7wCvNjZndJDx0oSSFjSeokEbEkfiEf5nu5LHnlsIzxgviFNLqbmBWVwSuH/klBfRHp4ZMD2yVJotpVS2poMtHmKNYVfIbL24JBrccv+TnZVMqksAmB/TMjpw3LuIabdqPwxcltiIIYSA/9OiGo1KDq/+evjk3Dm7cT7bRL+txP0BoRQ+UkBMEUinbKMsTgcFzrnsNXdgxx3AzmRM8k2a/lsZx/Bo4La2klNjSaEkc+LdvfwpuzFamlEcFkRQyJQpO6EO+JL3F9/DtMVz0TMEZ+pwNBZwqsdAB8jhLK1ryIt7IAQaVBf+53T+XWdEEUBC46J54ZE8J5/j8H+OM7B9FpVdhDDIh9GOofXZOBJElk59dS73RjNet4YU02a7bI0uYJERbqne4e1VxHG2WlcAaRGJLAk/N+wsUDzDLpjzRbKg/OuCuQ7TFUokwR3JF+E1Ntk1AJKgoauoq2Nbgb8fg92AxhJIWMRULineNr8Ut+chy5NHmcpJ0FE2x7ENXRWseksBSMmrMre2Q40c+/HuO3HkcV1v93SGWLRwyJQj//erSTl6CKmQSCiL+mKLBPsLOR1eX1LAyfxhS3ikhnIxGtbqrdDbgOrENqkf32+vNuk7OBolLQnXMlkrMWf70ck5L8Ppxv3EvLZ38KnFeSJJrX/AJ3ZQEA3vzdSMMY2LWHGLjnO1Mw6FQ4GlsJD+m7XsSgU2PUa5iZGsEFmWPISLFzyZwON9srHx3lnU151Dt77lMxmihG4QxDLaqHXV9nuNGoNMSao9lc8iVvHHk7oF3ULi5nM4SR0JYOua1sJ/uqstlbeQCdSkta2JlvFIxqAypB/j+YHzNnlEczugg6EyrbwILsgs6E6fInUcfJWVKCSoMYFN6lsM1XXci0FvjOxNVcp09EqCogrLwQvyBQrZXvueHC+1BHdqRLqxPkFaU3Lwvn24/Qul0uiPUWdvQ78JUdBY/8PVTFTEJqbcJfM7wFZLYQAwunyTGQzqmrA0EQBL51biLP3d01qeKLvQOTbj+dKEZB4ZTIiJhKi6+VbWW72FUu+4D3VR1ELaiID4pFr9Zx9YTLAPjH4bfYUrqDpJBxXVJfz1Tas7J+MO3WgAy0wqkhWmPwdVJo9VUXoLLFI4gi6tg0aHVib1NZrdCq8V30f4ixk7qeIygcITgSd9Y7+B0n8WR3VP5LXvlJ25u3C1RaEh78J/pFt8jvdarYBpDcLvx1g+uJ/VUmj5MzAiNOsfYgyKjlrm9P5pc3zyIzxc57WwuoqO2ejjuaKEZB4ZRYPGY+16WuRhRE3s9bx57KA2wvy2KqPS2grDknegbJ1iQ8bVo640YprfNUiAuKJdmaeEYFwc9GxNAYpIYKJE8rkt+Hv7oI0ZYAgKpt8rd7vBgFNWujw/lxzut8cXJbt/OoY3rWTvJVy6sBb+E+1GPSEDU6RGMwqojxeI583qXWwfXJH3D++2Ek96n3L4mLsPDIdZmsmJtwyueYNt5OVJiJK84bj1+S2JMzMKXe04ViFBROifY+BbdMvo5mr4uXsl9HLapZmXhRl/2uTFnFuTGzsWjMo57Fo3D6UcVMAknCm7cTf+1J8LlRtVVLi3oLqshkNBLcnXw5hrYez8cduYHj91YepNpVi3bKMvLHTmLvud9BFZWCOnEWAP7aYvzN9UjOWlSRHYV+2mmXIDXV4M3fheRuxldXiq9ELgr1FslZcf768lPSZBoXHYRGPXQXb2iQnrhwM29/nsvDf93erSPcaKEYBYUhMdk2MVBpmxExtVvVdLjRzuqUVTw1/9EuFdQK3wxUkcmIwZF4crbgq5BrWFQRHamchmX3oj/vNmKjp/LIrPuIDxpDY1vfcrfPw9+yX+OxL5+iXqvlL6oq/lW6iaK530G/+PugMeCvPSkbG0AM6xA4VI2ZjGCx4znyOS0bX6T53z8KbPPm7cJXmYvzXw/h3vVfpNbe+3TvqTzAh/mf4PK2DKlZUG+snDeWYLOWitrmM6YXg2IUFIZMe0bRFNukfvZU+KYhCALqcTPwlR/HW7QfwRgSkNIAELQGNIlyhzyVqCLGFEVVW8JCXWtHf+fn9v4l8Pe6gs+4a+NDfBIZht9Rgr9WzoJrT4mVryuiGT8HX1lOl4C0OvEcvMX78RYdAMC9by2ur2QxnWwspb61kQc3/ZSXsl/ng/xPuH/To7x48LVhvjswLdnOb++YS5BJy/pdxd2E9CRJIq+0gSaXZ9iv3RtKnYLCkJkRMY2EoDGjpg+kcGajjp+Ge+/7+IoPoh6b2WecJtxoo9HTxLqCzwIyL+OCE8irL8BmCGN21Azez1sHwGc6NxdUOhAMwQjGEERD1+ZMquhU2PO/wN+62VcieVrw5m7H3fY+gO9kNs3vP4lm0vm81pzD7sr9CAhISKTbJ3O8Lhenp5kD1Ydw+9xoh7m6XRAEJiZY2X6ogpc+OMKd3+qo/Vm3s4i3N+aSmWInMsxEfISFjJSR/Z0pKwWFISMIgmIQFHpFtCcgGGTtrc6uo56YEDoeqy6E9/M+5p3jcmX06uRLSbYmsXzski7FjwC0OvHm7UQVO7nbuVTh4wJ/6xd+D1VYHKqIpMB7mgnnojtnNSD3k3Ac+IA9lfIKQmpru3NT2tVcl7o6cMyxEZL5vvqCZGZPimBPThVFFR39G/Yck4PQWceqWLutgD++e3DEaxsUo6CgoDCiCIIYqF1QRSb1ue8YSwy/mPsj4i1jqHfLk6PdaOMH024hM3JaN1HAdi+/Oj69+3XVWvTn3Y5x1WOIbS4rQRDRTDgXAO30laiTZiMER0DkeI64KpCQuC/jDkBWCBAFkTRbKs8vfAK9SsfBtuZWw41Jr2HVfNmI5ZXKCr1PvbGH3NIG0pO6KgjvPtZ7Q6jhQHEfKSgojDiatAuQJKmLeF5fxAfFUthYjEGt7yJG+FUhxga1SIjXjzpuSs/XTZzZ7T3dnGvwTVnGG8WfMT18CjVzV/KvnDVgtmBS6UgIGsOv5z/epYhULapJDU0mu/oIfslPo7uJP+z7G5clryDZ2rehGyhhwXpMejWFFY2UVDvJKZZjKrPTImlu8ZBzsp7QIB3ZebUsnn7qfU36Y8hG4U9/+hMffvghKpUKSZK49dZbuegiOS3R5XLx8MMPc+jQIVQqFT/84Q9ZtGjRkAetoKBwdqEKG4Nh4U0D3n9ccAKbSr7E3kk+vh2L1hyQZa9PmEJk3DkIgyiKFNRatjecYHtZFtvLsrpsSxCMiILYY9vTCaHj2Vt1kN/ufoEGdxM1LbVkVewbNqMgCAJxERYKyxvZf0IOti+bFUd6ko30pDBcrT7WbMnny0PleH1+1KqRcfQM2Shcc8013HbbbQBUVFRw4YUXMnfuXIKDg3nppZcwm8188sknFBQUcPXVV7N+/XpMJlM/Z1VQUPgmkxExFas+pEvfhnYemXUf+fWF/PnAq7RMWogmorvrqD92lO8mxhyFTqUjr74g8H5IS++FbZFtKdX5DR1aTp3biw4HiTHBfPBlAbWNrSRGB3H5ok4xELWKtLGhfL63hNySelLirL2faAgM2dRYLB0dmJqbmxEEAX+bENVHH33E6tVykCYhIYG0tDQ2bdo01EsqKCh8zREFkaSQsejbGkh1xqwxkdjWA6ShtWFA5ytpKuPnO56h3FmBz++j3FnJxNAUZrQZlDlRM7lAFc6iknJ8lXk9nqNzPOMXc37EBOt4ql21g/xkfTNtvA1Jgganm3OnRnfbnhpvRSUKbDlYRklVE8eqCthQvHlYxzAsMYU333yTv//975SXl/PEE09gtcoWrLS0lJiYjuboUVFRlJd3bzivoKCgMBgMagNqUU2de2BG4W8HX6PSVc2+qmwywtPxST7CjXamh0+hsPEkF409n+BYD83Fv6Q16x2MF93f7Rzt8i0AIbpgbIZQ9lYeHLbPBBAfaSExJghbsIFzJnXv6mbQqVk0PYZPs06y9WA5hplyeu7c6FnD1giqX6OwatUqSktLe9y2bds2VCoVV155JVdeeSXHjh3j/vvvZ/bs2QHDMByEhZn73+kbgt1u6X+nbwjKvejgm3gvQg3BtAot3T77V197/T6qW+Qn+vLWctxauYI5OXoMY+w2/i+qI9ZRmZSOK3dfr/czKTQBjUpDeHgQCbXRbCndgSFYxKwdPpf4c//Xc9z199tfIS08hbtWz2ZqSgTPvNHRpdGjaybW2j3+cir0axTefXfgrfBSUlIIDw9n586dLF26lOjoaEpKSggNlZtbl5WVMWvWrEEPsqamCb9/8BolXzfsdgtVVY397/gNQLkXHXxT74VZbeZ4VT6l5bWBLoU93YsKZyV+yY+AwNHKXBKMCQBo3aZu+3qMEficdVQUlyLquxuGe9PlfuZVVY1YJLnt5sHC3C7NrUaCZk8zmwt3srlwJ2mWyUwaE8wlC8L5rC0EcvRkPmZvRxtQURRO+WF6yDGFEyc6ijmKi4s5cuQISUlycGTZsmX861//AqCgoICDBw8yf/78oV5SQUFBAb1aT0VzFesKN/S5X0WzXACWGZFOo6eJ3ZX7sGjMWDTdJ00xVNZPatdT6ouYtiB4adPQ5LgHQmFjx3j8kp8P8j/hM9c/Au+dbBw+t/yQYwq///3vOXHiBGq1GpVKxSOPPEJiotwk/qabbuKhhx7iggsuQBRFfvazn2E2K64gBQWFobMkbiGHa45RUF/UbZvL24JW1KASVQGjkB4+mV0Ve8mrL+SCuIU9ym2obAmgUuPN3YE6uu+GUCG6YAxqA/kNRYypj2HsCErDF3bqcrijfA8f5ss9JcLFBMqdVRyvKYK+i8UHzJCNwvPPP9/rNqPRyO9+97uhXkJBQUGhG+OtiUwPn0JRw0kKG4oJ1gUR6jfyZekuXj/6NjMipnHDpCupbK7CojGTFNzh4pkT3b2oDUDQm9GMn4snZwva6Svx155EPaa7hAbIdQVjg+LYWb6HneV7WDnuQpYkjEwd1om6fAxquXbi9SP/BuD2qTdhU0Xz6Id/p9hQiNvnGZYmVorMhYKCwllLuNFOdUstv8r6PY9te4oXs/7J60ffBmBXxV5qXLVUNFcRbrRj1poI0QUzwTqecKOt13Oq4qaCz0vz2qdxffTbPqW1r079DrMiM1AJKtYXfY7X7+1131PF6/eSW1/AjIhpfHfSlYH3U0PHEx5sRtsShQ8vP9zwNM9s/Qdu39C0kRSjoKCgcNZi1clCe1GmCAxqAxvy5a5ti8fIscvsmqOUOyuJaDMCd6V/j+snXdHnOduF9KT6ckDCX1/R674humCum7ia76Vdg8vrIqdTg6Dh4khtDm6fmxRrIilt1dNRpghEQUQQBL6dkQGAW9VAXms2P/zsN1Q0n7o+kqJ9pKCgcNYy1Z5GQUMxlyZeRKmznK2V25lpyyQ1dDybS7bz75w1AAEV38gBNHoSjSEIJitSS6O8YljzM/Tn34Fm3Ixej0kNTUan0pJVsY+sin2cGzubhKC4IX8+n9/Huyc+INxgY5ItFbWo5tFZ92PqVDOxcGoCb7fF2iWfCre6jpez3+Q3Y358StdUjIKCgsJZi0Vr5prUywBI1iYyNzk9kGbq8Xc0phmstLv+3O8CAq6PnpHPdeTzPo2CqqWJtOAkdpTLtQO1LQ7umf79QV2zJ/ZWHqCiuYqb064NiAFGfEUpFkAtqPBKPlqz5yAG1eKadOoZUYr7SEFB4WvJeWNkiexbJl/PZFvfmURfRT1mStcAcz+xAucb9zL94NbAa6/fR4t36D2Xt5Xtwm4IY4q9766G90z/PhclnM9jVy3EVzWGEE3vMZP+UIyCgoLC15JVSRfz/MInmGqfhCic2lSnSbsAAF9NEZLUcwGt1Na7OdHl4ZGMu0kIiiO/oZAndz4X2GfTyW385/h77Kk8MOBez01uJ8fr8sgIn9rv+McGx3PxuCXYgmWtKLtw6umxivtIQUHha4kgCKiFoU1x+jlXI4bG0rrpFaT6CoSQSCS/H8lZg2iRXVJSY3Vg/+DNbxBr9FEAVLfU4vK2YFDr5X4NwMbiLVyXuppZURm9XrOutZ53T3xAsDYIv+QP9EAfCEa9BqNOjcmVcCofF1BWCgoKCgp9ooqUq8J85TkAtG75O843H6D5vSfwVRfiqy4M7OsrPcKcghNEquRAcF59IXWt9V3Ot7EfVdMvTm4jq2IfnxXLitIxPciH98WYcDPbs3vPmOoPxSgoKCgo9IEYHIWgM+Mtz0FqdeI5+gUgG4nWbW/g3rcWwRiCYdk9aCYsIFQSuVuIQxRE9lYeYEeZHHzWq3SMscRQ3FQaaBL0VSRJYmf5nq5vHh2cNHZPktuDQTEKCgoKCn0gCAKqmFS8OVtoXvtUl22+8hz81YXoZl+FOi4d/bnfRQyJQeN0EGeJ5cuyXbyXtw69Ss+vz32cK1JWAfDotid7DESXOSuoa63nQjpUplu3vobfNTCJcIAZqeFc2tbv+VRQjIKCgoJCP6jj5GY8/hpZg8h01W8xXPwggjEEdcJ01J3SVUVzKFJTLVZ9h2qpIMiNg8aYY9CqtLj9Ho7WHu92nfbit8kFx1lY6+TGErlPs7dgT7d9ex2rSmROWvdeDANFMQoKCgoK/aCOT0cwd/QrEM2hqGMmYr7mOQxL7u4irieYw/A3VbMkeg5jNMGoEPh2tJweqxJV/Hr+T9GIGo7Xde/wllOXSygaQlUGvrPiN0yZeS3oTPgqjuNzlOJvGXmJdCX7SEFBQaEfBJ0J81XP4D64HtR9dzgTzWHgacG+7xPuOCGvBtTew5B0vvy3qGZccDwn6vK7HOeX/ORUHWFSfRMq+1gErQFNynzcR79Aaqyh+e0fIQRHYF799Mh8yPbxj+jZFRQUFL5GaCcvQZu6sM99RJtcI+A98SViSBSCOQxvwR5avng5sE+cJZYyZwW1LQ4A3D43hflbceEjyeVGkzyv43wWG75KuW+N1IcO03ChrBQUFBQUhhFV1AQEYwhSSxOGi+7H31SD670n8BzbhH7BjQBEmyPxST5+su1Jlo9dwvv560n1iAhqiSmXPonGGBo4n2i2gW/41Vd7QzEKCgoKCsOIIIro512P5G5GNIchmsPQzrwc985/I7ldCFpDl9qD9/PXA3BE42esNpTgTgYBQLB0layQJKnHBkHDxbC5j3bs2EFqaiqvv/564L3q6mpuvPFGli5dyooVK9i/f/9wXU5BQUHhjEWdMA1N8tzAazFIrn72N8iS1hG9CPQtSlrW7T0xqKsAnjSI9NRTYViMQlNTE7/5zW8499xzu7z/zDPPkJmZyccff8yjjz7KAw880Kt+iIKCgsLXlYBRaJRbg6pFNY9NujGw/c56NU/NfZSMyGndjlVFT0B/wZ3o5l0PgNRUM7JjHY6TPPXUU9x0001YrdYu769bt44rrpAbWmRmZqLVajl48OBwXFJBQUHhrKFdJ8nfJokheVow/O8prvWEcH6Dl4TgeCy6nvvXC4KIZmwmqqhk+Rz15SM71qGe4IsvvqCxsZFly7ouexwOB5IkERra4R+LioqivHxkP5CCgoLCmYagM6GOn4b7wDr8jVX4yo4CMKkwh/Mra1HHpvV7DjE4AkQVfkfJiI6130DzqlWrKC0t7XHbunXreOaZZ3jllVeGfWCdCQvr2YJ+E7HbLaM9hDMG5V50oNyLDs7Ue+FdcRvFL9yBePgjVFo9rrb3ddHjiZqzBGEA8t6tYdGom8pH9DP2axTefffdXrdlZWVRVVXFZZfJnY8cDgcbN26krq6OO++8E4Da2trAaqGsrIzIyMGXX9fUNOH3K7EIu90S6Cr1TUe5Fx0o96KDM/teaFElzqLxwOeg1qGKTEYMHYM681Kqq50DOoNkicJVdoLKinoEsXcjIorCKT9MDyklNTMzky+//DLw+qGHHiItLY1rrrkGgGXLlvHWW29x++23k5WVRUtLC2lp/S+TFBQUFL6OqCLH483ZAh4XunOuQBU+OOE69dgMvHk78Rz+DG1bA6DhZkQrmu+77z527tzJkiVLePzxx/nVr36F2Id1U1BQUPg6o7J3GAHRPnbQx6vHzUQMi8ebnzWcw+p6jeE82VNPdZWVtdvtvPrqq8N5CQUFBYWzFtEajWCyop289JQK0ARBQBWVjOfIF3iObkK0RqOKSBrWMSoVzQoKCgqnCUFUYb762SGdQxU5Hk/2J7RsehkQsNwyvIk+ii9HQUFB4SxCHTu50ysJye8f1vMrRkFBQUHhLELQGjCufATRlgDQpW7B73TgLRqanJBiFBQUFBTOMlQRSRjOuw0AX1Ue/uY6vAV7cX30W1zrnsXf0nMP6IGgxBQUFBQUzkKEIDtoDPirC2nJ2YqvPCewrXXra3D5A6d0XsUoKCgoKJyFCIKIyhaH5/CGbtv8lbmnfF7FfaSgoKBwltK51sF09bPsz/fbAAAGiUlEQVRoJi5GDInq44gBnHOog1JQUFBQGB20Uy9CtMbI/5qs6Oddhypm0pDOqbiPFBQUFM5SREMQxu/8out7wRH4Tg7hnEMck4KCgoLCKCIIQpfqaNFs62Pv/lFWCgoKCgpfI1Tx6YiS95SPV4yCgoKCwtcIQRBQJc485eMV95GCgoKCQgDFKCgoKCgoBFCMgoKCgoJCgCHHFB566CG2bduG1WoF5G5rt90ma3JUV1fz4IMPUlJSgk6n4+c//zlTp04d6iUVFBQUFEaIYQk033LLLYEWnJ155plnyMzM5OWXXyYrK4sHHniAjz/++JSaSygoKCgojDwj6j5at24dV1xxBSD3c9ZqtRw8eHAkL6mgoKCgMASGxSi88sorLF++nNtvv53cXFmIyeFwIEkSoaGhgf2ioqIoLy8fjksqKCgoKIwA/bqPVq1aRWlpaY/btm3bxr333ovdbkcURdasWcP3vvc9Pv3002EdpCgq7qZ2lHvRgXIvOlDuRQfKvRjaPejXKLz77rt9bo+IiAj8femll/Lkk09SXl5OTEwMALW1tYHVQllZGZGRkYMepNVqGvQxX1fCwsyjPYQzBuVedKDciw6UezE0huw+qqioCPy9efNmRFEMGIply5bx1ltvAZCVlUVLSwtpaWlDvaSCgoKCwgghSJIkDeUEN9xwAzU1NQiCgNls5sEHHyQ9PR2AqqoqHnjgAUpLS9HpdDz++ONMnz59WAauoKCgoDD8DNkoKCgoKCh8fVAqmhUUFBQUAihGQUFBQUEhgGIUFBQUFBQCKEZBQUFBQSGAYhQUFBQUFAIoRkFBQUFBIcAZbRTy8/NZvXo1S5cuZfXq1RQUFIz2kEYFh8PBzTffzNKlS1m+fDl33nkntbW1oz2sUeUPf/gDKSkp5OTkjPZQRpXW1lYee+wxlixZwvLly/nJT34y2kMaNTZu3Mill17KypUrWbFiBevXrx/tIZ0Wnn76aRYvXtzt93DK86d0BnPttddKa9askSRJktasWSNde+21ozyi0cHhcEjbt28PvH7qqaekhx9+eBRHNLpkZ2dLN910k7Ro0SLp2LFjoz2cUeXnP/+59Mtf/lLy+/2SJElSVVXVKI9odPD7/VJmZmbg+3DkyBEpPT1d8vl8ozyykWfXrl1SaWlpt9/Dqc6fZ+xKoaamhsOHD3PJJZcAcMkll3D48OFv5BNySEgIs2bNCrxOT0/vVaTw647b7eZnP/sZP/3pT0d7KKOO0+lkzZo1/OAHPwj0KLHZbKM8qtFDFEUaGxsBaGxsJDw8HFE8Y6e4YSMzM5OoqKgu7w1l/hyWJjsjQVlZGREREahUKgBUKhXh4eGUlZV1keP+puH3+3nzzTdZvHjxaA9lVHj++edZsWIFsbGxoz2UUae4uJiQkBD+8Ic/sGPHDkwmEz/4wQ/IzMwc7aGddgRB4LnnnuP222/HaDTidDr561//OtrDGjWGMn9+/c3o14yf//znGI3GHjvdfd3Zu3cv2dnZXHXVVaM9lDMCn89HcXExEydO5J133uH+++/nrrvuoqmpabSHdtrxer385S9/4YUXXmDjxo386U9/4p577sHpdI720M46zlijEBUVRUVFBT6fD5B/AJWVld2WSd8knn76aQoLC3nuuee+Ecvir7Jr1y5yc3M577zzWLx4MeXl5dx0001s2bJltIc2KkRFRaFWqwMugqlTp2K1WsnPzx/lkZ1+jhw5QmVlJRkZGQBkZGRgMBgCTb++aQxl/jxjZ5awsDBSU1NZu3YtAGvXriU1NfUb6zr67W9/S3Z2Nn/84x/RarWjPZxR4ZZbbmHLli1s2LCBDRs2EBkZyUsvvcS8efNGe2ijQmhoKLNmzWLr1q2AnG1SU1NDfHz8KI/s9BMZGUl5eTl5eXkA5ObmUlNTQ1xc3CiPbHQYyvx5Rquk5ubm8tBDD9HQ0EBQUBBPP/0048aNG+1hnXaOHz/OJZdcQkJCAnq9HoDY2Fj++Mc/jvLIRpfFixfz5z//meTk5NEeyqhRXFzMj370I+rq6lCr1dxzzz0sWLBgtIc1Krz33nu8+OKLgaD73Xffzfnnnz/Koxp5fvGLX7B+/Xqqq6uxWq2EhITwwQcfnPL8eUYbBQUFBQWF08sZ6z5SUFBQUDj9KEZBQUFBQSGAYhQUFBQUFAIoRkFBQUFBIYBiFBQUFBQUAihGQUHh/9urYwEAAACAQf7W+0ZREgGTAgCTAgAL+ww5Qh188c4AAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] } } ] }, { "cell_type": "markdown", "metadata": { "id": "BFWhdAC2D1XY" }, "source": [ "### Exploring Seaborn Plots\n", "\n", "The main idea of Seaborn is that it provides high-level commands to create a variety of plot types useful for statistical data exploration, and even some statistical model fitting.\n", "\n", "Let's take a look at a few of the datasets and plot types available in Seaborn. Note that all of the following *could* be done using raw Matplotlib commands (this is, in fact, what Seaborn does under the hood) but the Seaborn API is much more convenient." ], "id": "BFWhdAC2D1XY" }, { "cell_type": "markdown", "metadata": { "id": "RWMXqvfuCDp5" }, "source": [ "#### Histograms, KDE, and densities\n", "\n", "Often in statistical data visualization, all you want is to plot histograms and joint distributions of variables.\n", "We have seen that this is relatively straightforward in Matplotlib:" ], "id": "RWMXqvfuCDp5" }, { "cell_type": "code", "metadata": { "id": "dGpJyFVKCDp6", "colab": { "base_uri": "https://localhost:8080/", "height": 613 }, "outputId": "9ff73500-88f9-4cc3-c687-ca7a8bc0d338" }, "source": [ "data = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], size=2000)\n", "data = pd.DataFrame(data, columns=['x', 'y'])\n", "\n", "for col in 'xy':\n", " plt.hist(data[col], normed=True, alpha=0.5)" ], "id": "dGpJyFVKCDp6", "execution_count": null, "outputs": [ { "output_type": "error", "ename": "AttributeError", "evalue": "ignored", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcol\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m'xy'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mcol\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnormed\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malpha\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/matplotlib/pyplot.py\u001b[0m in \u001b[0;36mhist\u001b[0;34m(x, bins, range, density, weights, cumulative, bottom, histtype, align, orientation, rwidth, log, color, label, stacked, data, **kwargs)\u001b[0m\n\u001b[1;32m 2608\u001b[0m \u001b[0malign\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0malign\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morientation\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0morientation\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrwidth\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlog\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlog\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2609\u001b[0m color=color, label=label, stacked=stacked, **({\"data\": data}\n\u001b[0;32m-> 2610\u001b[0;31m if data is not None else {}), **kwargs)\n\u001b[0m\u001b[1;32m 2611\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2612\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/matplotlib/__init__.py\u001b[0m in \u001b[0;36minner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1563\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1564\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1565\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msanitize_sequence\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1566\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1567\u001b[0m \u001b[0mbound\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnew_sig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0max\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/matplotlib/axes/_axes.py\u001b[0m in \u001b[0;36mhist\u001b[0;34m(self, x, bins, range, density, weights, cumulative, bottom, histtype, align, orientation, rwidth, log, color, label, stacked, **kwargs)\u001b[0m\n\u001b[1;32m 6817\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpatch\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6818\u001b[0m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 6819\u001b[0;31m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6820\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlbl\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6821\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_label\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlbl\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36mupdate\u001b[0;34m(self, props)\u001b[0m\n\u001b[1;32m 1004\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1005\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mcbook\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_setattr_cm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0meventson\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1006\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0m_update_property\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mprops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1007\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1008\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mret\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1004\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1005\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mcbook\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_setattr_cm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0meventson\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1006\u001b[0;31m \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0m_update_property\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mk\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mprops\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitems\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1007\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1008\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mret\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/matplotlib/artist.py\u001b[0m in \u001b[0;36m_update_property\u001b[0;34m(self, k, v)\u001b[0m\n\u001b[1;32m 1000\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1001\u001b[0m raise AttributeError('{!r} object has no property {!r}'\n\u001b[0;32m-> 1002\u001b[0;31m .format(type(self).__name__, k))\n\u001b[0m\u001b[1;32m 1003\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1004\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mAttributeError\u001b[0m: 'Rectangle' object has no property 'normed'" ] }, { "output_type": "display_data", "data": { "image/png": "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1pYtW66avfLKK9azzz5rWZZltba2Wvfff7/V29sb13xPPvlkKFdvb6/14IMPWocPH45rhutpaGiwysvLrZ6eHsuyLKuzs9PmRJf19PRY3/zmN6158+Zd83jaobGx0bp48WLo3zNnzrQlx6xZs6yamhrLsiyrpqbGmjVrli05Pu306dPW3/72t9DXL774ovXMM8/YmOi/mpqarDlz5lgPPfSQdfToUbvjWCtXrrSef/55KxgMWpZl/+stGAxaubm5od/NkSNHrMmTJ1uBQKDfbRx35H/vvffq7rvvltt9bbTf//73Ki0tlSSNGzdOOTk5+tOf/hTXfC6XSz09l6/QeeHCBblcLkecsuK1117T/PnzNWzYMEnSyJEjbU502Ysvvqg5c+ZoxIgRdkeRJD300EMaNGiQJGny5Mk6efKkgsFgXDNcOd15YWGhpMunO29ubrb9KDs1NVV5eXmhrydPnqz29nYbE1128eJFrVixQsuXL7c7iiTp7Nmzqqmp0cKFC+VyXT6RmhNeb263O9RNPT09yszMvG6PXuHos3r+r/b2dmVlZYW+9nq9OnnyZFwzLF26VE888YTefPNNnTlzRj/4wQ80duzYuGa4npaWFh0+fFjr1q3TxYsXVVZWpm9/+9u2ZnrvvffU09OjgoIC/fGPf7Q1y/Vs3bpVDz74YNgXSCyEO925Ew4kJCkYDOqtt97SjBkz7I6idevWqbi42BGvM0lqa2tTamqq1q9fr3379mno0KFauHChcnNzbcvkcrn0k5/8RE899ZSGDBmis2fP6mc/+1nYbeJe/l//+tf7PZr4y1/+EnpB2CVSvrffflslJSWaO3euOjo6NGvWLOXk5GjSpEm25goEAvL5fHrzzTd1+vRpfec739HnPvc5felLX7Il086dO/XSSy+FLuYTL9E+v3bs2KG6ujpt3bo1nvFuGStXrtSQIUP06KOP2prjgw8+UFNTkxYvXmxrjk8LBAJqa2vTxIkT9cMf/lCHDx/WE088od27d4f+5x1vly5d0quvvqqNGzdq6tSpOnjwoBYtWqQdO3Zo6NCh190m7uX/29/+9oa3HTNmjP7973+Hjo58Pt9V/029GSLl27Jli/7whz9IkjIzM3Xfffdp//79MS//SLnGjBmjwsJCud1upaen6/7779ff//73mJZ/uEwHDhxQZ2envvWtb0m6/Gbinj171N3drfnz59uS6Yrdu3frxz/+sV5//XVb/rvu9Xp16tQpBQKB0OnOOzo65PV6457leqqrq/XRRx9p06ZNcf9f0f/av3+/WlpaNHPmTEnSyZMnNWfOHL3wwgt64IEHbMnk9XqVkJAQWrabNGmSRowYodbWVn3hC1+wJdORI0fU0dGhqVOnSpKmTp2qwYMHq6WlRV/84hevu43j1vzDKSgo0Ntvvy1JOnHihD788ENNmzYtrhnGjh2rP//5z5Kk3t5eHTx4UPfcc09cM1xPYWFhKNe5c+d08OBBff7zn7ctT25urv7617+qsbFRjY2Nys/P14IFC2Ja/NHYs2ePXnjhBW3evNm2ZQQnn+785ZdfVlNTkzZs2KDExES742jevHl6//33Q8+j0aNHa/PmzbYVvySlpaUpLy9Pe/fulXT5L7f8fr8++9nP2pZp9OjROnnypP75z39KurwM7Pf79ZnPfKbfbRx3Vs/6+nqtXr1aZ86c0aBBgzR48GC99tpruvvuu3Xu3DlVVFToyJEjcrvdWrJkib785S/HNV9TU5NWrVqlc+fO6dKlS/ra175me6FJl998XrZsmZqbmyVJJSUlmjdvns2p/quiokI5OTm2LyPcd999GjRo0FVF+/rrr8f9DWknnu78+PHjKiws1Lhx45ScnCzp8sHOhg0bbM31aTNmzNCmTZt077332pqjra1NS5cuVXd3txISErRo0SJNnz7d1kzbt2/Xz3/+89Cb0N/73vfC9qPjyh8AEHu31LIPAODmoPwBwECUPwAYiPIHAANR/gBgIMofAAxE+QOAgSh/ADDQfwD/6JN1LpHU/AAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": { "tags": [] } } ] }, { "cell_type": "markdown", "metadata": { "id": "XfYxJhYdCDp6" }, "source": [ "Rather than a histogram, we can get a smooth estimate of the distribution using a kernel density estimation, which Seaborn does with ``sns.kdeplot``:" ], "id": "XfYxJhYdCDp6" }, { "cell_type": "code", "metadata": { "id": "LM6phBMbCDp7" }, "source": [ "for col in 'xy':\n", " sns.kdeplot(data[col], shade=True)" ], "id": "LM6phBMbCDp7", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "WrHeHhiECDp7" }, "source": [ "Histograms and KDE can be combined using ``distplot``:" ], "id": "WrHeHhiECDp7" }, { "cell_type": "code", "metadata": { "id": "SpdBAq5dCDp8" }, "source": [ "sns.distplot(data['x'])\n", "sns.distplot(data['y']);" ], "id": "SpdBAq5dCDp8", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "aUh_AYukCDp8" }, "source": [ "If we pass the full two-dimensional dataset to ``kdeplot``, we will get a two-dimensional visualization of the data:" ], "id": "aUh_AYukCDp8" }, { "cell_type": "code", "metadata": { "id": "gxBhJMl0CDp9" }, "source": [ "sns.kdeplot(data);" ], "id": "gxBhJMl0CDp9", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "g1O0n3wUCDp9" }, "source": [ "We can see the joint distribution and the marginal distributions together using ``sns.jointplot``.\n", "For this plot, we'll set the style to a white background:" ], "id": "g1O0n3wUCDp9" }, { "cell_type": "code", "metadata": { "id": "k2gzsdSACDp-" }, "source": [ "with sns.axes_style('white'):\n", " sns.jointplot(\"x\", \"y\", data, kind='kde');" ], "id": "k2gzsdSACDp-", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "tkyWoazwCDp-" }, "source": [ "There are other parameters that can be passed to ``jointplot``—for example, we can use a hexagonally based histogram instead:" ], "id": "tkyWoazwCDp-" }, { "cell_type": "code", "metadata": { "id": "dKRVv5lwCDp_" }, "source": [ "with sns.axes_style('white'):\n", " sns.jointplot(\"x\", \"y\", data, kind='hex')" ], "id": "dKRVv5lwCDp_", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "oHSffVJsCDp_" }, "source": [ "#### Pair plots\n", "\n", "When you generalize joint plots to datasets of larger dimensions, you end up with *pair plots*. This is very useful for exploring correlations between multidimensional data, when you'd like to plot all pairs of values against each other.\n", "\n", "We'll demo this with the well-known Iris dataset, which lists measurements of petals and sepals of three iris species:" ], "id": "oHSffVJsCDp_" }, { "cell_type": "code", "metadata": { "id": "95rKSsXVCDqA" }, "source": [ "iris = sns.load_dataset(\"iris\")\n", "iris.head()" ], "id": "95rKSsXVCDqA", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "lgPWAzZiCDqA" }, "source": [ "Visualizing the multidimensional relationships among the samples is as easy as calling ``sns.pairplot``:" ], "id": "lgPWAzZiCDqA" }, { "cell_type": "code", "metadata": { "id": "oSPxC2V7CDqB" }, "source": [ "sns.pairplot(iris, hue='species', size=2.5);" ], "id": "oSPxC2V7CDqB", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "CIA5C-CNFxf3" }, "source": [ "## Plotly\n", "\n", "Plotly Python is an open-source library built on top of plotly.js which allows users to create professional quality, interactive, web-based or standalone visualizations or applications.
\n", "Visualizations can be displayed in Jupyter notebooks, standalone HTML files, or integrated into web-applications via the Dash framework.\n", "Over 40 unique charts and limitless customization options exist across statistical, financial, geographic, scientific, and 3-D plot types. (from plotly website)" ], "id": "CIA5C-CNFxf3" }, { "cell_type": "markdown", "metadata": { "id": "qs9Dbvw7GdBT" }, "source": [ "There are a couple more notes in regards to Plotly that is needed to be shared:\n", "\n", "Plotly - Online
\n", "Plotly - Enterprise\n" ], "id": "qs9Dbvw7GdBT" }, { "cell_type": "markdown", "metadata": { "id": "YQoCBrr1G1Db" }, "source": [ "## Intro to Model Building and inference frameworks" ], "id": "YQoCBrr1G1Db" }, { "cell_type": "markdown", "metadata": { "id": "_kK4RhV_IVn5" }, "source": [ "### scikit-learn\n", "\n", "Reference: [Scientific Python Stanford University](https://colab.research.google.com/drive/188vQVvDIH-9kbd4Un6BcPERKRztxIORe)" ], "id": "_kK4RhV_IVn5" }, { "cell_type": "markdown", "metadata": { "id": "JAZVbXHdIpCT" }, "source": [ "[Scikit-learn](https://scikit-learn.org/stable/) is a library that allows you to do machine learning, that is, make predictions from data, in Python. There are four basic machine learning tasks:\n", "\n", " 1. Regression: predict a number from datapoints, given datapoints and corresponding numbers\n", " 2. Classification: predict a category from datapoints, given datapoints and corresponding numbers\n", " 3. Clustering: predict a category from datapoints, given only datapoints\n", " 4. Dimensionality reduction: make datapoints lower-dimensional so that we can visualize the data\n", "\n", "Here is a [handy flowchart](https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html) of when to use each technique." ], "id": "JAZVbXHdIpCT" }, { "cell_type": "markdown", "metadata": { "id": "iCkDyIHpJF3-" }, "source": [ "![](https://scikit-learn.org/stable/_static/ml_map.png)" ], "id": "iCkDyIHpJF3-" }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "UvOyrbRBIOix" }, "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt" ], "id": "UvOyrbRBIOix", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "j6k2NWBgIOiy" }, "source": [ "#### Regression\n", "Abalone are a type of edible marine snail, and they have internal rings that correspond to their age (like trees). In the following, we will use a dataset of [abalone measurements](https://archive.ics.uci.edu/ml/datasets/abalone). It has the following fields:\n", "\n", " Sex / nominal / -- / M, F, and I (infant) \n", " Length / continuous / mm / Longest shell measurement \n", " Diameter\t/ continuous / mm / perpendicular to length \n", " Height / continuous / mm / with meat in shell \n", " Whole weight / continuous / grams / whole abalone \n", " Shucked weight / continuous\t/ grams / weight of meat \n", " Viscera weight / continuous / grams / gut weight (after bleeding) \n", " Shell weight / continuous / grams / after being dried \n", " Rings / integer / -- / +1.5 gives the age in years \n", "\n", "Suppose we are interested in predicting the age of the abalone given their measurements. This is an example of a regression problem." ], "id": "j6k2NWBgIOiy" }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "QYFz8g72IOi1" }, "source": [ "df = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data',\n", " header=None, names=['sex', 'length', 'diameter', 'height', 'weight', 'shucked_weight',\n", " 'viscera_weight', 'shell_weight', 'rings'])" ], "id": "QYFz8g72IOi1", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": false, "id": "LBuF5ZJ8IOi1" }, "source": [ "df.head()" ], "id": "LBuF5ZJ8IOi1", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "ahefjnACIOi2" }, "source": [ "df.describe()" ], "id": "ahefjnACIOi2", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "Q3o2m9qmIOi3" }, "source": [ "df['rings'].plot(kind='hist')" ], "id": "Q3o2m9qmIOi3", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": false, "id": "P_G245IbIOi3" }, "source": [ "df.plot('weight', 'rings', kind='scatter')" ], "id": "P_G245IbIOi3", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "BDgvb4nlIOi4" }, "source": [ "X = df[['weight']].to_numpy()\n", "y = df['rings'].to_numpy()" ], "id": "BDgvb4nlIOi4", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "mDk7dTMJIOi4" }, "source": [ "X" ], "id": "mDk7dTMJIOi4", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "wOEmW5I8IOi5" }, "source": [ "y" ], "id": "wOEmW5I8IOi5", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "eyxohibHIOi5" }, "source": [ "from sklearn import linear_model\n", "model = linear_model.LinearRegression()\n", "model.fit(X, y)\n", "print(model.coef_, model.intercept_)" ], "id": "eyxohibHIOi5", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "l5FPsT5aIOi6" }, "source": [ "print(model.score(X, y))" ], "id": "l5FPsT5aIOi6", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "92R_i04dIOi6" }, "source": [ "model.predict(np.array([[1.5], [2.2]]))" ], "id": "92R_i04dIOi6", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "fgM_9fdIIOi7" }, "source": [ "df.plot('weight', 'rings', kind='scatter')\n", "\n", "weight = np.linspace(0, 3, 10).reshape(-1, 1)\n", "plt.plot(weight, model.predict(weight), 'r')" ], "id": "fgM_9fdIIOi7", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "BzvMWHgmIOi7" }, "source": [ "df['root_weight'] = np.sqrt(df['weight'])" ], "id": "BzvMWHgmIOi7", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "-4pmQ1XQIOi8" }, "source": [ "X = df[['weight','root_weight']].to_numpy()\n", "y = df['rings'].to_numpy()\n", "model = linear_model.LinearRegression()\n", "model.fit(X, y)" ], "id": "-4pmQ1XQIOi8", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "xI1bvJryIOi8" }, "source": [ "weight = np.linspace(0, 3, 100).reshape(-1, 1)\n", "root_weight = np.sqrt(weight)\n", "\n", "features = np.hstack((weight,root_weight))" ], "id": "xI1bvJryIOi8", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "Wk3lrU6qIOi8" }, "source": [ "df.plot('weight', 'rings', kind='scatter')\n", "\n", "plt.plot(weight, model.predict(features), 'r')" ], "id": "Wk3lrU6qIOi8", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "h_bz_h27IOi9" }, "source": [ "model.score(X,y)" ], "id": "h_bz_h27IOi9", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "Aqszb3RuIOi9" }, "source": [ "plt.hist2d(df['weight'],df['rings'],bins=(50,30));" ], "id": "Aqszb3RuIOi9", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "lJYf3H2NKXWi" }, "source": [ "#### Classification\n", "\n", "Another example of a machine learning problem is classification. Here we will use a dataset of flower measurements from three different flower species of *Iris* (*Iris setosa*, *Iris virginica*, and *Iris versicolor*). We aim to predict the species of the flower. Because the species is not a numerical output, it is not a regression problem, but a classification problem." ], "id": "lJYf3H2NKXWi" }, { "cell_type": "code", "metadata": { "id": "nv_ER6FDKXWi" }, "source": [ "from sklearn import datasets\n", "iris = datasets.load_iris()\n", "print(iris.DESCR)" ], "id": "nv_ER6FDKXWi", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "jDkdMhtdKXWj" }, "source": [ "X = iris.data[:, 2:]\n", "y = iris.target_names[iris.target]\n", "for name in iris.target_names:\n", " plt.scatter(X[y == name, 0], X[y == name, 1], label=name)\n", "plt.xlabel('Petal length')\n", "plt.ylabel('Petal width')\n", "plt.legend();" ], "id": "jDkdMhtdKXWj", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "PfJrRE_UKXWj" }, "source": [ "from sklearn.model_selection import train_test_split\n", "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)\n", "print(X_train.shape, y_train.shape)\n", "print(X_test.shape, y_test.shape)" ], "id": "PfJrRE_UKXWj", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": false, "id": "kiTMK74oKXWk" }, "source": [ "from sklearn.neighbors import KNeighborsClassifier\n", "model = KNeighborsClassifier()\n", "model.fit(X_train, y_train)" ], "id": "kiTMK74oKXWk", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "XX1bV5TsKXWk" }, "source": [ "X_test" ], "id": "XX1bV5TsKXWk", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "PIe9aQKMKXWk" }, "source": [ "model.predict(X_test)" ], "id": "PIe9aQKMKXWk", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "vilx5oh3KXWl" }, "source": [ "import sklearn.metrics as metrics\n", "metrics.accuracy_score(model.predict(X_test), y_test)" ], "id": "vilx5oh3KXWl", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "scrolled": true, "id": "649AqvvYKXWl" }, "source": [ "print(metrics.classification_report(model.predict(X_test), y_test))" ], "id": "649AqvvYKXWl", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "D33vfL2xKXWn" }, "source": [ "#### Clustering\n", "\n", "Clustering is useful if we don't have a dataset labelled with the categories we want to predict, but we nevertheless expect there to be a certain number of categories. For example, suppose we have the previous dataset, but we are missing the labels. We can use a clustering algorithm like k-means to *cluster* the datapoints. Because we don't have labels, clustering is what is called an **unsupervised learning** algorithm." ], "id": "D33vfL2xKXWn" }, { "cell_type": "code", "metadata": { "id": "lf7UIAXoMjPM" }, "source": [ "from sklearn.datasets import make_blobs\n", "X,y = make_blobs(centers=4, n_samples=200, random_state=0, cluster_std=0.7)\n", "print(X[:10],y[:10])" ], "id": "lf7UIAXoMjPM", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Lbo31hO8MjPN" }, "source": [ "Now we plot these points, but without coloring the points using the labels:" ], "id": "Lbo31hO8MjPN" }, { "cell_type": "code", "metadata": { "id": "WalBVWi0MjPN" }, "source": [ "plt.scatter(X[:,0],X[:,1]);" ], "id": "WalBVWi0MjPN", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "4CV0HTccMjPO" }, "source": [ "We can still discern four clusters in the data set. Let's see if the k-means algorithm can recover these clusters. First we create the instance of the k-means model by giving it the number of clusters 4 as a hyperparameter." ], "id": "4CV0HTccMjPO" }, { "cell_type": "code", "metadata": { "id": "dSC5t9sJMjPO" }, "source": [ "from sklearn.cluster import KMeans\n", "model = KMeans(4)\n", "model.fit(X)\n", "print(model.cluster_centers_)" ], "id": "dSC5t9sJMjPO", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "dqqVQU2eMjPO" }, "source": [ "plt.scatter(X[:,0],X[:,1], c=model.labels_);\n", "plt.scatter(model.cluster_centers_[:,0], model.cluster_centers_[:,1], s=100, color=\"red\"); # Show the centres" ], "id": "dqqVQU2eMjPO", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Qhr6EyGuKXWs" }, "source": [ "#### Dimensionality reduction\n", "\n", "Dimensionality reduction is another unsupervised learning problem (that is, it does not require labels). It aims to project datapoints into a lower dimensional space while preserving distances between datapoints." ], "id": "Qhr6EyGuKXWs" }, { "cell_type": "markdown", "metadata": { "id": "tQ446lmW3kLM" }, "source": [ "---" ], "id": "tQ446lmW3kLM" }, { "cell_type": "markdown", "metadata": { "id": "260I-jj1N_jN" }, "source": [ "### Tensorflow\n", "\n", "TensorFlow is an end-to-end open source platform for machine learning.\n", "\n", "TensorFlow was originally created by Google as an internal machine learning tool, but an implementation of it was open sourced under the Apache 2.0 License in November 2015." ], "id": "260I-jj1N_jN" }, { "cell_type": "markdown", "metadata": { "id": "baA63HqQSDXE" }, "source": [ "Few reason for the popluarity of tensorflow\n", "\n", "* Python API\n", "* Portability: deploy computation to one or more CPUs or GPUs in a esktop, server, or mobile device with a single API\n", "* Flexibility: from Raspberry Pi, Android, Windows, iOS, Linux to server farms\n", "* Visualization\n", "* Checkpoints (for managing experiments)\n", "* Large community (> 10,000 commits and > 3000 TF-related repos in one year)\n", "* Awesome projects already using TensorFlow" ], "id": "baA63HqQSDXE" }, { "cell_type": "markdown", "metadata": { "id": "F9QR6fyVQ-wl" }, "source": [ "Reference: [Tensorflow beginner](https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/quickstart/beginner.ipynb)" ], "id": "F9QR6fyVQ-wl" }, { "cell_type": "code", "metadata": { "id": "0trJmd6DjqBZ" }, "source": [ "import tensorflow as tf" ], "id": "0trJmd6DjqBZ", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "7NAbSZiaoJ4z" }, "source": [ "Load and prepare the [MNIST dataset](http://yann.lecun.com/exdb/mnist/). Convert the samples from integers to floating-point numbers:" ], "id": "7NAbSZiaoJ4z" }, { "cell_type": "code", "metadata": { "id": "7FP5258xjs-v" }, "source": [ "mnist = tf.keras.datasets.mnist\n", "\n", "(x_train, y_train), (x_test, y_test) = mnist.load_data()\n", "x_train, x_test = x_train / 255.0, x_test / 255.0" ], "id": "7FP5258xjs-v", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "BPZ68wASog_I" }, "source": [ "Build the `tf.keras.Sequential` model by stacking layers. Choose an optimizer and loss function for training:" ], "id": "BPZ68wASog_I" }, { "cell_type": "code", "metadata": { "id": "h3IKyzTCDNGo" }, "source": [ "model = tf.keras.models.Sequential([\n", " tf.keras.layers.Flatten(input_shape=(28, 28)),\n", " tf.keras.layers.Dense(128, activation='relu'),\n", " tf.keras.layers.Dropout(0.2),\n", " tf.keras.layers.Dense(10)\n", "])" ], "id": "h3IKyzTCDNGo", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "9foNKHzTD2Vo" }, "source": [ "model.compile(optimizer='adam',\n", " loss='sparse_categorical_crossentropy',\n", " metrics=['accuracy'])" ], "id": "9foNKHzTD2Vo", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "ix4mEL65on-w" }, "source": [ "The `Model.fit` method adjusts the model parameters to minimize the loss: " ], "id": "ix4mEL65on-w" }, { "cell_type": "code", "metadata": { "id": "y7suUbJXVLqP" }, "source": [ "model.fit(x_train, y_train, epochs=5)" ], "id": "y7suUbJXVLqP", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "4mDAAPFqVVgn" }, "source": [ "The `Model.evaluate` method checks the models performance, usually on a \"[Validation-set](https://developers.google.com/machine-learning/glossary#validation-set)\" or \"[Test-set](https://developers.google.com/machine-learning/glossary#test-set)\"." ], "id": "4mDAAPFqVVgn" }, { "cell_type": "code", "metadata": { "id": "F7dTAzgHDUh7" }, "source": [ "model.evaluate(x_test, y_test, verbose=2)" ], "id": "F7dTAzgHDUh7", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "T4JfEh7kvx6m" }, "source": [ "The image classifier is now trained to ~98% accuracy on this dataset. To learn more, read the [TensorFlow tutorials](https://www.tensorflow.org/tutorials/)." ], "id": "T4JfEh7kvx6m" }, { "cell_type": "markdown", "metadata": { "id": "V9NZH-k4TwGz" }, "source": [ "### **Pytorch**\n", "\n", "PyTorch is a machine learning framework that is used in both academia and industry for various applications. An alternative to tensorflow" ], "id": "V9NZH-k4TwGz" }, { "cell_type": "markdown", "metadata": { "id": "2KdKKG8qEp4P" }, "source": [ "# **---**" ], "id": "2KdKKG8qEp4P" }, { "cell_type": "markdown", "metadata": { "id": "d94d2871" }, "source": [ "

3. Approaching a Tabular Problem

\n", "

Titanic Survivor Prediction Challenge

\n", "\n", "" ], "id": "d94d2871" }, { "cell_type": "markdown", "metadata": { "id": "1f9dca13" }, "source": [ "

3.1 Understanding the Problem


\n", "\n", "

This challenge is kind of a \"hello world\" program in the whole data Science and Machine Learning community.
The task is simple,use machine learning to create a model that predicts which passengers survived the Titanic shipwreck. In this challenge, we perform a basic EDA and build a predictive model that answers the question: “what sorts of people were more likely to survive?” using passenger data (ie name, age, gender, socio-economic class, etc).

While there was some element of luck involved in surviving, it seems some groups of people were more likely to survive than others.

" ], "id": "1f9dca13" }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.098282, "end_time": "2021-05-19T14:07:57.257046", "exception": false, "start_time": "2021-05-19T14:07:57.158764", "status": "completed" }, "tags": [], "id": "exact-peninsula" }, "source": [ "

Contents

\n", "\n", "

1. Data Description

\n", "

2. Exploratory Data Analysis

\n", "

3. Findings From EDA

\n", "

4. Data Preprocessing

\n", "

5. Feature Engineering and Feature Selection

\n", "

6. Data Modeling and Evaluation

\n", "

* Logistic Regression
* Gradient Boosting Classifier
* XgBoost
* SGB Classifier

\n", "

7. Models Comparison

\n" ], "id": "exact-peninsula" }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.09873, "end_time": "2021-05-19T14:07:57.455074", "exception": false, "start_time": "2021-05-19T14:07:57.356344", "status": "completed" }, "tags": [], "id": "legal-network" }, "source": [ "

Data Description


\n", "\n", "
    \n", "
  • Survival : 0 = No, 1 = Yes
  • \n", "
  • pclass(Ticket Class) : 1 = 1st, 2 = 2nd, 3 = 3rd
  • \n", "
  • Sex(Gender) : Male, Female
  • \n", "
  • Age : Age in years
  • \n", "
  • SibSp : Number of siblings/spouses abroad the titanic
  • \n", "
  • Parch : Number of parents/children abrod the titanic
  • \n", "
  • Ticket : Ticket Number
  • \n", "
  • Fare : Passenger fare
  • \n", "
  • Cabin : Cabin Number
  • \n", "
  • Embarked : Port of Embarkation, C = Cherbourg, Q = Queenstown, S = Southampton
  • \n", "
" ], "id": "legal-network" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:07:57.663692Z", "iopub.status.busy": "2021-05-19T14:07:57.663108Z", "iopub.status.idle": "2021-05-19T14:07:58.439813Z", "shell.execute_reply": "2021-05-19T14:07:58.439195Z" }, "papermill": { "duration": 0.884349, "end_time": "2021-05-19T14:07:58.439994", "exception": false, "start_time": "2021-05-19T14:07:57.555645", "status": "completed" }, "tags": [], "id": "cardiac-environment" }, "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "plt.style.use('fivethirtyeight')\n", "%matplotlib inline" ], "id": "cardiac-environment", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:07:58.654824Z", "iopub.status.busy": "2021-05-19T14:07:58.654308Z", "iopub.status.idle": "2021-05-19T14:07:58.668669Z", "shell.execute_reply": "2021-05-19T14:07:58.668231Z" }, "papermill": { "duration": 0.12592, "end_time": "2021-05-19T14:07:58.668772", "exception": false, "start_time": "2021-05-19T14:07:58.542852", "status": "completed" }, "tags": [], "id": "crude-humanitarian" }, "source": [ "#loading a CSV file using pandas dataframe\n", "train_df = pd.read_csv('./inputs/tab/train.csv')\n", "train_df.head()" ], "id": "crude-humanitarian", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:07:58.969748Z", "iopub.status.busy": "2021-05-19T14:07:58.969314Z", "iopub.status.idle": "2021-05-19T14:07:59.002179Z", "shell.execute_reply": "2021-05-19T14:07:59.001772Z" }, "papermill": { "duration": 0.102772, "end_time": "2021-05-19T14:07:59.002280", "exception": false, "start_time": "2021-05-19T14:07:58.899508", "status": "completed" }, "tags": [], "id": "growing-feelings" }, "source": [ "#Fetching some statistical information of the data\n", "train_df.describe()" ], "id": "growing-feelings", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:07:59.283610Z", "iopub.status.busy": "2021-05-19T14:07:59.283183Z", "iopub.status.idle": "2021-05-19T14:07:59.299024Z", "shell.execute_reply": "2021-05-19T14:07:59.299406Z" }, "papermill": { "duration": 0.085522, "end_time": "2021-05-19T14:07:59.299547", "exception": false, "start_time": "2021-05-19T14:07:59.214025", "status": "completed" }, "tags": [], "id": "least-native" }, "source": [ "#Information about a DataFrame including the index dtype and columns, non-null values and memory usage\n", "train_df.info()" ], "id": "least-native", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:07:59.437540Z", "iopub.status.busy": "2021-05-19T14:07:59.437091Z", "iopub.status.idle": "2021-05-19T14:07:59.445367Z", "shell.execute_reply": "2021-05-19T14:07:59.445002Z" }, "papermill": { "duration": 0.077892, "end_time": "2021-05-19T14:07:59.445467", "exception": false, "start_time": "2021-05-19T14:07:59.367575", "status": "completed" }, "tags": [], "id": "difficult-graduation" }, "source": [ "# Checking for null values\n", "train_df.isna().sum()" ], "id": "difficult-graduation", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.066897, "end_time": "2021-05-19T14:07:59.580266", "exception": false, "start_time": "2021-05-19T14:07:59.513369", "status": "completed" }, "tags": [], "id": "abroad-planner" }, "source": [ "

Exploratory Data Analysis (EDA)


\n", "\n", "

Exploratory Data Analysis refers to the critical process of performing initial investigations on data so as to discover patterns,to spot anomalies,to test hypothesis and to check assumptions with the help of summary statistics and graphical representations

\n" ], "id": "abroad-planner" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:07:59.718588Z", "iopub.status.busy": "2021-05-19T14:07:59.718152Z", "iopub.status.idle": "2021-05-19T14:08:00.528732Z", "shell.execute_reply": "2021-05-19T14:08:00.529096Z" }, "papermill": { "duration": 0.881894, "end_time": "2021-05-19T14:08:00.529225", "exception": false, "start_time": "2021-05-19T14:07:59.647331", "status": "completed" }, "tags": [], "id": "muslim-choice" }, "source": [ "# visualizing null values\n", "import missingno as msno\n", "\n", "msno.bar(train_df)\n", "plt.show()" ], "id": "muslim-choice", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:00.667813Z", "iopub.status.busy": "2021-05-19T14:08:00.667394Z", "iopub.status.idle": "2021-05-19T14:08:00.975656Z", "shell.execute_reply": "2021-05-19T14:08:00.976126Z" }, "papermill": { "duration": 0.378582, "end_time": "2021-05-19T14:08:00.976255", "exception": false, "start_time": "2021-05-19T14:08:00.597673", "status": "completed" }, "tags": [], "id": "contemporary-baker" }, "source": [ "# Correlation and mulitcolinarity\n", "plt.figure(figsize = (18, 8))\n", "corr = train_df.corr()\n", "\n", "sns.heatmap(corr, annot = True, fmt = '.2f', linewidths = 1, annot_kws = {'size' : 15})\n", "plt.show()" ], "id": "contemporary-baker", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.069434, "end_time": "2021-05-19T14:08:01.256086", "exception": false, "start_time": "2021-05-19T14:08:01.186652", "status": "completed" }, "tags": [], "id": "functional-republican" }, "source": [ "

Survived Column - Target


" ], "id": "functional-republican" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:01.398982Z", "iopub.status.busy": "2021-05-19T14:08:01.398534Z", "iopub.status.idle": "2021-05-19T14:08:01.516340Z", "shell.execute_reply": "2021-05-19T14:08:01.516769Z" }, "papermill": { "duration": 0.190554, "end_time": "2021-05-19T14:08:01.516919", "exception": false, "start_time": "2021-05-19T14:08:01.326365", "status": "completed" }, "tags": [], "id": "caring-irish" }, "source": [ "plt.figure(figsize = (12, 7))\n", "\n", "sns.countplot(y = 'Survived', data = train_df)\n", "plt.show()" ], "id": "caring-irish", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:01.660335Z", "iopub.status.busy": "2021-05-19T14:08:01.659911Z", "iopub.status.idle": "2021-05-19T14:08:01.786932Z", "shell.execute_reply": "2021-05-19T14:08:01.786549Z" }, "papermill": { "duration": 0.199188, "end_time": "2021-05-19T14:08:01.787035", "exception": false, "start_time": "2021-05-19T14:08:01.587847", "status": "completed" }, "tags": [], "id": "renewable-apparel" }, "source": [ "#pie chart to show Survived vs Not in percentage\n", "values = train_df['Survived'].value_counts()\n", "labels = ['Not Survived', 'Survived']\n", "\n", "fig, ax = plt.subplots(figsize = (5, 5), dpi = 100)\n", "explode = (0, 0.06)\n", "\n", "patches, texts, autotexts = ax.pie(values, labels = labels, autopct = '%1.2f%%', shadow = True,\n", " startangle = 90, explode = explode)\n", "\n", "plt.setp(texts, color = 'grey')\n", "plt.setp(autotexts, size = 12, color = 'white')\n", "autotexts[1].set_color('black')\n", "plt.show()" ], "id": "renewable-apparel", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.07013, "end_time": "2021-05-19T14:08:01.927411", "exception": false, "start_time": "2021-05-19T14:08:01.857281", "status": "completed" }, "tags": [], "id": "selective-investing" }, "source": [ "

Pclass Column


" ], "id": "selective-investing" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:02.077802Z", "iopub.status.busy": "2021-05-19T14:08:02.077135Z", "iopub.status.idle": "2021-05-19T14:08:02.079866Z", "shell.execute_reply": "2021-05-19T14:08:02.080375Z" }, "papermill": { "duration": 0.081457, "end_time": "2021-05-19T14:08:02.080524", "exception": false, "start_time": "2021-05-19T14:08:01.999067", "status": "completed" }, "tags": [], "id": "narrative-validation" }, "source": [ "#individual count of each class\n", "train_df.Pclass.value_counts()" ], "id": "narrative-validation", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:02.298537Z", "iopub.status.busy": "2021-05-19T14:08:02.297748Z", "iopub.status.idle": "2021-05-19T14:08:02.305355Z", "shell.execute_reply": "2021-05-19T14:08:02.304839Z" }, "papermill": { "duration": 0.117418, "end_time": "2021-05-19T14:08:02.305476", "exception": false, "start_time": "2021-05-19T14:08:02.188058", "status": "completed" }, "tags": [], "id": "accredited-garlic" }, "source": [ "#Individual counts of Pclass based on Target\n", "train_df.groupby(['Pclass', 'Survived'])['Survived'].count()" ], "id": "accredited-garlic", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:02.517775Z", "iopub.status.busy": "2021-05-19T14:08:02.517163Z", "iopub.status.idle": "2021-05-19T14:08:02.749045Z", "shell.execute_reply": "2021-05-19T14:08:02.748420Z" }, "papermill": { "duration": 0.341842, "end_time": "2021-05-19T14:08:02.749169", "exception": false, "start_time": "2021-05-19T14:08:02.407327", "status": "completed" }, "tags": [], "id": "administrative-blogger" }, "source": [ "#bar chart showing Pclass counts based on Targets\n", "plt.figure(figsize = (16, 8))\n", "\n", "sns.countplot('Pclass', hue = 'Survived', data = train_df)\n", "plt.show()" ], "id": "administrative-blogger", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:02.987043Z", "iopub.status.busy": "2021-05-19T14:08:02.985056Z", "iopub.status.idle": "2021-05-19T14:08:03.080904Z", "shell.execute_reply": "2021-05-19T14:08:03.081279Z" }, "papermill": { "duration": 0.223682, "end_time": "2021-05-19T14:08:03.081435", "exception": false, "start_time": "2021-05-19T14:08:02.857753", "status": "completed" }, "tags": [], "id": "associate-colorado" }, "source": [ "#Pie chart showing Pclass counts percentage based on Targets\n", "values = train_df['Pclass'].value_counts()\n", "labels = ['Third Class', 'Second Class', 'First Class']\n", "explode = (0, 0, 0.08)\n", "fig, ax = plt.subplots(figsize = (5, 6), dpi = 100)\n", "patches, texts, autotexts = ax.pie(values, labels = labels, autopct = '%1.2f%%', shadow = True,\n", " startangle = 90, explode = explode)\n", "plt.setp(texts, color = 'grey')\n", "plt.setp(autotexts, size = 13, color = 'white')\n", "autotexts[2].set_color('black')\n", "plt.show()" ], "id": "associate-colorado", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.074364, "end_time": "2021-05-19T14:08:03.704338", "exception": false, "start_time": "2021-05-19T14:08:03.629974", "status": "completed" }, "tags": [], "id": "wired-breach" }, "source": [ "

Name Column


" ], "id": "wired-breach" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:03.859190Z", "iopub.status.busy": "2021-05-19T14:08:03.858702Z", "iopub.status.idle": "2021-05-19T14:08:03.861999Z", "shell.execute_reply": "2021-05-19T14:08:03.862375Z" }, "papermill": { "duration": 0.08402, "end_time": "2021-05-19T14:08:03.862517", "exception": false, "start_time": "2021-05-19T14:08:03.778497", "status": "completed" }, "tags": [], "id": "blank-somewhere" }, "source": [ "#Count of uniqes names\n", "train_df.Name.value_counts()" ], "id": "blank-somewhere", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:04.017576Z", "iopub.status.busy": "2021-05-19T14:08:04.017123Z", "iopub.status.idle": "2021-05-19T14:08:04.020719Z", "shell.execute_reply": "2021-05-19T14:08:04.021142Z" }, "papermill": { "duration": 0.082248, "end_time": "2021-05-19T14:08:04.021269", "exception": false, "start_time": "2021-05-19T14:08:03.939021", "status": "completed" }, "tags": [], "id": "otherwise-decline" }, "source": [ "len(train_df.Name.unique()), train_df.shape" ], "id": "otherwise-decline", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.075265, "end_time": "2021-05-19T14:08:04.173597", "exception": false, "start_time": "2021-05-19T14:08:04.098332", "status": "completed" }, "tags": [], "id": "normal-breast" }, "source": [ "

Gender Column


" ], "id": "normal-breast" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:04.362959Z", "iopub.status.busy": "2021-05-19T14:08:04.362484Z", "iopub.status.idle": "2021-05-19T14:08:04.365310Z", "shell.execute_reply": "2021-05-19T14:08:04.364950Z" }, "papermill": { "duration": 0.084562, "end_time": "2021-05-19T14:08:04.365425", "exception": false, "start_time": "2021-05-19T14:08:04.280863", "status": "completed" }, "tags": [], "id": "naval-helena" }, "source": [ "#Indvidual count of Male vs Female\n", "train_df.Sex.value_counts()" ], "id": "naval-helena", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:04.520314Z", "iopub.status.busy": "2021-05-19T14:08:04.519862Z", "iopub.status.idle": "2021-05-19T14:08:04.525904Z", "shell.execute_reply": "2021-05-19T14:08:04.526259Z" }, "papermill": { "duration": 0.084636, "end_time": "2021-05-19T14:08:04.526378", "exception": false, "start_time": "2021-05-19T14:08:04.441742", "status": "completed" }, "tags": [], "id": "moral-mount" }, "source": [ "#Count of Male vs Female grouped on Target\n", "train_df.groupby(['Sex', 'Survived'])['Survived'].count()" ], "id": "moral-mount", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:04.680592Z", "iopub.status.busy": "2021-05-19T14:08:04.680157Z", "iopub.status.idle": "2021-05-19T14:08:04.801786Z", "shell.execute_reply": "2021-05-19T14:08:04.802135Z" }, "papermill": { "duration": 0.199724, "end_time": "2021-05-19T14:08:04.802266", "exception": false, "start_time": "2021-05-19T14:08:04.602542", "status": "completed" }, "tags": [], "id": "third-hawaiian" }, "source": [ "#Individual counts of Gender based on Target\n", "plt.figure(figsize = (16, 7))\n", "\n", "sns.countplot('Sex', hue = 'Survived', data = train_df)\n", "plt.show()" ], "id": "third-hawaiian", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:04.957938Z", "iopub.status.busy": "2021-05-19T14:08:04.957474Z", "iopub.status.idle": "2021-05-19T14:08:05.566849Z", "shell.execute_reply": "2021-05-19T14:08:05.567369Z" }, "papermill": { "duration": 0.688623, "end_time": "2021-05-19T14:08:05.567525", "exception": false, "start_time": "2021-05-19T14:08:04.878902", "status": "completed" }, "tags": [], "id": "victorian-sunday" }, "source": [ "#Individual counts of Gender based on Target for each Pclass Category\n", "sns.catplot(x = 'Sex', y = 'Survived', data = train_df, kind = 'bar', col = 'Pclass')\n", "plt.show()" ], "id": "victorian-sunday", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.079778, "end_time": "2021-05-19T14:08:06.827068", "exception": false, "start_time": "2021-05-19T14:08:06.747290", "status": "completed" }, "tags": [], "id": "extended-summary" }, "source": [ "

Age Column


" ], "id": "extended-summary" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:07.001326Z", "iopub.status.busy": "2021-05-19T14:08:07.000866Z", "iopub.status.idle": "2021-05-19T14:08:07.201761Z", "shell.execute_reply": "2021-05-19T14:08:07.201309Z" }, "papermill": { "duration": 0.295487, "end_time": "2021-05-19T14:08:07.201868", "exception": false, "start_time": "2021-05-19T14:08:06.906381", "status": "completed" }, "tags": [], "id": "charitable-investigation" }, "source": [ "#Age distribution\n", "plt.figure(figsize = (15, 6))\n", "plt.style.use('ggplot')\n", "\n", "sns.distplot(train_df['Age'])\n", "plt.show()" ], "id": "charitable-investigation", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:07.371299Z", "iopub.status.busy": "2021-05-19T14:08:07.370810Z", "iopub.status.idle": "2021-05-19T14:08:07.592930Z", "shell.execute_reply": "2021-05-19T14:08:07.592448Z" }, "papermill": { "duration": 0.3099, "end_time": "2021-05-19T14:08:07.593034", "exception": false, "start_time": "2021-05-19T14:08:07.283134", "status": "completed" }, "tags": [], "id": "northern-offset" }, "source": [ "#Checking outliers in Age grouped by Gender\n", "sns.catplot(x = 'Sex', y = 'Age', kind = 'box', data = train_df, height = 5, aspect = 2)\n", "plt.show()" ], "id": "northern-offset", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:07.762989Z", "iopub.status.busy": "2021-05-19T14:08:07.762510Z", "iopub.status.idle": "2021-05-19T14:08:08.271980Z", "shell.execute_reply": "2021-05-19T14:08:08.271484Z" }, "papermill": { "duration": 0.598278, "end_time": "2021-05-19T14:08:08.272090", "exception": false, "start_time": "2021-05-19T14:08:07.673812", "status": "completed" }, "tags": [], "id": "silver-commitment" }, "source": [ "#Checking outliers in Age grouped by Gender and Pclass\n", "sns.catplot(x = 'Sex', y = 'Age', kind = 'box', data = train_df, col = 'Pclass')\n", "plt.show()" ], "id": "silver-commitment", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "8dcfff41" }, "source": [ "

Cabin Column


\n", "\n", "\n", "\n" ], "id": "8dcfff41" }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.080796, "end_time": "2021-05-19T14:08:08.435523", "exception": false, "start_time": "2021-05-19T14:08:08.354727", "status": "completed" }, "tags": [], "id": "confirmed-italy" }, "source": [ "

Fare Column


" ], "id": "confirmed-italy" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:08.618643Z", "iopub.status.busy": "2021-05-19T14:08:08.607416Z", "iopub.status.idle": "2021-05-19T14:08:08.854098Z", "shell.execute_reply": "2021-05-19T14:08:08.853658Z" }, "papermill": { "duration": 0.33511, "end_time": "2021-05-19T14:08:08.854203", "exception": false, "start_time": "2021-05-19T14:08:08.519093", "status": "completed" }, "tags": [], "id": "rolled-coverage" }, "source": [ "#Histogram showing Fare counts\n", "plt.figure(figsize = (14, 6))\n", "\n", "plt.hist(train_df.Fare, bins = 60, color = 'orange')\n", "plt.xlabel('Fare')\n", "plt.show()" ], "id": "rolled-coverage", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:09.192273Z", "iopub.status.busy": "2021-05-19T14:08:09.190393Z", "iopub.status.idle": "2021-05-19T14:08:09.859094Z", "shell.execute_reply": "2021-05-19T14:08:09.858623Z" }, "papermill": { "duration": 0.756902, "end_time": "2021-05-19T14:08:09.859198", "exception": false, "start_time": "2021-05-19T14:08:09.102296", "status": "completed" }, "tags": [], "id": "powerful-diesel" }, "source": [ "#outliers in Fare based on Gender and Pclass\n", "sns.catplot(x = 'Sex', y = 'Fare', data = train_df, kind = 'box', col = 'Pclass')\n", "plt.show()" ], "id": "powerful-diesel", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.076815, "end_time": "2021-05-19T14:08:10.013683", "exception": false, "start_time": "2021-05-19T14:08:09.936868", "status": "completed" }, "tags": [], "id": "based-specific" }, "source": [ "

SibSp Column


" ], "id": "based-specific" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:10.174969Z", "iopub.status.busy": "2021-05-19T14:08:10.174369Z", "iopub.status.idle": "2021-05-19T14:08:10.176899Z", "shell.execute_reply": "2021-05-19T14:08:10.177365Z" }, "papermill": { "duration": 0.086483, "end_time": "2021-05-19T14:08:10.177502", "exception": false, "start_time": "2021-05-19T14:08:10.091019", "status": "completed" }, "tags": [], "id": "brazilian-proof" }, "source": [ "#Count of people with Siblings or spouse \n", "train_df['SibSp'].value_counts()" ], "id": "brazilian-proof", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:10.415478Z", "iopub.status.busy": "2021-05-19T14:08:10.414985Z", "iopub.status.idle": "2021-05-19T14:08:10.589368Z", "shell.execute_reply": "2021-05-19T14:08:10.588937Z" }, "papermill": { "duration": 0.295952, "end_time": "2021-05-19T14:08:10.589471", "exception": false, "start_time": "2021-05-19T14:08:10.293519", "status": "completed" }, "tags": [], "id": "stuck-contributor" }, "source": [ "#count of people with Sibilings or Spouse based on Target\n", "plt.figure(figsize = (16, 5))\n", "\n", "sns.countplot(x = 'SibSp', data = train_df, hue = 'Survived')\n", "plt.show()" ], "id": "stuck-contributor", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:10.751377Z", "iopub.status.busy": "2021-05-19T14:08:10.750773Z", "iopub.status.idle": "2021-05-19T14:08:11.087843Z", "shell.execute_reply": "2021-05-19T14:08:11.088226Z" }, "papermill": { "duration": 0.421161, "end_time": "2021-05-19T14:08:11.088352", "exception": false, "start_time": "2021-05-19T14:08:10.667191", "status": "completed" }, "tags": [], "id": "weird-italy" }, "source": [ "#Count of people who survived based on SibSp counts\n", "sns.catplot(x = 'SibSp', y = 'Survived', kind = 'bar', data = train_df, height = 5, aspect =2)\n", "plt.show()" ], "id": "weird-italy", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:11.263589Z", "iopub.status.busy": "2021-05-19T14:08:11.262899Z", "iopub.status.idle": "2021-05-19T14:08:11.812454Z", "shell.execute_reply": "2021-05-19T14:08:11.812803Z" }, "papermill": { "duration": 0.639754, "end_time": "2021-05-19T14:08:11.812958", "exception": false, "start_time": "2021-05-19T14:08:11.173204", "status": "completed" }, "tags": [], "id": "magnetic-laundry" }, "source": [ "#Count of people who survived based on SibSp counts Gender wise\n", "sns.catplot(x = 'SibSp', y = 'Survived', kind = 'bar', hue = 'Sex', data = train_df, height = 6, aspect = 2)\n", "plt.show()" ], "id": "magnetic-laundry", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:11.991611Z", "iopub.status.busy": "2021-05-19T14:08:11.991129Z", "iopub.status.idle": "2021-05-19T14:08:12.665522Z", "shell.execute_reply": "2021-05-19T14:08:12.665114Z" }, "papermill": { "duration": 0.767073, "end_time": "2021-05-19T14:08:12.665626", "exception": false, "start_time": "2021-05-19T14:08:11.898553", "status": "completed" }, "tags": [], "id": "appointed-housing" }, "source": [ "sns.catplot(x = 'SibSp', y = 'Survived', kind = 'bar', col = 'Sex', data = train_df)\n", "plt.show()" ], "id": "appointed-housing", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:12.843748Z", "iopub.status.busy": "2021-05-19T14:08:12.842459Z", "iopub.status.idle": "2021-05-19T14:08:13.546099Z", "shell.execute_reply": "2021-05-19T14:08:13.545548Z" }, "papermill": { "duration": 0.793747, "end_time": "2021-05-19T14:08:13.546200", "exception": false, "start_time": "2021-05-19T14:08:12.752453", "status": "completed" }, "tags": [], "id": "independent-ranch" }, "source": [ "#Count of people who survived based on SibSp counts Gender and Pclass wise\n", "sns.catplot(x = 'SibSp', y = 'Survived', col = 'Pclass', kind = 'bar', data = train_df)\n", "plt.show()" ], "id": "independent-ranch", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.086248, "end_time": "2021-05-19T14:08:14.483835", "exception": false, "start_time": "2021-05-19T14:08:14.397587", "status": "completed" }, "tags": [], "id": "superior-corner" }, "source": [ "

Parch Column


" ], "id": "superior-corner" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:14.664702Z", "iopub.status.busy": "2021-05-19T14:08:14.664121Z", "iopub.status.idle": "2021-05-19T14:08:14.666585Z", "shell.execute_reply": "2021-05-19T14:08:14.666975Z" }, "papermill": { "duration": 0.094913, "end_time": "2021-05-19T14:08:14.667096", "exception": false, "start_time": "2021-05-19T14:08:14.572183", "status": "completed" }, "tags": [], "id": "beneficial-distance" }, "source": [ "#Count of people with Parents/Childrens\n", "train_df.Parch.value_counts()" ], "id": "beneficial-distance", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:14.847921Z", "iopub.status.busy": "2021-05-19T14:08:14.847267Z", "iopub.status.idle": "2021-05-19T14:08:15.322517Z", "shell.execute_reply": "2021-05-19T14:08:15.322924Z" }, "papermill": { "duration": 0.567021, "end_time": "2021-05-19T14:08:15.323053", "exception": false, "start_time": "2021-05-19T14:08:14.756032", "status": "completed" }, "tags": [], "id": "strange-blake" }, "source": [ "sns.catplot(x = 'Parch', y = 'Survived', data = train_df, hue = 'Sex', kind = 'bar', height = 6, aspect = 2)\n", "plt.show()" ], "id": "strange-blake", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.087479, "end_time": "2021-05-19T14:08:15.501074", "exception": false, "start_time": "2021-05-19T14:08:15.413595", "status": "completed" }, "tags": [], "id": "familiar-advocacy" }, "source": [ "

Ticket Column


" ], "id": "familiar-advocacy" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:15.685683Z", "iopub.status.busy": "2021-05-19T14:08:15.682848Z", "iopub.status.idle": "2021-05-19T14:08:15.688739Z", "shell.execute_reply": "2021-05-19T14:08:15.689241Z" }, "papermill": { "duration": 0.098836, "end_time": "2021-05-19T14:08:15.689404", "exception": false, "start_time": "2021-05-19T14:08:15.590568", "status": "completed" }, "tags": [], "id": "acting-venture" }, "source": [ "#count based on ticket types\n", "train_df.Ticket.value_counts()" ], "id": "acting-venture", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:15.960680Z", "iopub.status.busy": "2021-05-19T14:08:15.959860Z", "iopub.status.idle": "2021-05-19T14:08:15.963096Z", "shell.execute_reply": "2021-05-19T14:08:15.963551Z" }, "papermill": { "duration": 0.139887, "end_time": "2021-05-19T14:08:15.963709", "exception": false, "start_time": "2021-05-19T14:08:15.823822", "status": "completed" }, "tags": [], "id": "unauthorized-southwest" }, "source": [ "len(train_df.Ticket.unique())" ], "id": "unauthorized-southwest", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.132593, "end_time": "2021-05-19T14:08:16.228860", "exception": false, "start_time": "2021-05-19T14:08:16.096267", "status": "completed" }, "tags": [], "id": "exclusive-netscape" }, "source": [ "

Embarked Column


" ], "id": "exclusive-netscape" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:16.502417Z", "iopub.status.busy": "2021-05-19T14:08:16.501652Z", "iopub.status.idle": "2021-05-19T14:08:16.506262Z", "shell.execute_reply": "2021-05-19T14:08:16.505647Z" }, "papermill": { "duration": 0.143835, "end_time": "2021-05-19T14:08:16.506389", "exception": false, "start_time": "2021-05-19T14:08:16.362554", "status": "completed" }, "tags": [], "id": "listed-pocket" }, "source": [ "train_df['Embarked'].value_counts()" ], "id": "listed-pocket", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:16.699174Z", "iopub.status.busy": "2021-05-19T14:08:16.698675Z", "iopub.status.idle": "2021-05-19T14:08:16.832975Z", "shell.execute_reply": "2021-05-19T14:08:16.832317Z" }, "papermill": { "duration": 0.231563, "end_time": "2021-05-19T14:08:16.833160", "exception": false, "start_time": "2021-05-19T14:08:16.601597", "status": "completed" }, "tags": [], "id": "given-parameter" }, "source": [ "#Count of people based on Port Embarked Gender wise\n", "plt.figure(figsize = (14, 6))\n", "\n", "sns.countplot('Embarked', hue = 'Survived', data = train_df)\n", "plt.show()" ], "id": "given-parameter", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:17.109835Z", "iopub.status.busy": "2021-05-19T14:08:17.109022Z", "iopub.status.idle": "2021-05-19T14:08:17.491860Z", "shell.execute_reply": "2021-05-19T14:08:17.491166Z" }, "papermill": { "duration": 0.523038, "end_time": "2021-05-19T14:08:17.491999", "exception": false, "start_time": "2021-05-19T14:08:16.968961", "status": "completed" }, "tags": [], "id": "welsh-lunch" }, "source": [ "sns.catplot(x = 'Embarked', y = 'Survived', kind = 'bar', data = train_df, col = 'Sex')\n", "plt.show()" ], "id": "welsh-lunch", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.091561, "end_time": "2021-05-19T14:08:17.676978", "exception": false, "start_time": "2021-05-19T14:08:17.585417", "status": "completed" }, "tags": [], "id": "ahead-teacher" }, "source": [ "\n", "

Findings From EDA :-

\n", "\n", "
    \n", "
  • Females Survived more than Males.
  • \n", "
  • Passengers Travelling in Higher Class Survived More than Passengers travelling in Lower Class.
  • \n", "
  • Name column is having all unique values so this column is not suitable for prediction, we have to drop it.
  • \n", "
  • In First Class Females were more than Males, that's why Fare of Females Passengers were high.
  • \n", "
  • Survival Rate is higher for those who were travelling with siblings or spouses.
  • \n", "
  • Passengers travelling with parents or children have higher survival rate.
  • \n", "
  • Ticket column is not useful and does not have an impact on survival.
  • \n", "
  • Cabin column have a lot of null values , it will be better to drop this column.
  • \n", "
  • Passengers travelling from Cherbourg port survived more than passengers travelling from other two ports.
  • \n", "
" ], "id": "ahead-teacher" }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.136998, "end_time": "2021-05-19T14:08:17.906660", "exception": false, "start_time": "2021-05-19T14:08:17.769662", "status": "completed" }, "tags": [], "id": "polished-spoke" }, "source": [ "

3.2 Data Preprocessing


" ], "id": "polished-spoke" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:18.180601Z", "iopub.status.busy": "2021-05-19T14:08:18.180027Z", "iopub.status.idle": "2021-05-19T14:08:18.181997Z", "shell.execute_reply": "2021-05-19T14:08:18.181508Z" }, "papermill": { "duration": 0.141156, "end_time": "2021-05-19T14:08:18.182109", "exception": false, "start_time": "2021-05-19T14:08:18.040953", "status": "completed" }, "tags": [], "id": "incredible-orientation" }, "source": [ "# dropping useless columns with noise, missing data ..etc\n", "train_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis = 1, inplace = True)" ], "id": "incredible-orientation", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:18.464810Z", "iopub.status.busy": "2021-05-19T14:08:18.461226Z", "iopub.status.idle": "2021-05-19T14:08:18.468929Z", "shell.execute_reply": "2021-05-19T14:08:18.468533Z" }, "papermill": { "duration": 0.152173, "end_time": "2021-05-19T14:08:18.469039", "exception": false, "start_time": "2021-05-19T14:08:18.316866", "status": "completed" }, "tags": [], "id": "classical-julian" }, "source": [ "train_df.head()" ], "id": "classical-julian", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:18.663050Z", "iopub.status.busy": "2021-05-19T14:08:18.662614Z", "iopub.status.idle": "2021-05-19T14:08:18.665092Z", "shell.execute_reply": "2021-05-19T14:08:18.665490Z" }, "papermill": { "duration": 0.101591, "end_time": "2021-05-19T14:08:18.665593", "exception": false, "start_time": "2021-05-19T14:08:18.564002", "status": "completed" }, "tags": [], "id": "killing-hands" }, "source": [ "train_df.isna().sum()" ], "id": "killing-hands", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:18.870155Z", "iopub.status.busy": "2021-05-19T14:08:18.869679Z", "iopub.status.idle": "2021-05-19T14:08:18.876665Z", "shell.execute_reply": "2021-05-19T14:08:18.876021Z" }, "papermill": { "duration": 0.108189, "end_time": "2021-05-19T14:08:18.876837", "exception": false, "start_time": "2021-05-19T14:08:18.768648", "status": "completed" }, "tags": [], "id": "french-resistance" }, "source": [ "# replacing Zero values of \"Fare\" column with mean of column\n", "train_df['Fare'] = train_df['Fare'].replace(0, train_df['Fare'].mean())" ], "id": "french-resistance", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:19.154160Z", "iopub.status.busy": "2021-05-19T14:08:19.153539Z", "iopub.status.idle": "2021-05-19T14:08:19.158789Z", "shell.execute_reply": "2021-05-19T14:08:19.158271Z" }, "papermill": { "duration": 0.139395, "end_time": "2021-05-19T14:08:19.158933", "exception": false, "start_time": "2021-05-19T14:08:19.019538", "status": "completed" }, "tags": [], "id": "included-dayton" }, "source": [ "# filling null values of \"Age\" column with mean value of the column\n", "\n", "train_df['Age'].fillna(train_df['Age'].mean(), inplace = True)" ], "id": "included-dayton", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:19.440186Z", "iopub.status.busy": "2021-05-19T14:08:19.439725Z", "iopub.status.idle": "2021-05-19T14:08:19.441366Z", "shell.execute_reply": "2021-05-19T14:08:19.441730Z" }, "papermill": { "duration": 0.143508, "end_time": "2021-05-19T14:08:19.441853", "exception": false, "start_time": "2021-05-19T14:08:19.298345", "status": "completed" }, "tags": [], "id": "social-least" }, "source": [ "# filling null values of \"Embarked\" column with mode value of the column\n", "\n", "train_df['Embarked'].fillna(train_df['Embarked'].mode()[0], inplace = True)" ], "id": "social-least", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:19.631739Z", "iopub.status.busy": "2021-05-19T14:08:19.631315Z", "iopub.status.idle": "2021-05-19T14:08:19.639022Z", "shell.execute_reply": "2021-05-19T14:08:19.638621Z" }, "papermill": { "duration": 0.103346, "end_time": "2021-05-19T14:08:19.639127", "exception": false, "start_time": "2021-05-19T14:08:19.535781", "status": "completed" }, "tags": [], "id": "modified-implement" }, "source": [ "# checking for null values after filling null values\n", "\n", "train_df.isna().sum()" ], "id": "modified-implement", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:19.838717Z", "iopub.status.busy": "2021-05-19T14:08:19.838251Z", "iopub.status.idle": "2021-05-19T14:08:19.842184Z", "shell.execute_reply": "2021-05-19T14:08:19.841777Z" }, "papermill": { "duration": 0.109337, "end_time": "2021-05-19T14:08:19.842281", "exception": false, "start_time": "2021-05-19T14:08:19.732944", "status": "completed" }, "tags": [], "id": "opposed-bolivia" }, "source": [ "train_df.head()" ], "id": "opposed-bolivia", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "8af949f1" }, "source": [ "

3.4 Feature Engineering and Feature Selection


" ], "id": "8af949f1" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:20.035372Z", "iopub.status.busy": "2021-05-19T14:08:20.034831Z", "iopub.status.idle": "2021-05-19T14:08:20.037832Z", "shell.execute_reply": "2021-05-19T14:08:20.037398Z" }, "papermill": { "duration": 0.102114, "end_time": "2021-05-19T14:08:20.037956", "exception": false, "start_time": "2021-05-19T14:08:19.935842", "status": "completed" }, "tags": [], "id": "coordinate-stretch" }, "source": [ "'''\n", "Not all Machine Leanrning Algorithms like categorical fearures as strings, \n", "so they need to me encoded to numeric formats so they these alorithms can ingest this data without throwing error \n", "labelEncoding : Converting String to numeric format'''\n", "train_df['Sex'] = train_df['Sex'].apply(lambda val: 1 if val == 'male' else 0)\n", "train_df.head()" ], "id": "coordinate-stretch", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "2d85e3c1" }, "source": [ "

OneHot Encoding : It is the representation of categorical variables as binary vectors


\n", "\n", "\n", "\n", "\n" ], "id": "2d85e3c1" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:20.230040Z", "iopub.status.busy": "2021-05-19T14:08:20.229556Z", "iopub.status.idle": "2021-05-19T14:08:20.231842Z", "shell.execute_reply": "2021-05-19T14:08:20.231489Z" }, "papermill": { "duration": 0.100606, "end_time": "2021-05-19T14:08:20.231980", "exception": false, "start_time": "2021-05-19T14:08:20.131374", "status": "completed" }, "tags": [], "id": "forced-thong" }, "source": [ "# Get one hot encoding of columns 'Embarked'\n", "one_hot = pd.get_dummies(train_df['Embarked'])\n", "# Drop column 'Embarked' as it is now encoded\n", "train_df = train_df.drop('Embarked',axis = 1)\n", "# Join the encoded train_df\n", "train_df = train_df.join(one_hot)" ], "id": "forced-thong", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:20.428374Z", "iopub.status.busy": "2021-05-19T14:08:20.427750Z", "iopub.status.idle": "2021-05-19T14:08:20.431280Z", "shell.execute_reply": "2021-05-19T14:08:20.430917Z" }, "papermill": { "duration": 0.105792, "end_time": "2021-05-19T14:08:20.431378", "exception": false, "start_time": "2021-05-19T14:08:20.325586", "status": "completed" }, "tags": [], "id": "several-dragon" }, "source": [ "train_df.head()" ], "id": "several-dragon", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:20.640766Z", "iopub.status.busy": "2021-05-19T14:08:20.639039Z", "iopub.status.idle": "2021-05-19T14:08:20.664309Z", "shell.execute_reply": "2021-05-19T14:08:20.663919Z" }, "papermill": { "duration": 0.135244, "end_time": "2021-05-19T14:08:20.664415", "exception": false, "start_time": "2021-05-19T14:08:20.529171", "status": "completed" }, "tags": [], "id": "chronic-senator" }, "source": [ "train_df.describe()" ], "id": "chronic-senator", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:20.859452Z", "iopub.status.busy": "2021-05-19T14:08:20.858851Z", "iopub.status.idle": "2021-05-19T14:08:20.862394Z", "shell.execute_reply": "2021-05-19T14:08:20.861945Z" }, "papermill": { "duration": 0.103584, "end_time": "2021-05-19T14:08:20.862506", "exception": false, "start_time": "2021-05-19T14:08:20.758922", "status": "completed" }, "tags": [], "id": "considerable-wrapping" }, "source": [ "#checking for feature variance is very important\n", "#High variance can affect the model performance, espcially for Linear models\n", "#so we need to normalize high variance features\n", "train_df.var()" ], "id": "considerable-wrapping", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:21.248518Z", "iopub.status.busy": "2021-05-19T14:08:21.247996Z", "iopub.status.idle": "2021-05-19T14:08:21.250726Z", "shell.execute_reply": "2021-05-19T14:08:21.249803Z" }, "papermill": { "duration": 0.102114, "end_time": "2021-05-19T14:08:21.250864", "exception": false, "start_time": "2021-05-19T14:08:21.148750", "status": "completed" }, "tags": [], "id": "minor-audio" }, "source": [ "#log normalization\n", "train_df['Age'] = np.log(train_df['Age'])\n", "train_df['Fare'] = np.log(train_df['Fare'])\n" ], "id": "minor-audio", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:21.453576Z", "iopub.status.busy": "2021-05-19T14:08:21.453089Z", "iopub.status.idle": "2021-05-19T14:08:21.456130Z", "shell.execute_reply": "2021-05-19T14:08:21.455646Z" }, "papermill": { "duration": 0.109325, "end_time": "2021-05-19T14:08:21.456237", "exception": false, "start_time": "2021-05-19T14:08:21.346912", "status": "completed" }, "tags": [], "id": "naval-milwaukee" }, "source": [ "train_df.var()" ], "id": "naval-milwaukee", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:21.846619Z", "iopub.status.busy": "2021-05-19T14:08:21.846167Z", "iopub.status.idle": "2021-05-19T14:08:21.858729Z", "shell.execute_reply": "2021-05-19T14:08:21.859119Z" }, "papermill": { "duration": 0.110671, "end_time": "2021-05-19T14:08:21.859254", "exception": false, "start_time": "2021-05-19T14:08:21.748583", "status": "completed" }, "tags": [], "id": "inside-fight" }, "source": [ "test_df = pd.read_csv('./inputs/tab/test.csv')\n", "test_df.head()" ], "id": "inside-fight", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:22.452860Z", "iopub.status.busy": "2021-05-19T14:08:22.452437Z", "iopub.status.idle": "2021-05-19T14:08:22.458183Z", "shell.execute_reply": "2021-05-19T14:08:22.457771Z" }, "papermill": { "duration": 0.104201, "end_time": "2021-05-19T14:08:22.458288", "exception": false, "start_time": "2021-05-19T14:08:22.354087", "status": "completed" }, "tags": [], "id": "micro-velvet" }, "source": [ "# dropping useless columns\n", "test_df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis = 1, inplace = True)\n", "# replacing Zero values of \"Fare\" column with mean of column\n", "test_df['Fare'] = test_df['Fare'].replace(0, test_df['Fare'].mean())\n", "# filling null values of \"Age\" column with mean value of the column\n", "test_df['Age'].fillna(test_df['Age'].mean(), inplace = True)\n", "# filling null values of \"Embarked\" column with mode value of the column\n", "test_df['Embarked'].fillna(test_df['Embarked'].mode()[0], inplace = True)" ], "id": "micro-velvet", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:22.655972Z", "iopub.status.busy": "2021-05-19T14:08:22.655464Z", "iopub.status.idle": "2021-05-19T14:08:22.658269Z", "shell.execute_reply": "2021-05-19T14:08:22.657866Z" }, "papermill": { "duration": 0.104291, "end_time": "2021-05-19T14:08:22.658370", "exception": false, "start_time": "2021-05-19T14:08:22.554079", "status": "completed" }, "tags": [], "id": "fitted-corporation" }, "source": [ "test_df.isna().sum()" ], "id": "fitted-corporation", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:23.460928Z", "iopub.status.busy": "2021-05-19T14:08:23.460290Z", "iopub.status.idle": "2021-05-19T14:08:23.464053Z", "shell.execute_reply": "2021-05-19T14:08:23.464413Z" }, "papermill": { "duration": 0.102972, "end_time": "2021-05-19T14:08:23.464538", "exception": false, "start_time": "2021-05-19T14:08:23.361566", "status": "completed" }, "tags": [], "id": "nervous-nelson" }, "source": [ "# filling null values of \"Fare\" column with mean value of the column\n", "test_df['Fare'].fillna(test_df['Fare'].mean(), inplace = True)\n", "test_df['Sex'] = test_df['Sex'].apply(lambda val: 1 if val == 'male' else 0)\n", "\n", "# Get one hot encoding of columns 'Embarked'\n", "one_hot_test = pd.get_dummies(test_df['Embarked'])\n", "# Drop column 'Embarked' as it is now encoded\n", "test_df = test_df.drop('Embarked',axis = 1)\n", "# Join the encoded train_df\n", "test_df = test_df.join(one_hot_test)\n", "# Log Normalization\n", "test_df['Age'] = np.log(test_df['Age'])\n", "test_df['Fare'] = np.log(test_df['Fare'])\n", "test_df.head()" ], "id": "nervous-nelson", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:24.487582Z", "iopub.status.busy": "2021-05-19T14:08:24.485670Z", "iopub.status.idle": "2021-05-19T14:08:24.490531Z", "shell.execute_reply": "2021-05-19T14:08:24.490868Z" }, "papermill": { "duration": 0.10742, "end_time": "2021-05-19T14:08:24.491029", "exception": false, "start_time": "2021-05-19T14:08:24.383609", "status": "completed" }, "tags": [], "id": "exempt-toyota" }, "source": [ "test_df.var()" ], "id": "exempt-toyota", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:24.696985Z", "iopub.status.busy": "2021-05-19T14:08:24.696440Z", "iopub.status.idle": "2021-05-19T14:08:24.700799Z", "shell.execute_reply": "2021-05-19T14:08:24.700398Z" }, "papermill": { "duration": 0.111132, "end_time": "2021-05-19T14:08:24.700934", "exception": false, "start_time": "2021-05-19T14:08:24.589802", "status": "completed" }, "tags": [], "id": "mysterious-convert" }, "source": [ "test_df.isna().any()" ], "id": "mysterious-convert", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:24.901650Z", "iopub.status.busy": "2021-05-19T14:08:24.901020Z", "iopub.status.idle": "2021-05-19T14:08:24.910365Z", "shell.execute_reply": "2021-05-19T14:08:24.910812Z" }, "papermill": { "duration": 0.109853, "end_time": "2021-05-19T14:08:24.910949", "exception": false, "start_time": "2021-05-19T14:08:24.801096", "status": "completed" }, "tags": [], "id": "circular-venue" }, "source": [ "test_df.head()" ], "id": "circular-venue", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:25.310340Z", "iopub.status.busy": "2021-05-19T14:08:25.309841Z", "iopub.status.idle": "2021-05-19T14:08:25.312407Z", "shell.execute_reply": "2021-05-19T14:08:25.312021Z" }, "papermill": { "duration": 0.106042, "end_time": "2021-05-19T14:08:25.312510", "exception": false, "start_time": "2021-05-19T14:08:25.206468", "status": "completed" }, "tags": [], "id": "arbitrary-external" }, "source": [ "# Dividing data to features and targets\n", "X = train_df.drop('Survived', axis = 1)\n", "y = train_df['Survived']" ], "id": "arbitrary-external", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:25.514022Z", "iopub.status.busy": "2021-05-19T14:08:25.513575Z", "iopub.status.idle": "2021-05-19T14:08:25.883200Z", "shell.execute_reply": "2021-05-19T14:08:25.883570Z" }, "papermill": { "duration": 0.471693, "end_time": "2021-05-19T14:08:25.883709", "exception": false, "start_time": "2021-05-19T14:08:25.412016", "status": "completed" }, "tags": [], "id": "ecological-honolulu" }, "source": [ "# splitting data intp training and test set\n", "\n", "from sklearn.model_selection import train_test_split\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.30, random_state = 0)" ], "id": "ecological-honolulu", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.099773, "end_time": "2021-05-19T14:08:26.084073", "exception": false, "start_time": "2021-05-19T14:08:25.984300", "status": "completed" }, "tags": [], "id": "adolescent-township" }, "source": [ "

Data Modeling


\n", "\n", "

Training Machine Learning models to predict survivors


" ], "id": "adolescent-township" }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.099202, "end_time": "2021-05-19T14:08:26.283145", "exception": false, "start_time": "2021-05-19T14:08:26.183943", "status": "completed" }, "tags": [], "id": "mounted-windows" }, "source": [ "

1. Logistic Regression


" ], "id": "mounted-windows" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:26.486843Z", "iopub.status.busy": "2021-05-19T14:08:26.486406Z", "iopub.status.idle": "2021-05-19T14:08:26.616797Z", "shell.execute_reply": "2021-05-19T14:08:26.616355Z" }, "papermill": { "duration": 0.2332, "end_time": "2021-05-19T14:08:26.616918", "exception": false, "start_time": "2021-05-19T14:08:26.383718", "status": "completed" }, "tags": [], "id": "gross-scotland" }, "source": [ "from sklearn.linear_model import LogisticRegression\n", "\n", "lr = LogisticRegression()\n", "lr.fit(X_train, y_train)\n", "\n", "# accuracy score, confusion matrix and classification report of logistic regression\n", "\n", "from sklearn.metrics import accuracy_score, confusion_matrix, classification_report\n", "\n", "lr_acc = accuracy_score(y_test, lr.predict(X_test))\n", "\n", "print(f\"Training Accuracy of Logistic Regression is {accuracy_score(y_train, lr.predict(X_train))}\")\n", "print(f\"Test Accuracy of Logistic Regression is {lr_acc}\")\n", "\n", "print(f\"Confusion Matrix :- \\n {confusion_matrix(y_test, lr.predict(X_test))}\")\n", "print(f\"Classofocation Report : -\\n {classification_report(y_test, lr.predict(X_test))}\")" ], "id": "gross-scotland", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:26.822702Z", "iopub.status.busy": "2021-05-19T14:08:26.822115Z", "iopub.status.idle": "2021-05-19T14:08:28.570110Z", "shell.execute_reply": "2021-05-19T14:08:28.569687Z" }, "papermill": { "duration": 1.853953, "end_time": "2021-05-19T14:08:28.570217", "exception": false, "start_time": "2021-05-19T14:08:26.716264", "status": "completed" }, "tags": [], "id": "casual-orientation" }, "source": [ "# hyper parameter tuning of logistic regression\n", "from sklearn.model_selection import GridSearchCV\n", "\n", "grid_param = {\n", " 'penalty': ['l1', 'l2'],\n", " 'C' : [0.001, 0.01, 0.1, 0.005, 0.5, 1, 10]\n", "}\n", "\n", "grid_search_lr = GridSearchCV(lr, grid_param, cv = 5, n_jobs = -1, verbose = 1)\n", "grid_search_lr.fit(X_train, y_train)" ], "id": "casual-orientation", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:28.777354Z", "iopub.status.busy": "2021-05-19T14:08:28.776665Z", "iopub.status.idle": "2021-05-19T14:08:28.780007Z", "shell.execute_reply": "2021-05-19T14:08:28.779604Z" }, "papermill": { "duration": 0.109751, "end_time": "2021-05-19T14:08:28.780116", "exception": false, "start_time": "2021-05-19T14:08:28.670365", "status": "completed" }, "tags": [], "id": "motivated-control" }, "source": [ "# best parameters and best score\n", "\n", "print(grid_search_lr.best_params_)\n", "print(grid_search_lr.best_score_)" ], "id": "motivated-control", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:28.990744Z", "iopub.status.busy": "2021-05-19T14:08:28.988940Z", "iopub.status.idle": "2021-05-19T14:08:29.002490Z", "shell.execute_reply": "2021-05-19T14:08:29.002118Z" }, "papermill": { "duration": 0.12053, "end_time": "2021-05-19T14:08:29.002595", "exception": false, "start_time": "2021-05-19T14:08:28.882065", "status": "completed" }, "tags": [], "id": "spiritual-puppy" }, "source": [ "# best estimator\n", "\n", "lr = grid_search_lr.best_estimator_\n", "\n", "# accuracy score, confusion matrix and classification report of logistic regression\n", "\n", "lr_acc = accuracy_score(y_test, lr.predict(X_test))\n", "\n", "print(f\"Training Accuracy of Logistic Regression is {accuracy_score(y_train, lr.predict(X_train))}\")\n", "print(f\"Test Accuracy of Logistic Regression is {lr_acc}\")\n", "\n", "print(f\"Confusion Matrix :- \\n {confusion_matrix(y_test, lr.predict(X_test))}\")\n", "print(f\"Classofocation Report : -\\n {classification_report(y_test, lr.predict(X_test))}\")" ], "id": "spiritual-puppy", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.153113, "end_time": "2021-05-19T14:08:41.327013", "exception": false, "start_time": "2021-05-19T14:08:41.173900", "status": "completed" }, "tags": [], "id": "parliamentary-constitution" }, "source": [ "

2. Random Forest


" ], "id": "parliamentary-constitution" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:41.642248Z", "iopub.status.busy": "2021-05-19T14:08:41.641568Z", "iopub.status.idle": "2021-05-19T14:08:41.971499Z", "shell.execute_reply": "2021-05-19T14:08:41.970991Z" }, "papermill": { "duration": 0.489435, "end_time": "2021-05-19T14:08:41.971628", "exception": false, "start_time": "2021-05-19T14:08:41.482193", "status": "completed" }, "tags": [], "id": "dangerous-price" }, "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "\n", "rd_clf = RandomForestClassifier()\n", "rd_clf.fit(X_train, y_train)\n", "\n", "# accuracy score, confusion matrix and classification report of random forest\n", "\n", "rd_clf_acc = accuracy_score(y_test, rd_clf.predict(X_test))\n", "\n", "print(f\"Training Accuracy of Decision Tree Classifier is {accuracy_score(y_train, rd_clf.predict(X_train))}\")\n", "print(f\"Test Accuracy of Decision Tree Classifier is {rd_clf_acc} \\n\")\n", "\n", "print(f\"Confusion Matrix :- \\n{confusion_matrix(y_test, rd_clf.predict(X_test))}\\n\")\n", "print(f\"Classification Report :- \\n {classification_report(y_test, rd_clf.predict(X_test))}\")" ], "id": "dangerous-price", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.097421, "end_time": "2021-05-19T14:08:59.699400", "exception": false, "start_time": "2021-05-19T14:08:59.601979", "status": "completed" }, "tags": [], "id": "vulnerable-prediction" }, "source": [ "

3. Gradient Boosting


" ], "id": "vulnerable-prediction" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:08:59.901067Z", "iopub.status.busy": "2021-05-19T14:08:59.900604Z", "iopub.status.idle": "2021-05-19T14:08:59.997423Z", "shell.execute_reply": "2021-05-19T14:08:59.997079Z" }, "papermill": { "duration": 0.199501, "end_time": "2021-05-19T14:08:59.997521", "exception": false, "start_time": "2021-05-19T14:08:59.798020", "status": "completed" }, "tags": [], "id": "mounted-smell" }, "source": [ "from sklearn.ensemble import GradientBoostingClassifier\n", "\n", "gb = GradientBoostingClassifier()\n", "gb.fit(X_train, y_train)\n", "\n", "# accuracy score, confusion matrix and classification report of gradient boosting classifier\n", "\n", "gb_acc = accuracy_score(y_test, gb.predict(X_test))\n", "\n", "print(f\"Training Accuracy of Decision Tree Classifier is {accuracy_score(y_train, gb.predict(X_train))}\")\n", "print(f\"Test Accuracy of Decision Tree Classifier is {gb_acc} \\n\")\n", "\n", "print(f\"Confusion Matrix :- \\n{confusion_matrix(y_test, gb.predict(X_test))}\\n\")\n", "print(f\"Classification Report :- \\n {classification_report(y_test, gb.predict(X_test))}\")" ], "id": "mounted-smell", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.097718, "end_time": "2021-05-19T14:09:00.193371", "exception": false, "start_time": "2021-05-19T14:09:00.095653", "status": "completed" }, "tags": [], "id": "sonic-cliff" }, "source": [ "

4. Stochastic Gradient Boosting (SGB)


" ], "id": "sonic-cliff" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:09:00.399307Z", "iopub.status.busy": "2021-05-19T14:09:00.398815Z", "iopub.status.idle": "2021-05-19T14:09:00.493810Z", "shell.execute_reply": "2021-05-19T14:09:00.493418Z" }, "papermill": { "duration": 0.202453, "end_time": "2021-05-19T14:09:00.493927", "exception": false, "start_time": "2021-05-19T14:09:00.291474", "status": "completed" }, "tags": [], "id": "stunning-dream" }, "source": [ "sgb = GradientBoostingClassifier(subsample = 0.90, max_features = 0.70)\n", "sgb.fit(X_train, y_train)\n", "\n", "# accuracy score, confusion matrix and classification report of stochastic gradient boosting classifier\n", "\n", "sgb_acc = accuracy_score(y_test, sgb.predict(X_test))\n", "\n", "print(f\"Training Accuracy of Decision Tree Classifier is {accuracy_score(y_train, sgb.predict(X_train))}\")\n", "print(f\"Test Accuracy of Decision Tree Classifier is {sgb_acc} \\n\")\n", "\n", "print(f\"Confusion Matrix :- \\n{confusion_matrix(y_test, sgb.predict(X_test))}\\n\")\n", "print(f\"Classification Report :- \\n {classification_report(y_test, sgb.predict(X_test))}\")" ], "id": "stunning-dream", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.107703, "end_time": "2021-05-19T14:09:00.706867", "exception": false, "start_time": "2021-05-19T14:09:00.599164", "status": "completed" }, "tags": [], "id": "intended-karma" }, "source": [ "

5. XGboost


" ], "id": "intended-karma" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:09:00.931572Z", "iopub.status.busy": "2021-05-19T14:09:00.931140Z", "iopub.status.idle": "2021-05-19T14:09:01.173931Z", "shell.execute_reply": "2021-05-19T14:09:01.174459Z" }, "papermill": { "duration": 0.361064, "end_time": "2021-05-19T14:09:01.174641", "exception": false, "start_time": "2021-05-19T14:09:00.813577", "status": "completed" }, "tags": [], "id": "employed-expansion" }, "source": [ "from xgboost import XGBClassifier\n", "\n", "xgb = XGBClassifier(booster = 'gbtree', learning_rate = 0.1, max_depth = 5, n_estimators = 180)\n", "xgb.fit(X_train, y_train)\n", "\n", "# accuracy score, confusion matrix and classification report of xgboost\n", "\n", "xgb_acc = accuracy_score(y_test, xgb.predict(X_test))\n", "\n", "print(f\"Training Accuracy of Decision Tree Classifier is {accuracy_score(y_train, xgb.predict(X_train))}\")\n", "print(f\"Test Accuracy of Decision Tree Classifier is {xgb_acc} \\n\")\n", "\n", "print(f\"Confusion Matrix :- \\n{confusion_matrix(y_test, xgb.predict(X_test))}\\n\")\n", "print(f\"Classification Report :- \\n {classification_report(y_test, xgb.predict(X_test))}\")" ], "id": "employed-expansion", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.108607, "end_time": "2021-05-19T14:09:06.914940", "exception": false, "start_time": "2021-05-19T14:09:06.806333", "status": "completed" }, "tags": [], "id": "cross-tourism" }, "source": [ "

Model Comparison


" ], "id": "cross-tourism" }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:09:07.140641Z", "iopub.status.busy": "2021-05-19T14:09:07.140186Z", "iopub.status.idle": "2021-05-19T14:09:07.145890Z", "shell.execute_reply": "2021-05-19T14:09:07.145469Z" }, "papermill": { "duration": 0.122396, "end_time": "2021-05-19T14:09:07.146007", "exception": false, "start_time": "2021-05-19T14:09:07.023611", "status": "completed" }, "tags": [], "id": "certain-accessory" }, "source": [ "models = pd.DataFrame({\n", " 'Model' : ['Logistic Regression', 'Gradient Boosting Classifier', 'Stochastic Gradient Boosting', 'XgBoost'], \n", " 'Score' : [lr_acc,gb_acc, sgb_acc, xgb_acc]\n", "})\n", "\n", "models.sort_values(by = 'Score', ascending = False)" ], "id": "certain-accessory", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2021-05-19T14:09:07.379088Z", "iopub.status.busy": "2021-05-19T14:09:07.378391Z", "iopub.status.idle": "2021-05-19T14:09:07.625087Z", "shell.execute_reply": "2021-05-19T14:09:07.624605Z" }, "papermill": { "duration": 0.366988, "end_time": "2021-05-19T14:09:07.625188", "exception": false, "start_time": "2021-05-19T14:09:07.258200", "status": "completed" }, "tags": [], "id": "optimum-handling" }, "source": [ "plt.figure(figsize = (15, 10))\n", "\n", "sns.barplot(x = 'Score', y = 'Model', data = models)\n", "plt.show()" ], "id": "optimum-handling", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "papermill": { "duration": 0.166379, "end_time": "2021-05-19T14:09:08.207799", "exception": false, "start_time": "2021-05-19T14:09:08.041420", "status": "completed" }, "tags": [], "id": "executive-implement" }, "source": [ "

Real Data Model Inference


" ], "id": "executive-implement" }, { "cell_type": "code", "metadata": { "id": "277219f8" }, "source": [ "pred_class = {0: 'Not Survived', 1: 'Survived'}\n", "def predict(test_input, test_target):\n", " final_results = gb.predict([test_input])\n", " test_input['Age'] = np.exp(test_input['Age'])\n", " test_input['Fare'] = np.exp(test_input['Fare'])\n", " print(f\"Features:\\n{test_input}\\n\\nClassifier prediction: {pred_class[final_results.item()]}\\nActual Target: {pred_class[test_target]}\")\n", " \n" ], "id": "277219f8", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "5f5011eb" }, "source": [ "test_input = X.iloc[0,:]\n", "test_target = y.iloc[0]\n", "predict(test_input, test_target)" ], "id": "5f5011eb", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "9d34da40" }, "source": [ "test_input = X.iloc[1,:]\n", "test_target = y.iloc[1]\n", "predict(test_input, test_target)" ], "id": "9d34da40", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "006866b9" }, "source": [ "

4. Approaching a Text (NLP) Problem



\n", "\n", "\n" ], "id": "006866b9" }, { "cell_type": "markdown", "metadata": { "id": "bc5ec3ec" }, "source": [ "## 4.1 Importance of solving NLP" ], "id": "bc5ec3ec" }, { "cell_type": "markdown", "metadata": { "id": "m4V2swA5V6t6" }, "source": [ "NLP — also known as computational linguistics — is the combination of AI and linguistics that allows us to talk to machines as if they were human.\n", "\n", "\n", "**In other words, NLP is an approach to process, analyze and understand large amount of text data.**" ], "id": "m4V2swA5V6t6" }, { "cell_type": "markdown", "metadata": { "id": "jM_IQ5lHWg5c" }, "source": [ "![](https://miro.medium.com/max/1400/0*Ql4uL-0XBOKWXHgk.jpg)" ], "id": "jM_IQ5lHWg5c" }, { "cell_type": "markdown", "metadata": { "id": "f3VVBg0hWv_X" }, "source": [ "### **1. Handling large volumes of text data**\n", "\n", "With the big data technology, NLP has entered the mainstream as this approach can now be applied to handle large volumes of text data via cloud/distributed computing at an unprecedented speed.\n" ], "id": "f3VVBg0hWv_X" }, { "cell_type": "markdown", "metadata": { "id": "ctqdYrZ9XF5M" }, "source": [ "### 2. Structuring highly unstructured data source\n", "\n", "Human language is astoundingly complex and diverse.\n", "\n", "NLP is important because it helps resolve ambiguity in language and adds useful numeric structure to the data for many downstream applications, such as speech recognition or text analytics." ], "id": "ctqdYrZ9XF5M" }, { "cell_type": "markdown", "metadata": { "id": "pgTP_KTxYb-n" }, "source": [ "## Components of NLP\n", "\n", "NLP can be divided into two basic components.\n", "\n", "* Natural Language Understanding\n", "* Natural Language Generation\n" ], "id": "pgTP_KTxYb-n" }, { "cell_type": "markdown", "metadata": { "id": "g0vcgPzhaC0P" }, "source": [ "#### Natural Language Understanding (NLU)\n", "\n", "There are lot of ambiguity while learning or trying to interpret a language.\n", "\n", "* Lexical Ambiguity can occur when a word carries different sense\n", "* Syntactical Ambiguity means when we see more than one meaning in a sequence of words. \n", "* Referential Ambiguity: Very often a text mentions as entity (something/someone)," ], "id": "g0vcgPzhaC0P" }, { "cell_type": "markdown", "metadata": { "id": "tYtjYCaNaZKJ" }, "source": [ "### Natural Language Generation (NLG)\n", "\n", "It involves :\n", "\n", "* Text planning − It includes retrieving the relevant content from knowledge base.\n", "* Sentence planning − It includes choosing required words, forming meaningful phrases, setting tone of the sentence.\n", "* Text Realization − It is mapping sentence plan into sentence structure." ], "id": "tYtjYCaNaZKJ" }, { "cell_type": "markdown", "metadata": { "id": "eceGeHrBdO30" }, "source": [ "## **Applications of NLP**" ], "id": "eceGeHrBdO30" }, { "cell_type": "markdown", "metadata": { "id": "vJuqd3lrdbbt" }, "source": [ "### Search Autocorrect and Autocomplete\n", "\n", "![](https://cdn.analyticsvidhya.com/wp-content/uploads/2020/07/na6-768x610.png)" ], "id": "vJuqd3lrdbbt" }, { "cell_type": "markdown", "metadata": { "id": "QGSNgu8mdjP2" }, "source": [ "### Language Translator\n", "\n", "![](https://cdn.analyticsvidhya.com/wp-content/uploads/2020/07/na7-768x283.png)" ], "id": "QGSNgu8mdjP2" }, { "cell_type": "markdown", "metadata": { "id": "GqLfpY43dr2Y" }, "source": [ "### Chatbots\n", "\n", "![](https://cdn.analyticsvidhya.com/wp-content/uploads/2020/07/na8-scaled-e1594202362955-768x1275.jpg)" ], "id": "GqLfpY43dr2Y" }, { "cell_type": "markdown", "metadata": { "id": "LWDzqBmSd3O9" }, "source": [ "# Sentiment analysis\n", "\n", "![](https://miro.medium.com/max/469/1*JSi4ZRojnsNZBJVtGUfXZQ.png)" ], "id": "LWDzqBmSd3O9" }, { "cell_type": "markdown", "metadata": { "id": "acdab842" }, "source": [ "

4.3 Basics of NLP


\n", "\n", "

Corpus

\n", "

A corpus is a large and structured set of machine-readable texts that have been produced in a natural communicative setting. Its plural is corpora. They can be derived in different ways like text that was originally electronic, transcripts of spoken language and optical character recognition, etc.

\n", "\n", "

Tokens

\n", "

The raw text is a sequence of characters (bytes), but most times it is useful to group those characters into contiguous units called tokens. In English, tokens correspond to words and numeric sequences separated by white-space characters or punctuation.

\n", "\n", "

Tokenization

\n", "

The process of breaking a text down into tokens is called tokenization.It can become more complicated than simply splitting text based on nonalphanumeric characters.

\n", "\n", "

N-grams

\n", "

They are basically a set of co-occurring words within a given window and when computing the n-grams you typically move one word forward. Trigram has three token, bigram has two tokens, a unigram one.

\n", "\n", "\n", "\n" ], "id": "acdab842" }, { "cell_type": "markdown", "metadata": { "id": "7bafa6b8" }, "source": [ "

Stemming

\n", "

Stemming algorithms work by cutting off the end or the beginning of the word, taking into account a list of common prefixes and suffixes that can be found in an inflected word.Stemming just removes the last few characters, often leading to incorrect meanings and spelling errors

\n", "\n", "

Lemmatization

\n", "

Lemmatization is similar to stemming but it brings context to the words. So it links words with similar meaning to one word.

" ], "id": "7bafa6b8" }, { "cell_type": "markdown", "metadata": { "id": "c48cf800" }, "source": [ "

4.4 NLP using Python


\n", "\n", "

NLTK

\n", "

It is a leading platform for building Python programs to work with human language data. It provides easy-to-use interfaces to over 50 corpora and lexical resources such as WordNet, along with a suite of text processing libraries for classification, tokenization, stemming, tagging, parsing, and semantic reasoning, wrappers for industrial-strength NLP libraries

\n", "\n", "

spaCy

\n", "

It is an open-source software library for advanced natural language processing, written in the programming languages Python and Cython.Unlike NLTK, which is widely used for teaching and research, spaCy focuses on providing software for production usage.spaCy also supports deep learning workflows that allow connecting statistical models trained by popular machine learning libraries like TensorFlow, PyTorch or MXNet through its own machine learning library Thinc

" ], "id": "c48cf800" }, { "cell_type": "markdown", "metadata": { "id": "4ec1df71" }, "source": [ "

4.5 Approaching real life NLP problem


\n", "\n", "

Intro to Twitter tweets processing using Python

\n", "\n", "\n" ], "id": "4ec1df71" }, { "cell_type": "markdown", "metadata": { "id": "2b2caa89" }, "source": [ "

Problem Statement


\n", "\n", "

With all of the tweets circulating every second it is hard to tell whether the sentiment behind a specific tweet will impact a company, or a person's, brand for being viral (positive), or devastate profit because it strikes a negative tone. Capturing sentiment in language is important in these times where decisions and reactions are created and updated in seconds. In order to get the sentiments out of tweets they need to by processed to tweaked in ways so that it could be fed into a algorithm to perform to sentiment inference. So we will take a look at pythonic approach of processing tweets

" ], "id": "2b2caa89" }, { "cell_type": "code", "metadata": { "id": "41075983" }, "source": [ "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import numpy as np\n", "from nltk.corpus import stopwords\n", "from nltk.util import ngrams\n", "from sklearn.feature_extraction.text import CountVectorizer\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from wordcloud import WordCloud, STOPWORDS\n", "from nltk.stem.wordnet import WordNetLemmatizer\n", "from nltk import word_tokenize\n", "from collections import defaultdict\n", "from collections import Counter\n", "plt.style.use('ggplot')\n", "stop=set(stopwords.words('english'))\n", "import re\n", "from nltk.tokenize import word_tokenize\n", "import gensim\n", "import string\n", "from tqdm import tqdm\n" ], "id": "41075983", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "7e030a21" }, "source": [ "tweet=pd.read_csv(\"./inputs/nlp/train.csv\")\n", "test=pd.read_csv(\"./inputs/nlp/test.csv\")" ], "id": "7e030a21", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "784f9a32" }, "source": [ "tweet.head(100)" ], "id": "784f9a32", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "927e46dc" }, "source": [ "print('There are {} rows and {} columns in train'.format(tweet.shape[0],tweet.shape[1]))\n", "print('There are {} rows and {} columns in train'.format(test.shape[0],test.shape[1]))" ], "id": "927e46dc", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "5c3b6dd1" }, "source": [ "

Class Distribution

\n", "\n", "

Before we begin with anything else,let's check the class distribution.There are only two classes 0 and 1.


\n" ], "id": "5c3b6dd1" }, { "cell_type": "code", "metadata": { "id": "f369049e" }, "source": [ "x=tweet.target.value_counts()\n", "sns.barplot(x.index,x)\n", "plt.gca().set_ylabel('samples')" ], "id": "f369049e", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "a5dfef16" }, "source": [ "

Exploratory Data Analysis (EDA)


" ], "id": "a5dfef16" }, { "cell_type": "code", "metadata": { "id": "e9f68ec8" }, "source": [ "#First,we will do very basic analysis,that is character level,word level and sentence level analysis.\n", "fig,(ax1,ax2)=plt.subplots(1,2,figsize=(10,5))\n", "tweet_len=tweet[tweet['target']==1]['text'].str.len()\n", "ax1.hist(tweet_len,color='red')\n", "ax1.set_title('disaster tweets')\n", "tweet_len=tweet[tweet['target']==0]['text'].str.len()\n", "ax2.hist(tweet_len,color='green')\n", "ax2.set_title('Not disaster tweets')\n", "fig.suptitle('Characters in tweets')\n", "plt.show()\n", "\n", "print(\"The distribution of both seems to be almost same.120 t0 140 characters in a tweet are the most common among both.\")" ], "id": "e9f68ec8", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "15e192cb" }, "source": [ "# Number of words in tweet\n", "fig,(ax1,ax2)=plt.subplots(1,2,figsize=(10,5))\n", "tweet_len=tweet[tweet['target']==1]['text'].str.split().map(lambda x: len(x))\n", "ax1.hist(tweet_len,color='red')\n", "ax1.set_title('disaster tweets')\n", "tweet_len=tweet[tweet['target']==0]['text'].str.split().map(lambda x: len(x))\n", "ax2.hist(tweet_len,color='green')\n", "ax2.set_title('Not disaster tweets')\n", "fig.suptitle('Words in a tweet')\n", "plt.show()\n", "\n", "print(\"Most of disaster tweets are around between 10 -20 word counts\\nand Non disaster tweet areound 15 to 20\")" ], "id": "15e192cb", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "2ae980eb" }, "source": [ "# Average word length in a tweet\n", "fig,(ax1,ax2)=plt.subplots(1,2,figsize=(10,5))\n", "word=tweet[tweet['target']==1]['text'].str.split().apply(lambda x : [len(i) for i in x])\n", "sns.distplot(word.map(lambda x: np.mean(x)),ax=ax1,color='red')\n", "ax1.set_title('disaster')\n", "word=tweet[tweet['target']==0]['text'].str.split().apply(lambda x : [len(i) for i in x])\n", "sns.distplot(word.map(lambda x: np.mean(x)),ax=ax2,color='green')\n", "ax2.set_title('Not disaster')\n", "fig.suptitle('Average word length in each tweet')\n" ], "id": "2ae980eb", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "a26f0c7c" }, "source": [ "

Creating Corpus

\n", "\n", "

We need to perform further EDA we need corpus


\n" ], "id": "a26f0c7c" }, { "cell_type": "code", "metadata": { "id": "7fa39f5c" }, "source": [ "#creating text corpus based on target\n", "#corpus of Disaster vs Non Disaster Tweet\n", "\n", "def build_corpus(target):\n", " corpus=[]\n", " \n", " for x in tweet[tweet['target']==target]['text'].str.split():\n", " for i in x:\n", " corpus.append(i)\n", " return corpus" ], "id": "7fa39f5c", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "4a33faed" }, "source": [ "#wordcloud of non disaster tweets\n", "comment_words = ''\n", "stopwords = set(STOPWORDS)\n", "comment_words += \" \".join(build_corpus(0))+\" \"\n", "\n", "wordcloud = WordCloud(width = 800, height = 800,\n", " background_color ='white',\n", " stopwords = stopwords,\n", " min_font_size = 10).generate(comment_words)\n", "\n", "plt.figure(figsize = (8, 8), facecolor = None)\n", "plt.imshow(wordcloud)\n", "plt.axis(\"off\")\n", "plt.tight_layout(pad = 0)\n", "plt.show()" ], "id": "4a33faed", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "4b5f89cf" }, "source": [ "#wordcloud of disaster tweets\n", "comment_words = ''\n", "stopwords = set(STOPWORDS)\n", "comment_words += \" \".join(build_corpus(1))+\" \"\n", "\n", "wordcloud = WordCloud(width = 800, height = 800,\n", " background_color ='black',\n", " stopwords = stopwords,\n", " min_font_size = 10).generate(comment_words)\n", "\n", "plt.figure(figsize = (8, 8), facecolor = None)\n", "plt.imshow(wordcloud)\n", "plt.axis(\"off\")\n", "plt.tight_layout(pad = 0)\n", " \n", "plt.show()" ], "id": "4b5f89cf", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "143d08ef" }, "source": [ "#creating corpus based of Non Disaster Tweet\n", "corpus=build_corpus(0)\n", "\n", "#build dicr based on NoN disaster tweet\n", "dic=defaultdict(int)\n", "for word in corpus:\n", " if word in stop:\n", " dic[word]+=1\n", " \n", "#choosing top tweets\n", "top=sorted(dic.items(), key=lambda x:x[1],reverse=True)[:10] \n", "top" ], "id": "143d08ef", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "443c251d" }, "source": [ "#bar chart showing common words in NoN disaster tweets\n", "x,y = zip(*top)\n", "plt.figure(figsize=(15,8))\n", "plt.bar(x, y);" ], "id": "443c251d", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "f776c8cf" }, "source": [ "#creating the same for Diaster Tweet\n", "corpus=build_corpus(1)\n", "\n", "dic=defaultdict(int)\n", "for word in corpus:\n", " if word in stop:\n", " dic[word]+=1\n", " \n", "top=sorted(dic.items(), key=lambda x:x[1],reverse=True)[:10] \n", " \n", "plt.figure(figsize=(15,8))\n", "x,y=zip(*top)\n", "plt.bar(x,y)\n", "plt.show()\n", "\n", "print(\"Words like 'the','in','of' dominates, but these words have not much meaning or prediction capabilities\")" ], "id": "f776c8cf", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "349c8bd4" }, "source": [ "#Analysing punctuations for Disaster Tweets\n", "plt.figure(figsize=(15,8))\n", "corpus=build_corpus(1)\n", "\n", "dic=defaultdict(int)\n", "import string\n", "special = string.punctuation\n", "for i in (corpus):\n", " if i in special:\n", " dic[i]+=1\n", " \n", "x,y=zip(*dic.items())\n", "plt.title(\"Analysing punctuations for Disaster Tweets\")\n", "plt.bar(x,y,color='orange');\n" ], "id": "349c8bd4", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "e954149c" }, "source": [ "#Analysing punctuations for Non-Disaster Tweets\n", "plt.figure(figsize=(15,8))\n", "corpus=build_corpus(0)\n", "\n", "dic=defaultdict(int)\n", "import string\n", "special = string.punctuation\n", "for i in (corpus):\n", " if i in special:\n", " dic[i]+=1\n", " \n", "x,y=zip(*dic.items())\n", "plt.title(\"Analysing punctuations for Non-Disaster Tweets\")\n", "plt.bar(x,y,color='red');" ], "id": "e954149c", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "1c946180" }, "source": [ "#performing the same for complete corpus\n", "complete_corpus = []\n", "complete_corpus.extend(build_corpus(0))\n", "complete_corpus.extend(build_corpus(1))\n", "counter=Counter(corpus)\n", "most=counter.most_common()\n", "x=[]\n", "y=[]\n", "for word,count in most[:40]:\n", " if (word not in stop) :\n", " x.append(word)\n", " y.append(count)\n", "plt.figure(figsize=(15,8))\n", "sns.barplot(x=y,y=x).set_title(\"Complete corpus common words\")\n", "plt.show()" ], "id": "1c946180", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "7f68c536" }, "source": [ "

N-gram Analysis

\n", "\n", "

We can perform Bigram and Trigram analysis on this data to understand word combinations in the dataset


\n", "\n" ], "id": "7f68c536" }, { "cell_type": "code", "metadata": { "id": "94e5b1c0" }, "source": [ "def get_top_tweet_bigrams(corpus, gram=2, n=None):\n", " vec = CountVectorizer(ngram_range=(gram, gram)).fit(corpus)\n", " bag_of_words = vec.transform(corpus)\n", " sum_words = bag_of_words.sum(axis=0) \n", " words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]\n", " words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)\n", " return words_freq[:n]" ], "id": "94e5b1c0", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ad2b33c9" }, "source": [ "#count of common bigrams\n", "plt.figure(figsize=(15,8))\n", "top_tweet_bigrams=get_top_tweet_bigrams(tweet['text'], gram=2)[:10]\n", "x,y=map(list,zip(*top_tweet_bigrams))\n", "sns.barplot(x=y,y=x).set_title(\"Common Bigram Count\")\n", "plt.show()" ], "id": "ad2b33c9", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ddcd245e" }, "source": [ "#count of common trigrams\n", "plt.figure(figsize=(15,8))\n", "top_tweet_trigrams=get_top_tweet_bigrams(tweet['text'], gram=3)[:10]\n", "x,y=map(list,zip(*top_tweet_trigrams))\n", "sns.barplot(x=y,y=x).set_title(\"Common Trigram Count\")\n", "plt.show()" ], "id": "ddcd245e", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "7c7f5e82" }, "source": [ "

After performing Bigram and Trigram analysis on this data clearly shows the need of data cleaning


" ], "id": "7c7f5e82" }, { "cell_type": "markdown", "metadata": { "id": "4199317e" }, "source": [ "

Data Cleaning

\n", "\n", "

Raw twitter data contains a lot of noise like stopwords, irrelavant punctuations, emojis ..etc which could kill its prediction capabilities. So let's clean them


\n", "\n" ], "id": "4199317e" }, { "cell_type": "code", "metadata": { "id": "d91d7534" }, "source": [ "df=pd.concat([tweet,test])\n", "df.shape" ], "id": "d91d7534", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "80618224" }, "source": [ "

Lemmatization

" ], "id": "80618224" }, { "cell_type": "code", "metadata": { "id": "85f6917a" }, "source": [ "def lemma(text):\n", " text = text.lower()\n", " lmtzr = WordNetLemmatizer()\n", " text = \" \".join([lmtzr.lemmatize(word) for word in word_tokenize(text)])\n", " return text\n", "\n", "ex = \"Our Deeds are the Reason of this #earthquake\"\n", "lemma(ex)" ], "id": "85f6917a", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "57fb679f" }, "source": [ "#apply lemmatization\n", "df['text']=df['text'].apply(lambda x : lemma(x))" ], "id": "57fb679f", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "22b2f525" }, "source": [ "

Removing URL

" ], "id": "22b2f525" }, { "cell_type": "code", "metadata": { "id": "3ab272e6" }, "source": [ "#Regex to remove URL\n", "def remove_URL(text):\n", " url = re.compile(r'https?://\\S+|www\\.\\S+')\n", " return url.sub(r'',text)\n", "\n", "sample_URL = \"please find the titanic project at https://www.kaggle.com/c/titanic/overview\"\n", "remove_URL(sample_URL)" ], "id": "3ab272e6", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "1a77ce09" }, "source": [ "#Apply the cleaning process\n", "df['text']=df['text'].apply(lambda x : remove_URL(x))" ], "id": "1a77ce09", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "0df25952" }, "source": [ "df[['text','target']].head()" ], "id": "0df25952", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "3d530aa8" }, "source": [ "

Removing HTML

" ], "id": "3d530aa8" }, { "cell_type": "code", "metadata": { "id": "90cb2b2b" }, "source": [ "#remove html\n", "def remove_html(text):\n", " html=re.compile(r'<.*?>')\n", " return html.sub(r'',text)\n", "\n", "sample_HTML = '''\n", "\n", "
\n", "

Welcome to HTML\n", "kaggle\n", "

test paragraph

\n", "

\n", "\n", "'''\n", "print(remove_html(sample_HTML))" ], "id": "90cb2b2b", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "a2188b79" }, "source": [ "" ], "id": "a2188b79", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "c9ef8c65" }, "source": [ "df['text']=df['text'].apply(lambda x : remove_html(x))\n", "df[['text','target']].head()" ], "id": "c9ef8c65", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "e7e0769b" }, "source": [ "

Removing Emojis 😔

\n", "\n", "

Emoji's have great prediction capabilities but they need to be encoded to some numeric problem, Lets just remove it for simplicity sake


\n", "\n" ], "id": "e7e0769b" }, { "cell_type": "code", "metadata": { "id": "5a4ebe7d" }, "source": [ "def remove_emoji(text):\n", " emoji_pattern = re.compile(\"[\"\n", " u\"\\U0001F600-\\U0001F64F\" # emoticons\n", " u\"\\U0001F300-\\U0001F5FF\" # symbols & pictographs\n", " u\"\\U0001F680-\\U0001F6FF\" # transport & map symbols\n", " u\"\\U0001F1E0-\\U0001F1FF\" # flags (iOS)\n", " u\"\\U00002702-\\U000027B0\"\n", " u\"\\U000024C2-\\U0001F251\"\n", " \"]+\", flags=re.UNICODE)\n", " return emoji_pattern.sub(r'', text)\n" ], "id": "5a4ebe7d", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "8eaf1eb0" }, "source": [ "\n", "remove_emoji(\"its flooding i am scared 😔😔\")\n", "\n" ], "id": "8eaf1eb0", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "a6f0d4cb" }, "source": [ "df['text']=df['text'].apply(lambda x: remove_emoji(x))\n", "df[['text','target']].head()" ], "id": "a6f0d4cb", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "82c2b58a" }, "source": [ "

Removing punctuations

\n", "\n", "

Just like emojis punctuation also posses some kind of meaning like :) :( ..etc ,\n", "But its hard to encode such punctation to numeric formats when vectorizing text. So we can remove it


\n", "\n" ], "id": "82c2b58a" }, { "cell_type": "code", "metadata": { "id": "4eec6b24" }, "source": [ "def remove_punct(text):\n", " table=str.maketrans('','',string.punctuation)\n", " return text.translate(table)\n", "\n", "example=\"There are #earthquakes\"\n", "print(remove_punct(example))" ], "id": "4eec6b24", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "3df59cc1" }, "source": [ "df['text']=df['text'].apply(lambda x : remove_punct(x))\n", "df[['text','target']].head()" ], "id": "3df59cc1", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "3313ced9" }, "source": [ "

Vectorization

\n", "\n", "

Text needs to be encoded to numerical format before we feed it into a machine learning algorithm


\n", "\n", "

Bag-Of-Words - The model is simple in that it throws away all of the order information in the words and focuses on the occurrence of words in a document.


\n", "\n", "\n", "\n" ], "id": "3313ced9" }, { "cell_type": "markdown", "metadata": { "id": "5d9c77f5" }, "source": [ "\n", "

BOW vectorization techniques

\n", "\n", "

1. convert text to word count vectors with CountVectorizer
2. convert text to word frequency vectors with TfidfVectorizer.


\n", "\n" ], "id": "5d9c77f5" }, { "cell_type": "markdown", "metadata": { "id": "8b09721c" }, "source": [ "

Word Counts with CountVectorizer

\n", "\n", "\n", "

The CountVectorizer provides a simple way to both tokenize a collection of text documents and build a vocabulary of known words, but also to encode new documents using that vocabulary.


\n", "\n" ], "id": "8b09721c" }, { "cell_type": "code", "metadata": { "id": "40024021" }, "source": [ "# list of text documents\n", "text = [\"heard about the earthquake is different city stay safe everyone and hope the city fast recovery\"]\n", "# create the transform\n", "vectorizer = CountVectorizer()\n", "# tokenize and build vocab\n", "vectorizer.fit(text)\n", "# summarize\n", "print(vectorizer.vocabulary_)\n", "# encode document\n", "vector = vectorizer.transform(text)\n", "# summarize encoded vector\n", "print(vector.shape)\n", "print(type(vector))\n", "print(vector.toarray())" ], "id": "40024021", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "4ba4c155" }, "source": [ "#vectorizing the whole tweet\n", "vectorizer = CountVectorizer()\n", "vectorizer.fit(df.text.values)\n", "vector = vectorizer.transform(df.text.values)\n", "print(vector.shape)" ], "id": "4ba4c155", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "69ef10ea" }, "source": [ "#to see the vocab dictionary \n", "# print(vectorizer.vocabulary_)\n" ], "id": "69ef10ea", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "3f078b9d" }, "source": [ "

Word Frequencies with TfidfVectorizer

\n", "\n", "

TF-IDF are word frequency scores that try to highlight words that are more interesting, e.g. frequent in a document but not across documents.


\n", "\n", "

* Term Frequency: This summarizes how often a given word appears within a document.
* Inverse Document Frequency: This downscales words that appear a lot across documents.


\n", "\n" ], "id": "3f078b9d" }, { "cell_type": "code", "metadata": { "id": "fc48325d" }, "source": [ "# list of text documents\n", "text = [\"The quick brown fox jumped over the lazy dog.\",\"The dog.\",\"The fox\"]\n", "# create the transform\n", "vectorizer = TfidfVectorizer()\n", "# tokenize and build vocab\n", "vectorizer.fit(text)\n", "# summarize\n", "print(vectorizer.vocabulary_)\n", "print(vectorizer.idf_)\n", "# encode document\n", "vector = vectorizer.transform([text[0]])\n", "# summarize encoded vector\n", "print(vector.shape)\n", "print(vector.toarray())" ], "id": "fc48325d", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ad881af0" }, "source": [ "vectorizer = TfidfVectorizer()\n", "# tokenize and build vocab\n", "vectorizer.fit(df.text.values);" ], "id": "ad881af0", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "3767faee" }, "source": [ "# print(vectorizer.vocabulary_)" ], "id": "3767faee", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "3f05a6f7" }, "source": [ "print(vectorizer.idf_.shape)\n", "print(vectorizer.idf_)" ], "id": "3f05a6f7", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "2ee8db5c" }, "source": [ "vector = vectorizer.transform([text[0]])\n", "# summarize encoded vector\n", "print(vector.shape)\n", "print(vector.toarray())" ], "id": "2ee8db5c", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "_2Pcu3EIMtQL" }, "source": [ "------" ], "id": "_2Pcu3EIMtQL" }, { "cell_type": "markdown", "metadata": { "id": "YLNGW22-lOYv" }, "source": [ "# **What is Computer Vision**\n", "\n", "Two definitions of computer vision:\n", "\n", "**Computer vision can be defined as\n", "a scientific field that extracts information out of digital images**. The type of\n", "information gained from an image can vary from identification, space\n", "measurements for navigation, or augmented reality applications.\n", "

\n", "Another way to define computer vision is through its applications.\n", "\n", "**Computer vision is building algorithms that can understand the content\n", "of images and use it for other applications**.\n", "\n", "\n", "
\n", "
\n", "Computer vision at\n", "the intersection of multiple\n", "scientific fields\n", "
\n", "\n", "Source: Computer Vision: Foundation and Applications by Ranjay Krishna - Stanford University\n" ], "id": "YLNGW22-lOYv" }, { "cell_type": "markdown", "metadata": { "id": "MM3XTRZFLSFC" }, "source": [ "
\n", "
\n", "\n", " Source: Lecture'1: Introductionto Computer Vision by Prof. FeiBFei Li\n", "Stanford Vision Lab\n", "\n" ], "id": "MM3XTRZFLSFC" }, { "cell_type": "markdown", "metadata": { "id": "r1QD2_khN2QK" }, "source": [ "
\n", "
\n", "\n", " Source: Lecture'1: Introductionto Computer Vision by Prof. FeiBFei Li\n", "Stanford Vision Lab" ], "id": "r1QD2_khN2QK" }, { "cell_type": "markdown", "metadata": { "id": "E9qfgEIfUMSW" }, "source": [ "

Why Computer Vision Matter

\n", "\n", "\n", "
\n", "\n", "
" ], "id": "E9qfgEIfUMSW" }, { "cell_type": "markdown", "metadata": { "id": "19MIfcFkng7a" }, "source": [ "# **How is computer vision used today?**" ], "id": "19MIfcFkng7a" }, { "cell_type": "markdown", "metadata": { "id": "8bDHgRMNo8aE" }, "source": [ "

Optical character recognition (OCR)

\n", "\n", "Technology to convert scanned docs to text\n", "\n", "
\n", "
\n", "License plate readers\n", "
\n", "\n" ], "id": "8bDHgRMNo8aE" }, { "cell_type": "markdown", "metadata": { "id": "PyFL7J36YRdr" }, "source": [ "

Face detection

\n", "\n", "
\n", "
\n", "
\n" ], "id": "PyFL7J36YRdr" }, { "cell_type": "markdown", "metadata": { "id": "CsvjHYaNYRXD" }, "source": [ "

Face analysis and recognition

\n", "\n", "
\n", "\n", "
" ], "id": "CsvjHYaNYRXD" }, { "cell_type": "markdown", "metadata": { "id": "13YvD2iqZRLV" }, "source": [ "

Login without a password

\n", "\n", "
\n", "\n", "
" ], "id": "13YvD2iqZRLV" }, { "cell_type": "markdown", "metadata": { "id": "6o0LGU0caCb2" }, "source": [ "

Special effects: shape capture

\n", "\n", "
\n", "\n", "
" ], "id": "6o0LGU0caCb2" }, { "cell_type": "markdown", "metadata": { "id": "ILBgNeRXaCku" }, "source": [ "

Special effects: motion capture

\n", "\n", "
\n", "\n", "
" ], "id": "ILBgNeRXaCku" }, { "cell_type": "markdown", "metadata": { "id": "9tlPJSA_aCt-" }, "source": [ "

3D face tracking with consumer cameras

\n", "\n", "
\n", "\n", "
" ], "id": "9tlPJSA_aCt-" }, { "cell_type": "markdown", "metadata": { "id": "M9oJ5P7paC4E" }, "source": [ "

Image synthesis

\n", "\n", "
\n", "\n", "
" ], "id": "M9oJ5P7paC4E" }, { "cell_type": "markdown", "metadata": { "id": "tmw6L4NBb01d" }, "source": [ "

Smart Cars

\n", "\n", "
\n", "\n", "
" ], "id": "tmw6L4NBb01d" }, { "cell_type": "markdown", "metadata": { "id": "xLm5oI6ib0vn" }, "source": [ "

Self Driving Cars

\n", "\n", "
\n", "\n", "
" ], "id": "xLm5oI6ib0vn" }, { "cell_type": "markdown", "metadata": { "id": "j8vvL2U2b08Q" }, "source": [ "

Image synthesis

\n", "\n", "
\n", "\n", "
" ], "id": "j8vvL2U2b08Q" }, { "cell_type": "markdown", "metadata": { "id": "9r8OFeY2mgVm" }, "source": [ "# **Challenges**\n", "\n", "
\n", "
" ], "id": "9r8OFeY2mgVm" }, { "cell_type": "markdown", "metadata": { "id": "AJdmshPZmy6s" }, "source": [ "
" ], "id": "AJdmshPZmy6s" }, { "cell_type": "markdown", "metadata": { "id": "6JN1V7LgM8xa" }, "source": [ "---" ], "id": "6JN1V7LgM8xa" }, { "cell_type": "markdown", "metadata": { "id": "m_eHPt4V0wxK" }, "source": [ "# **Image Processing**" ], "id": "m_eHPt4V0wxK" }, { "cell_type": "markdown", "metadata": { "id": "1peauNei7FPt" }, "source": [ "## **Point Operators**\n", "\n", "The simplest kind of image processing transforms are point operators, where each output pixel value depends on only the corresponding input pixel value (plus some globally collected information or parameters)" ], "id": "1peauNei7FPt" }, { "cell_type": "markdown", "metadata": { "id": "E8sx171T7oTE" }, "source": [ "### **Pixel Transforms**\n", "\n", "Pixel transforms are applying a funtion over each pixel value in the images.\n", "It can be a linear function with a gain and bias, which are used in brightness and contrast control, or can be a non-linear function which are used in gamma corrections in digital cameras\n" ], "id": "E8sx171T7oTE" }, { "cell_type": "markdown", "metadata": { "id": "JbFnGjoY7oEo" }, "source": [ "### **Color transforms**\n", "\n", "Brightening a picture by adding a constant value to all three color channels created undesired side effects like affecting its hue and saturation.\n", "\n", "chromaticity coordinates or even simpler color ratios\n", "can first be computed and then used after manipulating (e.g., brightening) the luminance Y to re-compute a valid RGB image with the same hue and saturation." ], "id": "JbFnGjoY7oEo" }, { "cell_type": "markdown", "metadata": { "id": "3CjLqIgwnlNp" }, "source": [ "### **Compositing and matting**\n", "\n", "The process of extracting the object from the original image is often called matting. \n", "
\n", "The process of inserting it into another image (without visible artifacts) is called compositing.\n", "

\n", "\n", "
\n", "\n", "(a) Original Image\n", "(b) alpha-matted color image\n", "(c) alpha channel \n", "(d) Composited image" ], "id": "3CjLqIgwnlNp" }, { "cell_type": "markdown", "metadata": { "id": "oGBCVKLPpULi" }, "source": [ "### **Histogram equalization**\n", "\n", "How can be determine the best values for brightness and gain controls?\n", "\n", "The answer is to plot the histogram of the individual color channels and luminance values. From this distribution, we can compute relevant statistics\n", "such as the minimum, maximum, and average intensity values\n", "\n", "
\n", "Example: Histogram equalization\n", "
\n", "
\n", "\n", "\n" ], "id": "oGBCVKLPpULi" }, { "cell_type": "markdown", "metadata": { "id": "ExiddWSMZ_YS" }, "source": [ "## **Linear Filtering**\n", "\n", "Neighborhood operator or local operator, which uses a collection of pixel values in the vicinity of a given pixel to determine its final output value.\n", "\n", "In addition to performing local tone adjustment, neighborhood operators can be used to filter images to add soft blur, sharpen details, accentuate edges, or remove noise." ], "id": "ExiddWSMZ_YS" }, { "cell_type": "markdown", "metadata": { "id": "-tzQru1Iewz6" }, "source": [ "### **1. Seperable Filtering**\n", "\n", "The process of performing convolution operation can be sped up by a convolution kernel which is called seperable.\n", "\n", "### **2. Band-pass and steerable filters**\n", "\n", "Band-pass filters filter out both low and high frequencies which kernel can be created by first smoothing the image with a Guassian filter and then taking the first or second derivatives." ], "id": "-tzQru1Iewz6" }, { "cell_type": "markdown", "metadata": { "id": "5UVlzxXojSRy" }, "source": [ "## **More neighborhood operators**\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "id": "5UVlzxXojSRy" }, { "cell_type": "markdown", "metadata": { "id": "HinLkjNrqPEl" }, "source": [ "### **1. Non-linear filtering**\n", "\n", "Consider an image with 'shot noise', noise with occasioanlly large pixel values. Using a linear filter such as regular blurring with gaussian filter will only turn those large values into softer spots.\n", "\n", "**Median filtering**\n", "
\n", "Selects the median value from each pixel’s neighborhood. Since the shot\n", "noise value usually lies well outside the true values in the neighborhood, the median filter is able to filter away such bad pixels." ], "id": "HinLkjNrqPEl" }, { "cell_type": "markdown", "metadata": { "id": "CVr8l1F2qRJt" }, "source": [ "### **2. Bilateral filtering**\n", "\n", "In the bilateral filter, the output pixel value depends on a weighted combination of neighboring pixel values. The idea is to simply reject (in a soft way) pixels whose values differ too much from the central pixel value." ], "id": "CVr8l1F2qRJt" }, { "cell_type": "markdown", "metadata": { "id": "uuRGmzljqTYs" }, "source": [ "### **Binary image processing**\n", "\n", "Binary images are thresholded grey scale image with pixel value either 0 or 1.\n", "
\n", "#### **Morphology**\n", "we first convolve the binary image with a binary structuring element and then select a binary output value depending on the thresholded result of the convolution.\n", "
\n", "Examples are **dilation, erosion, majority, opening, closing**\n", "
\n", "\n", "### **Distance Transforms**\n", "\n", "Also known as distance map or distance field, is a derived representation of a digital image.\n", "
\n", "The map labels each pixel of the image with the distance to the nearest obstacle pixel. A most common type of obstacle pixel is a boundary pixel in a binary image." ], "id": "uuRGmzljqTYs" }, { "cell_type": "markdown", "metadata": { "id": "lkIntz82rax1" }, "source": [ "## **Fourier Transforms**" ], "id": "lkIntz82rax1" }, { "cell_type": "markdown", "metadata": { "id": "P1qyn6uE5QJg" }, "source": [ "Is used to decompose an image into its sine and cosine components. The output of the transformation represents the image in the Fourier or frequency domain, while the input image is the spatial domain equivalent.\n", "\n", "The **Fast Fourier Transforms** is used to perform large-kernel operation in time that is independent of the kernel size." ], "id": "P1qyn6uE5QJg" }, { "cell_type": "markdown", "metadata": { "id": "2kXGT9tX7nKo" }, "source": [ "### **Two-dimensional Fourier transforms**\n", "\n", "#### **Wiener filtering***\n", "The fourier tranform can be sued to analyze the frequency spectrum of a whole class of image.
\n", "\n", "This linear filtering technique assumes that if noise is present in the system, then it is considered to be additive white Gaussian noise (AWGN). (*Not used in practice today)" ], "id": "2kXGT9tX7nKo" }, { "cell_type": "markdown", "metadata": { "id": "NvgIDyUm9DYJ" }, "source": [ "#### **Discrete cosine transform**\n", "\n", "The discrete cosine transform (DCT) is a variant of the Fourier transform particularly well suited to compressing images in a block-wise fashion.\n", "\n", "The DCT is widely used in today’s image and video compression algorithms." ], "id": "NvgIDyUm9DYJ" }, { "cell_type": "markdown", "metadata": { "id": "Sf4TBpvH9YqI" }, "source": [ "## **Pyramids and wavelets**\n", "\n", "We may need to alter the resolution of an image for speeding up an processing alogorithm or to match the resolution of a printer or a screen.\n" ], "id": "Sf4TBpvH9YqI" }, { "cell_type": "markdown", "metadata": { "id": "tYuWtvP39nWv" }, "source": [ "### **Interpolation**\n", "\n", "Image interpolation (or upsample) occurs when you resize or distort your image from one pixel grid to another.\n", "
\n", "\n", "Works by using known data to estimate values at unknown points. Common methods can be grouped into two categories:\n", "\n", "#### **Adaptive methods**\n", "\n", "Adaptive methods change depending on what they are interpolating. Examples include many proprietary algorithms in licensed software such as: Qimage, PhotoZoom Pro and Genuine Fractals.\n", "\n", "#### **Non-adaptive methods**\n", "Non-adaptive methods treat all pixels equally and examples are nearest neighbor, bilinear, bicubic, spline, sinc, lanczos etc.\n", "\n", "\n", "\n" ], "id": "tYuWtvP39nWv" }, { "cell_type": "markdown", "metadata": { "id": "dMiH8vEcARbg" }, "source": [ "A camera performs an optical zoom by moving the zoom lens so that it increases the magnification of light. However, a digital zoom degrades quality by simply interpolating the image. Even though the photo with digital zoom contains the same number of pixels, the detail is clearly far less than with optical zoom." ], "id": "dMiH8vEcARbg" }, { "cell_type": "markdown", "metadata": { "id": "LiB-Cbu1AWzB" }, "source": [ "### **Decimation**\n", "\n", "Reducing resolution of an image (downsampling). This is done by convolving the image with a low-pass filter and then keep every rth sample. Some examples of decimations filters are Direct subsampling, Block averaging, Sinc function.\n", "\n", "Applications include Image compression, Limited bandwidth image transmission etc.\n" ], "id": "LiB-Cbu1AWzB" }, { "cell_type": "markdown", "metadata": { "id": "M3fhbJspClL5" }, "source": [ "### **Multi-resolution representations**\n", "\n", "Consider a task of finding a face in an image, since we don't know what is the size of the face in an image, we can construct a pyramid of differently sized images and scan each one for possible faces.\n" ], "id": "M3fhbJspClL5" }, { "cell_type": "markdown", "metadata": { "id": "2cDDgYT3EEni" }, "source": [ "#### **Laplacian pyramid**\n", "To construct the pyramid, we first blur and subsample the original image by a factor of two and store this in the next level of the pyramid. Also known as an *octave pyramid*.\n", "\n", "Image pyramids are extremely useful for performing multi-scale editing operations such as blending images while maintaining details." ], "id": "2cDDgYT3EEni" }, { "cell_type": "markdown", "metadata": { "id": "gWFO-z3mEFi7" }, "source": [ "### **Wavelets**\n", "\n", "Wavelets provide a smooth way to decompose a signal into frequency components without blocking and are closely related to pyramids.\n", "\n", "Continuous Wavelet Transform, Inverse continuous wavelet transform, Discrete Wavelet Transform are three main methods of wavelets transforms.\n", "\n", "Application of wavelets include De-noising, Compression and Image fusion.\n", "\n" ], "id": "gWFO-z3mEFi7" }, { "cell_type": "markdown", "metadata": { "id": "Yf6Ox5SdF3l5" }, "source": [ "## **Geometric transformations**" ], "id": "Yf6Ox5SdF3l5" }, { "cell_type": "markdown", "metadata": { "id": "edmCMfB7F__L" }, "source": [ "### **Parametric transformations**\n", "\n", "Parametric transformations apply a global deformation to an image, where the behavior of the transformation is controlled by a small number of parameters.\n", "\n", "#### **MIP-mapping**\n", "\n", "A MIP-map is a standard image pyramid, where each level is prefiltered with a high-quality filter rather than a poorer quality approximation. \n", "\n", "Computer graphics rendering APIs, such as OpenGL and Direct3D, have parameters that can be used to select which variant of MIP-mapping should be used, depending on the desired tradeoff between speed and quality.\n", "\n", "#### **Anisotropic filtering**\n", "\n", "An alternative approach to filtering oriented textures, which is sometimes implemented in graphics hardware (GPUs), is to use anisotropic filtering.\n", "\n", "#### **Multi-pass transforms**\n", "\n", "The advantage of using a series of one-dimensional transforms is that they are much more efficient (in terms of basic arithmetic operations) than large, non-separable, two-dimensional filter kernels.\n", "\n", "\n" ], "id": "edmCMfB7F__L" }, { "cell_type": "markdown", "metadata": { "id": "MrYe1pjtId5r" }, "source": [ "### **Mesh-based warping**\n", "\n", "For example, changing the appearance of a face from a frown to a smile, what is needed in this case is to curve the corners of the mouth upwards while leaving the rest of the face intact. To perform such a transformation, different amounts of motion are required in different parts of the image.\n", "\n", "The mesh-warping algorithm relates features with nonuniform mesh in the source and destination images, i.e., the images are broken up into small regions that are mapped onto each other for the morph.\n" ], "id": "MrYe1pjtId5r" }, { "cell_type": "markdown", "metadata": { "id": "ftW6RQOUJkAy" }, "source": [ "---" ], "id": "ftW6RQOUJkAy" }, { "cell_type": "markdown", "metadata": { "id": "29a9e7f9" }, "source": [ "# **OpenCV**\n", "\n", "An opensource library which is supported by multiple platforms including Linux, Windows, Android and MacOS. \n", "It supports a wide variety of programming languages such as C++, Python, Java etc.\n", "
\n", "OpenCV-Python is the Python API for OpenCV, combining the best qualities of the OpenCV C++ API and the Python language." ], "id": "29a9e7f9" }, { "cell_type": "markdown", "metadata": { "id": "075a1dd5" }, "source": [ "### Pre-requisites\n", "\n", "1. Python 3.x\n", "2. Pip " ], "id": "075a1dd5" }, { "cell_type": "markdown", "metadata": { "id": "07abb1c1" }, "source": [ "To make sure that the Python version you currently have in your system is 3.x, run the below command on your systems terminal or command Prompt\n", "\n", "```powershell\n", "$ python3 --version\n", "```\n", "To make sure that PIP package manager is installed on your system\n", "\n", "```powershell\n", "$ pip3 --version\n", "```\n", "\n", "Or you can confirm the same by executing the below cells\n" ], "id": "07abb1c1" }, { "cell_type": "code", "metadata": { "id": "acfe8e85" }, "source": [ "! echo 'Python Version: '$(python3 --version)\n" ], "id": "acfe8e85", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "4323dd68" }, "source": [ "! echo 'PIP Version: ' $(pip3 --version)" ], "id": "4323dd68", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "iwgR_gMlEgq_" }, "source": [ "\n", "\n", "---\n", "\n" ], "id": "iwgR_gMlEgq_" }, { "cell_type": "markdown", "metadata": { "id": "unpEp_uyEXWg" }, "source": [ "## **Installing OpenCV-Python**" ], "id": "unpEp_uyEXWg" }, { "cell_type": "markdown", "metadata": { "id": "e5FdzMxPEtPu" }, "source": [ "The [OpenCV-Python](https://pypi.org/project/opencv-python/) package can be installed using the [PIP package manager](https://pip.pypa.io/en/stable/).\n", "\n", "To install, run the below command on your terminal or command prompt\n", "\n", "```powershell\n", "$ pip3 install opencv-python\n", "```" ], "id": "e5FdzMxPEtPu" }, { "cell_type": "markdown", "metadata": { "id": "yHWa7omzFtGS" }, "source": [ "You can do the same on this notebook as well, by executing the below cell" ], "id": "yHWa7omzFtGS" }, { "cell_type": "code", "metadata": { "id": "Oq4UQGrpD7Fi" }, "source": [ "! pip3 install opencv-python" ], "id": "Oq4UQGrpD7Fi", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "YLw9IgCRGVsp" }, "source": [ "After installation is done, confirm the same by checking the version of the package" ], "id": "YLw9IgCRGVsp" }, { "cell_type": "code", "metadata": { "id": "68qHIL7iHDSt" }, "source": [ "import cv2\n", "print(f'OpenCV-Python Version: {cv2.__version__}')" ], "id": "68qHIL7iHDSt", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "949CM7VAIb5B" }, "source": [ "\n", "\n", "---\n", "\n" ], "id": "949CM7VAIb5B" }, { "cell_type": "markdown", "metadata": { "id": "WCl4_sWxG_nZ" }, "source": [ "## **Now, let's dive into OpenCV**" ], "id": "WCl4_sWxG_nZ" }, { "cell_type": "markdown", "metadata": { "id": "zfQ9Q0HwMact" }, "source": [ "Before diving into OpenCV, let's import few packages that we will be using throghout this notebook\n", "1. OpenCV\n", "2. [Numpy](https://numpy.org/)\n", "3. [Matplotlib](https://matplotlib.org/)\n", "4. [os](https://docs.python.org/3/library/os.html)\n", "\n", "run the below cell to import them" ], "id": "zfQ9Q0HwMact" }, { "cell_type": "code", "metadata": { "id": "2joI4vraNJqQ" }, "source": [ "import os\n", "import cv2 # OpenCV\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline" ], "id": "2joI4vraNJqQ", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "67BZ00NcN_IQ" }, "source": [ "Let's set the path of the image directory" ], "id": "67BZ00NcN_IQ" }, { "cell_type": "code", "metadata": { "id": "cise8o8YOIgD" }, "source": [ "image_directory = 'images/'" ], "id": "cise8o8YOIgD", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "cegZOncY2SD0" }, "source": [ "Let's create a method to plot the images" ], "id": "cegZOncY2SD0" }, { "cell_type": "code", "metadata": { "id": "FG-Q_0r3WDJ4" }, "source": [ "# Method to plot the images\n", "def plot_images(original, result=None):\n", " \"\"\"Plot the images using matplotlib libray\"\"\"\n", " # Plot single image\n", " if result is None:\n", " plt.imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB))\n", " plt.show()\n", " return\n", " \n", " #Plot two images\n", " f, (axis1, axis2) = plt.subplots(1, 2, figsize=(15,15))\n", " axis1.imshow(cv2.cvtColor(original, cv2.COLOR_BGR2RGB))\n", " axis1.set_title('Origianl Image')\n", " axis2.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))\n", " axis2.set_title('Resultant Image')\n", " return" ], "id": "FG-Q_0r3WDJ4", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "aR1FDKJkJnzH" }, "source": [ "### **1. Changing colorspaces**" ], "id": "aR1FDKJkJnzH" }, { "cell_type": "markdown", "metadata": { "id": "yQinvt3tJ16G" }, "source": [ "There are more than 150 color-space conversion methods available in OpenCV. We will take a look at two of the widely used methods - \n", "
BGR ↔ Gray and BGR ↔ HSV\n", "\n", "**BGR** - Blue-Green-Red
\n", "**HSV** - Hue- Saturation-Lightness" ], "id": "yQinvt3tJ16G" }, { "cell_type": "markdown", "metadata": { "id": "VVxTYuJ1Kvfk" }, "source": [ "For color conversion, we can make use of the function `cv.cvtColor(input_image, flag)` where `flag` determines the type of conversion.\n", "\n", "Here, we will try to extract a blue colored object from an image by following the below steps\n", "\n", "1. Read the image\n", "2. Convert from BGR to HSV color-space\n", "3. Threshold the HSV image for a range of blue color" ], "id": "VVxTYuJ1Kvfk" }, { "cell_type": "code", "metadata": { "id": "eCmqBwdeHiKS" }, "source": [ "# Read the input image\n", "image = cv2.imread('images/color_spaces.jpg', cv2.IMREAD_COLOR)" ], "id": "eCmqBwdeHiKS", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "hIvEX83wQn3g" }, "source": [ "# Plot the image using matplotlib\n", "plot_images(image)" ], "id": "hIvEX83wQn3g", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ui_Yx5qDQwyw" }, "source": [ " # Convert BGR to HSV\n", "hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)" ], "id": "ui_Yx5qDQwyw", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "v-p-EBOATUHI" }, "source": [ "# define range of blue color in HSV\n", "lower_blue = np.array([110,50,50])\n", "upper_blue = np.array([130,255,255])\n", "\n", "# Threshold the HSV image to get only blue colors\n", "mask = cv2.inRange(hsv, lower_blue, upper_blue)" ], "id": "v-p-EBOATUHI", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "AjcuweUCThCY" }, "source": [ "# Bitwise-AND mask and original image\n", "res = cv2.bitwise_and(image, image, mask=mask)" ], "id": "AjcuweUCThCY", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "2YHiXH6qTpm4" }, "source": [ "# Plot the result image using matplotlib\n", "plot_images(image, res)" ], "id": "2YHiXH6qTpm4", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "LR1c48AUYlFE" }, "source": [ "\n", "\n", "---\n", "\n", "\n" ], "id": "LR1c48AUYlFE" }, { "cell_type": "markdown", "metadata": { "id": "AeXk6NRHYngp" }, "source": [ "### **2. Geometric Transformations of Images**" ], "id": "AeXk6NRHYngp" }, { "cell_type": "markdown", "metadata": { "id": "ZSCbsLzKZBjY" }, "source": [ "#### **1. Scaling**\n", "\n", "Scaling is just resizing of the image. OpenCV comes with a function `cv.resize()` for this purpose. \n", "\n", "Preferable interpolation methods are \n", "1. `cv.INTER_AREA` for shrinking \n", "2. `cv.INTER_CUBIC` (slow) \n", "3. `cv.INTER_LINEAR` for zooming\n", "\n", "By default `cv.INTER_LINEAR` is used for all resizing purposes\n", "\n", "Let's try resizing an image;" ], "id": "ZSCbsLzKZBjY" }, { "cell_type": "code", "metadata": { "id": "6EsQQdjfYvNY" }, "source": [ "# Read the image\n", "image = cv2.imread('images/color_spaces.jpg', cv2.IMREAD_COLOR)\n", "\n", "# Plot the image using matplotlib\n", "plot_images(image)" ], "id": "6EsQQdjfYvNY", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "U6xV7EIKlH18" }, "source": [ "# Apply resize with `INTER_CUBIC` Interpolation - 10 times the size of input image\n", "res = cv2.resize(image, None, fx=10, fy=10, interpolation = cv2.INTER_CUBIC)" ], "id": "U6xV7EIKlH18", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "kwaEaET0mVaS" }, "source": [ "print(f'Original size: {image.shape} \\nResized image size: {res.shape}')\n", "plot_images(res)" ], "id": "kwaEaET0mVaS", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Wag6HX0vnlqi" }, "source": [ "#### **2. Translation**\n", "\n", "Translation is the shifting of an image along the x- and y-axis. Using translation, we can shift an image up, down, left, or right, along with any combination of the above.\n", "\n", "If you want to rotate an image `tx` pixels along x-axis and `ty` pixels along y-axis, the tranformation matrix will be
`[1, 0, tx][0, 1, ty]`\n", "\n", "Translation can be done by applying this transformation metrics to the method `cv2.warpAffine()`.\n" ], "id": "Wag6HX0vnlqi" }, { "cell_type": "code", "metadata": { "id": "mVDZ4HV_q2XD" }, "source": [ "# Read the image\n", "image = cv2.imread('images/color_spaces.jpg', cv2.IMREAD_COLOR)\n", "\n", "# Plot the image using matplotlib\n", "plot_images(image)" ], "id": "mVDZ4HV_q2XD", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "8m9ytX86mbEj" }, "source": [ "# Shift the image 125 pixels to the right and 150 pixels down\n", "M = np.float32([[1, 0, 125], [0, 1, 150]])\n", "shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))\n", "\n", "# plot the image\n", "plot_images(image, shifted)" ], "id": "8m9ytX86mbEj", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "mISRbIifuZBD" }, "source": [ "# Shift the image 150 pixels to the left and 190 pixels up\n", "M = np.float32([[1, 0, -150], [0, 1, -190]])\n", "shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))\n", "\n", "# plot the image\n", "plot_images(image, shifted)" ], "id": "mISRbIifuZBD", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "tvAfI10gvf4E" }, "source": [ "#### **3. Rotation**\n", "\n", "OpenCV provides a function, `cv.getRotationMatrix2D` to create a transformation matrix, which can be applied to the image using the method `cv.warpAffine()`" ], "id": "tvAfI10gvf4E" }, { "cell_type": "code", "metadata": { "id": "JZ48bvPXvaec" }, "source": [ "# Read the image\n", "image = cv2.imread('images/color_spaces.jpg', cv2.IMREAD_COLOR)\n", "\n", "# Plot the image using matplotlib\n", "plot_images(image)" ], "id": "JZ48bvPXvaec", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "v3tARNaWunLE" }, "source": [ "# grab the dimensions of the image and calculate the center of the image\n", "(height, width) = image.shape[:2]\n", "(cX, cY) = (width // 2, height // 2)" ], "id": "v3tARNaWunLE", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "cLHX0CXZwJ57" }, "source": [ "# rotate our image by 45 degrees around the center of the image\n", "M = cv2.getRotationMatrix2D((cX, cY), 45, 1.0)\n", "rotated = cv2.warpAffine(image, M, (width, height))\n", "plot_images(image, rotated)" ], "id": "cLHX0CXZwJ57", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "fcsEu_AMwMwT" }, "source": [ "# rotate our image by -90 degrees around the image\n", "M = cv2.getRotationMatrix2D((cX, cY), -90, 1.0)\n", "rotated = cv2.warpAffine(image, M, (width, height))\n", "plot_images(image, rotated)" ], "id": "fcsEu_AMwMwT", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "RK-ehYTRwxFT" }, "source": [ "#### **5. Affine Transformation**\n", "\n", "To be added" ], "id": "RK-ehYTRwxFT" }, { "cell_type": "markdown", "metadata": { "id": "YC3-EQWxxRUr" }, "source": [ "#### **6. Perspective Transformation**\n", "\n", "To be added" ], "id": "YC3-EQWxxRUr" }, { "cell_type": "markdown", "metadata": { "id": "gRcQ6J7Rxjoj" }, "source": [ "### **3. Image Thresholding**" ], "id": "gRcQ6J7Rxjoj" }, { "cell_type": "markdown", "metadata": { "id": "B4O4i3L8yXMj" }, "source": [ "#### **1. Simple Thresholding**\n", "\n", "For every pixel, a threshold value is applied. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value.\n", "\n", "The function `cv.threshold` is used to apply the thresholding.\n", "\n", "OpenCV provides different types of thresholding \n", "1. `cv.THRESH_BINARY`\n", "2. `cv.THRESH_BINARY_INV`\n", "3. `cv.THRESH_TRUNC`\n", "4. `cv.THRESH_TOZERO`\n", "5. `cv.THRESH_TOZERO_INV`\n", "\n", "Detailed explanation about this different methods can be found [here](https://docs.opencv.org/4.5.2/d7/d1b/group__imgproc__misc.html#gaa9e58d2860d4afa658ef70a9b1115576).\n", "\n", "*Note: The input image must be a gray scale image*" ], "id": "B4O4i3L8yXMj" }, { "cell_type": "code", "metadata": { "id": "BGdeYveCzGyE" }, "source": [ "# read the input image\n", "image = cv2.imread('images/simple_threshold.PNG',0)\n", "\n", "# Plot the images\n", "plot_images(image)" ], "id": "BGdeYveCzGyE", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "FtNiFViYyWiS" }, "source": [ "# Apply various thershold methods\n", "ret,thresh1 = cv2.threshold(image,127,255,cv2.THRESH_BINARY)\n", "ret,thresh2 = cv2.threshold(image,127,255,cv2.THRESH_BINARY_INV)\n", "ret,thresh3 = cv2.threshold(image,127,255,cv2.THRESH_TRUNC)\n", "ret,thresh4 = cv2.threshold(image,127,255,cv2.THRESH_TOZERO)\n", "ret,thresh5 = cv2.threshold(image,127,255,cv2.THRESH_TOZERO_INV)" ], "id": "FtNiFViYyWiS", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "aXIAPqfEzTNw" }, "source": [ "titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']\n", "images = [image, thresh1, thresh2, thresh3, thresh4, thresh5]\n", "for i in range(6):\n", " plt.subplot(2,3,i+1),plt.imshow(images[i],'gray',vmin=0,vmax=255)\n", " plt.title(titles[i])\n", " plt.xticks([]),plt.yticks([])\n", "plt.show()" ], "id": "aXIAPqfEzTNw", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "c_-OPy3r1Aft" }, "source": [ "#### **2. Adaptive Thresholding**\n", "\n", "Using one global value for thresholding might not be good in all cases eg. if the image has different lighting conditions in different areas. \n", "\n", "**Adaptive Thresholding** determines the threshold for a pixel based on a small region around it. This can produce different thresholds for different regions of the same image which gives better results for image with varying illuminations. The method `cv.adaptiveThreshold` is used for it.\n", "\n", "Two different methods of calculating the threshold\n", "1. `cv.ADAPTIVE_THRESH_MEAN_C`: The threshold value is the mean of the neighbourhood area minus the constant C.\n", "2. `cv.ADAPTIVE_THRESH_GAUSSIAN_C`: The threshold value is a gaussian-weighted sum of the neighbourhood values minus the constant C.\n", "\n", "The `blockSize` determines the size of the neighbourhood area and `C` is a constant that is subtracted from the mean or weighted sum of the neighbourhood pixels." ], "id": "c_-OPy3r1Aft" }, { "cell_type": "code", "metadata": { "id": "pZzt2JkhzivS" }, "source": [ "# Read the image\n", "image = cv2.imread(f'{image_directory}calender.jpg',0)\n", "\n", "# plot the image\n", "plot_images(image)" ], "id": "pZzt2JkhzivS", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "wtzCMk954q4q" }, "source": [ "# Apply the thresholding methods to the image\n", "ret,thresh1 = cv2.threshold(image,127,255,cv2.THRESH_BINARY)\n", "thresh2 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,\\\n", " cv2.THRESH_BINARY,11,2)\n", "thresh3 = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\\\n", " cv2.THRESH_BINARY,11,2)" ], "id": "wtzCMk954q4q", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "hdfC66Yo-hL9" }, "source": [ "# Plot the thresholding resultant images\n", "titles = ['Original Image', 'Global Thresholding (v = 127)',\n", " 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']\n", "\n", "plot_images(image, thresh1)\n", "images = [image, thresh1, thresh2, thresh3]\n", "for i in range(4):\n", " plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')\n", " plt.title(titles[i])\n", " plt.xticks([]),plt.yticks([])\n", "plt.show()" ], "id": "hdfC66Yo-hL9", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "OWb5aFI7DQXS" }, "source": [ "#### **3. Otsu's Binarization**\n", "\n", "Otsu's method avoids having to choose an arbitrary value as threshold and determines it automatically.\n", "\n", "Determines an optimal global threshold value from the image histogram.\n", "\n", "The ` cv.threshold()` function is used, where `cv.THRESH_OTSU` is passed as an extra flag." ], "id": "OWb5aFI7DQXS" }, { "cell_type": "code", "metadata": { "id": "b27poyFyCxNE" }, "source": [ "# Read input image\n", "image = cv2.imread(f'{image_directory}periodic_table.png',0)\n", "\n", "# Otsu's thresholding\n", "ret2,thresh = cv2.threshold(image,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n", "plot_images(image, thresh)" ], "id": "b27poyFyCxNE", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "6sMhW78yG7I8" }, "source": [ "### **4. Smoothing Images**" ], "id": "6sMhW78yG7I8" }, { "cell_type": "markdown", "metadata": { "id": "OWRwRNEDHQSW" }, "source": [ "#### **1. 2D Convolution ( Image Filtering )**\n", "\n", "As in one-dimensional signals, images also can be filtered with various low-pass filters (LPF), high-pass filters (HPF), etc. LPF helps in removing noise, blurring images, etc. HPF filters help in finding edges in images.\n", "\n", "The function `cv.filter2D()` is used to convolve a kernel with an image." ], "id": "OWRwRNEDHQSW" }, { "cell_type": "markdown", "metadata": { "id": "CzKBwBH2IO_G" }, "source": [ "For example, lets try applying the 5x5 averaging filter kernel below" ], "id": "CzKBwBH2IO_G" }, { "cell_type": "code", "metadata": { "id": "LACqXPGjEpHs" }, "source": [ "kernel = np.ones((5,5),np.float32)/25\n", "print(kernel)" ], "id": "LACqXPGjEpHs", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "5WpIuMRbJohN" }, "source": [ "The operation works like this: keep this kernel above a pixel, add all the 25 pixels below this kernel, take the average, and replace the central pixel with the new average value" ], "id": "5WpIuMRbJohN" }, { "cell_type": "code", "metadata": { "id": "3eHkxCMDIeId" }, "source": [ "# Read input image\n", "image = cv2.imread(f'{image_directory}python_logo.png')\n", "\n", "# Plot the image\n", "plot_images(image)" ], "id": "3eHkxCMDIeId", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "YKpdEB3WIyBX" }, "source": [ "# Apply the kernel\n", "result = cv2.filter2D(image,-1,kernel)\n", "plot_images(image, result)" ], "id": "YKpdEB3WIyBX", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "qhz6PWPNJ8kc" }, "source": [ "#### **2. Image Blurring (Image Smoothing)**\n", "\n", "Image blurring is achieved by convolving the image with a low-pass filter kernel. It is useful for removing noise.\n", "\n", "OpenCV provides four different type of blurring techniques\n", "\n", "\n", "\n", "\n", "\n" ], "id": "qhz6PWPNJ8kc" }, { "cell_type": "markdown", "metadata": { "id": "PXSe1A4vOieD" }, "source": [ "##### **1. Averaging**\n", "This is done by convolving an image with a normalized box filter. (as explained above in 2D convolution section)\n", "The function `cv.blur()` can be used for averaging." ], "id": "PXSe1A4vOieD" }, { "cell_type": "code", "metadata": { "id": "Moo9vqplL_vV" }, "source": [ "# apply the averaging kernel\n", "blur = cv2.blur(image,(5,5))" ], "id": "Moo9vqplL_vV", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "ydmdwQvVMHqu" }, "source": [ "# plot the image\n", "plot_images(image, blur)" ], "id": "ydmdwQvVMHqu", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "gcpPyTI0L1NT" }, "source": [ "##### **2. Gaussian Blurring**\n", "In this method, instead of a box filter, a Gaussian kernel is used. The function `cv.GaussianBlur()` is used.
\n", "Gaussian blurring is highly effective in removing Gaussian noise from an image." ], "id": "gcpPyTI0L1NT" }, { "cell_type": "code", "metadata": { "id": "3ZJ9_AFnMaoP" }, "source": [ "# Apply gaussian blur\n", "blur = cv2.GaussianBlur(image,(5,5),0)\n", "\n", "# Plot the image\n", "plot_images(image, blur)" ], "id": "3ZJ9_AFnMaoP", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "5HYtf65EL3kj" }, "source": [ "##### **3. Median Blurring**\n", "The function `cv.medianBlur()` takes the median of all the pixels under the kernel area and the central element is replaced with this median value.
\n", "Effective against removing salt and pepper noises." ], "id": "5HYtf65EL3kj" }, { "cell_type": "code", "metadata": { "id": "5F_uOReDJDHN" }, "source": [ "# Add the Gaussian noise to the image - salt and pepper noise\n", "\n", "# Generate Gaussian noise\n", "gauss = np.random.normal(0,1,image.size)\n", "gauss = gauss.reshape(image.shape[0],image.shape[1],image.shape[2]).astype('uint8')\n", "\n", "# add generated noise to the image\n", "image_gauss = cv2.add(image,gauss)" ], "id": "5F_uOReDJDHN", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "gr9T4X_vNQ-w" }, "source": [ "# Apply kernel to the image\n", "median = cv2.medianBlur(image_gauss,5)\n", "\n", "#plot the image\n", "plot_images(image_gauss, median)" ], "id": "gr9T4X_vNQ-w", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "IlDqaJ-iO7o9" }, "source": [ "##### **4. Bilateral Filtering**\n", "\n", "`cv.bilateralFilter()` is highly effective in noise removal while keeping edges sharp.\n", "\n", "This filter preserve edges by considering Gaussian function of intensity difference that makes sure that only those pixels with similar intensities to the central pixel are considered for blurring. " ], "id": "IlDqaJ-iO7o9" }, { "cell_type": "code", "metadata": { "id": "cPJg3E0HQ_2W" }, "source": [ "# Load the input image\n", "image = cv2.imread(f'{image_directory}pattern.PNG')\n", "\n", "#plot the images\n", "plot_images(image)" ], "id": "cPJg3E0HQ_2W", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "_8MAOYvVOHVY" }, "source": [ "# Apply the filer\n", "blur = cv2.bilateralFilter(image,25,75,75)\n", "\n", "#plot the images\n", "plot_images(image, blur)" ], "id": "_8MAOYvVOHVY", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "yxmcLXpSS6jX" }, "source": [ "### **5. Morphological Transformations**\n", "\n", "Morpholofical operations are simple operatins(kernels) performed on a binary image based on its shape." ], "id": "yxmcLXpSS6jX" }, { "cell_type": "code", "metadata": { "id": "0XlPJ_8qTG1N" }, "source": [ "# read the input image\n", "image = cv2.imread(f'{image_directory}morphology.PNG')\n", "\n", "# Plot the image\n", "plot_images(image)" ], "id": "0XlPJ_8qTG1N", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "nX3mQTyDWHyw" }, "source": [ "# create a 5x5 kernel\n", "kernel = np.ones((5,5),np.uint8)" ], "id": "nX3mQTyDWHyw", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Kn9P0jKzTrNY" }, "source": [ "#### **1. Erosion**\n", "\n", "Erodes the bounderies of a forground object.\n", "\n", "Operaion of the erosion is as follows, a kernel slides through the image and if the pixel under the kernel is not 1, then its eroded(zero). Basically erodes the bounderies of an object.\n", "\n", "It is useful for removing small white noises, detach two connected object etc." ], "id": "Kn9P0jKzTrNY" }, { "cell_type": "code", "metadata": { "id": "CptF1LwsVGPP" }, "source": [ "# Apply the kenel to the input image\n", "erosion = cv2.erode(image,kernel,iterations = 1)\n", "\n", "# Plot the result image\n", "plot_images(image, erosion)" ], "id": "CptF1LwsVGPP", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "2bydg1V3VnCo" }, "source": [ "#### **2. Dilation**\n", "\n", "Dilation is opposite of erosion. A pixel element is 1, if atleast one pixel value under a kernel is 1.\n" ], "id": "2bydg1V3VnCo" }, { "cell_type": "code", "metadata": { "id": "7KsZyiOVVaH2" }, "source": [ "# apply the kernel\n", "dilation = cv2.dilate(image, kernel,iterations = 1)\n", "\n", "# Plot the images\n", "plot_images(image, dilation)" ], "id": "7KsZyiOVVaH2", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "75eglnd8WXE-" }, "source": [ "#### **3. Opening**\n", "\n", "Opening is just another name of erosion followed by dilation. \n", "Method used `cv.morphologyEx()`\n", "\n", "Userful for removing whitenoise." ], "id": "75eglnd8WXE-" }, { "cell_type": "code", "metadata": { "id": "_Zp1_6NKWhd8" }, "source": [ "# Add the Gaussian noise to the image - salt and pepper noise\n", "\n", "# Generate Gaussian noise\n", "gauss = np.random.normal(0,0.5,image.size)\n", "gauss = gauss.reshape(image.shape[0],image.shape[1],image.shape[2]).astype('uint8')\n", "\n", "# add generated noise to the image\n", "image_gauss = cv2.add(image,gauss)" ], "id": "_Zp1_6NKWhd8", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "zXFxuq9IWO8G" }, "source": [ "# Apply the kernel\n", "opening = cv2.morphologyEx(image_gauss, cv2.MORPH_OPEN, kernel)\n", "\n", "# plot the image\n", "plot_images(image_gauss, opening)" ], "id": "zXFxuq9IWO8G", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "NXjuZgVsXIk3" }, "source": [ "#### **4. Closing**\n", "\n", "Closing is reverse of Opening, Dilation followed by Erosion. \n", "
\n", "It is useful in closing small holes inside the foreground objects, or small black points on the object." ], "id": "NXjuZgVsXIk3" }, { "cell_type": "code", "metadata": { "id": "rXP3wQyNW7J5" }, "source": [ "# read the input image\n", "image = cv2.imread(f'{image_directory}morphology_closing.PNG')\n", "\n", "# apply the kernel\n", "closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)\n", "\n", "# Plot the images\n", "plot_images(image, closing)" ], "id": "rXP3wQyNW7J5", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "TuH8jS3PZcDf" }, "source": [ "#### **5. Morphological Gradient**\n", "It is the difference between dilation and erosion of an image." ], "id": "TuH8jS3PZcDf" }, { "cell_type": "code", "metadata": { "id": "LUC0FIAFXlsc" }, "source": [ "# read the image\n", "image = cv2.imread(f'{image_directory}morphology.PNG')\n", "\n", "# Apply the kernel to the image\n", "gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)\n", "\n", "# Plot the images\n", "plot_images(image, gradient)" ], "id": "LUC0FIAFXlsc", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "s_I3dDsLZ-Jp" }, "source": [ "### **6. Canny Edge Detection**\n", "\n", "Canny Edge Detection is a popular edge detection algorithm.\n", "It is a multistage alogorithm,\n", "1. Noise Reduction with a 5x5 Gaussian filter\n", "2. Finding Intensity Gradient of the Image \n", "3. Non-maximum Suppression to remove pixels which may not constitute the edges\n", "4. Hysteresis Thresholding to discard found edges based a threshold." ], "id": "s_I3dDsLZ-Jp" }, { "cell_type": "markdown", "metadata": { "id": "7-gSLaJEjYUd" }, "source": [ "`cv2.Canny()` method is used for canny edge detection" ], "id": "7-gSLaJEjYUd" }, { "cell_type": "code", "metadata": { "id": "unupDFopZz2F" }, "source": [ "# Read the input image\n", "image = cv2.imread(f'{image_directory}stop_sign.jpg')\n", "\n", "# Apply the canny edge detection\n", "edges = cv2.Canny(image,50,200)\n", "\n", "# Plot the images\n", "plot_images(image, edges)" ], "id": "unupDFopZz2F", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "tuErXYIgnpOK" }, "source": [ "### **7. Image Pyramids**\n", "\n", "In case of working with images where we need to create a set of the same image with different resoultion, we can use Image Pyramids. At each layer of the pyramid the image is downsized or smoothed.\n", "\n", "
\n", "
\n", "\n", "There are two types of image pyramids\n", "1. Gaussian Pyramid\n", "2. Laplacian Pyramids\n", "\n", "We can find Gaussian pyramids using `cv.pyrDown()` and `cv.pyrUp()` functions.\n", "\n", "Laplacian Pyramids are formed from the Gaussian Pyramids.\n" ], "id": "tuErXYIgnpOK" }, { "cell_type": "code", "metadata": { "id": "sp9LUN2Yl5O8" }, "source": [ "# Read input image\n", "image = cv2.imread(f'{image_directory}stop_sign.jpg')\n", "layer = image.copy()\n", "\n", "# Plot the pyramid\n", "for i in range(4):\n", " layer = cv2.pyrDown(layer)\n", " plot_images(layer)" ], "id": "sp9LUN2Yl5O8", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "6HqwlF6qdbmV" }, "source": [ "### **8. Contours**\n", "\n", "Contours are simply as a curve joining all the continuous points (along the boundary), having same color or intensity. Useful tool for shape analysis and object detection and recognition.\n", "\n", "OpenCV provides methods `cv.findContours()`, `cv.drawContours()` for the same.\n", "\n", "In OpenCV, finding contours is like finding white object from black background.\n", "\n" ], "id": "6HqwlF6qdbmV" }, { "cell_type": "code", "metadata": { "id": "Cd3wVoYydbGj" }, "source": [ "# Read the image\n", "image = cv2.imread(f'{image_directory}stop_sign.jpg')\n", "\n", "org_image = image.copy()" ], "id": "Cd3wVoYydbGj", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "XZFr0Qotaq-t" }, "source": [ "# Convert the image from color to gray\n", "imgray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n", "\n", "# Applying threshold\n", "ret, thresh = cv2.threshold(imgray, 127, 255, 0)" ], "id": "XZFr0Qotaq-t", "execution_count": null, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "D7VYK9Nvf2UP" }, "source": [ "# Find contours on the image\n", "contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n", "\n", "# Draw all the contours\n", "contour_img = cv2.drawContours(image, contours, -1, (0,255,0), 3)\n", "\n", "# Plot the image\n", "plot_images(org_image, contour_img)" ], "id": "D7VYK9Nvf2UP", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "tKQL7dp6gU2S" }, "source": [ "### **9. Histogram**\n", "\n", "You can consider histogram as a graph or plot, which gives you an overall idea about the intensity distribution of an image. It is a plot with pixel values (ranging from 0 to 255, not always) in X-axis and corresponding number of pixels in the image on Y-axis.\n", "\n", "
\n", "
\n", "\n", "`cv.calcHist()` is used for calculing the histogram for a image." ], "id": "tKQL7dp6gU2S" }, { "cell_type": "markdown", "metadata": { "id": "HJXD_TrdhtPO" }, "source": [ "Here, let us use the matplotlib library method to calculate the plot the histogram" ], "id": "HJXD_TrdhtPO" }, { "cell_type": "code", "metadata": { "id": "Tkww9ZS2f7EQ" }, "source": [ "# Read the input image\n", "img = cv2.imread(f'{image_directory}stop_sign.jpg',0)\n", "\n", "# plot the image\n", "plot_images(img)\n", "\n", "# Calculate histogram and plot \n", "plt.hist(img.ravel(),256,[0,256]); plt.show()" ], "id": "Tkww9ZS2f7EQ", "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "cooslghpjK5D" }, "source": [ "\n", "\n", "---\n", "\n" ], "id": "cooslghpjK5D" }, { "cell_type": "code", "metadata": { "id": "-Rtijwd4M9fx" }, "source": [ "" ], "id": "-Rtijwd4M9fx", "execution_count": null, "outputs": [] } ] }