Update handle_geotiff.ipynb
[notebooks.git] / tutorials / 01-Python-basics.ipynb
blob03de49b7949436417c16f12e712fd7b1253e333e
2  "cells": [
3   {
4    "cell_type": "markdown",
5    "metadata": {},
6    "source": [
7     "# Python Tutorial I: Python Basics\n",
8     "\n",
9     "We focus on presenting the ***basics*** of Python for scientific computing. \n",
10     "This is intended only to help break down the barriers of entry into using Python and some of the basic tools commonly used in scientific computing. \n",
11     "\n",
12     "For more thorough (and advanced) tutorials over some of the topics we are touching upon in these lectures, we recommend bookmarking https://docs.python.org/3/tutorial/index.html for more details on Python basics (e.g., data structures, conditionals, and loops) and http://scipy.org/ for more details on useful libraries that unlock the power of Python for scientific computing.\n",
13     "\n",
14     "We are using Jupyter Notebooks (http://jupyter.org/) in these tutorials. \n",
15     "More notebook-oriented tutorials are also available online."
16    ]
17   },
18   {
19    "cell_type": "markdown",
20    "metadata": {},
21    "source": [
22     "## Learning Objectives\n",
23     "\n",
24     "- Understand some of the more commonly used data types.\n",
25     "\n",
26     "\n",
27     "- Assign values to variables and perform basic *arithmetic* operations.\n",
28     "\n",
29     "\n",
30     "- Understand what a library is, and what libraries are used for.\n",
31     "\n",
32     "\n",
33     "- Load a Python library and use its functions.\n",
34     "\n",
35     "\n",
36     "- Perform operations on arrays of data.\n",
37     "\n",
38     "\n",
39     "- Display simple graphs via `matplotlib`\n",
40     "\n",
41     "\n",
42     "- Understand how to read error messages to do some basic debugging with the mini-exercises.\n",
43     "\n",
44     "\n",
45     "Along the way, we will use some simple ***built-in functions within Python.*** \n",
46     "In particular, we will make use of the functions `print`, `type`, and `range` early on in this tutorial. \n",
47     "Take a moment and review some of the documentation on these available at https://docs.python.org/3/library/functions.html."
48    ]
49   },
50   {
51    "cell_type": "markdown",
52    "metadata": {},
53    "source": [
54     "## Introduction to variables"
55    ]
56   },
57   {
58    "cell_type": "markdown",
59    "metadata": {
60     "collapsed": true
61    },
62    "source": [
63     "### Integers, floats, and casting:\n",
64     "\n",
65     "***Key Points:***\n",
66     "\n",
67     "- Integers are whole numbers that are specified without decimals. Examples are 1, 2, 0, and -10. \n",
68     "\n",
69     "\n",
70     "\n",
71     "- Floats (or floating point numbers) are finite representations of real numbers with a finite number of decimal places. Examples are 1.1, 2.3, 0.07, and -10.0.\n",
72     "\n",
73     "\n",
74     "- An arithmetic operation will **cast** the result as either the type of the ***most general variable used in the operation*** or as the type of ***output generally expected from the operation***, so whether or not we worry about casting depends upon what result we desire from the operation."
75    ]
76   },
77   {
78    "cell_type": "code",
79    "execution_count": 1,
80    "metadata": {},
81    "outputs": [],
82    "source": [
83     "# Here, we define some variables used below.\n",
84     "one_int = 1\n",
85     "\n",
86     "one_float = 1. #no need to do 1.0\n",
87     "\n",
88     "two_int = 2"
89    ]
90   },
91   {
92    "cell_type": "code",
93    "execution_count": 2,
94    "metadata": {},
95    "outputs": [
96     {
97      "name": "stdout",
98      "output_type": "stream",
99      "text": [
100       "\n",
101       "one_int is of type <class 'int'> and one_float is of type <class 'float'>\n",
102       "\n",
103       "The variable two_int is type  <class 'int'>\n",
104       "\n",
105       "The variable defined by one_int+two_int is type  <class 'int'>\n",
106       "\n",
107       "The variable defined by one_int/two_int is type  <class 'float'>\n",
108       "\n",
109       "The variable defined by one_float/two_int is type  <class 'float'>\n"
110      ]
111     }
112    ],
113    "source": [
114     "# We can print the types of variables using the print and type commands\n",
115     "\n",
116     "print() #This prints a blank line. \n",
117     "# Using spaces and blank lines when printing makes output more readable\n",
118     "print( 'one_int is of type', type(one_int), \n",
119     "       'and one_float is of type', type(one_float) )\n",
120     "#Breaking up long function calls across many lines helps readability\n",
121     "\n",
122     "print()\n",
123     "print( 'The variable two_int is type ', type(two_int) )\n",
124     "\n",
125     "print()\n",
126     "print( 'The variable defined by one_int+two_int is type ', \n",
127     "       type(one_int+two_int) )\n",
128     "\n",
129     "print()\n",
130     "print( 'The variable defined by one_int/two_int is type ', \n",
131     "       type(one_int/two_int) )\n",
132     "\n",
133     "print()\n",
134     "print( 'The variable defined by one_float/two_int is type ', \n",
135     "       type(one_float/two_int) )"
136    ]
137   },
138   {
139    "cell_type": "markdown",
140    "metadata": {},
141    "source": [
142     "Below, we observe the output of typical division."
143    ]
144   },
145   {
146    "cell_type": "code",
147    "execution_count": 3,
148    "metadata": {},
149    "outputs": [
150     {
151      "name": "stdout",
152      "output_type": "stream",
153      "text": [
154       "\n",
155       "one_int/two_int = 1 / 2 = 0.5\n",
156       "\n",
157       "two_int/three = 2 / 3 = 0.6666666666666666\n",
158       "\n",
159       "neg_one/two_int = -1.0 / 2 = -0.5\n"
160      ]
161     }
162    ],
163    "source": [
164     "print()\n",
165     "print( 'one_int/two_int =', one_int, '/', two_int, '=',  one_int/two_int )\n",
166     "\n",
167     "three = 3 #What type is this?\n",
168     "\n",
169     "print()\n",
170     "print( 'two_int/three =', two_int, '/', three, '=', two_int/three )\n",
171     "\n",
172     "neg_one = -1.0 #What type is this?\n",
173     "\n",
174     "print()\n",
175     "print( 'neg_one/two_int =', neg_one, '/', two_int, '=', neg_one/two_int )"
176    ]
177   },
178   {
179    "cell_type": "markdown",
180    "metadata": {},
181    "source": [
182     "The command `//` performs ***integer division***, which is sometimes claled ***floor division*** because it *rounds down to the nearest integer*. \n",
183     "This is useful in a variety of settings. \n",
184     "\n",
185     "Recall that an arithmetic operation will **cast** the result as either the type of the ***most general variable used in the operation*** or as the type of ***output generally expected from the operation***. \n",
186     "Let's observe the behavior of `//` below when the inputs are both integers and in cases where at least one input is a float."
187    ]
188   },
189   {
190    "cell_type": "code",
191    "execution_count": 4,
192    "metadata": {},
193    "outputs": [
194     {
195      "name": "stdout",
196      "output_type": "stream",
197      "text": [
198       "\n",
199       "one_int//two_int = 1 // 2 = 0\n",
200       "\n",
201       "one_float//two_int = 1.0 // 2 = 0.0\n",
202       "\n",
203       "neg_one//two_int = -1.0 // 2 = -1.0\n",
204       "\n",
205       "two_int//three = 2 // 3 = 0\n"
206      ]
207     }
208    ],
209    "source": [
210     "print()\n",
211     "print( 'one_int//two_int =', one_int, '//', two_int, '=', one_int//two_int )\n",
212     "\n",
213     "print()\n",
214     "print( 'one_float//two_int =', one_float, '//', two_int, '=',  one_float//two_int )\n",
215     "\n",
216     "print()\n",
217     "print( 'neg_one//two_int =', neg_one, '//', two_int, '=', neg_one//two_int )\n",
218     "\n",
219     "print()\n",
220     "print( 'two_int//three =', two_int, '//', three, '=', two_int//three )"
221    ]
222   },
223   {
224    "cell_type": "markdown",
225    "metadata": {},
226    "source": [
227     "## Mini-exercise 1: Reading error messages and fixing the code\n",
228     "\n",
229     "### The code block below will not execute correctly. Try running it and read the error messages as you systematically fix it to define and print the integer 4."
230    ]
231   },
232   {
233    "cell_type": "code",
234    "execution_count": 7,
235    "metadata": {},
236    "outputs": [
237     {
238      "name": "stdout",
239      "output_type": "stream",
240      "text": [
241       "The variable four = 4 is of type <class 'int'>\n"
242      ]
243     }
244    ],
245    "source": [
246     "four = 4\n",
247     "\n",
248     "print( 'The variable four =', four, 'is of type', type(four) )"
249    ]
250   },
251   {
252    "cell_type": "markdown",
253    "metadata": {},
254    "source": [
255     "### Specifying casting:\n",
256     "\n",
257     "***Key Points:***\n",
258     "\n",
259     "- We can specify how we want an integer or float to be treated to allow for greater control in the code. This is also referred to as **casting** whenever we specify that a variable should be treated as a different type from what it is in a particular context."
260    ]
261   },
262   {
263    "cell_type": "code",
264    "execution_count": null,
265    "metadata": {},
266    "outputs": [],
267    "source": [
268     "two = one_int + one_float\n",
269     "\n",
270     "print()\n",
271     "print( 'two =', two, 'is of type ', type(two) )"
272    ]
273   },
274   {
275    "cell_type": "code",
276    "execution_count": null,
277    "metadata": {},
278    "outputs": [],
279    "source": [
280     "two = one_int + int(one_float)\n",
281     "\n",
282     "print()\n",
283     "print( 'Two =', two, 'is now of type', type(two) )\n",
284     "\n",
285     "print()\n",
286     "print( 'Did one_float change type?', type(one_float) )"
287    ]
288   },
289   {
290    "cell_type": "code",
291    "execution_count": null,
292    "metadata": {},
293    "outputs": [],
294    "source": [
295     "x = -3.7\n",
296     "\n",
297     "print()\n",
298     "print( 'x =', x, 'is of type', type(x) )\n",
299     "\n",
300     "print()\n",
301     "print( 'int(x) =', int(x), 'is an integer' )\n",
302     "\n",
303     "print()\n",
304     "print( 'We can also change integers to floats.' )\n",
305     "\n",
306     "print()\n",
307     "print( 'float(ont_int) =', float(one_int), 'defines a float.')"
308    ]
309   },
310   {
311    "cell_type": "markdown",
312    "metadata": {},
313    "source": [
314     "## Mini-exercise 2\n",
315     "\n",
316     "Fill in the missing pieces of the code cell below to define variable `y` as a floating point number version of `int(x)` and print both `y` and `x`.\n",
317     "Can you explain any differences?"
318    ]
319   },
320   {
321    "cell_type": "code",
322    "execution_count": null,
323    "metadata": {},
324    "outputs": [],
325    "source": [
326     "x = 1.7583\n",
327     "\n",
328     "y = int(float(x))\n",
329     "\n",
330     "print()\n",
331     "print( 'y =', y, 'is of type', type(), \n",
332     "       'and x =', , 'is of type', type() )"
333    ]
334   },
335   {
336    "cell_type": "markdown",
337    "metadata": {},
338    "source": [
339     "How do we raise a number to a power?"
340    ]
341   },
342   {
343    "cell_type": "code",
344    "execution_count": null,
345    "metadata": {},
346    "outputs": [],
347    "source": [
348     "pt_one = 0.1\n",
349     "\n",
350     "print()\n",
351     "print( '0.1 cubed =', pt_one**3 )"
352    ]
353   },
354   {
355    "cell_type": "markdown",
356    "metadata": {},
357    "source": [
358     "### Complex valued variables\n",
359     "\n",
360     "***Key points:***\n",
361     "\n",
362     "- Python uses the electrical engineering $j$ convention.\n",
363     "\n",
364     "\n",
365     "- The letter `j` can still be used as a variable for another number with no issues."
366    ]
367   },
368   {
369    "cell_type": "code",
370    "execution_count": 8,
371    "metadata": {},
372    "outputs": [
373     {
374      "name": "stdout",
375      "output_type": "stream",
376      "text": [
377       "\n",
378       "(3-4j) is of type <class 'complex'>\n",
379       "\n",
380       "(3-4j)  has length 5.0\n",
381       "\n",
382       "If j = 3.68439876 then 3.0-4.0*j = -11.73759504\n"
383      ]
384     }
385    ],
386    "source": [
387     "j = 3.68439876 #Clearly not the square root of negative one\n",
388     "\n",
389     "alpha = 3.0-4.0j #3.0 - 4.0j is not equal to 3.0-4.0*j, see below\n",
390     "\n",
391     "print()\n",
392     "print( alpha, 'is of type', type(alpha) )\n",
393     "\n",
394     "print()\n",
395     "print( alpha, ' has length', abs(alpha) )\n",
396     "\n",
397     "print()\n",
398     "print( 'If j =', j, 'then 3.0-4.0*j =', 3.0-4.0*j )"
399    ]
400   },
401   {
402    "cell_type": "markdown",
403    "metadata": {},
404    "source": [
405     "### Strings:\n",
406     "- Many binary operations do not apply to strings, but one can do + to concatenate two strings into one. \n",
407     "***Concatenation is particularly useful when creating filenames for saving/loading data in loops.***\n",
408     "\n",
409     "\n",
410     "- We can also create multiples of the text."
411    ]
412   },
413   {
414    "cell_type": "code",
415    "execution_count": null,
416    "metadata": {},
417    "outputs": [],
418    "source": [
419     "text = 'Hello'\n",
420     "text += ', World : ' #This is the same as text = text + ', World : '\n",
421     "\n",
422     "print()\n",
423     "print( text )\n",
424     "\n",
425     "text *= 3 #This is the same as text = 3*text\n",
426     "print()\n",
427     "print( text )"
428    ]
429   },
430   {
431    "cell_type": "markdown",
432    "metadata": {},
433    "source": [
434     "Due to casting, printing a variable without its type may lead you into thinking a variable is of a different type than it actually is."
435    ]
436   },
437   {
438    "cell_type": "code",
439    "execution_count": null,
440    "metadata": {},
441    "outputs": [],
442    "source": [
443     "one_str = '1'\n",
444     "\n",
445     "print()\n",
446     "print( one_str, 'is of type', type(one_str))\n",
447     "\n",
448     "one_int = int(one_str)\n",
449     "\n",
450     "print()\n",
451     "print( one_int, 'is of type', type(one_int))\n",
452     "\n",
453     "one_float = float(one_str)\n",
454     "\n",
455     "print()\n",
456     "print( one_float, 'is of type', type(one_float))\n",
457     "\n",
458     "one_float_str = str(one_float)\n",
459     "\n",
460     "print()\n",
461     "print( one_float_str, 'is of type', type(one_float_str))"
462    ]
463   },
464   {
465    "cell_type": "markdown",
466    "metadata": {},
467    "source": [
468     "## Mini-exercise 3\n",
469     "\n",
470     "Fill in the missing pieces of the code below to create and print the string variable `str_var`."
471    ]
472   },
473   {
474    "cell_type": "code",
475    "execution_count": 9,
476    "metadata": {},
477    "outputs": [
478     {
479      "ename": "SyntaxError",
480      "evalue": "invalid syntax (<ipython-input-9-0316d5cadfbe>, line 3)",
481      "output_type": "error",
482      "traceback": [
483       "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-9-0316d5cadfbe>\"\u001b[0;36m, line \u001b[0;32m3\u001b[0m\n\u001b[0;31m    x_str =  #x cast as a string\u001b[0m\n\u001b[0m             ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
484      ]
485     }
486    ],
487    "source": [
488     "x = 3.14159\n",
489     "\n",
490     "x_str =  #x cast as a string\n",
491     "\n",
492     "x_int_str =  #x cast as an int and then cast as a string\n",
493     "\n",
494     "str_var = x_str + ' is a reasonable approximation of pi.'\n",
495     "\n",
496     "str_var += ' ' + x_int_str + ' is a terrible approximation of pi.'\n",
497     "\n",
498     "print()\n",
499     "print(str_var)"
500    ]
501   },
502   {
503    "cell_type": "markdown",
504    "metadata": {},
505    "source": [
506     "### Who's Who in Memory (the first magic command)\n",
507     "- IPython \"magic\" commands are conventionally prefaced by %. ***If you run these commands in a non-interactive Python environment, then they will not work. You typically only include these commands as you are debugging code in an interactive environment such as an IPython terminal or a Jupyter Notebook.***\n",
508     "\n",
509     "\n",
510     "- The `%whos` command is particularly useful for debugging as it returns all variables in memory along with their type."
511    ]
512   },
513   {
514    "cell_type": "code",
515    "execution_count": 10,
516    "metadata": {
517     "scrolled": false
518    },
519    "outputs": [
520     {
521      "name": "stdout",
522      "output_type": "stream",
523      "text": [
524       "Variable    Type       Data/Info\n",
525       "--------------------------------\n",
526       "alpha       complex    (3-4j)\n",
527       "four        int        4\n",
528       "j           float      3.68439876\n",
529       "neg_one     float      -1.0\n",
530       "one_float   float      1.0\n",
531       "one_int     int        1\n",
532       "three       int        3\n",
533       "two_int     int        2\n"
534      ]
535     }
536    ],
537    "source": [
538     "%whos"
539    ]
540   },
541   {
542    "cell_type": "code",
543    "execution_count": 11,
544    "metadata": {},
545    "outputs": [
546     {
547      "data": {
548       "application/json": {
549        "cell": {
550         "!": "OSMagics",
551         "HTML": "Other",
552         "SVG": "Other",
553         "bash": "Other",
554         "capture": "ExecutionMagics",
555         "debug": "ExecutionMagics",
556         "file": "Other",
557         "html": "DisplayMagics",
558         "javascript": "DisplayMagics",
559         "js": "DisplayMagics",
560         "latex": "DisplayMagics",
561         "markdown": "DisplayMagics",
562         "perl": "Other",
563         "prun": "ExecutionMagics",
564         "pypy": "Other",
565         "python": "Other",
566         "python2": "Other",
567         "python3": "Other",
568         "ruby": "Other",
569         "script": "ScriptMagics",
570         "sh": "Other",
571         "svg": "DisplayMagics",
572         "sx": "OSMagics",
573         "system": "OSMagics",
574         "time": "ExecutionMagics",
575         "timeit": "ExecutionMagics",
576         "writefile": "OSMagics"
577        },
578        "line": {
579         "alias": "OSMagics",
580         "alias_magic": "BasicMagics",
581         "autoawait": "AsyncMagics",
582         "autocall": "AutoMagics",
583         "automagic": "AutoMagics",
584         "autosave": "KernelMagics",
585         "bookmark": "OSMagics",
586         "cat": "Other",
587         "cd": "OSMagics",
588         "clear": "KernelMagics",
589         "colors": "BasicMagics",
590         "conda": "PackagingMagics",
591         "config": "ConfigMagics",
592         "connect_info": "KernelMagics",
593         "cp": "Other",
594         "debug": "ExecutionMagics",
595         "dhist": "OSMagics",
596         "dirs": "OSMagics",
597         "doctest_mode": "BasicMagics",
598         "ed": "Other",
599         "edit": "KernelMagics",
600         "env": "OSMagics",
601         "gui": "BasicMagics",
602         "hist": "Other",
603         "history": "HistoryMagics",
604         "killbgscripts": "ScriptMagics",
605         "ldir": "Other",
606         "less": "KernelMagics",
607         "lf": "Other",
608         "lk": "Other",
609         "ll": "Other",
610         "load": "CodeMagics",
611         "load_ext": "ExtensionMagics",
612         "loadpy": "CodeMagics",
613         "logoff": "LoggingMagics",
614         "logon": "LoggingMagics",
615         "logstart": "LoggingMagics",
616         "logstate": "LoggingMagics",
617         "logstop": "LoggingMagics",
618         "ls": "Other",
619         "lsmagic": "BasicMagics",
620         "lx": "Other",
621         "macro": "ExecutionMagics",
622         "magic": "BasicMagics",
623         "man": "KernelMagics",
624         "matplotlib": "PylabMagics",
625         "mkdir": "Other",
626         "more": "KernelMagics",
627         "mv": "Other",
628         "notebook": "BasicMagics",
629         "page": "BasicMagics",
630         "pastebin": "CodeMagics",
631         "pdb": "ExecutionMagics",
632         "pdef": "NamespaceMagics",
633         "pdoc": "NamespaceMagics",
634         "pfile": "NamespaceMagics",
635         "pinfo": "NamespaceMagics",
636         "pinfo2": "NamespaceMagics",
637         "pip": "PackagingMagics",
638         "popd": "OSMagics",
639         "pprint": "BasicMagics",
640         "precision": "BasicMagics",
641         "prun": "ExecutionMagics",
642         "psearch": "NamespaceMagics",
643         "psource": "NamespaceMagics",
644         "pushd": "OSMagics",
645         "pwd": "OSMagics",
646         "pycat": "OSMagics",
647         "pylab": "PylabMagics",
648         "qtconsole": "KernelMagics",
649         "quickref": "BasicMagics",
650         "recall": "HistoryMagics",
651         "rehashx": "OSMagics",
652         "reload_ext": "ExtensionMagics",
653         "rep": "Other",
654         "rerun": "HistoryMagics",
655         "reset": "NamespaceMagics",
656         "reset_selective": "NamespaceMagics",
657         "rm": "Other",
658         "rmdir": "Other",
659         "run": "ExecutionMagics",
660         "save": "CodeMagics",
661         "sc": "OSMagics",
662         "set_env": "OSMagics",
663         "store": "StoreMagics",
664         "sx": "OSMagics",
665         "system": "OSMagics",
666         "tb": "ExecutionMagics",
667         "time": "ExecutionMagics",
668         "timeit": "ExecutionMagics",
669         "unalias": "OSMagics",
670         "unload_ext": "ExtensionMagics",
671         "who": "NamespaceMagics",
672         "who_ls": "NamespaceMagics",
673         "whos": "NamespaceMagics",
674         "xdel": "NamespaceMagics",
675         "xmode": "BasicMagics"
676        }
677       },
678       "text/plain": [
679        "Available line magics:\n",
680        "%alias  %alias_magic  %autoawait  %autocall  %automagic  %autosave  %bookmark  %cat  %cd  %clear  %colors  %conda  %config  %connect_info  %cp  %debug  %dhist  %dirs  %doctest_mode  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %lf  %lk  %ll  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %lx  %macro  %magic  %man  %matplotlib  %mkdir  %more  %mv  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %pip  %popd  %pprint  %precision  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %rep  %rerun  %reset  %reset_selective  %rm  %rmdir  %run  %save  %sc  %set_env  %store  %sx  %system  %tb  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode\n",
681        "\n",
682        "Available cell magics:\n",
683        "%%!  %%HTML  %%SVG  %%bash  %%capture  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%markdown  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile\n",
684        "\n",
685        "Automagic is ON, % prefix IS NOT needed for line magics."
686       ]
687      },
688      "execution_count": 11,
689      "metadata": {},
690      "output_type": "execute_result"
691     }
692    ],
693    "source": [
694     "%lsmagic #Lists all available magic commands"
695    ]
696   },
697   {
698    "cell_type": "markdown",
699    "metadata": {},
700    "source": [
701     "### Python Lists\n",
702     "\n",
703     "***Key points:***\n",
704     "\n",
705     "- Lists are ***ordered arrays*** of almost any type of variable or mixed-types of variables you can think of using in Python. You can even make a ***list of lists***. You use lists when the order matters in the code, e.g., when you plan on looping through the elements of the list in some ordered way. Other popular ways to handle data structures include dictionaries and sets (e.g., see https://docs.python.org/3/tutorial/datastructures.html).\n",
706     "\n",
707     "\n",
708     "- Indexing ***starts at zero***. This means that the first element of a list is indexed by a zero, the second element is indexed by 1, and so on. If it seems confusing at first, don't worry, you'll get use to it. This is also discussed below in greater detail when using arrays."
709    ]
710   },
711   {
712    "cell_type": "code",
713    "execution_count": 12,
714    "metadata": {},
715    "outputs": [
716     {
717      "name": "stdout",
718      "output_type": "stream",
719      "text": [
720       "\n",
721       "float_list =  [1.0, 2.0, 3.0]\n",
722       "\n",
723       "float_list*2 =  [1.0, 2.0, 3.0, 1.0, 2.0, 3.0]\n",
724       "\n",
725       "float_list[0] =  1.0\n",
726       "\n",
727       "float_list[0]*2 =  2.0\n"
728      ]
729     }
730    ],
731    "source": [
732     "float_list = [1.0, 2.0, 3.0]  #list of floats  \n",
733     "\n",
734     "print()\n",
735     "print( 'float_list = ', float_list )\n",
736     "\n",
737     "print()\n",
738     "print( 'float_list*2 = ', float_list*2 ) #What do you think this should produce?\n",
739     "\n",
740     "print()\n",
741     "print( 'float_list[0] = ', float_list[0] )\n",
742     "\n",
743     "print()\n",
744     "print( 'float_list[0]*2 = ', float_list[0]*2 )"
745    ]
746   },
747   {
748    "cell_type": "code",
749    "execution_count": null,
750    "metadata": {},
751    "outputs": [],
752    "source": [
753     "int_list = list(range(1,20,2)) #build a list, start with 1, add 2 while less than 20\n",
754     "\n",
755     "print()\n",
756     "print( 'int_list = ', int_list )\n",
757     "\n",
758     "print()\n",
759     "print( '(type(int_list), type(float_list)) = ', (type(int_list), type(float_list)) )"
760    ]
761   },
762   {
763    "cell_type": "code",
764    "execution_count": null,
765    "metadata": {},
766    "outputs": [],
767    "source": [
768     "list_of_lists = [float_list, int_list] #build a list of lists\n",
769     "\n",
770     "print()\n",
771     "print( 'mixed_list = ', list_of_lists)\n",
772     "\n",
773     "print()\n",
774     "print( 'mixed_list[0] = ', list_of_lists[0] )\n",
775     "\n",
776     "print()\n",
777     "print( 'mixed_list[0] = ', list_of_lists[1] )\n",
778     "\n",
779     "print()\n",
780     "print( 'mixed_list[0][1] = ', list_of_lists[0][1] )\n",
781     "\n",
782     "print()\n",
783     "print( 'mixed_list[1][2] = ', list_of_lists[1][2] )"
784    ]
785   },
786   {
787    "cell_type": "code",
788    "execution_count": null,
789    "metadata": {},
790    "outputs": [],
791    "source": [
792     "conc_list = float_list + int_list #Concatenation of lists\n",
793     "\n",
794     "print()\n",
795     "print( 'conc_list = ', conc_list )"
796    ]
797   },
798   {
799    "cell_type": "markdown",
800    "metadata": {},
801    "source": [
802     "### A few quick notes about lists\n",
803     "* While we can build a list of numbers, their group behavior does ***not*** match the individual behavior. For example, in the code above, `float_list*2` produces a list of floats that is not equal to the floats within the list multiplied by 2.\n",
804     "\n",
805     "\n",
806     "* Below, we work with **numpy** *arrays* which are lists that behave more like vectors and matrices. Generally, if the objective is to do actual scientific computations on the lists that are like matrix or vector operations, then we want to use `numpy` arrays not lists."
807    ]
808   },
809   {
810    "cell_type": "markdown",
811    "metadata": {},
812    "source": [
813     "## Importing Packages (sometimes called Libraries), Subpackages, and Modules\n",
814     "\n",
815     "- A module is a single file of python code that is meant to be imported.\n",
816     "\n",
817     "\n",
818     "- A package is a collection of Python modules under a common namespace (in computing, a namespace is a set of symbols that are used to organize objects of various kinds, so that these objects may be referred to by name). In practice one is created by placing multiple python modules in a directory with a special `__init__.py` module (file). \n",
819     "\n",
820     "\n",
821     "- Unlike many scripting languages, Python follows the conventions of many compiled languages by accessing packages (libraries) via the **import** statement.  Three of the libraries you'll find yourself importing often are \n",
822     "\n",
823     "    - `numpy` (https://docs.scipy.org/doc/numpy/) \n",
824     "    \n",
825     "    - `scipy` (https://docs.scipy.org/doc/scipy/reference/)\n",
826     "    \n",
827     "    - `matplotlib` (http://matplotlib.org/). \n",
828     "    \n",
829     "There are several other libraries used commonly with Python when doing scientific computing, and the documentation at https://scipy.org/ is a particularly useful starting point.   "
830    ]
831   },
832   {
833    "cell_type": "code",
834    "execution_count": 13,
835    "metadata": {},
836    "outputs": [],
837    "source": [
838     "import numpy  #imports numpy as is"
839    ]
840   },
841   {
842    "cell_type": "markdown",
843    "metadata": {},
844    "source": [
845     "To access a function or class in a library, the syntax is `libraryname.functioname(args)`.\n",
846     "\n",
847     "For example, to access the `sin` function within `numpy` and evaluate it at $\\pi$, we do the following,"
848    ]
849   },
850   {
851    "cell_type": "code",
852    "execution_count": 14,
853    "metadata": {},
854    "outputs": [
855     {
856      "name": "stdout",
857      "output_type": "stream",
858      "text": [
859       "1.2246467991473532e-16\n"
860      ]
861     }
862    ],
863    "source": [
864     "print( numpy.sin(numpy.pi) )  #numpy.pi is a library constant"
865    ]
866   },
867   {
868    "cell_type": "markdown",
869    "metadata": {},
870    "source": [
871     "We can also import a library and assign it a convenient nickname. ***This is how we typically do things.***"
872    ]
873   },
874   {
875    "cell_type": "code",
876    "execution_count": 18,
877    "metadata": {},
878    "outputs": [
879     {
880      "name": "stdout",
881      "output_type": "stream",
882      "text": [
883       "1.0\n"
884      ]
885     }
886    ],
887    "source": [
888     "import numpy as np\n",
889     "\n",
890     "print( np.log(np.e) ) #np.e is also a library constant"
891    ]
892   },
893   {
894    "cell_type": "markdown",
895    "metadata": {},
896    "source": [
897     "Of course, you may just want to import a particular function or subpackage.\n",
898     "\n",
899     "Below, we use the `log` function from the `math` package and the `random` subpackage from `numpy` to generate a single sample from a normal distribution by taking the logarithm of a lognormal sample (see https://en.wikipedia.org/wiki/Log-normal_distribution for details). \n",
900     "This is only meant to show how to combine multiple imported functions/subpackages. "
901    ]
902   },
903   {
904    "cell_type": "code",
905    "execution_count": 19,
906    "metadata": {},
907    "outputs": [
908     {
909      "name": "stdout",
910      "output_type": "stream",
911      "text": [
912       "\n",
913       "1.1806664317518558\n"
914      ]
915     }
916    ],
917    "source": [
918     "from math import log #imports the (natural) log function from the math package, we could also just use np.log below\n",
919     "\n",
920     "from numpy import random #imports the random subpackage from numpy\n",
921     "\n",
922     "sample = log( random.lognormal() ) #generate one normal random sample\n",
923     "\n",
924     "print()\n",
925     "print( sample )"
926    ]
927   },
928   {
929    "cell_type": "markdown",
930    "metadata": {},
931    "source": [
932     "### Python help\n",
933     "If you are coding without internet activity and want to look something up about a Python library or function, then use the help command. Note that this will also provide information about where to find the proper documentation online in most cases."
934    ]
935   },
936   {
937    "cell_type": "code",
938    "execution_count": null,
939    "metadata": {
940     "scrolled": true
941    },
942    "outputs": [],
943    "source": [
944     "help(help)"
945    ]
946   },
947   {
948    "cell_type": "markdown",
949    "metadata": {},
950    "source": [
951     "## Mini-exercise 4\n",
952     "\n",
953     "Fill in the missing pieces of the code below to import the `uniform` function from `numpy.random` as `runif` and print a single uniform random number between 0.0 and 1.0, and then print a single uniform random number between 9.0 and 10.0.\n",
954     "\n",
955     "You may it useful to first look at the documention on this function (https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.uniform.html). "
956    ]
957   },
958   {
959    "cell_type": "code",
960    "execution_count": null,
961    "metadata": {},
962    "outputs": [],
963    "source": [
964     "from numpy.random import  as \n",
965     "\n",
966     "sample = runif(low=, high=) #generate one uniform random sample between 0.0 and 1.0\n",
967     "\n",
968     "print()\n",
969     "print( sample )\n",
970     "\n",
971     "sample =  #generate one uniform random sample between 9.0 and 10.0\n",
972     "\n",
973     "print()\n",
974     "print( sample )"
975    ]
976   },
977   {
978    "cell_type": "markdown",
979    "metadata": {},
980    "source": [
981     "### Cautionary Note On Importing\n",
982     "You can avoid typing the library name or nickname altogether by importing everything as `*`, e.g., typing \n",
983     "\n",
984     "    import numpy as * \n",
985     "    \n",
986     "loads every subpackage and function within the `numpy` library into the current namespace.\n",
987     "\n",
988     "This is ***discouraged*** because it\n",
989     "\n",
990     "- provides the opportunity for namespace collisions (e.g., if you had a variable name pi prior to the import of `numpy`);\n",
991     "\n",
992     "\n",
993     "- may be inefficient (e.g., if the number of objects imported is big);\n",
994     "\n",
995     "\n",
996     "- does not explicitly document the origin of the variable/method/class, which provides a type of auto-documentation in the program that makes it easier for others to know what you are doing (and for you to know what you are doing when you look at your code weeks, months, or even years later)."
997    ]
998   },
999   {
1000    "cell_type": "markdown",
1001    "metadata": {},
1002    "source": [
1003     "## Numpy arrays (and why we use these more than lists)\n",
1004     "\n",
1005     "Numpy arrays are not just more efficient than lists, they are also more convenient when needing to do most numerical linear algebra (and much of computational science boils down to numerical linear algebra). \n",
1006     "You get a lot of vector and matrix operations for free, which means we can often avoid unnecessary work that would be required if we were to just use lists. \n",
1007     "They are also more memory efficient. \n",
1008     "While lists are more flexible, this flexibility comes with a heavy price in terms of memory and not being able to apply a lot of built-in functionality on the lists.\n",
1009     "\n",
1010     "\n",
1011     "We make heavy use of `numpy` arrays. Below are some examples of array constructions. \n",
1012     "***Pay attention to the number and location of square brackets used to define the dimensional inputs of the arrays.***"
1013    ]
1014   },
1015   {
1016    "cell_type": "code",
1017    "execution_count": 20,
1018    "metadata": {},
1019    "outputs": [
1020     {
1021      "name": "stdout",
1022      "output_type": "stream",
1023      "text": [
1024       "\n",
1025       "arr_1d =\n",
1026       " [1 2 3 4]\n",
1027       "\n",
1028       "arr_2d =\n",
1029       " [[1 2]\n",
1030       " [3 4]\n",
1031       " [5 6]]\n",
1032       "\n",
1033       "arr_3d =\n",
1034       " [[[ 1  2  3]\n",
1035       "  [ 4  5  6]]\n",
1036       "\n",
1037       " [[ 7  8  9]\n",
1038       "  [10 11 12]]]\n"
1039      ]
1040     }
1041    ],
1042    "source": [
1043     "arr_1d = np.array( [1,2,3,4] )  #1D array (typically called a vector)\n",
1044     "\n",
1045     "arr_2d = np.array( [ [1,2],[3,4],[5,6] ] ) #2D array (sometimes called a matrix)\n",
1046     "\n",
1047     "arr_3d = np.array( [ [ [1,2,3], [4,5,6] ],[ [7,8,9],[10,11,12] ] ] ) #3D array\n",
1048     "\n",
1049     "print()\n",
1050     "print( 'arr_1d =\\n', arr_1d ) #Note the use of \\n in a string, which, when printed, creates a new line\n",
1051     "\n",
1052     "print()\n",
1053     "print( 'arr_2d =\\n', arr_2d )\n",
1054     "\n",
1055     "print()\n",
1056     "print( 'arr_3d =\\n', arr_3d )"
1057    ]
1058   },
1059   {
1060    "cell_type": "markdown",
1061    "metadata": {},
1062    "source": [
1063     "Once a numpy array is defined, we can investigate many of its attributes easily."
1064    ]
1065   },
1066   {
1067    "cell_type": "code",
1068    "execution_count": 21,
1069    "metadata": {},
1070    "outputs": [
1071     {
1072      "name": "stdout",
1073      "output_type": "stream",
1074      "text": [
1075       "\n",
1076       "The shape of arr_1d is (4,)\n",
1077       "\n",
1078       "The shape of arr_2d is (3, 2)\n",
1079       "\n",
1080       "The shape of arr_3d is (2, 2, 3)\n",
1081       "\n",
1082       "The total number of components of arr_3d is 12\n"
1083      ]
1084     }
1085    ],
1086    "source": [
1087     "#the numpy array attribute \"shape\"\n",
1088     "print()\n",
1089     "print( 'The shape of arr_1d is', arr_1d.shape )\n",
1090     "\n",
1091     "print()\n",
1092     "print( 'The shape of arr_2d is', arr_2d.shape )\n",
1093     "\n",
1094     "print()\n",
1095     "print( 'The shape of arr_3d is', arr_3d.shape )\n",
1096     "\n",
1097     "#the numpy array attribute \"size\"\n",
1098     "print()\n",
1099     "print( 'The total number of components of arr_3d is', arr_3d.size )"
1100    ]
1101   },
1102   {
1103    "cell_type": "markdown",
1104    "metadata": {},
1105    "source": [
1106     "## Mini-exercise 5\n",
1107     "\n",
1108     "Fill in the code below to create a 2D array of shape (2, 3) and a 3D array of shape (1,2,3)."
1109    ]
1110   },
1111   {
1112    "cell_type": "code",
1113    "execution_count": 22,
1114    "metadata": {},
1115    "outputs": [
1116     {
1117      "name": "stdout",
1118      "output_type": "stream",
1119      "text": [
1120       "\n",
1121       "my_array_2d =\n",
1122       " []\n",
1123       "\n",
1124       "The shape of my_array_2d is (2, 0)\n",
1125       "\n",
1126       "my_array_3d =\n",
1127       " []\n",
1128       "\n",
1129       "The shape of my_array_3d is (1, 2, 0)\n"
1130      ]
1131     }
1132    ],
1133    "source": [
1134     "my_array_2d = np.array( [ [],[] ] )\n",
1135     "\n",
1136     "print()\n",
1137     "print( 'my_array_2d =\\n', my_array_2d )\n",
1138     "\n",
1139     "print()\n",
1140     "print( 'The shape of my_array_2d is', my_array_2d.shape )\n",
1141     "\n",
1142     "my_array_3d = np.array( [ [ [],[] ] ] )\n",
1143     "\n",
1144     "print()\n",
1145     "print( 'my_array_3d =\\n', my_array_3d )\n",
1146     "\n",
1147     "print()\n",
1148     "print( 'The shape of my_array_3d is', my_array_3d.shape )"
1149    ]
1150   },
1151   {
1152    "cell_type": "markdown",
1153    "metadata": {},
1154    "source": [
1155     "### Casting the type of a `numpy` array.\n",
1156     "\n",
1157     "The `numpy` data type attribute `dtype` describes the type of data stored in `numpy` and it is cast as the ***minimal*** data type required to store all the data."
1158    ]
1159   },
1160   {
1161    "cell_type": "code",
1162    "execution_count": null,
1163    "metadata": {},
1164    "outputs": [],
1165    "source": [
1166     "#the numpy attribute \"dtype\" describes the data type stored in the array\n",
1167     "print()\n",
1168     "print( 'The type of a numpy array is the minimal type required to hold all the data' )\n",
1169     "\n",
1170     "print()\n",
1171     "print( 'The data type of the original arr_3d is', arr_3d.dtype )\n",
1172     "\n",
1173     "#What happens if we change the last entry of arr_3d in the above code block to be 12.0?\n",
1174     "\n",
1175     "arr_3d = np.array( [ [ [1,2,3], [4,5,6] ],[ [7,8,9],[10,11,12.0] ] ] ) #3D array\n",
1176     "\n",
1177     "print()\n",
1178     "print( 'arr_3d =\\n', arr_3d )\n",
1179     "\n",
1180     "print()\n",
1181     "print( 'The data type of this new arr_3d is', arr_3d.dtype )"
1182    ]
1183   },
1184   {
1185    "cell_type": "markdown",
1186    "metadata": {},
1187    "source": [
1188     "We can easily re-cast the data in `numpy` arrays as different data types using the special `astype` function, which creates a copy of the specified array that are cast to a specified type.\n",
1189     "\n",
1190     "You may also specify the data type during the array creation call."
1191    ]
1192   },
1193   {
1194    "cell_type": "code",
1195    "execution_count": null,
1196    "metadata": {},
1197    "outputs": [],
1198    "source": [
1199     "print()\n",
1200     "print( 'A copy of arr_2d as a float \\n', arr_2d.astype(float) )\n",
1201     "\n",
1202     "print()   \n",
1203     "print( 'The arr_2d is still an integer array \\n', arr_2d, '\\n', arr_2d.dtype )\n",
1204     "\n",
1205     "print() \n",
1206     "print( 'Here is a copy of of arr_2d as a str array \\n', arr_2d.astype(str) )"
1207    ]
1208   },
1209   {
1210    "cell_type": "markdown",
1211    "metadata": {},
1212    "source": [
1213     "## The indexing of an array.\n",
1214     "\n",
1215     "### Python indexing is 0 based! This means that the first entry in a row/column is indexed by 0! So, when we mention the (1,1) component of a 2-dimensional array, we must use [0,0] to access that specific component.\n",
1216     "***REMEMBER THIS!!!***\n",
1217     "There are also two common conventions for accessing components of arrays.\n",
1218     "\n",
1219     "The following diagram may prove useful for typical 2-D arrays.\n",
1220     "\n",
1221     "We say that the rows are aligned with ***axis 0*** and the columns are aligned with ***axis 1***.\n",
1222     "\n",
1223     "We explore these ideas and array slicing concepts below.\n"
1224    ]
1225   },
1226   {
1227    "cell_type": "code",
1228    "execution_count": null,
1229    "metadata": {},
1230    "outputs": [],
1231    "source": [
1232     "arr_2d = np.array( [ [1,2],[3,4],[5,6] ] )\n",
1233     "\n",
1234     "print()\n",
1235     "print(' arr_2d =\\n', arr_2d )\n",
1236     "\n",
1237     "print()\n",
1238     "print( '(1,1) component of arr_2d is given by arr_2d[0,0] =',  arr_2d[0,0] )\n",
1239     "\n",
1240     "print()\n",
1241     "print( '(1,2) component of arr_2d is given by arr_2d[0,1] =', arr_2d[0,1] )\n",
1242     "\n",
1243     "print()\n",
1244     "print( '(2,1) component of arr_2d is given by arr_2d[1,0] =', arr_2d[1,0] )\n",
1245     "\n",
1246     "print()\n",
1247     "print( '(3,2) component of arr_2d is given by arr_2d[2,1] =', arr_2d[2,1] )\n",
1248     "\n",
1249     "print()\n",
1250     "print( '(3,2) component of arr_2d is also given by arr_2d[-1,1] =', arr_2d[-1,1] )"
1251    ]
1252   },
1253   {
1254    "cell_type": "code",
1255    "execution_count": 23,
1256    "metadata": {},
1257    "outputs": [],
1258    "source": [
1259     "#Try uncommenting the next line to see an error. Can you explain it?\n",
1260     "#print( arr_2d[2,2] ) "
1261    ]
1262   },
1263   {
1264    "cell_type": "code",
1265    "execution_count": null,
1266    "metadata": {},
1267    "outputs": [],
1268    "source": [
1269     "arr_3d = np.array( [ [ [1,2,3], [4,5,6] ],[ [7,8,9],[10,11,12] ] ] )\n",
1270     "\n",
1271     "print()\n",
1272     "print(' arr_3d =\\n\\n', arr_3d )\n",
1273     "\n",
1274     "print()\n",
1275     "print( '(1,1) component of arr_2d is given by \\n\\n arr_3d[0,0] =\\n\\n',  arr_3d[0,0] )\n",
1276     "\n",
1277     "print()\n",
1278     "print( '(1,1,2) component of arr_2d is given by \\n\\n arr_3d[0,0,1] =\\n\\n', arr_3d[0,0,1] )"
1279    ]
1280   },
1281   {
1282    "cell_type": "code",
1283    "execution_count": null,
1284    "metadata": {},
1285    "outputs": [],
1286    "source": [
1287     "arr_2d = np.array( [ [1,2],[3,4],[5,6] ] )\n",
1288     "\n",
1289     "print()\n",
1290     "print(' arr_2d =\\n\\n', arr_2d )\n",
1291     "\n",
1292     "# Using a colon : by itself when calling entries of an array will access all entries in the row/column\n",
1293     "# where the colon appears.\n",
1294     "print()\n",
1295     "print( 'first row of arr_2d is given by arr_2d[0,:] =\\n\\n', arr_2d[0,:] ) #preferred \n",
1296     "\n",
1297     "print()\n",
1298     "print( 'first row of arr_2d is also given by arr_2d[0] =\\n\\n', arr_2d[0] ) #not preferred\n",
1299     "\n",
1300     "print()\n",
1301     "print( 'first column of arr_2d is given by arr_2d[:,0] =\\n\\n', arr_2d[:,0] )"
1302    ]
1303   },
1304   {
1305    "cell_type": "markdown",
1306    "metadata": {},
1307    "source": [
1308     "### Array Slicing\n",
1309     "\n",
1310     "The colon `:` operator is used to specify a range of values in a matrix.\n",
1311     "\n",
1312     "- The entry to the left of the `:` is included but the entry to the right of the `:` is not.\n",
1313     "\n",
1314     "\n",
1315     "- Think of `i:j` being interpreted as \"all entries starting at i up to, but ***not*** including, j\"\n",
1316     "\n",
1317     "\n",
1318     "- #### Also, think of `i:j` being interpreted as \"all entries starting at i up to, but ***not*** including, j\"\n",
1319     "\n",
1320     "\n",
1321     "- ### And, think of `i:j` being interpreted as \"all entries starting at i up to, but ***not*** including, j\"\n",
1322     "\n",
1323     "\n",
1324     "- ## Before we forget, you should also think of `i:j` being interpreted as \"all entries starting at i up to, but ***not*** including, j\"\n",
1325     "\n",
1326     "### Short quiz\n",
1327     "\n",
1328     "- How should you think of `i:j`?"
1329    ]
1330   },
1331   {
1332    "cell_type": "code",
1333    "execution_count": null,
1334    "metadata": {},
1335    "outputs": [],
1336    "source": [
1337     "A = np.reshape(range(1,13),(3,4)) # create a 3x4 array (i.e., a matrix) with the 1st 12 integers in it.\n",
1338     "\n",
1339     "print()\n",
1340     "print( 'The full matrix A is given by \\n\\n', A )\n",
1341     "\n",
1342     "print()\n",
1343     "print( 'To display just the 2nd and 3rd columns of A,\\n' +\n",
1344     "       'recall that we begin indexing from 0, so we use A[:,1:3] \\n\\n',\n",
1345     "        A[:,1:3])   #All rows, 2nd and 3rd (but not 4th) column"
1346    ]
1347   },
1348   {
1349    "cell_type": "markdown",
1350    "metadata": {},
1351    "source": [
1352     "## If you leave off a (later) index, it is implicity treated as if you had used a colon.\n",
1353     "\n",
1354     "If multiple rows and columns are being sliced, then this will ***generally*** give the same thing."
1355    ]
1356   },
1357   {
1358    "cell_type": "code",
1359    "execution_count": null,
1360    "metadata": {},
1361    "outputs": [],
1362    "source": [
1363     "print()\n",
1364     "print( 'A[0:2] =\\n\\n', A[0:2] )\n",
1365     "\n",
1366     "print()\n",
1367     "print( 'A[0:2,:] =\\n\\n', A[0:2,:] )\n",
1368     "\n",
1369     "print()\n",
1370     "print( 'A[0:2]-A[0:2,:] =\\n\\n', A[0:2] - A[0:2,:])"
1371    ]
1372   },
1373   {
1374    "cell_type": "markdown",
1375    "metadata": {},
1376    "source": [
1377     "***However, if we are slicing just a single row or column, then there can be differences that lead to undesirable results.***"
1378    ]
1379   },
1380   {
1381    "cell_type": "code",
1382    "execution_count": null,
1383    "metadata": {},
1384    "outputs": [],
1385    "source": [
1386     "print()\n",
1387     "print( 'A[:,1] =\\n\\n', A[:,1] )\n",
1388     "\n",
1389     "print()\n",
1390     "print( 'A[:,1].shape =', A[:,1].shape )\n",
1391     "\n",
1392     "print()\n",
1393     "print( 'A[:,1:2] =\\n\\n', A[:,1:2] )\n",
1394     "\n",
1395     "print()\n",
1396     "print( 'A[:,1:2].shape =', A[:,1:2].shape )\n",
1397     "\n",
1398     "print()\n",
1399     "print( 'A[:,1]-A[:,1:2] =\\n\\n', A[:,1] - A[:,1:2] )"
1400    ]
1401   },
1402   {
1403    "cell_type": "markdown",
1404    "metadata": {},
1405    "source": [
1406     "### Slicing from the end of an array\n",
1407     "\n",
1408     "The -n slice allows you to access entries from the last valid index.\n",
1409     "\n",
1410     "- Using a -1 in a slice will select the very last entry in the array.\n",
1411     "\n",
1412     "    - This implies that if you want to index from the 3rd entry in an array up to, but not including, the last entry in the array, then you would use `2:-1` in the slice"
1413    ]
1414   },
1415   {
1416    "cell_type": "code",
1417    "execution_count": null,
1418    "metadata": {},
1419    "outputs": [],
1420    "source": [
1421     "print()\n",
1422     "print( A )\n",
1423     "\n",
1424     "print()\n",
1425     "print( A[-1,:] )   #the last row\n",
1426     "\n",
1427     "print()\n",
1428     "print( A[:,-2:] ) #the last two columns\n",
1429     "\n",
1430     "print()\n",
1431     "print( A[:,1:] ) #the second column through the last\n",
1432     "\n",
1433     "print()\n",
1434     "print( A[:,1:-1] ) #the second column up to, but not including, the last"
1435    ]
1436   },
1437   {
1438    "cell_type": "markdown",
1439    "metadata": {},
1440    "source": [
1441     "We can give arrays as inputs to select some specific rows, columns, etc. "
1442    ]
1443   },
1444   {
1445    "cell_type": "code",
1446    "execution_count": null,
1447    "metadata": {},
1448    "outputs": [],
1449    "source": [
1450     "print()\n",
1451     "print( A )\n",
1452     "\n",
1453     "print()\n",
1454     "print( A[:,[1,3]] )"
1455    ]
1456   },
1457   {
1458    "cell_type": "markdown",
1459    "metadata": {},
1460    "source": [
1461     "### Elementwise vs standard operations in `numpy`\n",
1462     "\n",
1463     "`numpy` has functions for both elementwise multiplication of arrays (of the same size) and standard matrix-matrix/vector multiplication (where inner dimensions agree).\n",
1464     "\n",
1465     "- Use the `np.multiply` function for elementwise multiplication (can also just use `*`). Dimensions \n",
1466     "\n",
1467     "\n",
1468     "- Use the `np.dot` function for standard matrix-matrix, or matrix-vector multiplication"
1469    ]
1470   },
1471   {
1472    "cell_type": "markdown",
1473    "metadata": {},
1474    "source": [
1475     "Array multiplication is **not** matrix multiplication.  It is an *elementwise* operation."
1476    ]
1477   },
1478   {
1479    "cell_type": "code",
1480    "execution_count": null,
1481    "metadata": {},
1482    "outputs": [],
1483    "source": [
1484     "A = np.reshape(range(1,13),(3,4)) # create a 3x4 array (i.e., a matrix) with the 1st 12 integers in it.\n",
1485     "B = np.reshape(range(1,13),(4,3)) # create a 4x3 array (i.e., a matrix) with the 1st 12 integers in it.\n",
1486     "\n",
1487     "print()\n",
1488     "print( A )\n",
1489     "\n",
1490     "print()\n",
1491     "print( B )\n",
1492     "\n",
1493     "print()\n",
1494     "print( np.multiply(A,A) )  #elementwise multiplication\n",
1495     "\n",
1496     "print()\n",
1497     "print( A*A )  #also elementwise multiplication\n",
1498     "\n",
1499     "print()\n",
1500     "print( np.dot( A, B ) ) #standard matrix-matrix multiplication"
1501    ]
1502   },
1503   {
1504    "cell_type": "markdown",
1505    "metadata": {},
1506    "source": [
1507     "### More numpy functions and subpackages\n",
1508     "\n",
1509     "https://docs.scipy.org/doc/numpy/reference/ is a great reference. In particular, you should check out the available documentation on the `matlib`, `linalg`, and `random` subpackages. \n",
1510     "These are extremely useful subpackages that can do most of your everyday computations in undergraduate/beginning graduate mathematics.\n",
1511     "\n",
1512     "- The page on N-dimensional arrays (https://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html) is also very useful. Arrays inherent many methods (i.e., functions) from the `numpy` namespace that allow for quicker access to certain functionality (and shorter, more readable code).\n",
1513     "\n",
1514     "    - Many methods can be applied in one line of code where the order of operation is specified by the order in which the methods appear from left to right. ***We show some examples below.***"
1515    ]
1516   },
1517   {
1518    "cell_type": "code",
1519    "execution_count": null,
1520    "metadata": {},
1521    "outputs": [],
1522    "source": [
1523     "print()\n",
1524     "print( A )\n",
1525     "\n",
1526     "print()\n",
1527     "print( np.mean(A) )\n",
1528     "\n",
1529     "print()\n",
1530     "print( A.mean() )\n",
1531     "\n",
1532     "print()\n",
1533     "print( np.transpose(A) )\n",
1534     "\n",
1535     "print()\n",
1536     "print( A.transpose() )\n",
1537     "\n",
1538     "print()\n",
1539     "print( A.max() )"
1540    ]
1541   },
1542   {
1543    "cell_type": "markdown",
1544    "metadata": {},
1545    "source": [
1546     "***We sometimes only want to apply a function across rows or columns.***\n",
1547     "It is common to arrange a set of samples as an array where each row defines a single sample, and the columns define the various quantitative entries associated with that sample (e.g., think of how a Jacobian matrix is ordered). We often then want to perform some sort of computation across the columns to determine some bulk characteristic for each sample.\n",
1548     "- If this is true, then remember that we index from 0 and the rows are the first index (axis=0) and the columns are the second index (axis=1) in the array when you specify which axis you want to perform computations *across*.\n",
1549     "\n",
1550     "![Visual of axis numbers for an array go here](imgs/sample_array.png \"The axis numbers for an array\")"
1551    ]
1552   },
1553   {
1554    "cell_type": "code",
1555    "execution_count": null,
1556    "metadata": {},
1557    "outputs": [],
1558    "source": [
1559     "print()\n",
1560     "print( A )\n",
1561     "\n",
1562     "print()\n",
1563     "print( np.mean(A, axis=1) )\n",
1564     "\n",
1565     "print()\n",
1566     "print( A.mean(axis=1) )\n",
1567     "\n",
1568     "print()\n",
1569     "print( A.mean(axis=0) )\n",
1570     "\n",
1571     "print()\n",
1572     "print( A.transpose().mean(axis=1) ) #functions work from left to right\n",
1573     "\n",
1574     "print()\n",
1575     "print( A.transpose().mean(axis=0) ) #functions work from left to right"
1576    ]
1577   },
1578   {
1579    "cell_type": "markdown",
1580    "metadata": {},
1581    "source": [
1582     "## Plotting and `matplotlib`\n",
1583     "The mathematician Richard Hamming once said, “The purpose of computing is insight, not numbers,” and the best way to develop insight is often to visualize data. Visualization deserves an entire lecture (of course) of its own, but we can explore a few features of Python’s `matplotlib` library here. While there is no “official” plotting library, this package is the de facto standard. First, we will import the `pyplot` module from `matplotlib`. "
1584    ]
1585   },
1586   {
1587    "cell_type": "markdown",
1588    "metadata": {},
1589    "source": [
1590     "Python's `matplotlib` library emulates many features of Matlab plotting and uses the same layout for how it creates plots as illustrated below.\n",
1591     "\n",
1592     "![Illustration of plotting with matplotlib goes here](imgs/matplotlib_layout.png \"The items that make up a figure\")"
1593    ]
1594   },
1595   {
1596    "cell_type": "code",
1597    "execution_count": null,
1598    "metadata": {},
1599    "outputs": [],
1600    "source": [
1601     "#The next line enables the display of graphical output within Jupyter Notebooks and is NOT needed outside of Notebooks\n",
1602     "%matplotlib inline \n",
1603     "\n",
1604     "#This next line IS needed even outside of Jupyter Notebook\n",
1605     "import matplotlib.pyplot as plt "
1606    ]
1607   },
1608   {
1609    "cell_type": "markdown",
1610    "metadata": {},
1611    "source": [
1612     "The basic plot command plots xdata versus ydata.  The default behavior is to connect data pairs via a straight solid line.\n",
1613     "\n",
1614     "The `np.linspace(a,b,n)` generates $n$ points in the closed interval $[a,b]$, including the endpoints."
1615    ]
1616   },
1617   {
1618    "cell_type": "code",
1619    "execution_count": null,
1620    "metadata": {},
1621    "outputs": [],
1622    "source": [
1623     "x = np.linspace(-np.pi,np.pi,1000)\n",
1624     "\n",
1625     "y = x*np.sin(1.0/x)\n",
1626     "\n",
1627     "plt.title('$f(x)=x\\sin(x^{-1})$', fontsize=18)\n",
1628     "plt.plot(x,y)"
1629    ]
1630   },
1631   {
1632    "cell_type": "markdown",
1633    "metadata": {},
1634    "source": [
1635     "Another handy way of generating a vector is using `numpy.arange(start,stop,increment)`\n",
1636     "This will fill up the half-open interval $[start,stop)$."
1637    ]
1638   },
1639   {
1640    "cell_type": "code",
1641    "execution_count": null,
1642    "metadata": {},
1643    "outputs": [],
1644    "source": [
1645     "x_1 = np.arange(-np.pi,np.pi,1E-2)\n",
1646     "\n",
1647     "y_1 = x_1*np.sin(x_1)\n",
1648     "\n",
1649     "plt.plot(x_1,y_1,linestyle='--',c='r')  #dashed lines, red coloring"
1650    ]
1651   },
1652   {
1653    "cell_type": "markdown",
1654    "metadata": {},
1655    "source": [
1656     "Let's do a *scatter* plot of a noisy linear function"
1657    ]
1658   },
1659   {
1660    "cell_type": "code",
1661    "execution_count": null,
1662    "metadata": {},
1663    "outputs": [],
1664    "source": [
1665     "xcor = np.random.rand(100)\n",
1666     "\n",
1667     "ycor = 5*xcor + np.random.rand(100)\n",
1668     "\n",
1669     "plt.scatter(xcor,ycor)"
1670    ]
1671   },
1672   {
1673    "cell_type": "markdown",
1674    "metadata": {},
1675    "source": [
1676     "### Subplots and 3d plots using `mpl_toolkits`\n",
1677     "Subplots are one way to arrange multiple plots into one axes. The subplot function takes the following arguments: ** `add_subplot(nrows, ncols, plot_number)`**\n",
1678     "\n",
1679     "You may find https://docs.scipy.org/doc/numpy/reference/generated/numpy.meshgrid.html to be a useful reference when determining how you want to index an array in 2- or 3-D. "
1680    ]
1681   },
1682   {
1683    "cell_type": "code",
1684    "execution_count": null,
1685    "metadata": {},
1686    "outputs": [],
1687    "source": [
1688     "fig = plt.figure(1, figsize=(10, 6))\n",
1689     "\n",
1690     "axes1 = fig.add_subplot(1, 3, 1) #the first plot in a 1x3 array\n",
1691     "axes2 = fig.add_subplot(1, 3, 2) #the second plot in a 1x3 array\n",
1692     "axes3 = fig.add_subplot(1, 3, 3) #the third plot in a 1x3 array\n",
1693     "\n",
1694     "axes1.set_ylabel('average')\n",
1695     "axes1.scatter(np.arange(A.shape[1]),np.mean(A, axis=0))\n",
1696     "axes1.set_xticks(np.arange(A.shape[1]))\n",
1697     "axes1.set_aspect(1)\n",
1698     "\n",
1699     "axes2.set_ylabel('max')\n",
1700     "axes2.plot(np.max(A, axis=0))\n",
1701     "axes2.set_xticks(np.arange(A.shape[1]))\n",
1702     "axes2.set_aspect(2)\n",
1703     "\n",
1704     "axes3.set_ylabel('min')\n",
1705     "axes3.plot(np.min(A, axis=0))\n",
1706     "axes3.set_aspect(3)\n",
1707     "\n",
1708     "fig.tight_layout()"
1709    ]
1710   },
1711   {
1712    "cell_type": "code",
1713    "execution_count": null,
1714    "metadata": {},
1715    "outputs": [],
1716    "source": [
1717     "#We will pretend that A is a function over the unit square in the xy-plane that we want to plot\n",
1718     "x = np.linspace(0,1,4) #we create a regular uniform grid in the x-direction\n",
1719     "y = np.linspace(0,1,3) #we create a regular uniform grid in the y-direction\n",
1720     "x, y = np.meshgrid(x,y,indexing='xy') #we then create a meshgrid in the xy-plane\n",
1721     "#print( x )\n",
1722     "#print( y )\n",
1723     "#print( A )\n",
1724     "\n",
1725     "from mpl_toolkits.mplot3d import axes3d #This enables 3d plotting\n",
1726     "\n",
1727     "fig = plt.figure(2, figsize=(10, 6))\n",
1728     "\n",
1729     "ax1 = fig.add_subplot(1, 3, 1, projection='3d')\n",
1730     "ax1.scatter(x, y, A) #we then plot A over this grid as a scatter plot\n",
1731     "ax1.set_xlabel('x')\n",
1732     "ax1.set_ylabel('y')\n",
1733     "ax1.set_zlabel('A')\n",
1734     "\n",
1735     "ax2 = fig.add_subplot(1, 3, 2, projection='3d')\n",
1736     "ax2.plot_wireframe(x, y, A) #we then plot A over this grid as a wireframe\n",
1737     "ax2.set_xlabel('x')\n",
1738     "ax2.set_ylabel('y')\n",
1739     "ax2.set_zlabel('A')\n",
1740     "\n",
1741     "from matplotlib import cm #Allow for more colormaps\n",
1742     "ax3 = fig.add_subplot(1, 3, 3, projection='3d')\n",
1743     "ax3.plot_surface(x, y, A, rstride=1, cstride=1, cmap=cm.coolwarm) #we then plot A over this grid as a surface\n",
1744     "ax3.set_xlabel('x')\n",
1745     "ax3.set_ylabel('y')\n",
1746     "ax3.set_zlabel('A')\n",
1747     "\n",
1748     "fig.tight_layout()\n",
1749     "\n",
1750     "plt.show()"
1751    ]
1752   },
1753   {
1754    "cell_type": "markdown",
1755    "metadata": {},
1756    "source": [
1757     "## Exercise: Numpy curve-fitting tools and matplotlib\n",
1758     "\n",
1759     "`numpy` has a `polyfit` function (https://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html) to perform least-squares fits of polynomials to data.\n",
1760     "\n",
1761     "In the code block below, `num_data` denotes the number of data points used to fit a polynomial curve to the noisy data defined by (`xdata`,`ydata`) where the `xdata` belongs to the interval [-4,4].\n",
1762     "Finish the code block so that\n",
1763     "\n",
1764     "   * A scatter plot of (`xdata`,`ydata`) is generated;\n",
1765     "   \n",
1766     "   * A third-order polynomial is fitted to the noisy data (read the `polyfit` documentation and look over the examples to see how to use `poly1d` to generate a polynomial function `p` from the output of the `polyfit` function);\n",
1767     "   \n",
1768     "   * Use `linspace` within `numpy` to create a regular uniform grid of [-4,4] called `xgrid` and plot (`xgrid`,`p(xgrid)`) on the same plot as the scatter of the noisy data."
1769    ]
1770   },
1771   {
1772    "cell_type": "code",
1773    "execution_count": null,
1774    "metadata": {},
1775    "outputs": [],
1776    "source": [
1777     "num_data = 100\n",
1778     "\n",
1779     "xdata = np.random.rand(num_data)*8-4\n",
1780     "\n",
1781     "ydata = -xdata**3 + 2*xdata**2 + xdata + 2 + np.random.randn(num_data)*10"
1782    ]
1783   },
1784   {
1785    "cell_type": "markdown",
1786    "metadata": {},
1787    "source": [
1788     "# Summary\n",
1789     "\n",
1790     "We have seen how to \n",
1791     "* Cast variables, print to screen using `print`, and make arrays/lists using built-in Python functions.\n",
1792     "* Import and use a library. \n",
1793     "* Use the numpy library to work with arrays in Python.\n",
1794     "* The expression array.shape gives the shape of an array.\n",
1795     "* Use array[x, y] to select a single element from an array and correctly select components of an array by understanding that array indices start at 0, not 1.\n",
1796     "* Use low:high to specify a slice that includes the indices from low to high-1.\n",
1797     "* Use `#` to add some kind of explanation in the form of comments to programs.\n",
1798     "* Use numpy.mean(array), numpy.max(array), and numpy.min(array) to calculate simple statistics.\n",
1799     "* Use numpy.mean(array, axis=0) or numpy.mean(array, axis=1) to calculate statistics across the specified axis.\n",
1800     "* Use the pyplot library from matplotlib for creating simple visualizations.\n",
1801     "* Use numpy.linspace, and numpy.meshgrid to create regular grids.\n",
1802     "\n",
1803     "# So what is next?\n",
1804     "\n",
1805     "Much of scientific programming involves applications of basic logic (e.g., using conditional statements to determine an action), repeating operations across arrays (e.g., using for-loops), and making user-defined functions to handle problem-specific issues. We will study these ideas in more depth in the next notebook of our short course.  \n"
1806    ]
1807   },
1808   {
1809    "cell_type": "code",
1810    "execution_count": null,
1811    "metadata": {
1812     "collapsed": true
1813    },
1814    "outputs": [],
1815    "source": []
1816   }
1817  ],
1818  "metadata": {
1819   "anaconda-cloud": {},
1820   "kernelspec": {
1821    "display_name": "Python 3",
1822    "language": "python",
1823    "name": "python3"
1824   },
1825   "language_info": {
1826    "codemirror_mode": {
1827     "name": "ipython",
1828     "version": 3
1829    },
1830    "file_extension": ".py",
1831    "mimetype": "text/x-python",
1832    "name": "python",
1833    "nbconvert_exporter": "python",
1834    "pygments_lexer": "ipython3",
1835    "version": "3.8.5"
1836   },
1837   "latex_envs": {
1838    "LaTeX_envs_menu_present": true,
1839    "autoclose": false,
1840    "autocomplete": true,
1841    "bibliofile": "biblio.bib",
1842    "cite_by": "apalike",
1843    "current_citInitial": 1,
1844    "eqLabelWithNumbers": true,
1845    "eqNumInitial": 1,
1846    "hotkeys": {
1847     "equation": "Ctrl-E",
1848     "itemize": "Ctrl-I"
1849    },
1850    "labels_anchors": false,
1851    "latex_user_defs": false,
1852    "report_style_numbering": false,
1853    "user_envs_cfg": false
1854   },
1855   "toc": {
1856    "base_numbering": 1,
1857    "nav_menu": {},
1858    "number_sections": true,
1859    "sideBar": true,
1860    "skip_h1_title": false,
1861    "title_cell": "Table of Contents",
1862    "title_sidebar": "Contents",
1863    "toc_cell": false,
1864    "toc_position": {},
1865    "toc_section_display": true,
1866    "toc_window_display": false
1867   }
1868  },
1869  "nbformat": 4,
1870  "nbformat_minor": 1