1. Introduction to Python and Jupyter notebooks#

Estimated time to complete: 60 to 90 minutes.

1.1. Introduction#

Welcome to the online materials for this online course on programming in Python for mathematical computing (a.k.a. scientific computing).

This first unit is an introduction to some basic features of the Python software that we will be using. Specifically, we use Python 3, and even more specifically, Python version 3.9 or higher.

We start directly with Jupyter notebooks via the tool JupyterLab as a way to use Python interactively like a scientific calculator, and aim to work with these notebooks as much as possible.

Later, we will also learn the more advanced code development tools offered by the Integrated Development Environment Spyder. This supports both both interactive use of Python and also working with files of Python code: creating, opening and editing files, running and debugging code, and so on. It has more advanced tools for developing Python code than the Jupyter notebook system, so for more substantial programming tasks it can be better to develop code within Spyder — even if that code is then transferred into a notebook for final presentation.

However, the use of any Integrated Development Environment [“IDE”] is somewhat optional; it will be possible to work almost entirely with Jupyter notebooks. That for example would allow working with just the web-browser based system CoCalc

Aside: There are other IDE’s for Python, such as IDLE; that is the original IDE for Python and is pre-installed in some computers (Mac’s at least) but it is very rudimentary compared to Spyder.

1.2. Getting Python software for scientific computing#

I suggest that you get Anaconda for you own computers, even if you also have access to it via computers on-campus. Get the “current” version based on Python 3; more specifically, version 3.9 or higher.

The various components like the iPython console, Spyder and Jupyter can also be got separately (follow those links), but that involves getting under the hood with some command line installation activity, and maybe even compiling from source code.

A possible alternative is to use the purely online resource CoCalc, which has a free “trial” version. This provides Jupyter notebooks, but not all the other useful tools like Spyder.

1.3. “Python Scientific”: adding three packages that make Python more useful for scientific computing#

Anaconda and its components JupyterLab and Spyder include a collection of useful resources beyond basic Python: the packages

  • Numpy for numerical and scientific computing, particularly for working with vectors and matrices,

  • Matplotlib for producing graphs of numerical data and functions, and

  • Scipy which provides some more advanced scientific computing tools, such as for optimization and solving ordinary differential equations.

This combination is often called Python Scientific.

1.4. Online resources#

There is a vast array of free online tutorials and documentation for Python and the other tools that we are using, such as

These resources might be useful sometimes, but our direction is a bit different: aiming at what is most important to a mathematics student, rather than a computer science student. For our “Python Scientific” orientation, there is a comprehensive but more advanced reference, the Scipy Lecture Notes, as both a website and a downloadable PDF book.

There are also official sites for Python and the most important packages:

but these are not so readable as some of the sources listed above.

1.5. Using Python as a calculator #

For some tasks, Python can be used much like a scientific calculator (and later, like a graphing scientific calculator); that is how we will work for this section.

There are multiple ways of doing this, and I invite you to experiment with them all at some stage:

  • In Jupyter notebooks (like this one), which can be accessed from within the Anaconda Navigator by opening JupyterLab

  • With the “bare” IPython console; this is available in many ways, including from the Anaconda Navigator by opening Qt Console

  • With Spyder, which can be accessed from within the Anaconda Navigator by opening Spyder; within that, there is an interactive Python console frame at bottom-right.

I suggest learning one tool at a time, by using a Jupyter notebook for now; however if you wish to also try some or all of the alternatives above, go ahead!

1.6. Creating a course folder and a Jupyter notebook#

  1. Create a folder within your home folder or on your desktop named something like “PythonScientific”; all files for this course should be put in there. (If you use a different name, please use one that starts with a letter and contains only letters, digits, dashes, and underscores — no spaces or other punctuation. Other names can work for now, but these restrictions can help to avoid problems later!)

  2. Open Anaconda and within Anaconda-Navigator, launch JupyterLab.

  3. In the file system navigator frame at left, get to that folder (PythonScientific or whatever).

  4. Use the File menu at top-left to create a new notebook.

  5. It will have a single cell of type “Code”, as indicated at top-center; use that menu to change it to type “Markdown”, which is for entering text.

  6. In this cell, add a title, your name and the date; see the title cell at the top of this notebook.
    Note that:

    • the single hash “# ” (followed by a space) at the start of a line makes that line a level 1 heading.

    • surrounding text with single asterisks is for italic text.

    • surrounding text with pairs of asterisks is for boldface text.

    • surrounding text with angle brackets <…> creates a link to an email address.

  7. Next, add a new cell below this, using the “+” button above; this is automatically a Code cell, and you could put all subsequent code in that cell. However it would be better to spread the code over multiple cells, by adding more: one for each example and exercise. Also, I encourage you to intersperse Markdown cells with explanatory text — that will be good practice in the longer run.

Here is an example of the text to put into a simple code cell:

1.7. Running some or all cells in a notebook#

  1. Running a Code cell executes its Python code; running a Markdown cell formats its content for nicer output. There are several ways to do this:

  • The “Play” (triangle icon) button above runs the current cell, and moves to the next cell as “current”.

  • The menu “Run” above has various options — hopefully self-explanatory, at least after some experimentation.

  1. To get a Markdown cell back from nicely formatted “display mode” back to “edit mode”, double click in it.

1.8. Notes on organization and presentation#

For all exercises throughout this course, record both your input and the resulting output, in a document that you will submit for feedback and then grading.

We want a written or typed record of all our work and results, and I also encourage you to make notes of other things that you learn, beyond what must be submitted — they will help later in the course!

For now, one option is to make hand-written notes and then scan them to PDF for uploading, but it is better to create files directly, and Jupyter notebooks will usually be the best way to do that.

Also note that — as just described above and for every document that you submit, hand-written or electronic — start with:

  • A title,

  • your name (possibly also with your email address), and

  • the date, of the current version.

(See the Title Cell above for example.)

Revision dates will become important, as you will often be submitting successive revisions and refinements: our goal is mastery of each topic, not partial credit!

1.9. Arithmetic#

This is mostly easy stuff, but with a few quirks of notation and such to note.

First, let us see how to run Python code with the following famous formula. In a new code cell in notebook, type the following and run it with the “play” button or using the “Run” menu.

If you wish to also start learning the command line interface:

  • open Spyder (or another Python tool like IPython),

  • type the following into the IPython console (at bottom-right in the Spyder window), and

  • press “Return” or “Enter” to get the result.

2 + 2
4

No prize for predicting the above result, but division of two integers requires a little care:

11 / 4
2.75

Here Python 3 gives a real number answer, whereas some programing languages always given an integer answer for the division of integers, by rounding down (“truncating”) if necessary. (In fact, the older version Python 2 does that, which is one reason that we avoid using it!)

When you want the truncated, integer answer, use a double slash:

11 // 4
2

and to get the remainder, use the percent sign!

11 % 4
3

With multiplication, the only catch is that there is no multplication sign on the keyboard, so the asterisk is used instead:

7 * 4
28

There is no way to type superscripts either, so exponentiation uses a double-asterisk (not the “caret” or up-arrow, as used in some other programming languages and calculators):

2**5
32

Numbers with exponents can be expressed using this exponential notation, but note how Python outputs them:

5.3*10**21
5.3e+21
7*10**-8
7e-08

This “e” notation is the standard way to describe numbers with exponents, and you can input them that way too.

When printing numbers, Python decides if the number is big enough or small enough to be worth printing with an exponent:

2e-10, 3E-5, 3e-4, 5e+4, 10000000000, 6e15, 6e16
(2e-10, 3e-05, 0.0003, 50000.0, 10000000000, 6000000000000000.0, 6e+16)

Aside: note that either “e” or “E” can be used in input of exponents. However in most contexts, Python is case sensitive; we will see an example soon.

1.10. Naming quantities and displaying values#

It often helps to give named to quantities, which is done with assignment statements. For example, to solve for \(x\) in the very simple equation \(ax + b = 0\) for given values of \(a\) and \(b\), let us name all three quantities:

a = 3
b = 7
x = -b/a

Running the above cell just computes the three values with no output; to see the values we use the function print:

print(a, b, x)
3 7 -2.3333333333333335

That output does not expian things very clearly; we can add explanatory text to the printed results in several ways. Most basically:

print('For a =', a, 'and b =', b, 'the solution of ax + b = 0 is', x)
For a = 3 and b = 7 the solution of ax + b = 0 is -2.3333333333333335

The above prints six items — three text strings (each quoted), three numbers — separated by commas.

There is an alternative notation for such output, called “f-strings”, which use braces to specify that the value of a variable (or formula) be inserted into a string of text to display:

print(f'For a = {a} and b = {b} the solution of ax + b = 0 is {x}')
For a = 3 and b = 7 the solution of ax + b = 0 is -2.3333333333333335

In this example each pair of braces inserts the value of a variable; one can instead put a formula in there. So in fact we could skip the variable \(x\) entirely:

print(f'For a = {a} and b = {b} the solution of ax + b = 0 is {-b/a}')
For a = 3 and b = 7 the solution of ax + b = 0 is -2.3333333333333335

We can also display the equation more directly:

print(f'The solution of {a} x + {b} = 0 is {-b/a}')
The solution of 3 x + 7 = 0 is -2.3333333333333335

1.11. Some mathematical functions and constants: module math#

Python includes many modules and packages that add useful defintions of functions, constants and such, and for us, the most fundamental is math, which is a standard part of Python.

(Later will see some other modules that are not part of standard Python, but which Anaconda, Jupyter Lab and Spyder add to the basic collection: numpy, matplotlib and scipy mentioned above, and also pylab, which combines much but not all of numpy and matplotlib.)

We can access specific items with an import command; to start with, two variables containing famous values:

from math import pi, e

Aside: we will soon learn about the package numpy and then mostly use that instead of math. I mention math for now because it is a core part of Python whereas numpy is a separate “add-on”, and you should be aware of math if only because many references on doing mathematical stuff with Python will refer to it.)

Amongst many other things, math gives our two favorite trancendental numbers, accurate to about 16 significant digits:

print(f'pi is approximately {pi}')
pi is approximately 3.141592653589793
print(f'e is approximately {e}')
e is approximately 2.718281828459045

Each is accurate to about 16 significant digits; that is the precision of the 64-bit number system standard in most modern computers.

Names (like “e” and “pi”) are case-sensitive#

We can now return to the comment above about case sensitivity. Compare the results of the following two input lines:

print(f'e = {e}')
e = 2.718281828459045
print(f'E = {E}')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [20], in <cell line: 1>()
----> 1 print(f'E = {E}')

NameError: name 'E' is not defined

Some math functions#

Module math also provides a lot of familiar mathematical functions

from math import sin, cos, cosh

which can be used like this:

sin(pi/4)
0.7071067811865475
cos(pi) 
-1.0
cosh(0)
1.0

Notes#

  • All Python functions need parentheses around their arguments; no lazy shortcuts with trig. functions and such.

  • Trig. functions use radians, not degrees.

1.12. Wild imports (mostly avoid!) versus spelling things out explicitly#

There is a shortcut that alows importing all quantities from a module; above we could have used

from math import *

which means “use everything in this module”, and is called a wild import.

However it is usually better and safer to specify explicitly the items that you will use, as done above.

A third “middle way” is to import the module (rathe than items from it) and then refer to items by their “full names” with a dot notation “module.item”. For example, with

import math

we get access to many functions such as tan, but need to adress it by its full name math.tan:

print(f"tan(pi/4) = {math.tan(pi/4)}")
tan(pi/4) = 0.9999999999999999

If we had not already imported pi above, its full name math.pi would also be needed, as in:

print(f"tan(pi/4) = {math.tan(math.pi/4)}")
tan(pi/4) = 0.9999999999999999

Aside: The imperfection of computer arithmetic becomes clear: the exact value is 1 of course. However, the precision of about 16 decimal places is far greater than for any experimental measurement, so this is usually not a problem in practice.

These more specific approaches to importing are highly recommended when creating files and programs; wild imports are best only used as a convenience when working interactively, using Python like a scientific calculator.

One advantage of importing items specifically “by name” is that it is then unambiguous where a given item was imported from, even if multiple import commands are used. For example, we will see later than there are both math.cos and numpy.cos, and they behave differently in some situations.

Another reason for explicit imports is for internal efficiency in storage and execution time; wild imports can potentially load thousands of items from a large module when only a few are used.

1.13. Logarithmic functions#

There is also a function log in module math, but which base does it use?

math.log(10)
2.302585092994046
math.log(e)
1.0

Evaluating these two expressions reveals that “log()” is the natural logarithm, base \(e\);
what mathematicians usually call “\(\ln\)”.

1.14. Exercises#

Exercise A#

It is time to explore: try to find the base 10 logarithm.

A big hint: look at the following, which is yet another logarithm; one of particular interest in computer science.

math.log2(64)
6.0

Powers and roots#

A square root can of course be computed using the 1/2 power, as with

16**0.5
4.0

but this function is important enough to have a named form provided by module math:

math.sqrt(2)
1.4142135623730951

Exercise B#

Here is a wrong way to use the exponential notation in an attempt to compute a square root: explain what goes wrong!

16**1/2
8.0

Exercise C: Devising, evaluating and modifying formulas in Python#

It is time to practice with creating basic Python commands for mathematical expressions.

a) With the above information, warnings and examples in mind, evaluate \(\displaystyle \frac{1 + \sqrt{2}}{2 \pi}.\) (The correct answer is 0.384…)

b) [If using Spyder/IPython,] explore IPython’s command history and editing features, and use these to evaluate \(\displaystyle \frac{1 - \sqrt{2}}{2 \pi}\) by modifying the previous formula, without typing the entire new expression from scratch.

Exercise D: Putting this work into a file, and submitting for feedback#

Put your work so far into a Jupyter notebook.

For Exercises A and B, put your verbal answers into Markdown cells in the notbook.

For Exercise C, it is also worth adding some verbal explanation (again in a Markdown cell) along with the Python code and its output.

Then submit that notebook file; if using OAKS, submit it to the dropbox for this section.

Reminder: The goal is mastery (not partial credit)

I expect you to ask questions about how to do these tasks, but trying hands-on first is the best way to start; I will always give feedback and the opportunity to revise and perfect your work; the goal is working towards mastery of each topic.