python_2.md (13039B)
1 --- 2 jupyter: 3 jupytext: 4 formats: ipynb,md 5 text_representation: 6 extension: .md 7 format_name: markdown 8 format_version: '1.3' 9 jupytext_version: 1.13.8 10 kernelspec: 11 display_name: Python 3 12 language: python 13 name: python3 14 --- 15 16 # Basic Python 17 18 * Booleans and Control Flow 19 * Functions 20 * Exceptions 21 * Plotting 22 23 24 We'll be embedding some HTML into our notebook. To do so, we need to import a 25 library: 26 27 ```python 28 from IPython.display import HTML 29 ``` 30 31 We'll also probably use `numpy` so we might as well import it too: 32 33 ```python 34 import numpy as np 35 ``` 36 37 ## Booleans and Control Flow 38 39 These are pretty standard things in any language. I'll just show you a little 40 bit of syntax here. However, I will put a slight emphasis on the exception 41 testing. It's important. 42 43 44 #### Testing for membership 45 46 ```python 47 some_primes = [2, 3, 5, 7, 13] 48 print(4 in some_primes) 49 print(13 in some_primes) 50 ``` 51 52 #### Some basic if/elif/else statements 53 54 ```python 55 x = 9 56 if x in some_primes: 57 print('x is prime!') 58 elif x > 13: 59 print('x may or may not be prime.') 60 else: 61 print('x is definitely not prime.') 62 ``` 63 64 Breaking out of a loop: 65 66 ```python 67 for x in some_primes: 68 if x == 2: 69 print('Only even prime.') 70 break 71 72 ``` 73 74 Continuing a loop. Notice that everything after the continue statement is 75 ignored. 76 77 ```python 78 i = 0 79 while i < 10: 80 i += 1 81 if i <= 5: 82 print(i) 83 continue 84 print(i-1) 85 else: 86 print('Done with this.') 87 break 88 89 ``` 90 91 #### Some basic exception handling 92 93 Python has a number of built-in exceptions ([Built-in 94 exceptions](https://docs.python.org/3/library/exceptions.html), [Python Standard 95 Exceptions](https://www.tutorialspoint.com/python/standard_exceptions.htm)). It 96 is usually a good idea to let Python raise exceptions for you since it's really 97 good at it. However, there are times when you may want to write your own 98 exception (we won't talk about that now) or when you want to press ahead even in 99 the face of an error. 100 101 I can make that last statement clearer. You have two options: catch and 102 respond to errors when they're raised or ignore them. If you ignore them, then 103 Python's default exception-handling behavior takes over which will ultimately 104 print out the error message and terminate the program. If you respond to the 105 errors, then you need to tell your program what to do. In essence, you will 106 shift the control flow of your program if you choose this second option. 107 108 Let's look at an example or two. 109 110 ```python 111 x, y = 1.0, 0.0 112 z = x / y 113 z**2.0 114 ``` 115 116 Python took care of error for us and terminated the program at the second line. 117 118 But perhaps a division by zero isn't the end of the world. What if we have a 119 piece-wise function? 120 121 ```python 122 x, y = 1.0, 0.0 123 try: 124 z = x / y 125 except ZeroDivisionError: 126 z = 0.0 127 print('z = {}'.format(z)) 128 ``` 129 130 This could, of course, have been handled with an `if-else` block. 131 132 One old motivation for using exception handling is to check the input arguments 133 of a function for validity. You can still do this, but the latest advice is to 134 just let Python's exception handler deal with it and terminate the program if 135 need be. 136 137 138 ## Functions 139 140 #### Python functions are first class: 141 142 * You can assign variables to them 143 * You can pass them into functions 144 * You can return them from functions 145 146 147 ### Example: 148 149 ```python 150 alist = [1,13,5,7] 151 newmax = max # Assign newmax to the function max 152 mymax1 = max(alist) # Get the maximum of the list using the max built-in 153 mymax2 = newmax(alist) # Get the maximum of the list using the new max function 154 print('Original maximum gives {0} and new maximum gives {1}.'.format(mymax1, mymax2)) 155 ``` 156 157 The syntax for defining functions is pretty straightforward. 158 159 ```python 160 def rect(w,h): 161 return w * h 162 def circle(r): 163 return np.pi * r * r 164 def parabola(x, a, b, c): 165 return a * x * x + b * x + c 166 ``` 167 168 Notice that the function name is preceded by the keyword `def`. The end of the 169 line **must** have a colon! The function body is indented. The quantity to be 170 returned is returned using the `return` statement. 171 172 Because functions are first class, we can pass functions to other functions. 173 174 ```python 175 def parab_extrema(a, b, c, parab): 176 x_extreme = - b / 2.0 / a # Location of max or min 177 x_left = x_extreme - 1.0 # Point to the left of max or min 178 x_right = x_extreme + 1.0 # Point to the right of max or min 179 p_left = parab(x_left, a, b, c) # Value at left point 180 p_right = parab(x_right, a, b, c) # Value at right point 181 p_extreme = parab(x_extreme, a, b, c) # Value at max or min 182 # Check if extremum is maximum or minimum and print out result 183 if (p_left > p_extreme) & (p_right > p_extreme): 184 print('The extremum for this parabola has coordinates ({x:4.3f}, {y:4.3f}) and is a minimum.'.format(x=x_extreme, y=p_extreme)) 185 elif (p_left < p_extreme) & (p_right < p_extreme): 186 print('The extremum for this parabola has coordinates ({x:4.3f}, {y:4.3f}) and is a maximum.'.format(x=x_extreme, y=p_extreme)) 187 else: 188 print('Something went wrong.') 189 ``` 190 191 ```python 192 a, b, c = 0.2, 1.0, -3.0 193 parab_extrema(a, b, c, parabola) 194 a, b, c = -5.0, -5.0, 5.0 195 parab_extrema(a, b, c, parabola) 196 ``` 197 198 There are some other convenient ways to interact with function arguments. 199 200 One thing you may wish to do is provide a default argument to the function. If 201 that argument is not specified when the function is called, then the default 202 value is assumed. This is a type of keyword argument. 203 204 Let's return to our nice parabola example. We'll make $b=0$ the default. 205 206 ```python 207 def parabola(x, a, c, b=0.0): 208 return a * x * x + b * x + c 209 ``` 210 211 Notice that we had to move our default argument to a position _after_ the 212 mandatory arguments. That hurts the readability a little bit in this example, 213 but we'll press forward regardless. 214 215 Now call the `parab_extreme() function` again. 216 217 ```python 218 def parab_extrema(a, b, c, parab): 219 x_extreme = - b / 2.0 / a # Location of max or min 220 x_left = x_extreme - 1.0 # Point to the left of max or min 221 x_right = x_extreme + 1.0 # Point to the right of max or min 222 p_left = parab(x_left, a, c) # Value at left point 223 p_right = parab(x_right, a, c) # Value at right point 224 p_extreme = parab(x_extreme, a, c) # Value at max or min 225 # Check if extremum is maximum or minimum and print out result 226 if (p_left > p_extreme) & (p_right > p_extreme): 227 print('The extremum for this parabola has coordinates ({x:4.3f}, {y:4.3f}) and is a minimum.'.format(x=x_extreme, y=p_extreme)) 228 elif (p_left < p_extreme) & (p_right < p_extreme): 229 print('The extremum for this parabola has coordinates ({x:4.3f}, {y:4.3f}) and is a maximum.'.format(x=x_extreme, y=p_extreme)) 230 else: 231 print('Something went wrong.') 232 ``` 233 234 We changed the 235 [API](https://en.wikipedia.org/wiki/Application_programming_interface) a little 236 bit and so we had to update the calls to `parab()`. However, everything works 237 just fine if we're careful. 238 239 240 It's probably better to give all the parameter arguments default values. Let's 241 re-write the API again. 242 243 ```python 244 def parabola(x, a=1.0, b=-1.0, c=-1.0): 245 return a * x * x + b * x + c 246 247 def parab_extrema(parab, a=1.0, b=-1.0, c=-1.0): 248 x_extreme = - b / 2.0 / a # Location of max or min 249 x_left = x_extreme - 1.0 # Point to the left of max or min 250 x_right = x_extreme + 1.0 # Point to the right of max or min 251 p_left = parab(x_left, a, b, c) # Value at left point 252 p_right = parab(x_right, a, b, c) # Value at right point 253 p_extreme = parab(x_extreme, a, b, c) # Value at max or min 254 # Check if extremum is maximum or minimum and print out result 255 if (p_left > p_extreme) & (p_right > p_extreme): 256 print('The extremum for this parabola has coordinates ({x:4.3f}, {y:4.3f}) and is a minimum.'.format(x=x_extreme, y=p_extreme)) 257 elif (p_left < p_extreme) & (p_right < p_extreme): 258 print('The extremum for this parabola has coordinates ({x:4.3f}, {y:4.3f}) and is a maximum.'.format(x=x_extreme, y=p_extreme)) 259 else: 260 print('Something went wrong.') 261 262 parab_extrema(parabola) 263 ``` 264 265 Great! Looks pretty nice. 266 267 But there's more! We can also provide _positional_ and _keyword_ arguments to a 268 function. This allows permits a variable number of arguments to be passed to a 269 function. 270 271 * positional arguments: `def func(*args):` 272 + Python collects all the remaining positional arguments into a tuple. You 273 can then access the tuple with the usual indexing. 274 * keyword arguments: `def func(**kwargs):` 275 + Python collects all the remaining keyword arguments into a dictionary. You 276 can then access the dictionary with the usual indexing. 277 278 279 ### Variable Positional Arguments 280 281 We will once again work with the quadratic equation example. This time, we'll 282 just work with the `parabola` function to save some space. Let's change the 283 `parabola` function to permit a variable number of arguments. 284 285 ```python 286 def parabola(x, *args): 287 return args[0] * x * x + args[1] * x + args[2] 288 parabola(1.0, 1.0, -1.0, -1.0) 289 ``` 290 291 Seems to work okay. But this is not a very robust code. Everything breaks if 292 we don't provide the exact number of necessary arguments. 293 294 ```python 295 parabola(1.0) 296 ``` 297 298 ### Variable keyword arguments 299 300 We can make our API more flexible. Let's give more descriptive names to the 301 coefficients $a$, $b$, and $c$. We'll call $a$ the `width` since it controls 302 the width of the parabola, $b$ `trans` since it controls the horizontal 303 translation of the parabola, and we'll call $c$ `shift` since it controls the 304 vertical shift of the parabola. Our `parabola` function might now look like: 305 306 ```python 307 def parabola(x, **kwargs): 308 print(kwargs) 309 return kwargs['width'] * x * x + kwargs['trans'] * x + kwargs['shift'] 310 ``` 311 312 Calling it gives: 313 314 ```python 315 parabola(1.0, width=1.0, trans=-1.0, shift=-1.0) 316 ``` 317 318 **Note:** Using variable positional and keyword arguments provides exceptional 319 flexibility in how you design your programs. 320 321 322 One final note about variable arguments: You can perform the reverse operation 323 by passing in the `*` or `**` operators to the function. This will _unpack_ the 324 arguments whereas the previous pattern _packed_ the arguments. Let's take a 325 quick look. 326 327 ```python 328 def parabola(x, a, b, c): 329 return a * x * x + b * x + c 330 ``` 331 332 ```python 333 # Store coefficients in a list 334 coeffs = [1.0, -1.0, -1.0] 335 parabola(1.0, *coeffs) 336 337 # Store coefficients in a dictionary 338 coeffs = {'a':1.0, 'b':-1.0, 'c':-1.0} 339 parabola(1.0, **coeffs) 340 ``` 341 342 --- 343 344 ## Plotting 345 346 There are many, many ways to make plots in Python. The most common way is to 347 use [`matplotlib`](https://matplotlib.org/). 348 349 Another package, which is gaining popularity, is called 350 [`seaborn`](https://seaborn.pydata.org/). 351 352 I don't care which package you use, as long as your plots are readable and 353 reproducible. 354 355 356 To make plots in the Jupyter notebook, you need to include the line `%matplotlib 357 inline` before you make any plots. This line ensures that the plots will be 358 displayed in your notebook and not in a separate window. 359 360 ```python 361 %matplotlib inline 362 ``` 363 364 Next, you should `import matplotlib`: 365 366 ```python 367 import matplotlib # Import all of matplotlib 368 import matplotlib.pyplot as plt # Only import pyplot (which includes the plot function) and give it the alias `plt` 369 ``` 370 371 Now you're basically ready to do some plots. 372 373 **WARNING!** When making plots in an actual Python script, you must **always** 374 include the command `plt.show()` at the **end** of your program. **Always.** If 375 you don't do so, then your plots will not display and you will be wondering 376 where they are. However, when plotting in the Jupyter notebook, there is no 377 need to use `plt.show()`. 378 379 380 We can generate some toy data using `numpy`. 381 382 ```python 383 x = np.linspace(-2.0*np.pi, 2.0*np.pi, 500) # x-grid 384 ys = np.sin(x) # sin function 385 yc = np.cos(x) # cos function 386 ``` 387 388 Now plot! 389 390 ```python 391 plt.plot(x, ys, lw=4, ls='-', label=r'$\sin(x)$') # linewidth = 4, linestyle = solid, raw string label 392 plt.plot(x, yc, lw=4, ls='--', label=r'$\cos(x)$') 393 plt.legend() # show legend 394 plt.xlabel(r'$x$', fontsize=24) # label x axis 395 plt.ylabel(r'$y$', fontsize=24) # label y axis 396 plt.title('Sine and Cosine', fontsize=24) 397 ``` 398 399 Notice that we used a few things: 400 1. We changed the line widths 401 2. We changed the line style 402 3. We labeled the plots 403 4. We changed the font size of the labels 404 405 We also use a `raw string` because we're including Latex commands to render 406 mathematics. A `raw string` is preceded by the letter `r`. 407 408 409 There is **much** more to plotting than this example, but this should get you 410 started. Some things you may want to look up are how to change the size of the 411 tick marks and tick labels and how to use a `config` file: [Customizing a 412 Plot](https://matplotlib.org/users/customizing.html). You may also want to 413 understand _contour_ plots as well as _scatter_ plots and other statistical 414 plots such as `pdfs` and `histograms`. Note that `seaborn` has fantastic 415 support for statistical plots.