Introduction to Python and IPython using Jupyter

Functions¶

Many times we need to perform computations or things Python does not know how to do natively.

Other times, we want the same piece of code to work with other inputs (data, values, etc.)

What can we do?

Solution: Write a function¶

There are two ways of defining functions in Python:

  1. Using anonymous functions via the lambda notation:
    my_function = lambda x: some_computations_based_on_x
    
  • lambda notation is useful to create functions on the go, especially when passing a function to others. We'll see examples and use it quite a bit later when we go into data analyses.
  • A lambda function can take any number of arguments, but can only have one expression, i.e., you cannot perform complex computations.
  1. Using the def statement:
    def my_function(x, y, z=Z):
     '''
     Explanation of what the function does
     What are the inputs:
     x: variable to be passed
     y: variable to be passed
     z: variable to be passed, Z is the default value
     '''
     perform_computations
     return some_value_or_computation
    
  • Functions defined with the def statement can take any number of arguments, assign default values to them, and can have as many expressions as needed.
Note: There is special horizontal spacing below the line that starts with def and all lines below it are aligned in the def statement. This is similar to the other statements we have seen so far.

Example¶

Let's create a function that computes the square of a number. Let's do it first using an anonymous function.

In [1]:
my_square = lambda x: x**2
In [2]:
my_square(4)
Out[2]:
16
In [3]:
my_square(9)
Out[3]:
81

Now, let's create the same function using the def statement, which we will call create/write a function as opposed to an anonymous function.

In [4]:
def my_square_fn(x):
    '''
    This function take an input x and return x^2.
    Parameters:
    -----------
    x: float, integer or any other number
    
    Returns:
    --------
    out = x**2
    
    Example:
    >>> out = my_square_fn(4)
    >>> out
        16
    '''
    return x**2
In [5]:
my_square_fn(4)
Out[5]:
16
In [6]:
my_square_fn?

Some useful functions in Python¶

  • range(from, to, step) creates a sequence of numbers from from to to increasing by step
In [7]:
list(range(2, 20, 3))
Out[7]:
[2, 5, 8, 11, 14, 17]
In [8]:
list(range(5))
Out[8]:
[0, 1, 2, 3, 4]
  • print(string) prints a string
In [9]:
print('The square of 100 is', my_square(100))
print('The square of 100 is ' + str(my_square(100)))
The square of 100 is 10000
The square of 100 is 10000
  • len(x) returns the length of element x
In [10]:
len(range(100))
Out[10]:
100
In [11]:
len('Some random string ndsyafsdasdwne')
Out[11]:
33
  • type(x) returns the type of x
In [12]:
type('hello')
Out[12]:
str
In [13]:
type(3.1415)
Out[13]:
float
In [14]:
type(10)
Out[14]:
int
In [15]:
type([1, 'a', 3])
Out[15]:
list

But what if Python does not have the function we need?¶

Do we need to write all the functions?¶

Luckily...No¶

Extending Python's Functionality with Packages¶

One of the reasons people like Python is that there are lots of ready to use functions and solutions out there

X-files

In order to use a package/module in Python or IPython, say mypackage, you need to import it, by executing

import mypackage

After executing this command, you will have access to the functions and objects defined in mypackage. For example, if mypackage has a function squared that takes a real number x and computes its square, we can use this function by calling mypackage.squared(x).

Since the name of some packages might be too long, your can give them a nickname by importing them instead as

import mypackage as myp

so now we could compute the square of x by calling myp.squared(x).

Sometimes we want to import only a function or a subpackage, in which case we use

from mypackage import mysubpackage as mysp
from mypackage import function

We will use various packages that will be useful to do computations, statistics, plots, data management, etc.

Managing the Operating System with os¶

os is the fundamental module to interact with your computer's operating system.

With os you can:

  • Get environment variables
  • Verify if a file or directory exists
  • Change directories
  • Create a file or directory
  • Delete files or directories
In [16]:
import os

Get the home directory of current user¶

In [ ]:
os.getenv('HOME')

Verify if pics directory exists¶

In [17]:
os.path.exists('pics')
Out[17]:
True

Plots with¶

matplotlib

Matplotlib is the fundamental package for plotting in Python.

All other plotting packages are built on top of it and expand it.

You can find examples of plots in their Gallery.

It is the norm to import matplotlib and its pyplot component as follows (and I suggest you do the same):

import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib matlab_type

where matlab_type can be inline or widget.

widget provides interactive plots, while inline generates static plots.

In [18]:
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib widget

Let's plot a line connecting a few points in a plot

In [19]:
x = [0, 1, 2, 3]
y = [my_square(i) for i in x]
plt.plot(x, y, c='r', marker='o', linestyle=':', label=r'$y=x^2$')
plt.legend()
plt.show()
Figure

Let's plot various lines and points with different colors and styles, and add labels etc.

In [20]:
x = [i/10 for i in range(0, 100)]
y = [i**2 for i in x]
z = [100 - 1/4 * i**2 + i for i in x]
In [21]:
fig, ax = plt.subplots(figsize=(8,5))
ax.plot(x, y, c='r', marker='o', markersize=4, linestyle='-.', label=r'$y=x^2$')
ax.plot(x, z, c='b', marker='D', markersize=4, linestyle='-', label=r'$y=100-\frac{1}{4}x^2 + x$')
ax.set_xlabel(r'$x$', fontsize=18)
ax.set_ylabel(r'Value', fontsize=18)
ax.set_title(r'Some Plots', fontsize=24)
ax.legend(fontsize=18)
plt.savefig('./pics/some-plots.png')
plt.savefig('./pics/some-plots.pdf')
plt.show()
Figure

Numerical Computations with NumPy
¶

NumPy

NumPy is one of the fundamental packages for scientific computing with Python.

It provides many functions, objects, elements, etc. for doing all kinds of numerical computations, e.g.,

  • Constants: Euler, Pi
  • Matrices, vectors, arrays
  • Functions: Logarithms, roots, inverses
  • Random number generators
  • Optimization algorithms

It is the norm to import NumPy as follows (and I suggest you do the same):

import numpy as np
In [22]:
import numpy as np

Once it is imported we can start doing computations with it.

The main type of object we will use is a NumPy array, which we create by

my_array = np.array(list_of_numbers)

e.g.,

In [23]:
c = [1, 2]
d = [[1, 2], [3, 4]]
ca = np.array(c)
da = np.array(d) 
In [24]:
ca
Out[24]:
array([1, 2])
In [25]:
da
Out[25]:
array([[1, 2],
       [3, 4]])

With our arrays we can easily perform computations on each element.

In [26]:
ca**2
Out[26]:
array([1, 4])
In [27]:
da * 3
Out[27]:
array([[ 3,  6],
       [ 9, 12]])
In [28]:
ca*da
Out[28]:
array([[1, 4],
       [3, 8]])

Arrays also have their own functions and properties

In [29]:
print(ca.shape)
print(da.shape)
(2,)
(2, 2)
In [30]:
print('da=', da)
print('Sum of all elements da.sum()=', da.sum())
print('Sum of all elements in each column da.sum(axis=0)=', da.sum(axis=0))
print('Sum of all elements in each row da.sum(axis=1)=', da.sum(axis=1))
print('dot product is ca.dot(da)=', ca.dot(da))
da= [[1 2]
 [3 4]]
Sum of all elements da.sum()= 10
Sum of all elements in each column da.sum(axis=0)= [4 6]
Sum of all elements in each row da.sum(axis=1)= [3 7]
dot product is ca.dot(da)= [ 7 10]

We can create special arrays/matrices using Numpy's functions and classes¶

In [31]:
print(np.ones((3,4)))
print(np.zeros((2,2)))
print(np.eye(2))
print(np.ones_like(ca))
[[1. 1. 1. 1.]
 [1. 1. 1. 1.]
 [1. 1. 1. 1.]]
[[0. 0.]
 [0. 0.]]
[[1. 0.]
 [0. 1.]]
[1 1]

Random Numbers with np.random¶

Uniform distribution np.random.uniform(low, high, size)¶

In [32]:
np.random.uniform(-1,1,10)
Out[32]:
array([ 0.08183555, -0.90068916, -0.3861001 ,  0.24515804, -0.07788098,
       -0.24446539, -0.24307422,  0.34988979,  0.12040606, -0.86218082])

Normal Distribution np.random.normal(loc, scale, size)¶

In [33]:
np.random.normal(10, 1, size=(3,3))
Out[33]:
array([[ 9.86381478,  9.19635859,  9.51404729],
       [ 8.73820829, 10.38539136,  9.95531535],
       [10.33150078,  9.17957557,  8.48430286]])

Example¶

Let's create a simple random walk and plot it. A random walk is a variable $x_t$ that satisfies $$ x_{t+1} = a_0 + x_t + \varepsilon_t $$ where $a_0$ is some real number, known as drift, and $\varepsilon_t$ is a random variable that is Normally distributed with mean 0 and standard deviation $\sigma^2$, i.e., $$ \varepsilon_t\sim\mathcal{N}(0,\sigma^2). $$ For simplicity, let's assume the drift $a_0=0$.

In [34]:
#np.random.seed(123456)
x0 = 0
x = [x0]
[x.append(x[-1] + np.random.normal() ) for i in range(500)]
fig, ax = plt.subplots(figsize=(8,5))
ax.plot(x)
plt.title(r'A simple random walk', fontsize=24)
plt.xlabel(r'Period $t$', fontsize=18)
plt.ylabel(r'$x_t$', fontsize=18)
plt.show()
Figure
Exercise 1: Write a function that computes the level of output generated by a constant returns to scale Cobb-Douglas production function, i.e., such that it computes $A\cdot K^\alpha \cdot L^{1-\alpha}$.
In [35]:
def CobbDouglas(K=1, L=1, A=1, alpha=0.3):
    '''
    This function computes the level of output of a Cobb-Douglas production function.
    Given values of K, L, A, and alpha it returns $A\cdot K^\alpha \cdot L^{1-\alpha}$.
    Parameters:
    -----------
    K: float, integer or any other number, default=1
    L: float, integer or any other number, default=1
    A: float, integer or any other number, default=1
    alpha: float between (0,1), default=0.3
    
    Returns:
    --------
    out = A * K**alpha * L**(1-alpha)
    
    Example:
    >>> out = CobbDouglas(4, 9, 2, 1/2)
    >>> out
        12
    '''
Exercise 2: Use the function you created and plot the production function as a function of $K$ for given values of $A$ and $L$. Hint: Use the np.linspace(0, 5, 100) function to create an array of values of $K$ with 100 point between 0 and 5.
Exercise 3: Show with a plot the effect of increasing $A$ from 1 to 2, 4, or 5.
Exercise 4: Show with a plot the effect of increasing $L$ from 1 to 2, 4, 5.
Exercise 5: Save the previous plots into png, jpeg, and pdf files using the plt.savefig function.

Notebook written by Ömer Özak for his students in Economics at Southern Methodist University. Feel free to use, distribute, or contribute.