Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"###### Content modified under Creative Commons Attribution license CC-BY 4.0, code under BSD 3-Clause License © 2020 R.C. Cooper"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Working with Python"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Good coding habits\n",
"## naming folders and files"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"# [Stanford file naming best practices](https://library.stanford.edu/research/data-management-services/data-best-practices/best-practices-file-naming)\n",
"\n",
"1. Include information to distinguish file name e.g. project name, objective of function, name/initials, type of data, conditions, version of file, \n",
"2. if using dates, use YYYYMMDD, so the computer organizes by year, then month, then day\n",
"3. avoid special characters e.g. !, #, \\$, ...\n",
"4. avoid using spaces if not necessary, some programs consider a space as a break in code use dashes `-` or underscores `_` or CamelCase"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Commenting your code\n",
"\n",
"Its important to comment your code \n",
"\n",
"- what are variable's units,\n",
"\n",
"- what the is the function supposed to do, \n",
"\n",
"- etc. \n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def code(i):\n",
" '''Example of bad variable names and bad function name'''\n",
" m=1\n",
" for j in range(1,i+1):\n",
" m*=j;\n",
" return m"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"3628800"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"code(10)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Choose variable names that describe the variable\n",
"\n",
"You might not have recognized that `code(i)` is meant to calculate the [factorial of a number](https://en.wikipedia.org/wiki/Factorial), \n",
"\n",
"$N!= N*(N-1)*(N-2)*(N-3)*...3*2*1$. \n",
"\n",
"For example, \n",
"\n",
"- 4! = 24\n",
"\n",
"- 5! = 120\n",
"\n",
"- 10! = 3,628,800\n",
"\n",
"In the next block, we have rewritten `code` so the output is unchanged, but another user can read the code *and* help debug if there is an issue. \n",
"\n",
"A function is a compact collection of code that executes some action on its arguments. \n",
"\n",
"Once *defined*, you can *call* a function as many times as you want. When we *call* a function, we execute all the code inside the function. The result of the execution depends on the *definition* of the function and on the values that are *passed* into it as *arguments*. Functions might or might not *return* values in their last operation. \n",
"\n",
"The syntax for defining custom Python functions is:\n",
"\n",
"```python\n",
"def function_name(arg_1, arg_2, ...):\n",
" '''\n",
" docstring: description of the function\n",
" '''\n",
" <body of the function>\n",
"```\n",
"\n",
"The **docstring** of a function is a message from the programmer documenting what he or she built. Docstrings should be descriptive and concise. They are important because they explain (or remind) the intended use of the function to the users. You can later access the docstring of a function using the function `help()` and passing the name of the function. If you are in a notebook, you can also prepend a question mark `'?'` before the name of the function and run the cell to display the information of a function. \n",
"\n",
"Try it!"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"def factorial_function(input_value):\n",
" '''Good variable names and better help documentation\n",
" \n",
" factorial_function(input_number): calculates the factorial of the input_number\n",
" where the factorial is defined as N*(N-1)*(N-2)*...*3*2*1\n",
" \n",
" Arguments\n",
" ---------\n",
" input_value: an integer >= 0\n",
" \n",
" Returns\n",
" -------\n",
" factorial_output: the factorial of input_value'''\n",
" \n",
" factorial_output=1 # define 0! = 1\n",
" for factor in range(1,input_value+1):\n",
" factorial_output*=factor; # mutliply factorial_output by 1*2*3*...*N (factor)\n",
" return factorial_output\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"24"
]
},
"execution_count": 97,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"factorial_function(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Defining the function with descriptive variable names and inputs helps to make the function much more useable. \n",
"\n",
"Consider the structure of a Python function:\n",
"\n",
"```python\n",
"def factorial_function(input_value):\n",
"```\n",
"This first line declares that we are `def`-ining a function that is named `factorial_function`. The inputs to the line are given inside the parantheses, `(input_value)`. We can define as many inputs as we want and even assign default values. \n",
"\n",
"```python\n",
" '''Good variable names and better help documentation\n",
" \n",
" factorial_function(input_number): calculates the factorial of the input_number\n",
" where the factorial is defined as N*(N-1)*(N-2)*...*3*2*1'''\n",
"```\n",
"The next 4 lines define a help documentation that can be accessed with in a couple ways:\n",
"\n",
"1. `?factorial_function`\n",
"\n",
"2. `factorial_function?`\n",
"\n",
"3. `help(factorial_function)`\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\u001b[0;31mSignature:\u001b[0m \u001b[0mfactorial_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput_value\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mDocstring:\u001b[0m\n",
"Good variable names and better help documentation\n",
" \n",
"factorial_function(input_number): calculates the factorial of the input_number\n",
"where the factorial is defined as N*(N-1)*(N-2)*...*3*2*1\n",
"\u001b[0;31mFile:\u001b[0m ~/Documents/UConn/ME3255/ME3255-CompMech/CompMech01-Getting-started/notebooks/<ipython-input-12-618dbbaab728>\n",
"\u001b[0;31mType:\u001b[0m function\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"factorial_function?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
" factorial_output=1 # define 0! = 1\n",
"```\n",
"\n",
"This line sets the variable `factorial_output` to 1. In the next 2 lines we update this value based upon the mathematical formula we want to use. In this case, its $1*1*2*3*...*(N-1)*N$\n",
"\n",
"```python\n",
" for factor in range(1,input_value+1):\n",
" factorial_output*=factor; # mutliply m by 1*2*3*...*N (factor)\n",
"``` \n",
"\n",
"These two lines perform the computation that we set out to do. The `for`-loop is going to start at 1 and end at our input value. For each step in the `for`-loop, we will mulitply the factorial_output by the factor. So when we calculate 4!, the loop updates factorial_output 4 times:\n",
"\n",
"1. i=1: factorial_output = $1*1=1$\n",
"\n",
"2. i=2: factorial_output = $1*1*2=2$\n",
"\n",
"3. i=3: factorial_output = $1*1*2*3=6$\n",
"\n",
"4. i=4: factorial_output = $1*1*2*3*4=24$\n",
"\n",
"\n",
"\n",
"```python\n",
" return factorial_output\n",
"```\n",
"\n",
"This final line in our function returns the calculated value, `factorial_output`. We can also return as many values as necessary on this line, \n",
"\n",
"for example, if we had variables: `value_1`, `value_2`, and `value_3` we could return all three as such,\n",
"\n",
"```python\n",
" return value_1,value_2,value_3\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Play with NumPy Arrays\n",
"\n",
"\n",
"In engineering applications, most computing situations benefit from using *arrays*: they are sequences of data all of the _same type_. They behave a lot like lists, except for the constraint in the type of their elements. There is a huge efficiency advantage when you know that all elements of a sequence are of the same type—so equivalent methods for arrays execute a lot faster than those for lists.\n",
"\n",
"The Python language is expanded for special applications, like scientific computing, with **libraries**. The most important library in science and engineering is **NumPy**, providing the _n-dimensional array_ data structure (a.k.a, `ndarray`) and a wealth of functions, operations and algorithms for efficient linear-algebra computations.\n",
"\n",
"In this lesson, you'll start playing with NumPy arrays and discover their power. You'll also meet another widely loved library: **Matplotlib**, for creating two-dimensional plots of data."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Importing libraries\n",
"\n",
"First, a word on importing libraries to expand your running Python session. Because libraries are large collections of code and are for special purposes, they are not loaded automatically when you launch Python (or IPython, or Jupyter). You have to import a library using the `import` command. For example, to import **NumPy**, with all its linear-algebra goodness, we enter:\n",
"\n",
"```python\n",
"import numpy as np\n",
"```\n",
"\n",
"Once you execute that command in a code cell, you can call any NumPy function using the dot notation, prepending the library name. For example, some commonly used functions are:\n",
"\n",
"* [`np.linspace()`](https://docs.scipy.org/doc/numpy/reference/generated/np.linspace.html)\n",
"* [`np.ones()`](https://docs.scipy.org/doc/numpy/reference/generated/np.ones.html#np.ones)\n",
"* [`np.zeros()`](https://docs.scipy.org/doc/numpy/reference/generated/np.zeros.html#np.zeros)\n",
"* [`np.empty()`](https://docs.scipy.org/doc/numpy/reference/generated/np.empty.html#np.empty)\n",
"* [`np.copy()`](https://docs.scipy.org/doc/numpy/reference/generated/np.copy.html#np.copy)\n",
"\n",
"Follow the links to explore the documentation for these very useful NumPy functions!"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating arrays\n",
"\n",
"To create a NumPy array from an existing list of (homogeneous) numbers, we call **`np.array()`**, like this:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 3, 5, 8, 17])"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.array([3, 5, 8, 17])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"NumPy offers many [ways to create arrays](https://docs.scipy.org/doc/numpy/reference/routines.array-creation.html#routines-array-creation) in addition to this. We already mentioned some of them above. \n",
"\n",
"Play with `np.ones()` and `np.zeros()`: they create arrays full of ones and zeros, respectively. We pass as an argument the number of array elements we want. "
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1., 1., 1., 1., 1.])"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.ones(5)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0., 0., 0.])"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.zeros(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another useful one: `np.arange()` gives an array of evenly spaced values in a defined interval. \n",
"\n",
"*Syntax:*\n",
"\n",
"`np.arange(start, stop, step)`\n",
"\n",
"where `start` by default is zero, `stop` is not inclusive, and the default\n",
"for `step` is one. Play with it!\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0, 1, 2, 3])"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.arange(4)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2, 3, 4, 5])"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.arange(2, 6)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2, 4])"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.arange(2, 6, 2)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5])"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.arange(2, 6, 0.5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`np.linspace()` is similar to `np.arange()`, but uses number of samples instead of a step size. It returns an array with evenly spaced numbers over the specified interval. \n",
"\n",
"*Syntax:*\n",
"\n",
"`np.linspace(start, stop, num)`\n",
"\n",
"`stop` is included by default (it can be removed, read the docs), and `num` by default is 50. "
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2. , 2.02040816, 2.04081633, 2.06122449, 2.08163265,\n",
" 2.10204082, 2.12244898, 2.14285714, 2.16326531, 2.18367347,\n",
" 2.20408163, 2.2244898 , 2.24489796, 2.26530612, 2.28571429,\n",
" 2.30612245, 2.32653061, 2.34693878, 2.36734694, 2.3877551 ,\n",
" 2.40816327, 2.42857143, 2.44897959, 2.46938776, 2.48979592,\n",
" 2.51020408, 2.53061224, 2.55102041, 2.57142857, 2.59183673,\n",
" 2.6122449 , 2.63265306, 2.65306122, 2.67346939, 2.69387755,\n",
" 2.71428571, 2.73469388, 2.75510204, 2.7755102 , 2.79591837,\n",
" 2.81632653, 2.83673469, 2.85714286, 2.87755102, 2.89795918,\n",
" 2.91836735, 2.93877551, 2.95918367, 2.97959184, 3. ])"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.linspace(2.0, 3.0)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"50"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(np.linspace(2.0, 3.0))"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([2. , 2.2, 2.4, 2.6, 2.8, 3. ])"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.linspace(2.0, 3.0, 6)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([-1. , -0.75, -0.5 , -0.25, 0. , 0.25, 0.5 , 0.75, 1. ])"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.linspace(-1, 1, 9)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Array operations\n",
"\n",
"Let's assign some arrays to variable names and perform some operations with them."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"x_array = np.linspace(-1, 1, 9)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we've saved it with a variable name, we can do some computations with the array. E.g., take the square of every element of the array, in one go:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1. 0.5625 0.25 0.0625 0. 0.0625 0.25 0.5625 1. ]\n"
]
}
],
"source": [
"y_array = x_array**2\n",
"print(y_array)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also take the square root of a positive array, using the `np.sqrt()` function:"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1. 0.75 0.5 0.25 0. 0.25 0.5 0.75 1. ]\n"
]
}
],
"source": [
"z_array = np.sqrt(y_array)\n",
"print(z_array)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we have different arrays `x_array`, `y_array` and `z_array`, we can do more computations, like add or multiply them. For example:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 0. -0.1875 -0.25 -0.1875 0. 0.3125 0.75 1.3125 2. ]\n"
]
}
],
"source": [
"add_array = x_array + y_array \n",
"print(add_array)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Array addition is defined element-wise, like when adding two vectors (or matrices). Array multiplication is also element-wise:"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[-1. -0.5625 -0.25 -0.0625 0. 0.0625 0.25 0.5625 1. ]\n"
]
}
],
"source": [
"mult_array = x_array * z_array\n",
"print(mult_array)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also divide arrays, but you have to be careful not to divide by zero. This operation will result in a **`nan`** which stands for *Not a Number*. Python will still perform the division, but will tell us about the problem. \n",
"\n",
"Let's see how this might look:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/opt/miniconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in true_divide\n",
" \"\"\"Entry point for launching an IPython kernel.\n"
]
},
{
"data": {
"text/plain": [
"array([-1. , -1.33333333, -2. , -4. , nan,\n",
" 4. , 2. , 1.33333333, 1. ])"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x_array / y_array"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Multidimensional arrays\n",
"\n",
"### 2D arrays \n",
"\n",
"NumPy can create arrays of N dimensions. For example, a 2D array is like a matrix, and is created from a nested list as follows:"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[1 2]\n",
" [3 4]]\n"
]
}
],
"source": [
"array_2d = np.array([[1, 2], [3, 4]])\n",
"print(array_2d)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2D arrays can be added, subtracted, and multiplied:"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [],
"source": [
"X = np.array([[1, 2], [3, 4]])\n",
"Y = np.array([[1, -1], [0, 1]])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The addition of these two matrices works exactly as you would expect:"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[2, 1],\n",
" [3, 5]])"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X + Y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"What if we try to multiply arrays using the `'*'`operator?"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 1, -2],\n",
" [ 0, 4]])"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X * Y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The multiplication using the `'*'` operator is element-wise. If we want to do matrix multiplication we use the `'@'` operator:"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1],\n",
" [3, 1]])"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X @ Y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Or equivalently we can use `np.dot()`:"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1],\n",
" [3, 1]])"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.dot(X, Y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3D arrays\n",
"\n",
"Let's create a 3D array by reshaping a 1D array. We can use [`np.reshape()`](https://docs.scipy.org/doc/numpy/reference/generated/np.reshape.html), where we pass the array we want to reshape and the shape we want to give it, i.e., the number of elements in each dimension. \n",
"\n",
"*Syntax*\n",
" \n",
"`np.reshape(array, newshape)`\n",
"\n",
"For example:"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"a = np.arange(24)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[[ 0 1 2 3]\n",
" [ 4 5 6 7]\n",
" [ 8 9 10 11]]\n",
"\n",
" [[12 13 14 15]\n",
" [16 17 18 19]\n",
" [20 21 22 23]]]\n"
]
}
],
"source": [
"a_3D = np.reshape(a, (2, 3, 4))\n",
"print(a_3D)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can check for the shape of a NumPy array using the function `np.shape()`:"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(2, 3, 4)"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.shape(a_3D)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Visualizing the dimensions of the `a_3D` array can be tricky, so here is a diagram that will help you to understand how the dimensions are assigned: each dimension is shown as a coordinate axis. For a 3D array, on the \"x axis\", we have the sub-arrays that themselves are two-dimensional (matrices). We have two of these 2D sub-arrays, in this case; each one has 3 rows and 4 columns. Study this sketch carefully, while comparing with how the array `a_3D` is printed out above. \n",
"\n",
"<img src=\"../images/3d_array_sketch.png\" style=\"width: 400px;\"/> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When we have multidimensional arrays, we can access slices of their elements by slicing on each dimension. This is one of the advantages of using arrays: we cannot do this with lists. \n",
"\n",
"Let's access some elements of our 2D array called `X`."
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 2],\n",
" [3, 4]])"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"X"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Grab the element in the 1st row and 1st column \n",
"X[0, 0]"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Grab the element in the 1st row and 2nd column \n",
"X[0, 1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Exercises:\n",
"\n",
"From the X array:\n",
"\n",
"1. Grab the 2nd element in the 1st column.\n",
"2. Grab the 2nd element in the 2nd column."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Play with slicing on this array:"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 3])"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Grab the 1st column\n",
"X[:, 0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When we don't specify the start and/or end point in the slicing, the symbol `':'` means \"all\". In the example above, we are telling NumPy that we want all the elements from the 0-th index in the second dimension (the first column)."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1, 2])"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Grab the 1st row\n",
"X[0, :]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Exercises:\n",
"\n",
"From the X array:\n",
"\n",
"1. Grab the 2nd column.\n",
"2. Grab the 2nd row."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's practice with a 3D array. "
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[[ 0, 1, 2, 3],\n",
" [ 4, 5, 6, 7],\n",
" [ 8, 9, 10, 11]],\n",
"\n",
" [[12, 13, 14, 15],\n",
" [16, 17, 18, 19],\n",
" [20, 21, 22, 23]]])"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a_3D"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we want to grab the first column of both matrices in our `a_3D` array, we do:"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0, 4, 8],\n",
" [12, 16, 20]])"
]
},
"execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a_3D[:, :, 0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The line above is telling NumPy that we want:\n",
"\n",
"* first `':'` : from the first dimension, grab all the elements (2 matrices).\n",
"* second `':'`: from the second dimension, grab all the elements (all the rows).\n",
"* `'0'` : from the third dimension, grab the first element (first column).\n",
"\n",
"If we want the first 2 elements of the first column of both matrices: "
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 0, 4],\n",
" [12, 16]])"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a_3D[:, 0:2, 0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below, from the first matrix in our `a_3D` array, we will grab the two middle elements (5,6):"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([5, 6])"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a_3D[0, 1, 1:3]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Exercises:\n",
"\n",
"From the array named `a_3D`: \n",
"\n",
"1. Grab the two middle elements (17, 18) from the second matrix.\n",
"2. Grab the last row from both matrices.\n",
"3. Grab the elements of the 1st matrix that exclude the first row and the first column. \n",
"4. Grab the elements of the 2nd matrix that exclude the last row and the last column. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## NumPy == Fast and Clean! \n",
"\n",
"When we are working with numbers, arrays are a better option because the NumPy library has built-in functions that are optimized, and therefore faster than vanilla Python. Especially if we have big arrays. Besides, using NumPy arrays and exploiting their properties makes our code more readable.\n",
"\n",
"For example, if we wanted to add element-wise the elements of 2 lists, we need to do it with a `for` statement. If we want to add two NumPy arrays, we just use the addtion `'+'` symbol!\n",
"\n",
"Below, we will add two lists and two arrays (with random elements) and we'll compare the time it takes to compute each addition."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Element-wise sum of a Python list\n",
"\n",
"Using the Python library [`random`](https://docs.python.org/3/library/random.html), we will generate two lists with 100 pseudo-random elements in the range [0,100), with no numbers repeated."
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [],
"source": [
"#import random library\n",
"import random"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [],
"source": [
"lst_1 = random.sample(range(100), 100)\n",
"lst_2 = random.sample(range(100), 100)"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[78, 71, 32, 12, 88, 84, 7, 49, 93, 13]\n",
"[24, 70, 98, 72, 17, 48, 82, 55, 87, 54]\n"
]
}
],
"source": [
"#print first 10 elements\n",
"print(lst_1[0:10])\n",
"print(lst_2[0:10])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We need to write a `for` statement, appending the result of the element-wise sum into a new list we call `result_lst`. \n",
"\n",
"For timing, we can use the IPython \"magic\" `%%time`. Writing at the beginning of the code cell the command `%%time` will give us the time it takes to execute all the code in that cell. "
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 30 µs, sys: 8 µs, total: 38 µs\n",
"Wall time: 41 µs\n"
]
}
],
"source": [
"%%time\n",
"res_lst = []\n",
"for i in range(100):\n",
" res_lst.append(lst_1[i] + lst_2[i])"
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[102, 141, 130, 84, 105, 132, 89, 104, 180, 67]\n"
]
}
],
"source": [
"print(res_lst[0:10])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Element-wise sum of NumPy arrays\n",
"\n",
"In this case, we generate arrays with random integers using the NumPy function [`np.random.randint()`](https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/np.random.randint.html). The arrays we generate with this function are not going to be like the lists: in this case we'll have 100 elements in the range [0, 100) but they can repeat. Our goal is to compare the time it takes to compute addition of a _list_ or an _array_ of numbers, so all that matters is that the arrays and the lists are of the same length and type (integers)."
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [],
"source": [
"arr_1 = np.random.randint(0, 100, size=100)\n",
"arr_2 = np.random.randint(0, 100, size=100)"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ 3 67 98 94 87 71 28 60 12 24]\n",
"[29 59 1 45 85 92 17 0 15 58]\n"
]
}
],
"source": [
"#print first 10 elements\n",
"print(arr_1[0:10])\n",
"print(arr_2[0:10])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can use the `%%time` cell magic, again, to see how long it takes NumPy to compute the element-wise sum."
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 27 µs, sys: 7 µs, total: 34 µs\n",
"Wall time: 39.6 µs\n"
]
}
],
"source": [
"%%time\n",
"arr_res = arr_1 + arr_2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice that in the case of arrays, the code not only is more readable (just one line of code), but it is also faster than with lists. This time advantage will be larger with bigger arrays/lists. \n",
"\n",
"(Your timing results may vary to the ones we show in this notebook, because you will be computing in a different machine.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Exercise\n",
"\n",
"1. Try the comparison between lists and arrays, using bigger arrays; for example, of size 10,000. \n",
"2. Repeat the analysis, but now computing the operation that raises each element of an array/list to the power two. Use arrays of 10,000 elements. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Time to Plot\n",
"\n",
"You will love the Python library **Matplotlib**! You'll learn here about its module `pyplot`, which makes line plots. \n",
"\n",
"We need some data to plot. Let's define a NumPy array, compute derived data using its square, cube and square root (element-wise), and plot these values with the original array in the x-axis. "
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[0. 0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45 0.5 0.55 0.6 0.65\n",
" 0.7 0.75 0.8 0.85 0.9 0.95 1. 1.05 1.1 1.15 1.2 1.25 1.3 1.35\n",
" 1.4 1.45 1.5 1.55 1.6 1.65 1.7 1.75 1.8 1.85 1.9 1.95 2. ]\n"
]
}
],
"source": [
"xarray = np.linspace(0, 2, 41)\n",
"print(xarray)"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
"pow2 = xarray**2\n",
"pow3 = xarray**3\n",
"pow_half = np.sqrt(xarray)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction to plotting\n",
"\n",
"To plot the resulting arrays as a function of the orginal one (`xarray`) in the x-axis, we need to import the module `pyplot` from **Matplotlib**.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Set up default plotting parameters\n",
"\n",
"The default Matplotlib fonts and linewidths are a little small. Pixels are free, so the next two lines increase the fontsize and linewidth"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [],
"source": [
"plt.rcParams.update({'font.size': 22})\n",
"plt.rcParams['lines.linewidth'] = 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The line `%matplotlib inline` is an instruction to get the output of plotting commands displayed \"inline\" inside the notebook. Other options for how to deal with plot output are available, but not of interest to you right now. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll use the **pyplot** `plt.plot()` function, specifying the line color (`'k'` for black) and line style (`'-'`, `'--'` and `':'` for continuous, dashed and dotted line), and giving each line a label. Note that the values for `color`, `linestyle` and `label` are given in quotes."
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.legend.Legend at 0x7f856cb8b910>"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAYkAAAECCAYAAAALqiumAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzdd1xV9f/A8de5ICIy3YgGmqa5QFERd5q4yA2OnOUuy/yVNhxkrtIk/ebINM3KcmDfysyROVMpNUduxT1wsURkfn5/IPfL9V7ggsAFeT8fj/vA8/mc8b7IPe97zvkMTSmFEEIIYYrO0gEIIYQouCRJCCGEyJAkCSGEEBmSJCGEECJDkiSEEEJkyNrSAeSGMmXKKA8PD0uHIYQQhcrBgwfvKKXKZrbOU5EkPDw8OHDggKXDEEKIQkXTtEtZrSO3m4QQQmRIkoQQQogMSZIQQgiRIUkSQgghMiRJQgghRIYkSQghhMiQJAkhhBAZkiQhhBCF0PHjx7l161aeH0eShBBCFDJKKQYOHEjVqlWZNGkS0dHReXasp6LHdXYppYiJiSE6OpoHDx6QnJxs6ZCEKJCsra1xcnKiVKlSWFsXydNFgRQSEsKhQ4cAmDNnDiNHjsTR0TFPjlXk/teVUty6dYvY2FhKlSpFhQoVsLKyQtM0S4cmRIGilCIhIYG7d+9y5coV3N3d0enk5oOlJSUlMXHiRP3ymDFjcHNzy7PjFbn/8ZiYGGJjY3F3d8fZ2Rlra2tJEEKYoGkaxYsXx9XVFWtrayIiIiwdkgC++eYbTp8+DYCjoyMTJkzI0+MVuSQRHR1NqVKlsLKysnQoQhQKmqbh7OxMbGyspUMp8uLj4wkKCtIvv/POO5QuXTpPj2lWktA0rbWmacrM1zNm7nNFFvs59WRvzbQHDx5gb2+fF7sW4qllZ2dHXFycpcMo8hYvXszly5cBKFu2LG+++WaeH9PcZxI3ga8zqW8MPA+cB65kM4Y/gXMmym9kcz9mSU5OlqsIIbJJp9ORkpJi6TCKtPv37zN9+nT98gcffICDg0OeH9esJKGUOgUMzqhe07Tjj/75lVJKZTOGpUqpFdnc5onIMwghskc+M5Zna2vLjBkz+PDDD9HpdIwcOTJfjvvEzyQ0TfMFagHJZH61IYQQIoesra0ZOnQoZ8+e5ddff6V48eL5ctzceHD9yqOfm5RS13Jhf0IIITJga2tLnTp18u14T9RPQtM0O6D3o8VlOdzNC5qm1QPsgXBgD7BVKSU3QIUQwsKetDNdAOAA3AI25HAfA02UndA0rY9S6liOIxNCiKfAZ599Rv369WnVqpVFjv+kt5vSbjWtVEolZnPbw8AbQG1SryIqAv7AEVKfcfyuaVredSMUQogC7vz587zzzju0bt2aDh06cP/+/XyPIcdXEpqmVQNaPlr8KrvbK6U+e6woFvhV07StwE6gCfAe8HoGxx8ODAd45hmzumYIIUShMnnyZJKSkgB4+PAhJUuWzPcYnuRKIu0qYp9S6mRuBAOglEoAZj5a7JTJekuUUg2VUg3Lli2bW4cXQogC4dChQ3z//ff65ZkzZ1qkKXKOkoSmaVb871lCTh9YZyatt7XcbrKA06dPM2jQINzd3bGxscHBwQEPDw+6d+9OSEiIwbqJiYnMmTOHWrVqYWtrS4UKFRgwYACXLl0iKCgITdMMhhEAGDx4MJqmsWLFCpPHz2i7mJgYlixZQrdu3ahWrRp2dnbY29tTv359pk+fnmGPYE3T9B+uZcuW4ePjg6OjI5qmERkZafBeFi9eTIsWLXBxccHW1pbq1aszbtw4bt++nb1fohBPQCnF22+/TVq3sy5duuDr62uRWHJ6u6k9qSfwWGB17oWjlzYYSf7fgCvijh07RrNmzYiJiaFmzZq89NJLaJrGtWvX2Lx5M3FxcfTs2ROAlJQUevTowYYNG7C1taVNmzY4ODiwbds2fvvtNzp37pyrsR05coQRI0ZQrlw5atSoQcOGDbl79y6hoaFMnDiRn3/+mZ07d2Jra2ty+zFjxrBw4UKaNWuGv78/Z86c0SeP6OhoOnfuzJ49e3BycsLb2xtnZ2cOHTpEcHAwISEh7Ny5Ew8Pj1x9T0KYsnHjRrZv3w6AlZUVH3/8seWCUUpl+wWEAApYlpPtzdh/8KP9bzJnfW9vb2WuEydOZFr/6LiF8pUbhgwZogA1Y8YMo7qYmBi1d+9e/fL8+fMVoNzc3NTZs2f15XFxcapnz576uKZMmWKwn0GDBilALV++3GQMU6ZMMbndlStX1LZt21RycrJBeUREhOrQoYMC1KxZs4z2lxaHk5OTCg0NNXnM3r17K0D16tVL3bt3T1+elJSkxo8frwDVqlUrk9sWFVl9dkTuSExMVM8//7z+73bUqFF5dizggMrqfJzVCkYbQBkg/tEbaJrFujNJvXU087FyL1JbMlk9Vm4NjCO197YC2psTkySJ3EsSnTp1UoD6559/sly3atWqGZ7sw8PDVYkSJXI1SWTmzJkzClANGzY0qkv7/UyfPt3ktsePH1eAcnd3Vw8ePDCqT05OVvXq1VOAOnr0qNkxPW0kSeSPL774Qv836+DgoMLDw/PsWOYkiZzcbhoA2ACnlFJ7s1jXFajx6Gd6HsCPwD1N084AV0ntb1GX1KawKcAEpdTmHMQnnkDjxo3ZuHEjI0eO5KOPPqJly5Ymu/9fvXqVsLAwdDod/fr1M6ovV64cfn5+/PTTT7kan1KKP//8k127dnH16lXi4uLSf8ngzJkzGW7bo0cPk+W//fYbAP7+/pQoUcKoXqfT0bx5c44ePcq+ffuoW7duLrwTIYzFxMQwefJk/fK7775LuXLlLBhRzp5JDHn0M9vNXtM5AswjdfRYd6A+qZnzKrAcWKCUOvgE+8+xtJNNUfXOO++we/dutm3bhp+fH8WLF8fLy4tWrVrRv39//Qny6tWrAFSsWBEbGxuT+8rt+/fh4eH06NGDvXsz/m6S2Vy/7u7uJsvDwsIAWLBgAQsWLMg0BnmALfLS7NmzCQ8PB6BSpUqMHTvWwhHlIEkopeplY93BmBg9Vil1AbD8uxdG7Ozs+P333wkNDWXTpk38+eef7Nu3j9DQUD755BM+/PBDg286eSGjIamHDh3K3r17adasGUFBQXh6euLs7EyxYsVISEjIcsAzU1cJgH6Oc29v7yzHxKldu7YZ70CInBk4cCAnT55k3bp1TJ8+HTs7O0uHVPTmuBbm8fHxwcfHB4CEhARWrVrFsGHDCAoKonfv3vo5da9fv05CQoLJq4mLFy+a3Hfauhn1Hr106ZJRWWxsLBs3bsTKyooNGzbg7OxsUH/unKkpScxTuXJlAF544QVmz56d4/0I8aSqVavG2rVrOXDgAA0aNLB0OEARnL5UZJ+NjQ2DBw+mSZMmKKU4evQolStXpkqVKqSkpPDDDz8YbXP79m22bt1qcn9pCebUKePJB+Pi4tixY4dReVRUFCkpKTg4OBglCIDvvvsum+/qfzp27AjAf//7X33vViEsqWHDhuh0BeP0XDCiEAXGwoUL9ZOspxcWFsbx46lzS6Xd23/jjTcAmDhxov6+PqTOw/vaa6/x4MEDk8do27YtYDihO6QmiFGjRumnZ0yvfPnyuLi4EBkZyapVqwzqNm3axNy5c7PzNg00aNCAbt26ce7cOQIDA/XPW9K7ceMGn332mSQRUfRk1fypMLxyswlsUefp6akAVbVqVdWlSxfVr18/1aZNG2VjY6MA1adPH/26SUlJqmPHjgpQtra2qlOnTiowMFBVrFhRlSpVSg0cODDDpqz+/v4KUHZ2dqp9+/aqc+fOqnz58srV1VXfV+Px7ebMmaNvGujr66v69u2rGjdurAD1/vvvZ9gUOKPy9KKiolSrVq3078XHx0cFBgaqDh06qNq1ayudTqcAFRcXl6Pf69NAPjt5Y8uWLWrSpEkqOjo6349NXvSTKIgvSRK555dfflEjRoxQXl5eqkyZMsrGxkZVrlxZtWvXTq1Zs8aoI1tCQoKaNWuWqlmzprKxsVFly5ZVffr0UWFhYZn2d4iLi1Pvvvuu8vDwUMWKFVOurq7qlVdeUdevX890u3Xr1qkmTZooR0dH5eDgoHx9fdW3336rlMo4GZiTJJRKTXorV65Ufn5+qkyZMsra2lqVLVtWeXp6qtGjR6vNmzeb90t8SslnJ/clJSWpOnXqKECVL19e7dixI1+PL0nCBPlDzz856RQnCi757OS+pUuX6r/ElCxZUl2/fj1fj29OkpBnEkIIYQFRUVG8//77+uXx48fj6vp4v2PLkyQhhBAWMHXqVG7dugWktvj7v//7PwtHZJokCSGEyGenTp1i/vz5+uU5c+ZYZEIhc0hnOpFngoKCjOaEEKKoU0oxduxYfXPqFi1a0Lt3bwtHlTG5khBCiHy0YcMGNm9OHbtUp9Mxf/58i8w4Zy5JEkIIkU/i4+N566239MvDhg3Dy8vLghFlTZKEEELkk6+//prz588D4OzszLRp0ywcUdYkSQghRD559dVXWbRoEaVLl2bq1KmUKVPG0iFlSR5cCyFEPrGysmLkyJEEBgbi6Oho6XDMIklCCCHyWalSpSwdgtnkdpMQQuShlJSUDCfSKgwkSQghRB5avnw5TZs25a+//rJ0KDkiSUIIIfJIZGQk7733HqGhofj4+LB+/XpLh5RtkiSEECKPfPDBB9y+fRtInSa3Q4cOFo4o+yRJCIvw8PBA07QM58EWorD7+++/WbRokX75008/xc7OzoIR5YwkCSGEyGVJSUmMGDEiddIeUudR79Wrl4WjyhlJEkIIkcsWLlzIP//8A4CtrS2ff/55gR6fKTOSJIQQIhddu3aNiRMn6pcnTpxI1apVLRjRk5EkIUyKjY1lzpw5+Pr64uzsTIkSJahatSoBAQFs3LhRv56maZl+QzLn2UNISAhNmzbFwcEBJycn/Pz82LNnT4brJyYmsnjxYlq0aIGLiwu2trZUr16dcePG6R8SCmEpb731FjExMQDUrFmTt99+28IRPRlJEsLIpUuX8Pb25p133uHff//F19eXrl274urqym+//cYnn3ySa8eaN28evXr1IiUlhZdeeomqVauydetWWrduzdq1a43Wj46Opk2bNowaNYpjx47RoEEDOnfuTFJSEsHBwTRs2FAehguL2bRpk8Hf7aJFiyhevLgFI8oFWU2CXRhe3t7eZk/8bc5k7lOmTNFPTp7Va9iwYUbbDxs2zOztp0yZYrS9v7+/2dt/8cUXZr93cyQnJ6v69esrQHXt2lXdu3fPoD46Olr9/vvv+uW0ODLi7u6uAHXhwgWT5TqdTq1evdqgbuHChQpQDg4O6saNGwZ1vXv3VoDq1auXQWxJSUlq/PjxClCtWrXK5rsW5jDns1PUBQcHK2trawWogQMHWjqcLAEHVBbnV7mSEAZ+/vln/vnnHzw8PPj+++9xcXExqHdwcKBt27a5drzu3bsTGBhoUDZq1ChatmxJTEwMy5Yt05efOHGC1atX4+7uzsqVKw1is7KyYubMmdSrV4+dO3dy7NixXItRCHONHTuWw4cP89JLLzF79mxLh5MrJEkIA5s2bQLg5ZdfpkSJEnl+vP79+5ssHzBgAAA7duzQl/32228A+Pv7m4xNp9PRvHlzAPbt25fLkQphntq1a/Pzzz9Trlw5S4eSK2QUWBOedG7mJUuWsGTJkhxv/8svv+R42yd16dIlIPWBW36oUqWKyXIPDw8Arl69qi8LCwsDYMGCBSxYsCDT/coDbCFyhyQJkaeedPTL9C2nkpOTAfD29qZOnTqZble7du0nOq4Q5tqxYwdVq1blmWeesXQoeUKShDDg7u4OwOnTp81av1ixYiQmJnL//n3s7e0N6hITE7lx40am21+8eBFPT0+T5QAVK1bUl1WuXBmAF1544am53ysKtzt37hAQEEBcXBxBQUG8+eabFCtWzNJh5Sp5JiEMtG/fHoBvv/2Whw8fZrm+m5sbAKdOnTKq27JlC0lJSZlu/91332Va3rp1a31Zx44dAfjvf/+b5X6FyA9vvvkmd+7cITY2lnnz5pn1mSlsJEkIA127dsXLy4uLFy/y8ssvExUVZVAfExPDtm3b9MtpLZ2mTp1KQkKCvvz48eOMGTMmy+OFhIQQEhJiULZkyRJ27NiBvb09r776qr68QYMGdOvWjXPnzhEYGGjwvCLNjRs3+OyzzySJiDy3YcMGVq1apV/+4osvcHBwsGBEeSSrNrKF4ZXb/SSKurCwMFWtWjV9X4WOHTuqPn36qGbNmqmSJUsa9EM4d+6ccnR0VIDy8PBQPXv2VM2aNVM2Njaqf//+WfaTGDt2rAJUkyZNVN++ffV9NHQ6nfr++++NYouKilKtWrVSgLK1tVU+Pj4qMDBQdejQQdWuXVvpdDoFqLi4uDz+LRU98tn5n8jISOXm5qbvJ9S/f39Lh5QjmNFPwuIn+Nx4SZLIfdHR0Wr69OmqQYMGyt7eXpUoUUJVqVJF9e7dW23atMlg3WPHjqkuXbooZ2dnZWtrq+rUqaPmzZunUlJSskwSFy5cUD/88IPy8fFRJUuWVA4ODurFF19UO3fuzDC2pKQktXLlSuXn56fKlCmjrK2tVdmyZZWnp6caPXq02rx5c178Soo8+ez8T/oOs+XKlVN37tyxdEg5Yk6S0FLXK9waNmyoDhw4YNa6J0+e5Pnnn8/jiIR4+shnJ9Uff/xh0KF0zZo1BAQEWDCinNM07aBSqmFm68gzCSGEMFNsbCxDhw7VL3fr1q3QzhNhLkkSQghhpokTJ3LhwgUAnJ2dWbhwYaGdJ8JckiSEEMIMFy5cYP78+frluXPn4urqasGI8ockCSGEMEOVKlXYunUrVapUwc/Pj8GDB1s6pHwhPa6FEMJMbdq04dixY9y/f/+pv82URpKEEEJkQ8mSJSlZsqSlw8g3crtJCCEyEB8fz61btywdhkVJkhBCiAxMnjyZOnXq8NNPP1k6FIsxO0lomrZC0zSVyct4hLes96nTNO01TdMOaJp2X9O0KE3Tdmua1je7+xJCiNy0Z88eZs+eze3bt+nWrRtbt261dEgWkZNnEn8C50yUZz4m9GM0TbMC1gNdgGhgC1AcaAus0jTNVyn1Rg7iE0KIJxITE8PAgQNJG5GiXbt2uTptb2GSkySxVCm1IheOPZbUBHECaKOUCgfQNK06sBsYo2naNqVU0b3OE0JYxLhx4ww6zX311VfodEXz7rxF3vWjq4jxjxZHpSUIAKXUWWDCo8UP8js2IUTRtmHDBpYuXapfXrBgAZUqVbJgRJZlqdToC5QDriqldpmoXwskAo00TXPL18iEEEXW7du3DcZmCggIoG/fov2INCe3m17QNK0eYA+EA3uArUqp7ExmXP/Rz79NVSqlHmiadhzwevS6loM4hRDCbEopRo4cSXh46o0NV1dXFi1aVGQ6zWUkJ0lioImyE5qm9VFKHTNzH1Ue/byUyTqXSU0QVTJZRwghcsW3337L+vXr9cvLli2jdOnSFoyoYMjO7abDwBtAbVKvIioC/sARoBbwezZuDdk/+hmbyTr3H/00OR+gpmnDHzWdPXD79m0zDytE0XXx4kU0TcPDw8PSoRQ4t2/f5vXXX9cvjxgxQj+nelFndpJQSn2mlPqPUuqEUipWKXVDKfUr0BjYT+ozhvfM3F3a9VuOZzxSSi1RSjVUSjUsW7ZsTncjhBCULVuWxYsX4+zszLPPPsucOXMsHVKB8cRjNymlEjRNmwn8BHQyc7OYRz/tM1knrS4mk3WEECJX9O3bl5YtW3Lnzh3s7TM7NRUtudW6Ka23tbm3my4++umeyTqVH1tX5JPTp08zaNAg3N3dsbGxwcHBAQ8PD7p3705ISIjR+omJicyZM4datWpha2tLhQoVGDBgAJcuXSIoKAhN0wgKCjLYZvDgwWiaxooVK0zGkNF2MTExLFmyhG7dulGtWjXs7Oywt7enfv36TJ8+nbi4OJP70zRN/wBy2bJl+Pj44OjoiKZpREZGGryXxYsX06JFC1xcXLC1taV69eqMGzeO7N7WXLFiBZqmMXjwYO7evcsbb7xBlSpVsLGxoVu3bgbr7t27l549e1KhQgVsbGyoUKECvXr1Yv/+/RnuPzY2lunTp+Pp6Ym9vT0lS5bEy8uLGTNm8ODBA4N1Bw8eTJUqqY/3Ll26pP99yO0nQ25ubnh6elo6jAIlt0aBTXu6cz/Ttf7n0KOfjUxVappmB9R5tPjPE8QlsunYsWM0a9aMmJgYatasyUsvvYSmaVy7do3NmzcTFxdHz5499eunpKTQo0cPNmzYgK2tLW3atMHBwYFt27bx22+/0blz51yN78iRI4wYMYJy5cpRo0YNGjZsyN27dwkNDWXixIn8/PPP7Ny5E1tbW5PbjxkzhoULF9KsWTP8/f05c+aMPnlER0fTuXNn9uzZg5OTE97e3jg7O3Po0CGCg4MJCQlh586d2T6p3rlzh0aNGhEVFUWLFi1o2LChwQPRRYsW8frrr5OSkkKjRo1o06YN586dIyQkhB9//JHFixczbNgwo32mDVvt4uJCu3bt0DSN7du388EHH7BmzRr++OMPSpUqBUDz5s25f/8+ISEhlCxZ0mDKzTJlymTr/TwtoqKiAHBycrJwJAWcUuqJX0Awqc8XNpm5vhWpzWcV0NJE/aBHdX+Zsz9vb29lrhMnTmS5zpQpU9Sj46spU6YY1Y8bN05fP2fOHKP6YcOG6eu/+OILo/q+ffvq67/77jujen9/f339zz//bFTfqlUrff327duzfD/ZMWTIEAWoGTNmGNXFxMSovXv3GpTNnz9fAcrNzU2dPXtWXx4XF6d69uyZ4e9x0KBBClDLly83GUfa/8Hj2125ckVt27ZNJScnG5RHRESoDh06KEDNmjXLaH9pcTg5OanQ0FCTx+zdu7cCVK9evdS9e/f05UlJSWr8+PEKUK1atTK5rSnLly/XH9fPz09FR0cbrXP48GFlbW2tdDqdWrNmjUHd999/r3Q6nSpWrJg6duyYQV1AQIACVIsWLVRERIS+/N69e6pp06YKUH369DHY5sKFCwpQ7u7uZr+H9Mz57BQWKSkpKiAgQFWpUiXDv4eiADigsji/mnW7SdM0L03T/B/1lE5fbq1p2jhSWz2lJYv09Ss1TTuladrr6cuVUsnA7EeLizRNK5dum+rArEeL082JT+SetDbiplp22Nvb4+vra1D22WefATBt2jSqVaumL7e1tWXhwoWUKFEiV+OrVKkSbdq0MRoiwdnZWT+15Lp16zLcfvz48TRu3Nio/MSJE6xevRp3d3dWrlyJi4uLvs7KyoqZM2dSr149du7cybFj5rb0TlWsWDG++OILHByMG+rNnz+fpKQk+vTpQ0BAgEFdnz596NWrF4mJicybN09ffunSJdatW4dOp2PJkiU4Ozvr61xcXPjyyy/R6XSsWbOGK1euZCvWomLp0qWsXbuWCxcu0KxZM86ePWvpkAosc59JeAC/ALc0TdunadpaTdM2kdrP4dNH60xQSm1+bLtngBqAqevZ4Ef7rAWc1TRtvaZpvwBHgQrAf5SM25Tv0k6gI0eOZOvWrcTHx2e47tWrVwkLC0On09GvXz+j+nLlyuHn55frMSql2LNnDzNmzGD06NEMGTKEwYMHM23aNADOnDmT4bY9evQwWf7bb78B4O/vbzKx6XQ6mjdvDsC+ffuyFW+DBg0yvEW1c+dOgAynwnzllVcA2LFjh75s9+7dKKVo0qQJNWvWNNqmVq1aNG7cmJSUFHbtMjWgQdF2/Phx3njjf2OHvvrqq1SvXt2CERVs5j6TOALMI7W5qzupPaYVcBVYDixQSh3MzoGVUsmapnUDRgNDgPZAMnAQWKiUWpWd/eWmoKAgowem6X366ad8+umnGdYvWbKEJUuWZFi/atUqVq3K+O398ssvmcaX/oSR29555x12797Ntm3b8PPzo3jx4nh5edGqVSv69+9P3bp19etevXoVgIoVK2JjY2Nyf7n9UDQ8PJwePXqwd+/eDNeJjo7OsM7d3XRbibCwMCB1nJ4FCxZkGkN2H2BndEyAa9dSBxNIe6j8uGeffdZgPXO2Sdtu//79BtsJiIuLo3fv3jx8+BCA2rVrExwcnMVWRZtZSUIpdYHUUVuzRSnVOov6FODzRy9RANjZ2fH7778TGhrKpk2b+PPPP9m3bx+hoaF88sknfPjhh0yePDnP40hJMT3Ky9ChQ9m7dy/NmjUjKCgIT09PnJ2dKVasGAkJCRQvXjzT/WZ0+ys5ORkAb29v6tSpY3KdNLVr1zbjHWR9zPQyGvpBKeOuRGllmQ0XYWo7kTq66/Hjx4HU/5fVq1fn+i3Rp43McS1M8vHxwcfHB4CEhARWrVrFsGHDCAoKonfv3tSoUQM3t9QWz9evXychIcHk1cTFixdN7j9t3fv3TTeIu3TJeMSW2NhYNm7ciJWVFRs2bDC4Fw9w7pypaU7MU7lyaovrF154gdmzZ2exdu5xc3Pj/PnzhIWF6a8a0ksbrjrtdw3oRyRNu/oxxdR2RV1ISAiLFy/WL3/22WfZTvhFUdEcIF1ki42NDYMHD6ZJkyYopTh69CiQemKtUqUKKSkp/PDDD0bb3b59O8PZvNJOXqdOGU9oGBcXZ/KWWlRUFCkpKTg4OBglCIDvvvsuO2/LQNqD+v/+978kJSXleD/Z1apVKwBWrlxpsn758uUAtG7dWl/WokULNE1j//79Jp+/nDx5ktDQUHQ6HS1bttSXpyXm/Hx/BcXFixd59dVX9csBAQFGzYpFBrJq/lQYXrndBLYoW7BggTp16pRR+fnz55WLi4sCDJoMBgcHK0BVrlxZnT9/Xl/+8OFDfTNNTDRl3b17twKUo6OjwfEePHigbx77+HZJSUn6GB5vOvzbb78pW1tb/XaPy6g8vW7duilAde/eXV25csWo/vr16yo4OFglJiZmup80aU1gBw0alOE6aU1grays1Pr16w3q1qxZo28Ce/ToUYO6Xr16KUC1bt1aRUZG6ssjIiJUixYtTDaBTUhIUDY2Nsra2tqgia+5Cutn5+HDh6px48b6v3kcaoAAACAASURBVAF3d3eDZsNFGWY0gbX4CT43XpIkco+np6cCVNWqVVWXLl1Uv379VJs2bZSNjY3JE09SUpLq2LGjApStra3q1KmTCgwMVBUrVlSlSpVSAwcOzLC/SVp/EDs7O9W+fXvVuXNnVb58eeXq6qrvr/H4dnPmzNF/2H19fVXfvn31J4D333//iZJEVFSUvg+Kra2t8vHxUYGBgapDhw6qdu3aSqfTKUDFxcWZ9bs0J0kolZqYNU1TgPLx8VH9+vXTvyedTmeyr83t27dVnTp1FKBKlSqlevTooXr06KFPop6enuru3btG23Xv3l1/ouzXr5969dVX1YQJE8x6P4X1s7N06VL9/7+VlZVRX5+iTJKECYX1Dz2//PLLL2rEiBHKy8tLlSlTRtnY2KjKlSurdu3aqTVr1hh1YlMq9RvqrFmzVM2aNZWNjY0qW7as6tOnjwoLC8uwU5xSqR3u3n33XeXh4aGKFSumXF1d1SuvvKKuX7+e6Xbr1q1TTZo0UY6OjsrBwUH5+vqqb7/9VimVcTIwJ0kolZr0Vq5cqfz8/FSZMmWUtbW1Klu2rPL09FSjR49WmzdvzvqX+Ii5SUIppfbs2aO6d++uypUrp6ytrVW5cuVUjx49Mj2hxcTEqI8++kjVrVtXlShRQpUoUULVq1dPTZ8+Xd2/f9/kNnfu3FGvvvqqqlSpkrK2ts5W57rC+tlJSUlRc+fOVVZWVmru3LmWDqdAMSdJaKnrFW4NGzZUBw4cMGvdkydP8vzzz+dxRCJNUFAQH374IVOmTMm0WbEo+Ar7Z+f48ePUqlWryE8ilJ6maQeVUg0zW0daNwkhigRpyZQz0rpJCPFUSUlJYfHixZmOFiDMJ0lCCPFU+fjjjxk1ahQtWrTg8uXLlg6n0JMkIfJUUFAQSil5HiHyxbZt25g4cSIAf//9d6bD4wjzSJIQQjwVrly5Qp8+ffRDujRv3pwpU6ZYOKrCT5KEEKLQi4+PJyAggDt37gBQoUIF1qxZQ7FixSwcWeEnSUIIUagppRg1ahShoaFA6vwfa9aswdXV1cKRPR2KZJJ4GvqGCJGfCvJnJjg4WD/GFcDs2bNp0aKFBSPKH0opvvnmGzp27KgfxTgvFLkkYW1tTUJCgqXDEKJQSUxMxMrKKusV89nGjRt555139MuDBw9m7Nhsz2pQ6Fy4cIEOHTowcOBANm3apJ+VMS8UuSTh5OTE3bt3C/Q3IyEKmujoaJPTr1rSyZMn6du3r/5BddOmTVm8ePFT3aM6OTmZ4OBg6tSpw5YtW/TlS5cuzbPRfYtckihVqhTx8fFcvXqVmJgYkpOTJWEIYYJSioSEBO7cuUNERASlSpWydEgGSpUqpZ8g6plnnmH9+vVZTjpVmB09ehRfX1/GjRvHgwcPgNSJp8aOHUtoaCjW1nkzgEaRG5bD2toad3d3IiIiiIiI4Pr16xnOgiZEUWdlZYWDgwPPPPNMgTsBly9fnj/++IOxY8cyYsQIypcvb+mQ8sTDhw+ZNm0aH3/8scHVQp06dVi6dKl+crC8UuQG+BNCiMJi165dDBs2zGByKRsbGyZPnsw777yT4dzy5jJngL8id7tJCFF4XblyxdIh5It79+4xdOhQWrVqZZAgmjdvzpEjR/jggw+eOEGYS5KEEKJQ+OOPP6hWrRqzZ89+ap8jKqX49ttvqVmzJsuWLdOXOzg4sGjRInbu3EnNmjXzNaYi90xCCFH4nDhxgp49e5KQkMD48eOJj4/Xj9H0tDh79iyjRo1i27ZtBuXdu3dn/vz5VKpUySJxyZWEEKJAu379Oh07diQyMhIAV1dXhgwZYuGock9CQgLTpk2jbt26BgmicuXK/PTTT6xfv95iCQLkSkIIUYBFR0fTqVMn/ZDf9vb2/Prrr7i5uVk4styxa9cuRo4cycmTJ/VlOp2ON998k6lTp2Jvb2/B6FJJkhBCFEiJiYn06tWLI0eOAKnNcdetW0f9+vUtHNmTCw8P55133uGbb74xKPf29mbJkiU0aNDAQpEZk9tNQogCRynFsGHD2Lp1q77syy+/pH379haM6sklJyezcOFCatSoYZAg7O3tmTdvHqGhoQUqQYBcSQghCqApU6bw9ddf65c//PDDQv8c4q+//mL06NEcPHjQoDwwMJC5c+cW2FtociUhhChQvvzySz766CP98quvvsqkSZMsGNGTiYiIYNSoUTRp0sQgQVSrVo3NmzezevXqApsgQJKEEKKAcXV1xc7ODoCOHTuyaNGiQjloX0pKCsuWLeO5555j8eLF+r4dtra2TJ06lWPHjuHn52fhKLMmSUIIUaD4+/uzY8cOOnbsWGhnl/vrr79o0qQJQ4cO1c+WB9CpUyeOHz/OpEmTsLW1tWCE5pMkIYQocBo1asTGjRsLRBPQ7Lh9+zZDhw7Fx8eHv//+W19euXJl1q9fz4YNG6hataoFI8w+SRJCCIs6d+4cly5dsnQYTyQpKYnPP/+c5557zmA4jeLFizNp0iROnTpF9+7dC+VtM2ndJISwmCtXrtC2bVuUUmzdupUaNWpYOqRs27lzJ2PGjOHYsWMG5V26dCE4OLjQXTk8Tq4khBAWER4ezosvvsjly5e5cuUKHTp0KFRTC1+8eJGAgABat25tkCCqVavGr7/+yk8//VToEwRIkhBCWEBERATt27fXD4NdrFgxFi9enG/DXz+J+/fvM3HiRGrWrMm6dev05XZ2dsycOZN///2XTp06WTDC3CW3m4QQ+er+/ft06tRJP9yGTqfj+++/L/C9qVNSUvjuu+949913uX79ukHdyy+/zKxZsyw6EF9ekSQhhMg3Dx8+pGvXruzfv19f9tVXX9GzZ08LRpW10NBQ3nzzTUJDQw3KGzZsyLx582jatKmFIst7crtJCJEvEhISCAwM5I8//tCX/ec//2HQoEEWjCpzly9f5uWXX6ZJkyYGCaJChQqsWLGC0NDQpzpBgFxJCCHyQXx8PAEBAfzyyy/6sunTp/P6669bMKqMRUdHM2vWLObOnUt8fLy+3MbGhnHjxvH+++/j4OBgwQjzjyQJIUSeW7x4sUGCmDBhAu+9954FIzItKSmJZcuWMXnyZG7dumVQ16NHD2bPnv1UtFjKDrndJITIc6+//jp9+/YF4N1332XmzJkFrmPZpk2b8PT0ZOTIkQYJomHDhuzatYuQkJAilyBAriSEEPnAysqKlStX0rVrVwIDAwtUgvjnn3+YMGGCwdwVkDqUxsyZM+nbty86XdH9Pi1JQgiR6+Lj47GxsTFIBtbW1vTu3duCURm6ePEikyZN4ttvvzUot7e357333uOtt96iRIkSFoqu4Ci66VEIkScePHhA586dGTdunH547ILk7t27/N///R81atQwSBA6nY7hw4dz7tw53n//fUkQj8iVhBAi18TGxvLSSy+xfft2tm3bhk6nY86cOQXi9lJcXBzz589n5syZREVFGdR16dKFmTNnUqtWLQtFV3CZdSWhaVoxTdPaapr2qaZp+zVNu6FpWoKmadc0TVunaVrr7B5Y07QVmqapTF6nsv1uhBAWExkZSfv27dm+fbu+rHTp0hZPEGktlp577jneffddgwTRpEkTdu/ezU8//SQJIgPmXkm0AtKe6twEDgKxQC2gJ9BT07SPlFKTcxDDn8A5E+U3crAvIYQF3Lhxgw4dOnD06FF92axZs5gwYYLFYkpJSSEkJIRJkyZx+vRpg7rq1aszc+ZMevToYfEkVtCZmyRSgBBgnlJqd/oKTdN6A98BkzRN266U2m5qB5lYqpRakc1thBAFxPnz5/Hz8yMsLExfFhwczNixYy0Sj1KKLVu28P7773Po0CGDunLlyhEUFMTQoUML5Yx3lmBWklBK/QH8kUHdak3T2gGvAv2B7CYJIUQhdeTIEdq3b094eDiQ2tR1+fLlDBgwwCLx7N+/n/fee48dO3YYlDs6OjJ+/HjefPPNQjfbnaXl1oPrfx79fPqGQBRCmLRnzx78/f319/htbW1Zu3Yt/v7++R7L4cOHmTJlCj///LNBua2tLWPGjGHChAmULl063+N6GuRWkqj+6GdOniO8oGlaPcAeCAf2AFuVUim5FJsQIpclJCTQv39/fYJwdHRkw4YNtGjRIl/jOH78OFOmTCEkJMSg3MrKiqFDhzJp0iTc3NzyNaanzRMnCU3TKgCDHy2GZLJqRgaaKDuhaVofpdQxE3VCCAuzsbFh/fr1tG7dGjs7OzZt2oSXl1e+Hf/MmTMEBQXxww8/GPXF6Nu3L1OnTqVatWr5Fs/T7ImShKZp1sC3gBOwTSn1SxabpHeY1FZS24BLgCPQAJgOeAK/a5rWQCl1LYNjDweGAzzzzDM5fg9CiJxp0KABv/76K66urvl2Qg4LC2Pq1Kl88803pKQY3mzo1q0bH374IfXq1cuXWIoK7Ul6RGqatpTUB9ZXgMZKqZtPHJCm2QA7gSbAAqVUlmMJN2zYUB04cOBJDy2EyEBCQgInT57E09PTIscPCwtj5syZrFixgqSkJIO6Tp06MXXqVLy9vS0SW2GmadpBpVTDzNbJ8bAcmqbNIzVB3ATa5kaCAFBKJQAzHy0+PRPFClFIRURE0KFDB1q0aMGxY/l7B/j8+fO88sorPPfccyxdutQgQbRr1469e/fy66+/SoLIQzlKEpqmfQq8AdwmNUGczdWoIK23tTxxEsKCzp8/j6+vL9u3bycmJgZ/f38iIiLy/Lhnz55l8ODB1KhRg+XLl5OcnKyva9myJTt37mTLli34+vrmeSxFXbafSWia9gkwDrgLtFNKncj1qCCtrdr9PNi3EMIMf/75J926dePOnTv6spEjR+Ls7Jxnxzxz5gzTpk3ju+++M3rm0Lp1a6ZMmULr1q3z7PjCWLaShKZps4B3gAhSE8SRPIkKAh/9/DuP9i+EyMT333/P4MGDSUhIAKB48eKsXLmSwMDALLbMmaNHjzJjxgzWrl1rlBzatm3L5MmTadmyZZ4cW2RBKWXWC/gIUKQmCG8zt5lJ6q2jmY+VewH+gNVj5dakXqUkPzpWe3OO4+3trYQQTy45OVkFBQWpR58/BaiyZcuqffv25cnx9u3bp/z9/Q2Ol/Zq166d2rNnT54cV6QCDqgszq9mXUlomtYFmPho8RwwJoNBsU4ppWalW3YFajz6mZ4H8CNwT9O0M8BVwAGoC1QkdayoCUqpzebEJ4R4clFRUQwcONCg1/Lzzz/Pr7/+SpUqVXLtOEop/vjjD2bMmMEffxiP9tOhQwcmT54szxsKCHNvN5VK9++Gj16m7ARmZVCX3hFgHtAYcAfqk/rt4SqwnNSmrwfNjE0I8YQePnxIkyZNOHXqfyP0t23blnXr1uXaM4iUlBQ2bNjAjBkzCA0NNajTNI0ePXrw/vvv06BBg1w5nsglWV1qFIaX3G4S4slNmzZNf6vn7bffVomJibmy34cPH6ply5apmjVrGt1SsrKyUgMHDlQnTpzIlWOJ7CG3bjcJIZ5+7733HidPnsTf358+ffo88f6io6NZsmQJwcHBXL9+3aDOxsaGV155hfHjx+fqrSyR+yRJCFEE3blzh+TkZMqXL68v0+l0BnM+59SNGzeYP38+ixYtMpom1MHBgREjRvDWW29RsWLFJz6WyHuSJIQoYv7++28CAgJ45pln2LZtW65NvvPvv/8SHBzMt99+q286m6ZChQqMHTuWESNG5Gk/C5H7cjwshxCicElJSWHOnDk0bdqUS5cusXv3bt5+++0n2qd6NAtchw4dqFu3Ll999ZVBgnjuuef48ssvuXDhAhMmTJAEUQjJlYQQRUB4eDiDBg1i8+b/tSp3dHSkbdu2OdpffHw8q1atYu7cufz7779G9T4+PkyYMIGuXbui08l30cJMkoQQT7mtW7cyYMAA/RSjAI0bN+b777+natWq2drXrVu3+OKLL1iwYIHB/iD1mUb37t0ZN24cTZs2zZXYheVJkhDiKZWYmMjkyZP5+OOPDSbmGT9+PNOmTcvWs4hDhw4xf/58vv/+e6PnDSVLluTVV1/lzTffzHbSEQWfJAkhnkIXLlygX79+7N+/X19Wrlw5vvnmG/z8/MzaR2JiIj/++CPz58/nzz//NKqvVKkSb7zxBsOGDZNnDU8xSRJCPIWWL19ukCD8/PxYuXKlQZPXjNy+fZulS5eycOFCrl69alTfpEkTxowZQ0BAQK61jBIF1xPNTFdQyMx0QhiKj4+nYcOGnDp1iunTp/P2229n+gBZKcX+/ftZuHAha9asMbqlVKxYMXr37s2YMWNo3LhxXocv8ok5M9PJlYQQhZxSigcPHlCyZEl9WfHixfnmm2+Ij4/Hx8cnw21jY2NZtWoVCxcu5PDhw0b15cuXZ+TIkYwYMQJX18fH6RRFgSQJIQqx69evM2LECJKSkti4cSPpR2f28vLKcLtTp06xaNEiVqxYQXR0tFG9j48Pr732GoGBgRQvXjxPYheFgyQJIQohpRSrVq1izJgx+ulEly9fziuvvJLhNg8fPiQkJIQlS5awa9cuo/oSJUrQr18/Ro0aJXNGCz1JEkIUMhcuXGDMmDH8+uuvBuXnzp0zuf6JEyf48ssv+frrr03OT129enVGjx7NoEGDcHFxyZOYReElSUKIQiIhIYG5c+cydepU4uLi9OUeHh4sX77cYO7nuLg41q5dy5IlS0w2X7WysqJLly6MHj2aNm3aSK9okSFJEkIUArt27WLUqFGcOHFCX6ZpGqNGjeLjjz/G3t4epRR///03X331FT/88IPRCKyQmlCGDRvGkCFD5EF0IXPw4EHu3r1LVFQUXbp0ybdnRZIkhCjAUlJSGDZsGF999ZVBuZeXF4sXL8bHx4fw8HC++OILli9fzvHjx432YW1tTdeuXRk+fDgvvviiXDVYyN69e7l69SpRUVF07dqVcuXKGdT379+f48ePExUVxdatW3n22WcN6l966SVu3LgBwJUrV6hUqVK+xC1JQogCTKfTYW39v4+pvb09H330ESNGjGDr1q10796dDRs2kJSUZLTts88+y7Bhwxg8eLBZnehE5kJDQzl9+jSRkZH4+flRs2ZNg/rXXnuNLVu2EBUVxapVq3jxxRcN6sePH6+/9VejRg2jJHHixAl9M+R79+4ZJQknJyd9koiKipIkIYRINXPmTH788UdatmzJkCFD2LJlC+7u7ty+fdtoXTs7OwIDA3nllVdo3ry5QZPYou7o0aMcOnSIyMhImjRpQpMmTQzqp06dyldffUVkZCSffPIJw4cPN6j//PPP9ZMyrVixwihJhIeH6xsP3Lt3z+j4Tk5O+n+buhWYVb2vry9ubm44Ojrma7NkSRJCFBBnz55l0qRJzJ0712DWttjYWIYOHcp///tf/P39TW7brFkzXnnlFQICAnBwcMivkPPVhQsX2LdvH5GRkVSrVs1oDKrly5fz0UcfERkZyciRI5kxY4ZB/bp16/joo48AmDJlilGSuH//PpcuXQIw2Qos/fhUkZGRRvXpT/Km6ps3b06JEiVwcnIyOSvfvHnzSEhIwMnJyeRVwuO3HPOLJAkhLOzevXt89NFHfP755yQlJVGyZEnmzp3Ljz/+yDfffMP27dsxNXyOm5sb/fv3Z8iQIdSoUcMCkWfPvXv3CA0N5d69ezg5ORklvG3btjF27FgiIiJo06YNK1euNKjfsWOHvh/IgAEDjJJEfHw8Fy5cAODu3btGx0/fvDcnJ3kfHx8iIyNxcnKibt26RvVTpkxh/PjxODk5UapUKaP69957z6gsvXr16mVabymSJISwkISEBBYtWsSHH35o8M11+fLlJqcAhdRhuXv27MmAAQN44YUXsLKyytd4T548yb1790hMTDQ6SZ89e5YRI0YQERFB5cqV+fnnnw3qjx07RqdOnYDUK5/Hk0RSUpJ+AqO0e+/ppT/J5+Sbft26dRkwYADOzs60bNnSqH748OH07dsXZ2dnHB0djer79+9P//79jcrTPPPMMxnWFWaSJITIZ0lJSXz33XdMnTqVsLAwo3qllEGC0Ol0tG3bloEDB9K9e3eDMZqyI22Mp7t37xIREYGnp6dBfWxsLK+//jr37t0jKSnJqLPezZs39UN9VKxYkWvXrhntf/v27QAmh/rI6iSfVX3VqlUJCAjAxcWF+vXrG9V37NiRM2fO4OLiYnBVkObFF180epicXtmyZSlbtmyG9UWVJAkh8klycjKrV6/mww8/5MyZM1muX79+ffr27Uu/fv1wc3Mz2pemaUbNWf/zn/9w/fp17t69S3BwsFFCcXJyIjk5GUgdpiP9A1AbGxtWrFgBpCamlJQUg/2nv4Vi6iSeVX358uXx8/PDxcXF5OREderU4Z9//sHFxcXk7Zp69eqxZs0ao/L0781UchBPRoYKFyIfJCUl0aFDB7Zt25bpepUrV6ZZs2bUrVuXoUOHGjWT9PPz49ChQ9y7d49Tp07x3HPPGdRXqlRJ/w3/woULeHh4GNSXLVuWO3fuAKm3dCpUqGBQ7+joSExMDJB6ok9/C0cphbe3N46Ojri4uLB27VqD5rnJycls374dFxcXSpcubXRsUfDIUOFCWEB8fDy3b9/Gzs6Of/75h7Vr17J+/XqTTVYh9QF0nz596Nu3L2PHjuWHH37ghx9+oGnTpkZJIioqSv9Q9s6dO0ZJonTp0vokcffuXaMTtZubGzY2NpQuXZqHDx8axbJkyRKKFy9OqVKlsLOzM6jTNI1Dhw5l+L6trKwyvZ0jCidJEkJkw7///svhw4e5desWzZs3N5qAZ8iQIfpbNnZ2djx48CDDfbVr146JEyfSvHlz/W2d9PfE077xp1e6dGn9v009nB0+fDj37t2jTJkyRreoAJNzRqTXp0+fTOtF0SNJQhQ5Simio6NJTk42uvf9yy+/8PXXXxMeHk7v3r15/fXXDeq//fZbPv74YwCmTZtG48aNiYyMZMOGDaxdu5YNGzbo1308Qbi6ulKmTBmOHTsGQOfOnY1a2TRq1Ij4+HjKlCljsi39l19+ibW1NaVKlTI5dehrr72Wjd+EEFmTJCGeKpGRkRw7doybN2/i4uJidPtj5cqVDB8+nPj4eIYOHcqXX35pUH/x4kVCQkIATLaFT3/7Z+vWrezYsYPt27frHwY/zs3NjR49ehAQEECzZs04ePAgFy5coEyZMjz//PNG62fVlt7U1YEQeUmShChUrly5wooVK7h58ybly5dn8uTJBvW7d++mS5cuQGqTyMeThL29PfHx8UDqMAqPS58Ebt26pf93cnIy+/fv56+//sLR0ZHo6Gh27tyZYZx2dnYMGDCAefPmGbQgatSoEY0aNcrGOxbCsiRJCItITk4mIiKCMmXKGJRfvXqVkSNHcv36dZycnPTt7tPcvn1bnxjq1q1rlCTSD2R38+ZNo+Om1dvZ2ZnsiNasWTN++OEHypcvj5OTE6tXr2bjxo1s3LjR5DOCx1WuXJn33nuPwYMHU6JEiSzXF6KgkyQhcl1sbCzXrl3jxo0btGrVyqAuMjKSOnXqcPPmTRwcHIza0xcrVkzficvULGnpm2yaSgJubm74+vpSvnx5k7dzGjduTExMDPb29kZ1KSkp3Lx5kzNnzjBv3jxCQ0NJSUkx6z03aNCACRMm0KNHD4NmoUIUdvLXLLLt3LlzXLp0iStXrtC7d2+Db8xKKUqVKqXvMXz//n2DDl2Ojo7cunWL5ORkIiMjefDggUFTy7Jly2JlZaW/0nj48CG2trb6+nLlyjFhwgRcXV1NTprj5ubG3r17M4y9WLFiBg98w8PD+f3339m0aRObN2/OsJkqpCYof39/bt68yYYNG7CysqJHjx6MHj2aVq1ayYir4qkkSUIYOXz4MGfPnuXy5cu8/PLLRh2u2rVrx8WLF4HU4YvTDy6naRpubm76gdauXbtm0JZfp9NRsWJFLl26ROnSpblz547BmDc6nY5ffvmFUqVK4erqio2NjcGxra2tmTVrVo7f24MHD9i1axe///47W7du5ejRo5mu7+LiwhtvvEHnzp3x9vZGp9Nx+vRpvL29GT58uMkWSEI8TSRJFEHHjh3j+PHjXLx4ke7duxuNIPrGG2+we/duIHUGtMeTROXKlfVJ4sqVK0bbP/vssyilqFSpEomJiUbH37dvHy4uLgZXCOl17Ngxp2/NSGJiIocOHWLbtm1s3bqVvXv3mhw4L421tbXBBD7379/n9ddfN3h2UqNGDYKCgnItRiEKMkkST6HLly9z7NgxwsLCaNq0Kd7e3gb106dPZ/Xq1UBq2/3HT/Lpv9lfvnzZaP8NG6b24q9cubLBsA1ptm7dmml8eTm3clJSEocOHdI3Td2zZw/379/PcH2dToeLiwuRkZEkJycbzfDm4uLCyZMnadGiRZ7FLERBJkmiEIqPjycsLIyzZ8/i5uZmlAQ+++wzgoODgdQOX4/Xpx9cLe2KID1fX19iYmJ45plnTM5TMHfu3Fx4F7kjISGBQ4cOsXv3bnbs2MHu3bv1Yw9lpHbt2qSkpBAWFkZ8fLzR3AM2Nja89NJLDB48mPbt25vstCZEUSFJooBSSnHnzh0ePHiAu7u7Qd38+fMZP348ACNHjjRKAtWrV9f/29RQ1I0bN6Z79+54eHjQtGlTo/rXXnutwPbcjY6OZt++fezZs4fdu3cTGhpqcgyi9CpXrswLL7ygHyra1dWVPn36cPLkSYP1GjVqxODBg+nTp4/JUUiFKIokSRRAmzdv5uWXX+bu3bt06dKFn376yaA+/dy6aXPqplevXj3atGnDs88+ywsvvGBU361bN7p165b7gecypRRhYWHs37+f/fv38+eff3LkyJEsm6WmXV3Z2tpy5coVnn32Wb7++muDdQICAli9ejW1a9cmICCAwMBAk01mhSjqJElYwPXr15k7dy5Hjx7F1tbWaAavcuXK6W+BnDp1+xM7nQAAEFdJREFUymj7559/Hnd3d6pXr27ySqBZs2ZZDkldEEVHR/P333/rk8L+/fvN6sBWpUoVmjZtSpkyZYiIiGDPnj0Gv9MTJ06QlJRk0H+hU6dOnDhxQhKDEFmQJJFHkpKSOH78OKdOnaJ3795G9Z9++imQOh3l45O71KxZE51Oh62tLc7Ozkb11apVM/ksoTB58OABhw8f5sCBA/rXqVOnTM7lnJ6maXh6etKsWTMqVqxITEwMhw4d4scff8xwxNWoqCgOHz6sf+AOUKJECUkQQphBkkQeiIuLo0KFCvopHDt06GAwY5arqyvly5cnPDyc2NhYLl26RJUqVfT1JUqU4PLly7i6uhrNPFYYRUdHc/ToUQ4fPszBgwc5ePAgx48fN6s3s7OzM02aNDF4OTk58fDhQ5ydnfXjMD3O3t6edu3a0blzZzp27Cj9GYTIIUkSORQREcGWLVvYtWsXH3zwgcFJqESJEnh4eOg7ah0+fNhgeApN0/jkk0+wt7enbt26JidQL4yjfSqluHr1KkeOHOHw4cP61/nz583aXqfTUbduXXx9falbty42NjZcuXKF/fv34+XlRfv27fXr2tra0rRpU4OxnapXr07nzp3p3LkzLVq0MBhYTwiRM5Ikcqhdu3YcPHgQgFatWhEYGGhQ36xZM+7du0fjxo1NdhobOHBgvsSZV27fvs2///7L8ePH+ffff/X/NjURjimaplGzZk0aNmyIl5cXLi4uxMXFcfDgQXbu3MnixYsN1n/++efp2rWrQVmvXr2oXLkybdq04YUXXjCZbIUQT0bmuM5EUlISf/75J87Oznh6ehrUTZo0iWnTpgEwZswY5s+fb1AfHx9f6L/JpqSkcPnyZU6fPs2pU6f0P48fP24wjHZWrKysqFWrFl5eXnh5edGoUSO8vLz47bffmDFjBidOnDDZMzu9xo0bExoa+qRvSQiRTp7Mca1pWj9gFFAPsAJOAcuBRUop84bMNNxfB2Ac0BCwBcKA74E5SinTN5zzwYEDB+jfvz+nT59m0KBB+ikp03Tp0oUtW7bQoUMHOnXqZLR9YUkQSilu3LjB+fPnOX/+POfOnePs2bOcOnWKs2fPEhcXl639OTk54enpyfPPP0/p0qWxsrIiOjoapRTz5s0zWDcpKYkjR46Y3I+VlRWenp74+vrqX0KI/JetKwlN0xYAo4GHwDYgEWgLOAA/AgFKKdNTdJne33jgYyAZ2AFEAK2AssB+oK1SKuNJgh/JiyuJ4OBgxo0bB0CZMmW4efOmyfkHCoOoqCguXbrEpUuXuHjxIhcvXtQnhbCwsEznYc6InZ0dNWvWxM3NDRcXF6ytrUlISOD69eucOHHCaBhvOzs7YmJiDB7Enzx5klq1agGpvcC9vLxo3Lgxvr6+eHt7G4weK4TIfbl6JaFpWk9SE8RNoKVS6uyj8vLAdqA78DowL8OdGO6vITALeAC0UUqFPiq3B34FWgLTgbfMjTE3vfnmm2zdupWdO3fSs2dP7t+/b9BCqaCIiYnh2rVrRq/Lly/rE0NUVFSO9+/s7IyrqytOTk506tSJ+vXrU6tWLTw8PLh165bZ4zA9ePCAy5cv4+HhoS977rnn2LFjB56enibHgBJCWJ7ZVxKaph0AvIFBSqmVj9W1IvVK4CbgZs5tJ03T1gE9gSlKqamP1VUFzgJJQHmlVKZPQ3PjSkIpZTQfwK1bt4iOjqZatWpPtO/sxhEbG8vdu3cJDw/n1q1bhIeHG7xu3br1/+2dfWzV1RnHP9/SlwuUdnb2JQVGaH1JIXHDF3yZ03UNkRhHRnzZgoUYRyRTMxLMnJNoyBwR2dymG7qQ4QjTJVNgS4yTyJySuSlBnS+TdGUaJhZcXItUC1paz/44v1tuL/fe3tv7uy+9PJ/k5Nx7fuc8nPPw3D6/3++c8xwOHTpET0/PmHGK0iESiVBdXT0SlvvYsWP09/efdG7z7t27mT9//qi+Tp06NeErqaqqKs466yza2tpoa2tjzpw5LFy4kJqamqz7axhGOIT2JCFpBt5BDAJPxF93zu2S1ANMBy4Ckp/64uVVAtF40I8lkPeOpBeBLwNXAr9Lp5/jYXBwkNWrV9PX18emTZtGXWtoaBh15nE6HD9+nIGBAY4ePcrAwABHjhyhv79/VB79fPjwYXp7e+nt7aWvr28kTxXKerxIYu7cuVxyySXMmjWLlpYWWltb6ezspLu7e8z4R+CPFo11EpJYsGABw8PDtLa20tLSQktLC21tbcyePXvCvp4zDOME6b5umhfkbznnks1k7sE7iXmM4SSAs4EpQJ9zLtki+j14JzGPHDmJu+66iw0bNowcobljxw5qampwzo1Kw8PDDA8PU1VVRXNzM4ODgwwODo5EEP3www8ZGhpiaGhozB3DYVNWVpbWpjTnHMuXL2flypWjypubm+nu7k7Ypr6+nhkzZoykmTNnnlQnPq6UYRilRbpOIrod+D8p6kQPHpidok68vJMPKxifvHGxbdu2UWcsHzx4kIMHD6Zss2/fvlx1Z0wuvfRSVqxYQWNjI42NjUyfPp1Vq1axZcuWsRsDfX19J5UtXbqU9vZ2GhsbaWhooLGxkaamJpqbm5MeCmQYxqlDuk4iemr8QIo60ZNdpuVDnqSbgJuAcW+iynfIi5kzZ7Jo0SJqamqora2ltraWnTt3sn379qRtIpEIkydPZsqUKVx22WV0dnaOut7R0cHUqVOZNm3aqFRbW0tdXR11dXWcdtpp1NXVJVwtdOONN4Y+TsMwSod0nUR0RjesdylZy3PObQQ2gp+4Ho+MO++8k+7ubvbu3cukSZMoKytD0kgeTRUVFZSXl1NfX097ezuVlZUjqaenh66uLiKRCJFIhMrKSsrKykZSeXn5SGpqamLevHmj+rB48WLWrl07SmZVVRUVFRVEIpExHdmyZcsm/O5twzCKl3SdRHQJTXWKOtFr6Sy3CVveuFiyZEnWMs4555yszmSOvjoyDMMoRtJ937I/yGelqBOd1dyfok68vFTviTKRZxiGYeSAdJ3EP4J8rqTJSepcEFc3FV3AMaBOUmuSOtG1lunIMwzDMHJAWk7COXcAeBWoBK6Nvx5sppuB30z3YhryBoGng6/XJ5DXAlyM35fxVDp9NAzDMMInk+U99wb5fZJGtiBLagAeCr6ui91tLelWSV2SEq3RXIefuP6+pPkxbaqBR4K+PTTWbmvDMAwjd6TtJJxzW4GHgSbgTUlPStqOD58xB/gj8Mu4ZqfjN86dNPfgnNsD3IHfVPd3Sc9Iehx4Gx/kbzewOuMRGYZhGKGRUahw59zNkl4AbsH/IY+GCn+EcYQKd86tl/QGcBt+TiMaKvxBChwq3DAMw7BDhwzDME5Z0gnwVxJOQtIHpA4ZMhanA/8LqTunAqavzDB9ZYbpKzOy0dcs51x9qgol4SSyRdLLY3lT4wSmr8wwfWWG6Sszcq2v/AYvMgzDMCYU5iQMwzCMpJiT8GwsdAcmGKavzDB9ZYbpKzNyqi+bkzAMwzCSYk8ShmEYRlLMSRiGYRhJKTknIWmJpL9KOiLpY0kvS7pF0rjGKmlhEDKkT9JRSf+UtFpSVdh9LwRh6UvSGkkuRfokV2PIB5LOlrRS0qNBPLLPgnFdk6XcUO21WAhbX5I2j2FfXWGPIV9IqpDUIel+SS9JOiRpUFKPpK2SvpqF7KztK6OwHMWOpA3AzcAnwLPAcaADH1OqQ9K1zrnhDOTdDtwHDAPPA4fx4Uh+BFwlqcM5dzTUQeSRsPUV8DrwWoLy49n0tQj4DrAyTIE50n+xELq+Av4G/DtB+aEc/Fv54nJgZ/D5feAV/NHOc4Crgasl3eOcuzsToaHZl3OuJFKgTIc3ljNjyhuBvcG1lRnIOx/4LPjPujCmvBrYFcj7WaHHXUT6WhO0WVPoseVIX8uB9cB1QCv+psEB1xSD/ost5UBfm4P2NxR6bDnQ1deArcBXElz7JjAUjL29EPZVcAWFqOiXg4EvS3Dt8hiFlaUpb2vQ5u4E11rwTxefAp8r9NiLRF8l7SQSjDfbP3qh6r/YkzmJrHT362DsmzJoE5p9Tej3nlEkzQDOwx9S9ET8defcLqAHH+b8ojTkVQLRg6sfSyDvHfzhSpXAlePueIEIW19GZpj+jQyJns45I53KYdtXSTgJYF6Qv+WcO5akzp64uqk4G3/ORZ9z7u0Q5BUbYesrlnMl3Sdpo6R1khYHTtc4QS71X+q0S/ppYF/3SLpiok/yp8GZQZ7uvEuo9lUqE9ezgzxVJNh34+qmI+/dFHUykVdshK2vWL4epFjek9QZ3MEYudV/qbMsQdleSd9yzr2Z997kGElNwA3B121pNgvVvkrFA1cH+UCKOh8H+bQCyCs2cjG+t4EfAF8CaoF6/ITcLvxj8p8kfTHzrpYkpW5fueA14LvAXLz+moGr8Kvp5gB/ljS9cN0LH0nlwKP439Ozzrkn02waqn2VypOEgjysGCNhyys2Qh+fc+63CYqfA56TtBW/2mIt/od9qlPq9hU6zrmfxxUNAE9J2om/EbkIf5Nya777lkN+hV+yegDozKBdqPZVKk8SHwV5dYo60WsfpaiTK3nFRr7H98MgXyCpIgR5E51St6+84ZwbBO4Nvk64RSTJkPQA8G38vokO59z7GTQP1b5KxUnsD/JZKerMjKubjrwvhCSv2Ngf5GHpayyiu2Er8adonersD/J86b/UidpXSbxuknQ//tXaB3gHsS9DEfuDPBT7KhUnEV0iNlfS5CR1Loirm4ou4BhQJ6k1SZ35GcgrNsLW11h8Pubzx0lrnTrkW/+lTtS+JrxtSVoPrAJ6gQXOub3jEBOqfZWEk3DOHQBexd+pXht/XdLl+MnT9/H7G8aSNwg8HXy9PoG8FuBi/Drkp8bd8QIRtr7S4Log/5dz7pR/fVIA/Zc6Ufvak7JWkSNpHfA9fPifBc6518cjJ3T7KvRuwhB3JV7DiV2EZ8SUNwBvkWAbOn6SqwvYkkDeBZwIyzE/pryaE7tHJ3JYjtD0hX8ttwSoiisXsBQ4GshbUehxh6i/qA0k3UGMf1feBdwbhv4ncspGX/gVc1cBk+LKy/F33cOB7CsKPc4s9HNPMIbDwHlptsmLfRVcOSEr+qFg8MeAJ4HtwJGg7A8JjGxNcO35JPJuD64PAc8AjwP/DcpeAqYUeszFoK/gR+yAfvzd3LZA3jtBuQN+UejxZqmrc4P/82jqD8bVHVse12ZzUGdzGPqfSClMfQHfCMp78Xe+TwA78LuGXeAkbi/0mLPQ1aKY38meQA+J0h2FsK9SWQILgHPuZkkvALfg45NMwnvaR4CHnXOfZShvvaQ3gNvwTxYR/B++B4GfOOc+DbP/+SZEfR0AfozX0Rn4texl+MfZ3wMbnXN/Cbn7+aYGuDBB+ZkJytIibHstMsLU1+vAA/h5wFn4XcIOeA/4DbDBOffKOPtZDNTFfD4/SInYBaxLV2hY9mXHlxqGYRhJKYmJa8MwDCM3mJMwDMMwkmJOwjAMw0iKOQnDMAwjKeYkDMMwjKSYkzAMwzCSYk7CMAzDSIo5CcMwDCMp5iQMwzCMpPwfjTPiDdNA6OEAAAAASUVORK5CYII=\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"#Plot x^2\n",
"plt.plot(xarray, pow2, color='k', linestyle='-', label='square')\n",
"#Plot x^3\n",
"plt.plot(xarray, pow3, color='k', linestyle='--', label='cube')\n",
"#Plot sqrt(x)\n",
"plt.plot(xarray, pow_half, color='k', linestyle=':', label='square root')\n",
"#Plot the legends in the best location\n",
"plt.legend(loc='best')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To illustrate other features, we will plot the same data, but varying the colors instead of the line style. We'll also use LaTeX syntax to write formulas in the labels. If you want to know more about LaTeX syntax, there is a [quick guide to LaTeX](https://users.dickinson.edu/~richesod/latex/latexcheatsheet.pdf) available online.\n",
"\n",
"Adding a semicolon (`';'`) to the last line in the plotting code block prevents that ugly output, like `<matplotlib.legend.Legend at 0x7f8c83cc7898>`. Try it."
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"#Plot x^2\n",
"plt.plot(xarray, pow2, color='red', linestyle='-', label='$x^2$')\n",
"#Plot x^3\n",
"plt.plot(xarray, pow3, color='green', linestyle='-', label='$x^3$')\n",
"#Plot sqrt(x)\n",
"plt.plot(xarray, pow_half, color='blue', linestyle='-', label='$\\sqrt{x}$')\n",
"#Plot the legends in the best location\n",
"plt.legend(loc='best'); "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That's very nice! By now, you are probably imagining all the great stuff you can do with Jupyter notebooks, Python and its scientific libraries **NumPy** and **Matplotlib**. We just saw an introduction to plotting but we will keep learning about the power of **Matplotlib** in the next lesson. \n",
"\n",
"If you are curious, you can explore all the beautiful plots you can make by browsing the [Matplotlib gallery](http://matplotlib.org/gallery.html)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Exercise:\n",
"\n",
"Pick two different operations to apply to the `xarray` and plot them the resulting data in the same plot. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What we've learned\n",
"\n",
"* Good coding habits and file naming\n",
"* How to define a function and return outputs\n",
"* How to import libraries\n",
"* Multidimensional arrays using NumPy\n",
"* Accessing values and slicing in NumPy arrays\n",
"* `%%time` magic to time cell execution.\n",
"* Performance comparison: lists vs NumPy arrays\n",
"* Basic plotting with `pyplot`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## References\n",
"\n",
"1. [Best practices for file naming](https://library.stanford.edu/research/data-management-services/data-best-practices/best-practices-file-naming). Stanford Libraries\n",
"\n",
"1. _Effective Computation in Physics: Field Guide to Research with Python_ (2015). Anthony Scopatz & Kathryn D. Huff. O'Reilly Media, Inc.\n",
"\n",
"2. _Numerical Python: A Practical Techniques Approach for Industry_. (2015). Robert Johansson. Appress. \n",
"\n",
"2. [\"The world of Jupyter\"—a tutorial](https://github.com/barbagroup/jupyter-tutorial). Lorena A. Barba - 2016"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Problems\n",
"\n",
"1. Create a function called `sincos(x)` that returns two arrays, `sinx` and `cosx` that return the sine and cosine of the input array, `x`. \n",
"\n",
" a. Document your function with a help file in `'''help'''`\n",
" \n",
" b. Use your function to plot sin(x) and cos(x) for x=$0..2\\pi$\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def sincos(x):\n",
" '''help'''\n",
" # your code here, replace '''help''' with your documentation\n",
" \n",
" return sinx, cosx\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2. Use a for-loop to create a variable called `A_99`, where every element is the product\n",
"of the two indices from 0 to 9 e.g. A_99[3,2]=6 and A_99[4,4]=16. \n",
"\n",
" a. time your script using `%%time` \n",
" \n",
" b. Calculate the mean of `A_99`\n",
"\n",
" c. Calculate the standard deviation of `A_99`\n",
" \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3. Use the two arrays, X and Y, given below to create A_99 using numpy array math rather than a for-loop. \n",
"\n",
" a. time your script using `%%time` \n",
" \n",
" b. Calculate the mean of `A_99`\n",
"\n",
" c. Calculate the standard deviation of `A_99`\n",
" \n",
" d. create a filled contour plot of X, Y, A_99 [contourf plot documentation](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.contourf.html)"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {},
"outputs": [],
"source": [
"# given X and Y arrays\n",
"X,Y=np.meshgrid(np.arange(0,10),np.arange(0,10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"4. The following linear interpolation function has an error. It is supposed to return y(x) given the the two points $p_1=[x_1,~y_1]$ and $p_2=[x_2,~y_2]$. Currently, it just returns and error."
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {},
"outputs": [],
"source": [
"def linInterp(x,p1,p2):\n",
" '''linear interplation function\n",
" return y(x) given the two endpoints \n",
" p1=np.array([x1,y1])\n",
" and\n",
" p2=np.array([x2,y2])'''\n",
" slope = (p2[2]-p1[2])/(p2[1]-p1[1])\n",
" \n",
" return p1[2]+slope*(x - p1[1])\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {},
"outputs": [
{
"ename": "IndexError",
"evalue": "index 2 is out of bounds for axis 0 with size 2",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-93-db508f40e1df>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mp1\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\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 3\u001b[0m \u001b[0mp2\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\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----> 4\u001b[0;31m \u001b[0mlinInterp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mp1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mp2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# answer should be 1.5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-92-711e290f4cb8>\u001b[0m in \u001b[0;36mlinInterp\u001b[0;34m(x, p1, p2)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m p2=np.array([x2,y2])'''\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mslope\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mp2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\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 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mslope\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\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;31mIndexError\u001b[0m: index 2 is out of bounds for axis 0 with size 2"
]
}
],
"source": [
"# test cases for linInterp\n",
"p1=np.array([0,1])\n",
"p2=np.array([2,2])\n",
"linInterp(1,p1,p2) # answer should be 1.5"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [
{
"ename": "IndexError",
"evalue": "index 2 is out of bounds for axis 0 with size 2",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-94-7b3f7f87fb7f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mp1\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m3\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 2\u001b[0m \u001b[0mp2\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m12\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m5\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----> 3\u001b[0;31m \u001b[0mlinInterp\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m11.25\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mp1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mp2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# answer should be -3.5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-92-711e290f4cb8>\u001b[0m in \u001b[0;36mlinInterp\u001b[0;34m(x, p1, p2)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m p2=np.array([x2,y2])'''\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mslope\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mp2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\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 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0mslope\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mp1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\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;31mIndexError\u001b[0m: index 2 is out of bounds for axis 0 with size 2"
]
}
],
"source": [
"p1=np.array([11,-3])\n",
"p2=np.array([12,-5])\n",
"linInterp(11.25,p1,p2) # answer should be -3.5"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}