## Tutorial 03: Python Basics¶

These notes are adapted from the Python tutorial available at: https://docs.python.org/3/tutorial/.

Here we see the basic functionality of Python by using it as a fancy calculator.

Many of the examples in these tutorials, even those entered at the interactive prompt, include comments. Comments in Python start with the hash character, #, and extend to the end of the physical line. A comment may appear at the start of a line or following whitespace or code, but not within a string literal. A hash character within a string literal is just a hash character. Since comments are to clarify code and are not interpreted by Python, they may be omitted when typing in examples.

In [1]:
# this is the first comment
spam = 1  # and this is the second comment
# ... and now a third!
text = "# This is not a comment because it's inside quotes."


### Using Python as a Calculator¶

The interpreter acts as a simple calculator: you can type an expression at it and it will write the value. Expression syntax is straightforward: the operators +, -, * and / work just like in most other languages (for example, Pascal or C); parentheses (()) can be used for grouping. For example:

In [2]:
2 + 2

Out[2]:
4
In [3]:
50 - 5*6

Out[3]:
20
In [4]:
(50 - 5*6) / 4

Out[4]:
5.0
In [5]:
8 / 5              # division always returns a floating point number

Out[5]:
1.6

The integer numbers (e.g. 2, 4, 20) have type int, the ones with a fractional part (e.g. 5.0, 1.6) have type float.

Division (/) always returns a float. To do floor division and get an integer result (discarding any fractional result) you can use the // operator; to calculate the remainder you can use %:

In [6]:
17 / 3             # classic division returns a float

Out[6]:
5.666666666666667
In [7]:
17 // 3            # floor division discards the fractional part

Out[7]:
5
In [8]:
17 % 3             # the % operator returns the remainder of the division

Out[8]:
2
In [9]:
5 * 3 + 2          # result * divisor + remainder

Out[9]:
17

With Python, it is possible to use the ** operator to calculate powers:

In [10]:
5 ** 2  # 5 squared

Out[10]:
25
In [11]:
2 ** 7  # 2 to the power of 7

Out[11]:
128

### Assigning variables¶

The equal sign (=) is used to assign a value to a variable. Afterwards, no result is displayed before the next interactive prompt:

In [12]:
width = 20
height = 5 * 9
width * height

Out[12]:
900

If a variable is not “defined” (assigned a value), trying to use it will give you an error:

In [13]:
n  # try to access an undefined variable

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-13-6268bfea6a2c> in <module>()
----> 1 n  # try to access an undefined variable

NameError: name 'n' is not defined

Make sure that you review these notes several times, particularly if you have not prior experience with programming.

## Practice¶

For these practice problems using Python as a calculator, we are going to approximate the trigonometric function $sin$ using a Taylor series. To start, run the following block of code to load the sin function in Python (more on this next time):

In [14]:
from math import sin


The Taylor series of $sin(x)$ around zero gives a first order approximation of:

$$sin(x) \approx x$$

If you think about the shape of the sin function, this should make sense. In the next block of code define a variable named x and set this value to 0.1

In [15]:
x = 0.1


Now, create a new variable err (for error) that computes the absolute error in the first order approximation of $sin(x)$ for the current value of x. Note that Python has the function abs to compute the absolute value. Print out the value of err at the end of the code chunk.

In [16]:
err = abs(sin(x) - x)
err

Out[16]:
0.0001665833531718508

Now, in the code block below, combine the assignment of x and the calculation of err together. Copy this code five times, but set x to 0.2, 0.3, 0.4, and 0.5, running each chunk.

In [17]:
x = 0.1
err = abs(sin(x) - x)
err

Out[17]:
0.0001665833531718508
In [18]:
x = 0.2
err = abs(sin(x) - x)
err

Out[18]:
0.0013306692049387947
In [19]:
x = 0.3
err = abs(sin(x) - x)
err

Out[19]:
0.004479793338660443
In [20]:
x = 0.4
err = abs(sin(x) - x)
err

Out[20]:
0.0105816576913495
In [21]:
x = 0.5
err = abs(sin(x) - x)
err

Out[21]:
0.020574461395796995

Describe the pattern you see above. Where is the approximation best? Where is worst? (Note: double click on the cell below to input your answer)

Answer: The error gets worse as x increases away from zero.

The third order approximation of $sin$ is given as:

$$sin(x) \approx x - \frac{x^3}{6}$$

Again set x equal to 0.1. Store the first order approximation error as the variable err1 and the third order approximation as err3. Print out the value err1/err3.

In [22]:
x = 0.1
err1 = abs(sin(x) - (x))
err3 = abs(sin(x) - (x - x**3 / 6))
err1/err3

Out[22]:
1999.4762379009107

Describe in words what the value of the output above means.

Answer: It shows that the error of the third order approximation is 2000 times smaller than the first order approximation.

## Extra Practice¶

When you see an "Extra Practice" section, these are optional questions meant to stretch your understanding. They are often geared for students with prior programming experience.

More generally, we can write the sin function using an infinite Taylor series:

$$sin(x) = \sum_{n=0}^\infty (-1)^{n} \cdot \frac{x^{2n + 1}}{(2n + 1)!}$$

We can get the factorial function from the math library:

In [23]:
from math import factorial


Let's set a value for x and n:

In [24]:
n = 2
x = 1


Write out the $n$'th term of this approximation in Python code using the variable n and variable x. You can assign $n$ to an integer in order to test it:

In [25]:
(-1)**n * x**(2*n + 1) / factorial(2 * n + 1)

Out[25]:
0.008333333333333333

If you have some prior experience programming, try to write a function (a quick search will show the syntax in Python) called sin_approx that takes a value of n and a value of x and returns the n-term approximation to the sin function at x.

In [26]:
def sin_approx(x, n):
val = 0
for m in range(n):
val += (-1)**m * x**(2 * m + 1) / factorial(2 * m + 1)

return val


How well does your approximation work at x=1 for various values of n?

In [27]:
for n in range(10):
err = abs(sin(x) - sin_approx(1, n=n))
print("n={0:d}  err={1:0.016f}".format(n, err))

n=0  err=0.8414709848078965
n=1  err=0.1585290151921035
n=2  err=0.0081376514745631
n=3  err=0.0001956818587702
n=4  err=0.0000027308396425
n=5  err=0.0000000248922799
n=6  err=0.0000000001598285
n=7  err=0.0000000000007619
n=8  err=0.0000000000000028
n=9  err=0.0000000000000000


Answer: The error decreases as n increases, and is zero to 16 decimal places for n=9 (and presumably, higher values as well).

Finally, copy the sin_approx function below and add a docstring with the conventions according to PEP 257.

In [28]:
def sin_approx(x, n):
"""Compute Taylor series approximation of the sine function.

Args:
x: Numeric value at which to approximate the function.
n: Positive integer. Number of terms to include in the
Taylor series.
Returns:
The numeric approximation.
"""
val = 0
for m in range(n):
val += (-1)**m * x**(2 * m + 1) / factorial(2 * m + 1)

return val

In [29]:
help(sin_approx)

Help on function sin_approx in module __main__:

sin_approx(x, n)
Compute Taylor series approximation of the sine function.

Args:
x: Numeric value at which to approximate the function.
n: Positive integer. Number of terms to include in the
Taylor series.
Returns:
The numeric approximation.