<jupyter_start><jupyter_text>Lesson 2: Introduction to Python Using Python in Scientific ComputingThis lecture by <NAME> is licensed under theCreative Commons Attribution 4.0 International License.  All code examples are also licensed under the [MIT license](http://opensource.org/licenses/MIT). This book is intended for students in science/engineering/math and presumes an introductory knowledge of computer programming (be it whatever language).  This book *is not* an introduction to computer programming.For a complete introduction to IPython Notebooks, see the [IPython Notebook Tutorial](IPython Notebook Tutorial.ipynb).  Topics- [What is Python](what_is)  - [How do I use Python?](how_to_py)- [Minimal Python Stack](minimal_stack)  - [numpy](minimal_py_numpy)  - [matplotlib](minimal_py_matplotlib)  - [sympy](minimal_py_sympy)- [Dive in to Python](dive_in_to_py)  - [Variable Assignment](var_assign)  - [Variable Types](var_types)  - [Program Flow Control](prog_flow)    - [Indentation](indentation)    - [Branching](branching)    - [Looping](looping)  - [Functions](functions)  - [Modules](modules)  - [Numpy](numpy)    What is Python?[](top) As your read through each section, you will see cells labeled with  Try it which are places meant for you to try the new concept introduced.  Keep in mind, that these Notebooks are interactive, fee free to modify values, move things, test things out.[Python](www.python.org) is an open source, general-purpose language, with hundreds of modules geared toward scientific computing.  Some main features of Python are:  - Object Oriented, Procedural, and Functional programming styles  - Easy to interface with C/C++/Fortran/Java/etc  - Interactive environment  - Becoming the de-facto standard interfacing language for many commercial FE codes  - Clean and simple  - [Duck typed](http://en.wikipedia.org/wiki/Duck_typing)  - Interpretive (no compiling necessary)  - Automatic memory management  Some relative advantages of Python  - Ease of programming  - Fast prototyping  - Interfacing with other languages  - Large library of modules and add-on packages  - Open source  Some relative disadvantages  - Speed of execution compared to compiled languages  - Programs can become platform/environment dependent  There is no way a short tutorial can describe everything in Python (I've been using/developing Python for 10+ years and learn new stuff all the time).  But, it will introduce enough concepts to complete the first homework assignment and provide a foundation for future learning.    How do I use Python?[](top) Python is synonomous with the programming language, the interactive Python shell, and the interpreter under the hood that actually reads and processes the code.  There are several ways to write/interact with Python: the interactive python shell, the IPython notebook, and by interpreting program files containing python statemtns.  This is one of Python's relative strengths: it allows users to select the environment that best suits their needs.  The Interactive Python Shell The Python shell is a program that reads and executes Python statements as you enter them.  It is opened by invoking the following at a command prompt    $ python Try it!Before moving, enter a python shell and execute the following  Interactive Python sessions are fantastic for quick calculations, or testing snippets of code.  IPython Notebook [Ipython Notebooks](http://ipython.org/notebook.html) provide an interactive environment for Python similar to Maple's worksheets or Mathematica's notebooks.  It is based on the IPython shell (a modified interactive Python shell).  IPython notebook sessions are stored locally as `json` files with a `.ipynb` file extension and are interpreted graphically as a web session using IPython's notebook server.  This file is an example of an IPython Notebook file.The IPython Notebook server is launched locally by executing the following at the command prompt:    $ ipython notebook    This will open a new web browser (or tab in an existing window) in which notebooks can be created/edited/deleted.  The fact that you are reading this means you have already launched the notebook server.IPython notebooks consist of a collection of text cells and code cells.  `code` cells execute Python code in the notebook's kernel.  Text cells are labeled `markdown`, `Raw NBConvert`, and `Heading1-6`.   Different text cell types display their contents differently.  Looking at the toolbar above, can you tell what type of cell this is?  (`Markdown`).See the full [IPython Tutorial](Lesson01_IPythonNotebookTutorial.ipynb) for more details.  Python Program Files Often, it is not convenient to work in an interactive or notebook environment.  In those situations, Python statements can be saved in Python program files that are later interpreted and executed by the Python interpreter (this is not strictly true, but is true enough for now).  Python program files (usually) have the file extension "`.py`", e.g. `baz.py`.  With the exception of comment lines, every line in a Python program file is considered to be a Python statement and statements are executed in order from the top of the file to the bottom.  Any statement proceding a "``" (until the end of the line) is considered a comment.  Python program files can be executed individually by executing the following at a command prompt:    python filename.py CAUTION python files that you intend to later be imported should be valid python variable names.  i.e., do not use special characters in Python program file names (+, -, /, *, @, $, etc.).  More on this later.    Minimal Software Stack for Scientific Computing in Python[](top) The strength of Python for scientific computing lies in the large number of add-on modules on which users can build applications.  Many of the most important modules, namely   - [`numpy`](http://www.numpy.org/‎)  - [`scipy`](http://www.scipy.org/‎)  - [`sympy`](http://www.sympy.org/‎)  - [`matplotlib`](http://www.matplotlib.org/‎)  - [`ipython notebook`](http://www.ipython.org/notebook.html)  are not part of the standard Python distribution.  Historically, users had to install each module (and their dependencies) individually.  This painful process can, in large part, be bypassed by using one of the fantastic commercially available Python distributions.  I recommend [annoconda](https://store.continuum.io/cshop/anaconda/) or [enthought](https://www.enthought.com/products/epd/‎).  Both offer free and paid versions.  The free versions offer all the utility that we will need in this book.    numpy The `numpy` package is the foundation of most scientific computations performed in Python.  It is implemented in C/Fortran, so performance is greatly improved over native Python data types.  `numpy` provides:  - the `ndarray` (n dimensinal array) is `numpy`'s primary object  - fast array operations  - large library of linear algebra procedures  - and much, much, more...    matplotlib `matplotlib` is a 2D and 3D graphics library for generating scientific figures.  `matplotlib`:  - easy to use interface very similar to Matlabs  - support for $\LaTeX$ labels and text  - publication quality output in most popular formats  - GUI and batch modes    sympy `sympy` provides a Computer Algebra System (CAS) for Python.  `sympy` is a regular Python module and integrates very well with IPython notebooks.    Dive in to Python[](top)    Variable Assignment Since python is a dynamically interpreted language, a variable's type need not be declared - its type is inferred at the time of assignment.  Variables are assigned a value by the assignment operateor "`=`"<jupyter_code>spam = 4
spam<jupyter_output><empty_output><jupyter_text>An assignment statement assigns to the variable name on the left of the assignment operator the expression on the right.  Reminder:  To execute the cell above move focus to the cell and press shift enter   Variable Types Python supports many `type`s of variables.  The most commonly used in this course are `str`, `float`, `int`, `bool`, `list`, `tuple`, and `dict`.  Strings Strings are groups of one or more characters of type `str` enclosed by single or double quotes<jupyter_code>string = "foo"
type(string)<jupyter_output><empty_output><jupyter_text>String concatenation is by the "+" operator<jupyter_code>post = " bar"
string + post<jupyter_output><empty_output><jupyter_text>Python's support for string parsing, manipulation, etc. is, in my opinion, one of its greatest strengths.  Numbers Python supports many types of numbers, the most common being reals and integersReal numbers are of type `float`<jupyter_code>a = 4.
a = float(4)
a = float("4.")
type(a)<jupyter_output><empty_output><jupyter_text>Integers have type `int`<jupyter_code>b = 4
b = int(4.)
type(b)<jupyter_output><empty_output><jupyter_text>Be careful when converting strings to integers<jupyter_code>b = int("4.")<jupyter_output><empty_output><jupyter_text>The above code "raised" a `ValueError`.  Instead, first convert to float, then integer<jupyter_code>b = int(float("4."))
b<jupyter_output><empty_output><jupyter_text>Arithmetic is through the standard arithmetic operators<jupyter_code>a = 3.
b = 2.
a + b # addition
a - b # subtraction
a * b # multiplication
a / b # division<jupyter_output><empty_output><jupyter_text>"**Warning!** by default "/" is integer division<jupyter_code>1 / 2
a ** 2 # raising to power (note, do not use ^)<jupyter_output><empty_output><jupyter_text>Booleans Python supports a boolean type `bool` with two members `True` and `False`.  Python also supports the related `None`.  Lists Lists are mutable container objects of type `list`.  Lists are composed of comma separated members enclosed by paired brackets<jupyter_code>a = [1, 2, 3]
type(a)<jupyter_output><empty_output><jupyter_text>Lists can be appended to and extended by other lists<jupyter_code>a.append(4)
a.extend([5, 6, 7])
a<jupyter_output><empty_output><jupyter_text>Important:  In the cell above a is an object of type list.  Put differently, a is an "instance" of the list class.  The dot . following a above, is a special operator that accesses "methods" associated with a.  Simplistically, methods are functions that are owned by a class and are accessible only by instances of the class through the dot operator.  Classes will be covered more in depth later, it is sufficient for now to recognize the action of the dot operator.List members need not be of the same type<jupyter_code>b = [1, 2, "spam", True, [3, 4]]
b<jupyter_output><empty_output><jupyter_text>Attention:  matlab and fortran users: in Python, indexing is 0 based<jupyter_code>first = b[0]
first
last = b[-1]
last<jupyter_output><empty_output><jupyter_text>Slices of lists can be accessed<jupyter_code>c = a[0:3]
c<jupyter_output><empty_output><jupyter_text>Tuples Tuples are container objects like lists, but are immutable (not modifiable after creation) and use parenthesis in place of brackets<jupyter_code>a = (1, 2, 3, 4)<jupyter_output><empty_output><jupyter_text>Once created, immutable objects cannot be modified.  For instance, attempting a number to the end of `a` will result in an error<jupyter_code># a.append(4) # tuples are immutable, so this will result in an error<jupyter_output><empty_output><jupyter_text>Dictionaries Like lists and tuples, dictionaries are yet another container object (type: `dict`).  Dictionaries consist of `key:value` pairs enclosed by braces.  Valid keys are any immutable object (other items can be used, but for now immutable objects will suffice).  For example, in the cell below a dictionary `d` is created with key `a` and value `5`.  A new `key:value` pair is then added to `d`.<jupyter_code>d = {"a": 5}
d["b"] = "spam"
d
d["b"]<jupyter_output><empty_output><jupyter_text>A `KeyError` is raised when attempting to access a non-existent key<jupyter_code># d[12]<jupyter_output><empty_output><jupyter_text>The `dict` constructor can be used to create a dictionary from a list of tuples<jupyter_code>d = dict([('a', 5), ('b', 'spam'), (0, (1,2,3))])
d<jupyter_output><empty_output><jupyter_text>Try it!  Add key:value pairs "c": [1,2,3] and "d": (3,4,5) to d.  Note:  dictionaries do not preserve order.    Program Flow Control    Indentation Unlike many other languages that explicitly define the scope of code blocks (opening/closing braces, begin/end keywords, etc.), Python implicitly defines code block scope through indentation    compound_statement ":"        code block        The Python language does not define a standard indentation depth, but the following advice should be followed:  - Don't mix tabs and spaces  - Spaces are preferred in new code  - Be consistent with indentation  - Use 4 spaces in new code (4 spaces will be used in this class) Branching (if, else, etc.) Branching within a code is handled by `if`, `elif`, and `else` clauses<jupyter_code>if True:
    print "I'm True!"
a = 4
if a < 3:
    print "Less than 3"
elif a < 6:
    print "Between 3 and 6"
else:
    print "Greater than 6"<jupyter_output><empty_output><jupyter_text>Notice that the indentation of the above code defines the scope of each block.    Looping Python provides two forms of looping: `for` and `while`.  `for` Loops `for` loops in Python is one area that took me some time to become accustomed to, in comparison to C or Fortran.  In C, Fortran, and many other languages, loops are constructed by incrementing an integer value from a starting value to an ending value.  For example, in C    for(int i=0; i<10; i++){      std::cout << i << "\n";    }    In Python, `for` loops are performed by iterating through an "iterable".  Iterables are objects, such as `lists`, `tuples`, and `dict`s, that support iteration.  In python, the previous loop could be written:<jupyter_code>for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
    print i<jupyter_output><empty_output><jupyter_text>or,<jupyter_code>for i in range(10):
    print i<jupyter_output><empty_output><jupyter_text>Strings also support iteration.  To loop through the values of a string,<jupyter_code>for letter in "a very long string":
    print letter<jupyter_output><empty_output><jupyter_text>Try It!Insert a new cell below and write a block of code that loops through integers 1-20 and prints those that are even.  `while` Loops Similar to `for` loops, while loops iterate until a condition is met.<jupyter_code>a = 1
while a < 5:
    a += a  # lhs += rhs is a shortcut for lhs = lhs + rhs
print a<jupyter_output><empty_output><jupyter_text>List Comprehension A list comprehension is a concise way of creating a `list`.  Consider the following<jupyter_code>a = []
for i in range(4):
    a.append(i ** 2)
a<jupyter_output><empty_output><jupyter_text>The range function creates a list, starting from 0 by default, with n elements<jupyter_code>range(9)<jupyter_output><empty_output><jupyter_text>The above code can be simplified to<jupyter_code>a = [i ** 2 for i in range(4)]
a<jupyter_output><empty_output><jupyter_text>List comprehensions consist of an opening bracket, expression, a for loop, followed by 1 or more for loops and/or if statements and are ended with a closing bracket. Try It!Open a cell below and create a list of the cubes of the first 5 integers using 1) a for loop and append statement and 2) a list comprehension.   Functions Functions contain collections of related Python code.  Functions have the following syntax    def function_name "(" [parameter_list] ")" ":"        suiteFor example<jupyter_code>def print_hello():
    print "Hello, World!"
print_hello()
def print_hello_with_args(a, b):
    print "{0} says hello to {1}!".format(a, b)
print_hello_with_args("Fred", "Sue")<jupyter_output><empty_output><jupyter_text>The .format string method, in its simplest form, replaces occurrences of {n} with its nth argument.  For example<jupyter_code>"Mary {0} {3} {2} {1}".format("had", "lambs", "little", 5)<jupyter_output><empty_output><jupyter_text>Functions can return 1 or more objects<jupyter_code>def add(a, b):
    c = a + b
    return c
add(4, 5)<jupyter_output><empty_output><jupyter_text>Lambda Functions Another type of function is the `lambda` function, or "anonymous" function.  `lambda`s allow the creation of functions on the fly<jupyter_code>f = lambda x: x ** 2
f(4.)<jupyter_output><empty_output><jupyter_text>`lambda` functions are useful in many situations and we'll have a chance to use them often throughtout this book.Try It!Write a function that multiplies 2 numbers and returns the result.   Modules Python modules are importable files containing Python code.  Importing a module in to the current file exposes that modules contents to the current file.  The syntax for importing a module takes one of several forms, demonstrated below.<jupyter_code>import math
math.pi
math.cos(0.)<jupyter_output><empty_output><jupyter_text>Modules names can be aliased for convenience with the ``as`` designator<jupyter_code>import math as m
m.pi<jupyter_output><empty_output><jupyter_text>Alternatively, all contents of a module can be imported in to the current namespace<jupyter_code>from math import *
pi<jupyter_output><empty_output><jupyter_text>However, this can lead to pollution of the current namespace and is not recommended - unless the module was designed to be imported in this way.  For example, suppose you do    from spam import *    from eggs import *        baz = foo    from which module did `foo` come from?The Python standard library comes with many useful modules.  In this course we will also use heavily the nonstandard `numpy`, `sympy`, and `matplotlib` modules.Try It!Import the `os` module and determine your current working directory with its `cwd` method.    `numpy` `numpy` is a powerful Python module that is the defacto standard for scientific computing in Python.  It is customary to import `numpy` as<jupyter_code>import numpy as np<jupyter_output><empty_output><jupyter_text>The power of `numpy` lies in the array object that it provides.  `numpy` arrays are similar to Python lists, but are statically typed and contain only one object type.  Arrays can be instantiated in a number of ways, - from an existing list<jupyter_code>a = np.array([1., 2., 3.])
a<jupyter_output><empty_output><jupyter_text>- the `numpy.linspace` function<jupyter_code>a = np.linspace(0, 10, 5) # np.linspace takes (start, stop, number of elements)
a<jupyter_output><empty_output><jupyter_text>- the `numpy.arange` function<jupyter_code>a = np.arange(0, 5, 1.5) # np.arange takes (start, stop, step size)
a<jupyter_output><empty_output><jupyter_text>And still others.  Linear Algebra with `numpy` `numpy` provides many common operations from linear algebra to operate on n dimensional arrays.  Consider the following arrays<jupyter_code># Define 1 and 2D arrays.  2D arrays are similar to matrices in matlab
a = np.array([3, 2, 6])
b = np.array([1, 1, 7])
M = np.array([[1, 5, 4], [5, 5, 3], [8, 2, 9]])
L = np.array([[3, 1, 5], [5, 7, 2], [1, 0, 3]])<jupyter_output><empty_output><jupyter_text>The transpose of an array<jupyter_code>np.transpose(M)<jupyter_output><empty_output><jupyter_text>The `*` operator operates element wise on arrays, so `a * b` will not yield a scalar<jupyter_code>a * b<jupyter_output><empty_output><jupyter_text>The `dot` Product Scalar product of two vectors<jupyter_code>np.dot(a, b)<jupyter_output><empty_output><jupyter_text>Matrix/Vector multiplication is also through the `dot` method<jupyter_code>np.dot(M, a)<jupyter_output><empty_output><jupyter_text>Think of the `dot` method in terms of the indicial summation representation of matrix operations, e.g. matrix/vector multiplication$\{c\} = [A]\{b\} \Rightarrow c_i = \sum_jA_{ij}b_{j}$would be written    c = np.dot(A, b)    Or matrix multiplication,$[C] = [A][B] \Rightarrow C_{ij} = \sum_m A_{im}B_{mj}$is    C = np.dot(A, B)    Or the vector/vector (scalar) product$c = \{v\}\cdot\{w\} = \sum_i v_i w_i$is    c = np.dot(v, w)    Inverse of a square matrix<jupyter_code>np.linalg.inv(M)<jupyter_output><empty_output><jupyter_text>Determinant of a matrix<jupyter_code># Determinant of a 2D array
np.linalg.det(M)<jupyter_output><empty_output><jupyter_text>Solution of linear systems: solve for $\{x\}$: $[M]\{x\} = \{b\}$<jupyter_code># Solution to system of equations (using inv is *very* inefficient)
x = np.dot(np.linalg.inv(M), b)
x<jupyter_output><empty_output><jupyter_text>Better to use the `solve` method<jupyter_code>x = np.linalg.solve(M, b)
x<jupyter_output><empty_output><jupyter_text>`sympy` `sympy` is a Computer Algebra System implemented in python.  `sympy` allows the creation of symbols and functions (among many other objects) and allows algebraic manipulation of those objects.<jupyter_code>import sympy as sp
sp.init_printing()  # allow pretty math output<jupyter_output><empty_output><jupyter_text>Variables can be designated as `Symbol`s and used in symbolic computation<jupyter_code>x = sp.Symbol("x")
y, z = sp.symbols("y z")
f = x + y * sp.pi
f<jupyter_output><empty_output><jupyter_text>Values can be substituted in place of symbols<jupyter_code>f.subs({x: 2, y: 3})<jupyter_output><empty_output><jupyter_text>Evaluate the value numerically<jupyter_code>f.subs({x: 2, y: 3}).evalf()<jupyter_output><empty_output><jupyter_text>Calculus with `sympy` Evaluate the following integral: $\int_{0}^{2} x \,dx$<jupyter_code>expr = sp.integrate("x", ("x", 0, 2))
expr<jupyter_output><empty_output><jupyter_text>Lets compare with `numpy`'s built in `trapz` method<jupyter_code>func = lambda x: x
xvals = np.linspace(0, 2, 5)
func_vals = np.array([func(x) for x in xvals])
a = np.trapz(func_vals, x=xvals)
a
expr.evalf() == a<jupyter_output><empty_output><jupyter_text>Differential Equations with Sympy Find $u(x)$ for $u'(x) + u(x) = x$, with $u(0) = 3$.  Plot the solution on $x\in[0,10]$<jupyter_code>x = sp.Symbol("x")
u = sp.Function("u")

# recast equation so that all terms appear on left hand side
ics = {u(0): 3}
de = sp.diff(u(x), x) + u(x) - x

gen_sol = sp.dsolve(de, u(x)).rhs
spec_sol = sp.dsolve(de, u(x), ics=ics, simplify=False)
gen_sol
spec_sol<jupyter_output><empty_output><jupyter_text>Find the coefficients<jupyter_code>coeffs = sp.solve([gen_sol.subs(x, 0) - 3], "C1")
sol = gen_sol.subs(coeffs)
sol<jupyter_output><empty_output><jupyter_text>Now we plot with `matplotlib`.  `matplotlib` offers plotting functionality very similar to Matlab<jupyter_code># "magic" command below allows inline printing of plots
import matplotlib.pyplot as plt
%matplotlib inline
dx = .25
xvals = np.arange(0, 10, dx)
sol_vals = [float(sol.subs({x: xval}).evalf()) for xval in xvals]
plt.plot(xvals, sol_vals)<jupyter_output><empty_output><jupyter_text>Numeric Solution Let's now find $u(x)$ numerically using finite differencing.The forward difference operator is$\frac{df}{dx} \approx \frac{f(x + \Delta{x}) - f(x)}{\Delta{x}}$Substituting the finite difference relation for $u'$ gives$u(x + \Delta{x}) = u(x)(1 - \Delta{x}) + x \Delta{x}$Since $u(x=0)$ is known ($u(0) = 3$), we are now ready to solve for $u(x + \Delta{x})$ iteratively<jupyter_code># dx and xvals defined above

u = [3.]
for x in xvals[1:]:
    u.append(u[-1] * (1 - dx) + x * dx)
u = np.array(u)<jupyter_output><empty_output><jupyter_text>Comparison<jupyter_code># Evaluate solution over interval [0, 10] and plot
plt.plot(xvals, sol_vals, label="Analytic")
plt.plot(xvals, u, "r+", label="Approximate")
plt.xlabel("x")
plt.ylabel("u(x)")
plt.legend(loc="best");<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0959621773575262, 'fraction_numerical': 0.008050089445438283, 'mean_word_length': 5.23943661971831, 'pattern_counts': {'":': 6, '<': 242, '<?xml version=': 0, '>': 234, 'https://': 2, 'lorem ipsum': 0, 'www.': 7, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '58', 'dir': 'jupyter-structured-clean-dedup', 'id': '256374', 'license': 'MIT', 'n_tokens_mistral': 7423, 'n_tokens_neox': 7104, 'n_words': 2844, 'path': 'Lessons/Lesson02_IntroductionToPython.ipynb', 'repo_name': 'jzw0025/fem-with-python', 'size': '23482'}