#!/usr/bin/env python
# coding: utf-8
# # 1 | BASIC MATHEMATICS USING PYTHON
# >Dr J H Klopper
# >Department of Biostatistics and Bioinformatics
# >Milken Institute School of Public Health
# >George Washington University
# This chapter of Algebra for Health Data Science by Dr JH Klopper is licensed under Attribution-NonCommercial-NoDerivatives 4.0 International![](https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1)
![](https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1)
![](https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1)
![](https://mirrors.creativecommons.org/presskit/icons/nd.svg?ref=chooser-v1)
# - Watch the pencil and paper video lecture [HERE](https://youtu.be/FQwnkxolP_4)
#
# - Watch the Python video lecture [HERE](https://youtu.be/wwBoL0wbxXg)
# ## 1.1 Packages used in this chapter
# While there are many functions in Python, we can extend the language with packages that contains even more functions.
# The `sympy` package, short for _symbolic Python_ is a Python package that contains functions for symbolic mathematics. This means that we can use Python to do mathematics in the same way that we would do it by hand. The `sympy` packages turns Python into a computer algebra system (CAS).
# The import keyword is used to import packages into Python. It is typical to use an abbreviation for packages and use the code `import sympy as sym`. This means that we can use the abbreviation `sym` to refer to the `sympy` package and call functions in the package such as `sqrt` (the square root) by typing `sym.sqrt`. By not using an abbreviation, we will have to type `sympy.sqrt` every time we want to use the square root function. The choice is a matter of style.
# In[2]:
import sympy
# The `init_printing` function is used to make the output of the `sympy` package look like typeset mathematics. This is because it use LaTeX to print proper mathematical notation. The `init_printing` function has several arguments, which we will not use here.
# In[3]:
sympy.init_printing()
# Python has a built-in package for mathematics called `math`. The `math` package does not use symbolic mathematics, but rather numerical mathematics. This means that the `math` package will give us numerical answers, while the `sympy` package will give us symbolic answers.
# In[4]:
import math
# The `matplotlib` package is a large Python packages used for generating plots. Here we use one of the models in the package called `pyplot`.
# In[5]:
from matplotlib import pyplot
# Monitors with high-resolution screen such as those found on Apple computer can print plotrs with more detail. To make use of the detail, we use a magic command as is shown below.
# In[6]:
get_ipython().run_line_magic('config', "InlineBackend.figure_format = 'retina'")
# While not the focus of this course, we will perform some statistical tests as motivation for the algebra that we want to explore. The `stats` module in the `scipy` package contains functions for statistical tests. We will use the `ttest_ind` function from this module to perform a _t_-test.
# In[7]:
from scipy.stats import ttest_ind
# Lastly, we import the `numpy` package, short for numerical Python. This package is used for numerical mathematics and is used to create arrays. Arrays are similar to lists, but are more efficient for numerical calculations.
# In[8]:
import numpy
# ## 1.2 Introduction
# We use numbers every day of our lives. We count objects and we consider costs and spending. While counting usually involves the use of whole numbers, we also consider parts of the whole when we cut a pizza into slices. Pizza slices are fractions of the whole and we pay for the pizza using decimal numbers. Numbers are everywhere in the life sciences. We count, add, multiply, calculate means and standard deviations. The list is endless.
# Computers and most computer languages can do mathematics. However, they do not do mathematics in the same way that we do mathematics by hand. Computers use numerical mathematics, which means that they use numbers to represent quantities. This is different from symbolic mathematics, which means that we use symbols to represent quantities. For example, we use the symbol $\pi$ to represent the ratio of the circumference of a circle to its diameter and we use the symbols $x$ and $y$ to denote unknowns, and $f$ and $g$ to denote fuctions. With `numpy` and `sympy` Python can do both and we will explore how Python works with numbers and mathematical symbols.
# In this textbook, we explore mathematics, and algebra in particular. While being able to use a pencil and paper is part of the visceral experience and understanding of mathematics, we live in an era of powerful computers in our pockets and software on our computers. The calculations in these notebooks will be laid out in detail. We will, though, also make use of modern technology to gain a level of familiarity. We will use technology to verify our results or to simply do the calculations for us. We will use modern technology as a tool to learn about and understand the mathematics. Technology is a companion to our pencils and paper.
# Our technology partner will be the Python programming language. This is not a set of notebooks on Python and the material will not teach you how to become a Python programmer. It will simply serve as our calculator instead of the calculator on our phones. Along the way, though, we will come to realize how Python can help us build our intuition for the mathematics and how it helps us understand the mathematics. Python will be our teaching aid when we need it.
# In this chapter, we explore basic number types and arithmetic as a foundation to exploring the use of algebra in public health and biomedical research in later notebooks. A proper foundation in algebra can be an enjoyable and simple task. Being comfortable with algebra will allow everyone who goes on to study statistics, to concentrate on the understanding of data and its analysis instead of being _afraid of_ or _sparring with_ the mathematics.
# ## 1.3 Number types
# ### 1.3.1 Computer number types
# In Python we can refer to any item as an object. There are many object types in Python. Not all of these types are numbers.
# We use the `type` function to return the type of a Python object. Strings are not numbers or mathematical symbols. They are letter, words, sentences, and paragraphs of text. Strings are denoted by their enclosure in single or double quotes. Below we pass the string `'This is Python.'` to the `type` function.
# In[9]:
type('This is Python.')
# Python returns the value `str` to indicate that the object is a string. Next, we pass the number $1$ to the `type` function.
# In[10]:
type(1)
# Python returns the value `int` to indicate that the object is an integer. Integers are whole numbers. We can also pass the number $1.0$ to the `type` function.
# In[11]:
type(1.0)
# We see that $1.0$ is of type `float`. This is short for floating point number. Floating point numbers are numbers with a decimal point. We can also pass the number $1j$ to the `type` function.
# In[12]:
type(1j)
# The `1j` symbol is a complex number. Complex numbers are numbers with an imaginary part. The imaginary part is denoted by the symbol $i$ or $j$. In Python, the symbol $j$ is used to denote the imaginary part of a complex number. The symbol $i$ is used to denote the imaginary part of a complex number in mathematics.
# Boolean values are either `True` or `False`. We can pass the boolean values `True` and `False` to the `type` function.
# In[13]:
type(True)
# Python returns the value `bool` to indicate that the object is a Boolean value.
# In[14]:
type(False)
# `False` is also a Boolean value as indicated by the returned `bool` type.
# `True` and `False` in Python are stored as $1$ and $0$ respectively. We can add `True` and `False` values together.
# In[15]:
True + True
# In[16]:
False + False
# In[17]:
True + False
# We can also pass the value `None` to the `type` function.
# In[18]:
type(None)
# `None` indicates that the object has no value and has the specific type `NoneType`. This is different from `False`, which is a Boolean value.
# ### 1.3.2 Mathematical number types
# In mathematics we put the elements of a set in curly braces, $\{ 1,2,3,4, \ldots \}$. A __set__ is any collection of objects, such as all the toppings on a pizza. With respect to numbers, the elememts are just that, numbers Each member (number) is separated by a comma. We also see a set of ellipses, $\ldots$. Intuitively, we read this as a continuation along, or, following the pattern of elements, before the ellipses. This indicates a continuation of the numbers. The numbers never stop. The $\ldots$ at the end of the list of elements indicate an infine extension of values. The numbers go to infinity. We refer to the number of elements in a set as the __cardinality__ or __size__ of the set.
# In this section, we investigate the commonly used number system in mathematics.
# Consider a list of the smoking status of participants in a study. In the table below, we note $10$ participants. Each participant has either _never smoked_, _smoked in the past_, or _currently smokes_.
# | Participant | Smoking status |
# |:------------|:-------------------|
# | 1 | Currently smokes |
# | 2 | Never smoked |
# | 3 | Smoked in the past |
# | 4 | Never smoked |
# | 5 | Never smoked |
# | 6 | Smoked in the past |
# | 7 | Smoked in the past |
# | 8 | Smoked in the past |
# | 9 | Currently smokes |
# | 10 | Currently smokes |
# We can count the number of occurrences of each smoking status. We can count the number of participants who _never smoked_ by counting the number of times the smoking status is _never smoked_. We can count the number of participants who _smoked in the past_ by counting the number of times the smoking status is _smoked in the past_. Lastly, we can count the number of participants who _currently smoke_ by counting the number of times the smoking status is _currently smokes_. From the data we note that $3$ participants _never smoked_, $4$ participants _smoked in the past_, and $3$ participants _currently smoke_. We cannot have that three-and-half participants _never smoked_, nor can we have a negative number of people who _cuttenrly smoke_. Each count is a counting number.
# __Definition 1.3.2.1__ The __counting numbers__, symbolized by $\mathbb{N}$, are the set of numbers $\{ 1,2,3,4,\ldots \}$.
# To accomodate the case where we count a zero number of participants for a specific smoking status, we add the number $0$ to the set of counting numbers. We call this set of numbers the natural numbers.
# __Definition 1.3.2.2__ The __natural numbers__, symbokized by $\mathbb{N}_{0}$ are the set of numbers $\{ 0,1,2,3,4,\ldots \}$.
# In many cases, we want to consider a range of natural numbers. Age is a good example in public health and biomedical research, that is, caputuring peoples’ age in years. In fancy terms, we would say the unit of measurement of age is years.
# We consider a baby in its first year of life as being $0$ years old. Most humans do not live beyond $100$ years of age, let alone an infinite number of years. Maybe one day. As an android. Imagine that. Age can be expressed as a natural number in an interval. An interval (of ages) has a minimum and a maximum value. The minimum and maximum values for a set of age values are often quoted in research papers. In the set $\{ 1,2,3, \ldots , 10 \}$ we use ellipses do denote the we follow the _pattern_ up until a maximum number. In this case, $10$.
# To sound like mathematicians, we note that there is an order to the symbols that we use to denote numbers. We know that $5$ is larger than $2$. In fact ordering is an imporant and sophisticated topic in math. Even so, we have a natural intuition for order and make use of it all the time.
# It is typical to use a lowercase letter as a symbol for an arbitrary number. For natural numbers, we typically use the symbols $i$, $j$, $k$, and $n$. In human research studies, we have a number of participants in a study. This number of people is referred to as the sample size. In many calculations, we would use the symbols $n$ to denote a sample size. The symbol $n$ takes on a specific value depending on how many people are in our study.
# Finally, we visualize the natural numbers (at least the first few) as markers on the horizontal axis of the familiar Cartesian plane (mutually perpendicular lines), shown in __Figure 1.3.2.1__.
# In[19]:
x = range(10)
y = numpy.repeat(0, 10)
pyplot.figure(figsize=(8, 2))
pyplot.scatter(
x=x,
y=y,
s=60,
marker='o',
alpha=.8,
lw=1
)
pyplot.grid(color='0.5')
for pos in ['right', 'top', 'bottom', 'left']:
pyplot.gca().spines[pos].set_visible(False)
# It may be useful to include what we refer to as additive inverses to the natural numbers. The additive inverse of a number is the number that we add to the number to get $0$. For example, the additive inverse of $3$ is $-3$ because $3+(-3)=0$. The additive inverse of $-3$ is $3$ because $-3+3=0$. We can extend the natural numbers to include the additive inverses of the natural numbers. We call this set of numbers the integers.
# __Definition 1.3.2.3__ The __integers__, symbolized by $\mathbb{Z}$ are the whole numbers $\{ \ldots , -3,-2,-1,0,1,2,3, \ldots \}$. The integers is the set of all natural numbers and their additive inverses. The additivive inverse of a natural number $n$ is $-n$ such that $n + (-n) = 0$.
# Arbitrary integers are usually denoted by lowercase letters such as $a$, $b$, $c$, $p$, and $q$.
# As with natural numbers, there are an infinite number of integers, and we can also specify an interval or intervals of integers.
# Consider the case where we have $100$ participants in a study and $34$ of them suffer from hypertension. We might want to express the proportion of participants who have a diagnosis of hypertensions. To do this, we introduce rational numbers.
# __Definition 1.3.2.4__ The __rational numbers__, symbolized by $\mathbb{Q}$, are defined in __Equation 1.3.2.1__.
# $$
# \mathbb{Q} \text{ is the set of all numbers of the form } \frac{a}{b} \text{ where } a,b \in \mathbb{Z} \text{ and } b \neq 0.
# \tag{Equation 1.3.2.1}
# $$
# The top number $a$ is the __numerator__ and the bottom number, $b$, is the __denominator__. Rational numbers are fractions and are often used when we express ratios, shown in __Equation 1.3.2.2__.
# $$
# \frac{a}{b} = \frac{\text{part}}{\text{whole}} = \frac{\text{numerator}}{\text{denominator}} = \text{numerator} \div \text{denominator}
# \tag{Equation 1.3.2.2}
# $$
# In the case of our $100$ participants where $34$ have hypertensions, we could state that the proportion of hypertensive participants is $34 \div 100 =0.34$. We confirm this calculation using code below.
# In[20]:
34 / 100
# The word fraction is typically reserved for cases in which the numerator is smaller than the denominator. We consider a fraction of the whole. If we have are investigating $100$ people and $53$ of them are ill, we can concern ourlselves with the fraction of the whole (all $100$ people) that are ill. Fractions are very, very usefull. In resaerch, we often use these fractions as proportions.
# The `Rational` function in the `sympy` package creates a rational number. The numerator and the denominator of the rational number are passed to the `Rational` function as arguments, separated by a comma.
# In[21]:
sympy.Rational(3, 4)
# An integer can also be expressed as a rational number, shown in __Equation 1.3.2.3__.
# $$
# 3 = \frac{3}{1} = \frac{6}{2} = \frac{9}{3} = \ldots
# \tag{Equation 1.3.2.3}
# $$
# Rational numbers can be expressed as decimal numbers and the latter can be expressed as a finite decimal number or a repeating decimal number. We see three such rational numbers in __Equation 1.3.2.4__, which is then expressed in code. They are treated as numerical value by Python when we do not use the `Rational` function from `sympy`.
# $$
# \begin{align*}
# & \frac{3}{4} = 0.75 \text{ (finite decimal digits)} \\ \\
# & \frac{1}{3} = 0.\overline{3} \text{ (repeating pattern of decimal digits)} \\ \\
# & \frac{1}{7} = 0.\overline{142857} \text{ (repeating pattern of decimal digits)}
# \end{align*}
# \tag{Equation 1.3.2.4}
# $$
# A bar is drawn over the repeating sequences of numbers. The bar is called a __vinculum__. The vinculum is used to indicate that the sequence of numbers repeats indefinitely. A finite numbert of decimal digits or an infinite number of repeating decimal digits are both hallmarks of rational numbers.
# In[22]:
# Example of a finite decimal representation of a rational number
3/4
# In[23]:
# Example of a repeating decimal number
1/3
# In[24]:
# Example of a longer repeating decimal number (142857 142857)
1/7
# In the case of the repeating decimal expansion, the repeating part is truncated in the output. As such, the decimal number is an approximation of the exact rational number as shown in __Equation 1.3.2.5__.
# $$
# \begin{align*}
# &\text{Exact value: } \frac{1}{3} \\ \\
# &\text{Numerical approximation: } 0.\overline{3}
# \end{align*}
# \tag{Equation 1.3.2.5}
# $$
# The set of rational numbers also contain the primes numbers.
# While we typically do not concern ourselves with prime nunmbers in the day-to-day analysis of data, we have to mention the prime numbers.
# __Definition 1.3.2.5__ A __prime number__ is an integer that is divisible only by itself AND $1$.
# __Definition 1.3.2.6__ A number $a$ is divisible by a number $b \ne 0$ if there exists an integer $c$ such that $a = bc$. In other words, there is no remainder after division, shown in __Equation 1.3.2.6__.
# $$
# \frac{a}{b} = c \text{ (with no remainder)}
# \tag{Equation 1.3.2.6}
# $$
# By definition, $1$ is not a prime number. The `isprime` function in the `sympy` package returns `True` if the input is a prime number and `False` otherwise.
# In[25]:
# Use sympy to detrmine if 1 is a prime number
sympy.isprime(1)
# In the code cell below, we use list comprehension to print the prime numbers between $1$ and $100$.
# In[26]:
# Use list comprehension and the isprime function to determine the prime numbers less than 100
[p for p in range(100) if sympy.isprime(p)]
# This brings us to our first exmaple problem.
# __Problem 1.3.2.1__ Determine if $997$ is prime.
# We use the `isprime` function in `sympy` and pass the number $997$ as an argument.
# In[27]:
sympy.isprime(997)
# We see that $997$ is indeed a prime number.
# __Homework 1.3.2.1__ Determine if $1001$ is prime.
# __Defintion 1.3.2.7__ A __composite number__ is an integer that is not prime number, and is therefor divisible by at least one other number other than itself or $1$.
# The number $16$ is a composite number as it is divisible by $2$ and $8$ and $4$, as well as $1$ and $16$.
# In[28]:
# Determine if 16 is a prime number
sympy.isprime(16)
# The `factorint` function in the `sympy` package returns the prime factors of a number and how many times each prime factor occurs.
# In[29]:
# Determine the prime factorization of 16
sympy.factorint(16)
# The `factorint` function returns a Python dictionary object (key-value pairs). The keys are the prime factors and the values are the number of times they occur in the prime factorization of the given number. This is termed the __multiplicities__ of a prime factor. For $16$ there is only a single prime. The prime factor is $2$ and there are four $2$’s in the prime facotrization of $16$. We write $16=2^{4}$.
# __Defintition 1.3.2.8__ The __prime factorization__ of a number is the product of its prime factors.
# __Defintion 1.3.2.9__ The __multiplicity__ of a prime factor is the number of times the prime factor occurs in the prime factorization of a number.
# __Homework 1.3.2.2__ Determine the prime factors of $23452$ and their multiplicities.
# __Definition 1.3.2.10__ The set of __irrational numbers__ are the set of numbers that canot be expressed as a ratio of two integers.
# In[30]:
# Example of an irrational number
sympy.sqrt(2)
# The decimal approximation of $\sqrt{2}$ is calculated below using the `evalf()` method.
# In[31]:
# Decimal approximation of square root of 2
sympy.sqrt(2).evalf()
# There is no termination to, and no repeating pattern in, the decimal expansion of $\sqrt{2}$, making it an irrational number. If a decimal expansion of a number does not terminate or repeat, then it is irrational.
# The number $\pi$ is also an irrational number. The `pi` function from the `sympy` package returns the value of $\pi$. The `evalf()` method returns the decimal approximation of $\pi$ to the specified number of digits.
# In[32]:
# Print the number pi
sympy.pi
# In[33]:
# Print the value of pi with 50 digits
sympy.pi.evalf(50)
# So much of the data that we collected are real numbers, which we define in __Definition 1.3.2.9__.
# __Defintion 1.3.2.11__ The __real numbers__, $\mathbb{R}$, are the union (combination) of the rational and irrational numbers.
# While we are often constrained by the accuracy of our measuring instruments, we can consider the real numbers to be continuous. So, if we state that a person is $6$ foot and $6$ inches tall, it is only an approximation. The person (well me, since I am $6' \, 6''$ tall) is not exactly $6$ foot and $6$ inches tall. The person is $6$ foot and $6$ inches tall to the accuracy of our measurement accuracy.
# The real numbers can be represented as a point on the number line. There are an infinite number of real numbers. In many cases, most of the statistical analysis that we do in public health and in biomedical research involve real numbers. We generate some pseudo-random numbers below and assign them to the computer variable `x`
# In[34]:
# Seed the pseudorandom number generator with the integer 1
numpy.random.seed(1)
# Create a variable called x that holds an array of 10 real numbers from the standard normal distribution
x = numpy.random.randn(10)
x
# __Figure 1.3.2.1__ shows the number line with the real numbers $x$.
# In[35]:
# Plot the values in the array x as markers on the x-axis
pyplot.figure(figsize=(10, 2))
pyplot.scatter(
x,
[0,0,0,0,0,0,0,0,0,0],
color='red',
marker='o',
s=100,
alpha=0.5,
label='x'
)
pyplot.grid()
pyplot.xlim(-3, 3)
pyplot.hlines(0, -3, 3, color='black')
pyplot.gca().spines['top'].set_visible(False)
pyplot.gca().spines['right'].set_visible(False)
pyplot.gca().spines['bottom'].set_visible(False)
pyplot.gca().spines['left'].set_visible(False)
pyplot.title('Figure 1.3.2.1');
# ## 1.4 Simple arithmetic
# ### 1.4.1 Rounding
# We often find that the results of statistical calculations contain values with many decimal places and we need to round these. Look at the _p_ value for the equal variance _t_ test below.
# In[36]:
# Seed the numpy pseudorandom number generator with the integer 2
numpy.random.seed(2)
# Create a variable named groupA that holds an array of 100 real numbers from a normal distribution with a mean of 100 and a standard deviation of 10
groupA = numpy.random.normal(100, 10, 100)
# Create a variable named groupB that holds an array of 100 real numbers from a normal distribution with a mean of 101 and a standard deviation of 9
groupB = numpy.random.normal(101, 9, 100)
# Conduct an equal variance t test comparing the values in groupA to those in group Band show the p-value
ttest_ind(groupA, groupB, equal_var=True)[1]
# We would typically round this _p_ value to two decimal places and write $0.03$.
# The `round` function rounds a number to a specified number of decimal places. By default, the number fo decimal places is zero, but the function will add a single decimal place with a value of $0$ to the output.
# In[37]:
# Round a number to the nearest whole number
round(10.4)
# In[38]:
# Round a number to the nearest whole number
round(10.51)
# A decimal digit with a value $5$ and up leads to the preceding digit being rounded up and a decimal value with a value of $4$ and down leads to the preceding decimal digit being rounded down.
# Below, we round the _p_ value from our equal variance _t_ test to two decimal places.
# In[39]:
# Round the number 10.59 to the nearest tenth (a single decimal place)
round(0.0285355177361664, 2)
# __Homework 1.4.1.1__ Round $3.666666666667$ to two decimal places.
# In our solution the value $0.0285355177361664$ was printed to the screen. The decimal digits $0$ and $2$ are in the _first two places_. The next digit is an $8$, which is $5$ or more. As such, we round the $2$ up to a $3$ and the output is $0.03$.
# Rounding to the nearest integer is not always desired. In many statistical tests we calculate the degrees of freedom. Below, we calculate the degrees of freedom for an unequal variance _t_ test. (Ignore the code and consider the results.)
# In[40]:
# Seed the numpy pseudorandom number generator with the integer 2
numpy.random.seed(2)
# Create a variable named groupC that holds an array of 100 real numbers from a normal distribution with a mean of 101 and a standard deviation of 20
groupC = numpy.random.normal(101, 20, 100)
# Conduct an unequal variance t test comparing the values in groupA to those in group C and show the degrees of freedom
ttest_ind(groupA, groupC, equal_var=False)
# We note that the degrees of freedom is $145.58823529411765$. In the case of degrees of freedom, we want to round down.
# The `math` package has a `floor` function that rounds a number down to the nearest integer. The `ceil` function rounds a number up to the nearest integer.
# In[41]:
# Round down the number 145.58823529411765to the nearest whole number
math.floor(145.58823529411765)
# We would state that the _T_ statistic has $145$ degrees of freedom.
# As example, we also show the `ceil` function for the sake of completeness.
# In[42]:
# Round up the number 10.1 to the nearest whole number
math.ceil(10.1)
# __Homework 1.4.1.2__ Determine the ceiling an the floor of $\pi$.
# ### 1.4.2 Addition
# Arithmetic is the branch of mathematics that deals with the study of numbers and their operations. The four basic operations are addition, subtraction, multiplication, and division. We will explore each of these operations in turn.
# The `+` symbol placed between two numbers add the numbers in Python. Below we calculate $3+4$.
# In[43]:
# Adding 3 and 4
3 + 4
# More than two numbers can be added. We calculate $3+4+5$.
# In[44]:
# Adding 3 and 4 and 5
3 + 4 + 5
# __Problem 1.4.2.1__ Calculate $3+4+5+6+7+8+9+10$.
# We use the `+` symbol to add the numbers.
# In[45]:
3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
# __Homework 1.4.2.1__ Calculate $1+2+3+4+5+6+7+8+9+10$.
# ### 1.4.3 Subtraction
# The `-` symbol placed between two numbers subtracts the second number from the first number in Python. We calculate $3-4$.
# In[46]:
# Subtracting 4 from 3
3 - 4
# More than one number can be subtracted. We calculate $23-6-10$.
# In[47]:
# Subtracting 6 and 10 from 23
23 - 6 - 10
# __Problem 1.4.3.1__ Calculate $23-6-10-4-5-6-7-8-9-10$.
# We use the `-` symbol to subtract the numbers.
# In[48]:
23 - 6 - 10 - 4 - 5 - 6 - 7 - 8 - 9 - 10
# __Homework 1.4.3.1__ Calculate $100-1-2-3-4-5-6-7-8-9-10$.
# ### 1.4.4 Multiplication
# The `*` symbol between two numbers multiplies the numbers in Python. We calculate $3 \times 4$.
# In[49]:
# Mutiplying 3 and 4
3 * 4
# More than two numbers can be multiplied. We calculate $3 \times 4 \times 2$.
# In[50]:
# Multiplying 3 and 4 and 2
3 * 4 * 2
# __Problem 1.4.4.1__ Calculate $3 \times 4 \times 2 \times 5 \times 6 \times 7 \times 8 \times 9 \times 10$.
# We use the `*` symbol to multiply the numbers.
# In[51]:
3 * 4 * 2 * 5 * 6 * 7 * 8 * 9 * 10
# __Homework 1.4.4.1__ Calculate $1 \times 2 \times 3 \times 4 \times 5 \times 6 \times 7 \times 8 \times 9 \times 10$.
# ### 1.4.5 Division
# The forward slash `/` symbol between two numbers divides the first number by the second number in Python. We calculate $10$ divided by $2$.
# In[52]:
# Dividing 10 by 2
10 / 2
# Note that we can write division using the $\div$ symbols as $10 \div 2$. We can also use the notation in __Equation 1.4.5.1__.
# $$
# \frac{10}{2}
# \tag{Equation 1.4.5.1}
# $$
# In the notation used in __Equation 1.4.5.1__, the number above the line is called the __numerator__ and the number below the line is called the __denominator__.
# __Problem 1.4.5.1__ Calculate $10$ divided by $2$ divided by $5$.
# We use the `/` symbol to divide the numbers.
# In[53]:
10 / 2 / 5
# __Homework 1.4.5.1__ Calculate $100$ divided by $2$ divided by $5$.
# ### 1.4.6 Powers
# We use short-hand notation when we want to multiply a number by itself a number of times. Instead of writing $2 \times 2 \times 2$, which is $2$ multiplied by itself $3$ times, we write $2^{3}$, placing the $3$ as a superscript after the $2$. This short-hand notation shows $2$ raised to the power $3$, which simply means $2$ multiplied by itself $3$ times.
# Th double star symbol `**` between two numbers raises the first number to the power of the second number in Python. We calculate $2$ raised to the power of $3$ or $2 \times 2 \times 2$.
# In[54]:
# 2 to the power of 3
2 ** 3
# __Problem 1.4.6.1__ Calculate $2$ raised to the power of $4$.
# __Equation 1.4.6.1__ shows the general form of the power in this problem.
# $$
# 2^{4} = 2 \times 2 \times 2 \times 2 = 16
# \tag{Equation 1.4.6.1}
# $$
# __Homework 1.4.6.1__ Calculate $2$ raised to the power of $5$.
# __Problem 1.4.6.2__ Calculate $3$ raised to the power of $2$ raised to the power of $3$.
# __Equation 1.4.6.2__ shows the mathematical notations for this problem.
# $$
# 3^{2^{3}} = 3^{2 \times 2 \times 2} = 3^{\left( 2^{3} \right)} = 3^{8} = 6561
# \tag{Equation 1.4.6.2}
# $$
# We use the `**` symbol to raise the first number to the power of the second number.
# In[55]:
3**2**3
# ### 1.4.7 Square root
# Consider the number resulting form $3 \times 3$. The result is $9$. We state that the square roots of $9$ is $3$.
# __Definition 1.4.7.1__ The __square root__ of a number $a$ is the number $b$ such that $b \times b = a$. We write $b = \sqrt{a}$.
# We can also write $\sqrt{a}$ as $a^{\frac{1}{2}}$.
# Below, we calculate the square root of $9$ using the calculation in __Equation 1.4.7.1__.
# $$
# \sqrt{9} = 9^{\frac{1}{2}}
# \tag{Equation 1.4.7.1}
# $$
# In[56]:
# Positive square root of 9
9 ** (1/2)
# We can also use the `sqrt` function in the `math` package to calculate the square root of $9$.
# In[57]:
# Positive square root of 9 using the sqrt function from numpy
math.sqrt(9)
# Only the positive square root value is returned. We do remember that $\left(-3\right)^{2}$ is also equal to $9$.
# In[58]:
# Show that -3 squared is 9
(-3) ** 2
# The `sympy` package has a `sqrt` function that calculates the square root of a number analytically. We calculate the square root of $10$ using the `sqrt` function.
# In[59]:
# Symbolic computation of the square root of 10
sympy.sqrt(10)
# Note that the exact solution $\sqrt{10}$ is returned. We can also use the `evalf` method to return the decimal approximation of the square root of $10$ in `sympy`.
# In[60]:
# Symbolic computation of the square root of 10 using numerical approximation
sympy.sqrt(10).evalf()
# __Problem 1.4.7.1__ Calculate the square root of $81$.
# We use the `sqrt` funtion in the `sympy` package to calculate the square root of $81$.
# In[61]:
sympy.sqrt(81)
# __Homework 1.4.7.1__ Calculate the square root of $100$.
# The `sympy` package can manage the square root of negative numbers. We calculate the square root of $-9$.
# In[62]:
sympy.sqrt(-9)
# We see a complex number returned. The `sympy` package uses the symbol $i$ to denote the imaginary part of a complex number.
# ### 1.4.8 Higher roots
# We can also calculate higher roots. Below we calculate the cube root of $27$, using the calculation in __Equation 1.4.8.1__.
# $$
# \sqrt[3]{27} = 27^{\frac{1}{3}}
# \tag{Equation 1.4.8.1}
# $$
# In[63]:
# Cube root of 27
27 ** (1/3)
# Next we calculate the root in __Equation 1.4.8.2__.
# $$
# \sqrt[4]{16} = 16^{\frac{1}{4}}
# \tag{Equation 1.4.8.2}
# $$
# In[64]:
# Fourth root of 16
16 ** (1/4)
# Obviously $-2$ is also a solution to $\sqrt[4]{16}$ since $(-2) \times (-2) \times(-2) \times(-2) =16$. We confirm this result using code below.
# In[65]:
# Show that (-2) to the power of 4 is 16
(-2) ** 4
# __Problem 1.4.8.1__ Calculate the cube root of $64$.
# We use power notation, `**`, in Python to calculate the cube root of $64$.
# In[66]:
64**(1/3)
# The `sympy` package contains the `root` function that calculates the $n$-th root of a number. We calculate the cube root of $64$ using the `root` function.
# In[67]:
# Calculate the cube root of 64 using sympy
sympy.root(64, 3)
# __Homework 1.4.8.1__ Calculate the cubic root of $1000$. Use pure Python (the exponentiation operator `**`), the `math` package, and the `sympy` package. Try to explain the difference in the results.
# ### 1.4.9 Absolute values
# The absolute value of a number is the distance of the number from zero on the number line. The absolute value of a number is always positive. It is written as $\lvert a \rvert$.
# The `abs()` function from the `numpy` package returns the absolute value of a number. We calculate $\lvert -3 \rvert$ below.
# In[68]:
# Asbolute value of -3
abs(-3)
# __Defintion 1.4.9.1__ The __absolute value__ of a number $a$ is defined in __Equation 1.4.9.1__.
# $$
# \lvert a \rvert = \begin{cases} a & \text{if } a \ge 0 \\ -a & \text{if } a < 0 \end{cases}
# \tag{Equation 1.4.9.1}
# $$
# Form the defintion we have see why $\lvert -3 \rvert = - (-3) = 3$.
# ### 1.4.10 Logarithm
# Logarithms are very frequenctly used in health data science. In __Figure 1.4.10.1__ we show a histogram of the age of participants in a study. (Ignore the code and consider the results.) We see that the distribution of age is skewed to the right. We can transform the data using a logarithm to make the distribution more symmetric, shown in __Figure 1.4.10.2__.
# In[69]:
# Create an array of 100 values for a variable named "age" that is right-skewed
age = numpy.random.gamma(2, 2, 100)
# Create a histogram of the values in the array Age
pyplot.figure(figsize=(10, 2))
pyplot.hist(
age,
color='red',
alpha=0.5,
bins=20
)
pyplot.grid()
pyplot.xlim(0, 20)
pyplot.gca().spines['top'].set_visible(False)
pyplot.gca().spines['right'].set_visible(False)
pyplot.gca().spines['bottom'].set_visible(False)
pyplot.gca().spines['left'].set_visible(False)
pyplot.title('Figure 1.4.10.1');
# In[70]:
# Recreate the histogram but use a logarithm transformation of the values in the array age
pyplot.figure(figsize=(10, 2))
pyplot.hist(
numpy.log(age),
color='red',
alpha=0.5,
bins=20
)
pyplot.grid()
pyplot.xlim(0, 3)
pyplot.gca().spines['top'].set_visible(False)
pyplot.gca().spines['right'].set_visible(False)
pyplot.gca().spines['bottom'].set_visible(False)
pyplot.gca().spines['left'].set_visible(False)
pyplot.title('Figure 1.4.10.2');
# The logarithm of a number is defined by a base. As an example, we consider a base $10$. We can raise $10$ to any power. As an example we have that $10^{2}=100$. For the number $100$ and the base $10$, we would state that the logarithm base $10$ of $100$ is $2$, since it is $10$ raised to the power $2$ that is $100$. For the values $a,b,c$, we state the logarithm in __Equation 1.4.10.1__.
# $$
# \text{If } a^{b}=c, \text{ then } \log_{a}{c}=b, \, \text{ for all } a>0
# \tag{Equation 1.4.10.1}
# $$
# From the defintion we note that $c>0$, since a positive value raised to any real-valued power is positive.
# The `log()` function from the `numpy` package calculates the logarithm of a number. The default base is $e$, Euler's number.
# In[71]:
# Natural log of 1000
numpy.log(1000)
# The `sympy` package can also calculate logarithms. The `log()` function from the `sympy` package calculates the logarithm of a number. The default base is $e$. Note that `sympy` returns an exact value.
# In[72]:
sympy.log(1000)
# To return a number approximation in `sympy`, we use the `evalf` method.
# In[73]:
sympy.log(1000).evalf()
# Euler's number, $e$, is approximated below using the `numpy` function `exp`. We pass the argument value $1$, since any number raised to the power $1$ is itself and so $e^{1}=e$.
# In[74]:
# Express Euler's number e as a decimal number
numpy.exp(1)
# Once again, `sympy` returns an exact value.
# In[75]:
sympy.exp(1)
# __Problem 1.4.10.1__ Calculate the logarithm base $e$ of $43545$.
# The `log` function from the `numpy` package is used to calculate this natural logarithm.
# In[76]:
numpy.log(43545)
# The `log10` function from the `numpy` package calculates the logarithm of a number with a base of $10$.
# In[77]:
# Log base 10 of 1000
numpy.log10(1000)
# __Homework 1.4.10.1__ Calculate the logarithm base $10$ of $43545$.
# __Homework 1.4.10.2__ Calculate the natural logarithm of $43545$.
# ### 1.4.11 Exponent
# Euler's number is useful when modeling growth or decay. For a simple model, we may have exponential growth at every time step $t$. The number of cases may then be as shown in __Equation 1.4.11.1__ with the number of cases at $t=0$ of $1$.
# $$
# \text{Number of organisms at time }t = e^{t}
# \tag{Equation 1.4.11.1}
# $$
# At time $t=5$, we have $e^{5}$ organisms, which is $e \times e \times e \times e \times e$, calculated below using the `exp` function in `numpy`.
# In[78]:
numpy.exp(5)
# We can use the `floor` function to round the result down.
# In[79]:
# Round exp(5) to 3 digits
math.floor(numpy.exp(5))
# Starting with $e^{0}=1$ organism at $t=0$, we have that there will be $148$ organisms as $t=5$. __Figure 1.4.11.1__ demonstrates the growth.
# In[80]:
fig, ax = pyplot.subplots()
ax.plot([0, 1, 2, 3, 4, 5],
[numpy.exp(t) for t in [0, 1, 2, 3, 4, 5]],
"b--")
ax.grid()
ax.set_title("Figure 1.4.11.1")
ax.set_xlabel("Time")
ax.set_ylabel("Number of cases")
pyplot.hlines(0, 0, 5, color='black')
pyplot.vlines(0, 0, 150, color='black')
pyplot.gca().spines['top'].set_visible(False)
pyplot.gca().spines['right'].set_visible(False)
pyplot.gca().spines['bottom'].set_visible(False)
pyplot.gca().spines['left'].set_visible(False);
# We will learn more about exponents in later chapters.
# ### 1.4.12 Order of arithmetic operations
# We will commonly use the following order of arithmetic operations, expressed using the acronymn PEMDAS.
#
# 1. __P__ arentheses
# 2. __E__ xponents (powers and roots)
# 3. __M__ ultiplication and __D__ ivision
# 4. __A__ ddition and __S__ ubtraction
# We start by calculating $3+2 \times 4$, which is $3 + (2 \times 4) = 3 + 8 = 11$.
# In[81]:
# Expression with addition and multiplication
3 + 2 * 4
# We can use parenthesis to first add $3+2$ and then multiply by $4$. This is $(3+2) \times 4 = 5 \times 4 = 20$.
# In[82]:
# Using parenthesis to change the order of operations (addition before multiplication)
(3 + 2) * 4
# In the examples below, we simplificy the indicated expressions, where the symbol $\cdot$ indicates multiplication.
# __Problem 1.4.12.1__ Calculate the expression in __Equation 1.4.12.1__.
# $$\left( 3 \cdot 2 \right)^{2} - 4 (6+2) \tag{Equation 1.4.12.1}$$
# In[83]:
(3 * 2)**2 - 4 * (6 + 2)
# __Problem 1.4.12.2__ Calculate the expression in __Equation 1.4.12.2__.
# $$\frac{5^{2} - 4}{7} - \sqrt{11-2} \tag{1.4.12.2}$$
# In[84]:
((5**2 - 4) / 7) - numpy.sqrt(11 - 2)
# __Problem 1.4.12.3__ Calculate the expression in __Equation 1.4.12.3__.
# $$6 - \lvert 5 - 8 \rvert + 3(4 - 1) \tag{1.4.12.3}$$
# In[85]:
6 - abs(5 - 8) + 3 * (4 - 1)
# __Homework 1.4.12.1__ Calculate the expression in __Equation 1.4.12.4__.
# $$
# \frac{3\pi - \lvert 1 - \sqrt{2} \rvert}{27 \times 3 - 1}
# \tag{Equation 1.4.12.4}
# $$
# ## 1.5 Properties of real numbers
# There are many useful properties of real numbers. We will explore some of these properties in this section.
# __Defintion 1.5.1__ The __commutative property of addition__ states that the order of the numbers does not matter. We have that $a+b=b+a$ for two real numbers $a$ and $b$
# We use the comparison operator `==` to test if two numbers are equal. The output is a boolean value, either `True` or `False`.
# __Problem 1.5.1__ Determine if $3+4=4+3$.
# In[86]:
# Show that 3 + 4 = 4 + 3
3 + 4 == 4 + 3
# The result is the boolean value `True`, which indicates that indeed $3+4=4+3$.
# __Defintion 1.5.2__ The __commutative property of multiplication__ states that the order of the numbers does not matter. We have that $a \cdot b = b \cdot a$ for two real numbers $a$ and $b$.
# __Problem 1.5.2__ Determine if $3 \times 4 = 4 \times 3$.
# In[87]:
# Show that 3 x 4 = 4 x 3
3 * 4 == 4 * 3
# Neither subtraction nor division is commutative. The `!=` comparison operator tests if two numbers are not equal.
# __Problem 1.5.3__ Determine if $3-4=4-3$.
# In[88]:
# Show that 3 - 4 is not equal to 4 - 3
3 - 4 != 4 - 3
# By using the _is equal to_ `==` comparison operator, we see that $3-4 \neq 4-3$, by considering that the result is `False`. The $\neq$ symbol means that the tow sides are not equal to each other.
# In[89]:
# Alternative way to show that 3 - 4 is not equal to 4 - 3
3 - 4 == 4 - 3
# __Problem 1.5.4__ Determine if $10 \div 5 = 5 \div 10$.
# We use the `/` symbol for division and consider the cases using `!=` and `==`.
# In[90]:
# Show that 10 divided by 5 is not equal to 5 divided by 10
10 / 5 != 5 / 10
# In[91]:
# Alternative way to show that 10 divided by 5 is not equal to 5 divided by 10
10 / 5 == 5 / 10
# __Defintion 1.5.3__ The __associative property of addition__ holds such that $a + (b + c) = (a + b) + c$, for three real numbers $a,b,c$.
# __Problem 1.5.5__ Determine if $3 + (4 + 5) = (3 + 4) + 5$.
# In[92]:
# Show that (3 + 4) + 5 is equal to 3 + (4 + 5)
(3 + 4) + 5 == 3 + (4 + 5)
# __Definition 1.5.4__ The __associative property of multiplication__ holds such that $a \cdot (b \cdot c) = (a \cdot b) \cdot c$, for three real numbers $a,b,c$.
# __Problem 1.5.6__ Determine if $\left( 3 \times 4 \right) \times 5 = 3 \times \left( 4 \times 5 \right)$.
# In[93]:
# Show that (3 x 4) x 5 is equal to 3 x (4 x 6)
(3 * 4) * 5 == 3 * (4 * 5)
# Associativity does not hold for subtraction or division. We see an example of subtraction below, where the `==` logical operator tests if two numbers are equal and returns a value of `False` when comparing $(10-3)-2$ and $10 - (3 - 2)$.
# In[94]:
# Show that (10 - 3) - 2 is not equal to 10 - (3 - 2)
(10 - 3) - 2 == 10 - (3 - 2)
# __Defintion 1.5.5__ The __distributive property__ holds such that $a(b + c) = ab + ac$, for three real numbers $a,b,c$. That is to say that multiplication distributes over addition. The distributive property also holds for subtraction. It does, however, not hold for division, nor for addition or subtraction over multiplication.
# __Problem 1.5.7__ Determine if $3 \left( 2 + 7 \right) = \left( 3 \times 2 \right) + \left() 3 \times 7 \right)$.
# In[95]:
# Show that 3(2 + 7) is equal to 3 x 2 + 3 x 7
3 * (2 + 7) == 3 * 2 + 3 * 7
# There is a special case of distributivity that holds for subtraction. We have that $a - (b + c) = a - b - c$, for three real numbers $a,b$ and $c$.
# __Problem 1.5.8__ Determine if $3 - \left( 2 + 1 \right) = 3 - 2 - 1$.
# In[96]:
# Show that 3 - (2 + 1) is equal to 3 - 2 - 1
3 - (2 + 1) == 3 - 2 - 1
# __Definition 1.5.6__ The number $0$ is the __additive identity__ of real numbers. We have that $a + 0 = a$ for any real number $a$.
# __Problem 1.5.9__ Determine if $5 + 0 = 5$.
# In[97]:
# Show to 5 + 0 = 5
5 + 0 == 5
# __Definition 1.5.7__ The number $1$ is the __multiplicative identity__ of real numbers. We have that $a \cdot 1 = a$ for any real number $a$.
# __Problem 1.5.10__ Determine if $5 \times 1 = 5$.
# In[98]:
# Show that 5 x 1 i s equal to 5
5 * 1 == 5
# __Defintion 1.5.8__ The __additive inverse__ of a real number $a$ is the number $-a$ such that $a + (-a) = 0$.
# __Problem 1.5.11__ Determine if $5 + \left( -5 \right) = 0$.
# In[99]:
# Show that 5 + (-5) is equal to 0
5 + (-5) == 0
# __Problem 1.5.12__ Show that $3$ is the additive inverse of $-3$.
# If $3$ is the additiive inverse of $-3$ then $3 + \left( -3 \right)$ should be equal to $0$.
# In[100]:
# Show that 3 is the additive inverse of -3
(-3) + 3 == 0
# Since the additive inverse of any number $a$ is $-a$ we have that the additive inverse of $-3$ is $- \left( -3 \right)$, which is $3$.
# __Definition 1.5.9__ The __multiplicative inverse__ of a real number $a$ is the number $\frac{1}{a}$ such that $a \cdot \frac{1}{a} = 1$ and $a \ne 0$.
# __Problem 1.5.13__ Show that $\frac{1}{5}$ is the multiplicative inverse of $5$.
# In[101]:
# Show that 5 x (1/5) is equal to 1
5 * (1/5) == 1
# ## 1.6 The uses of division
# In health data science we often have to consider values that are fractions. Fractions involve division, which we also commonly use in ratios.
# ### 1.6.1 Fractions and ratios
# A __fraction__ is a part of a whole and is calculated using division. A __ratio__ similarly involves division.
# We see both, and the fact that we accomplish both calculations by division, in __Equation 1.6.1.1__. In either case, $a$ and $b$ are integers and we always have that $b \ne 0$. We also omit the case where $a=b$, which is simply $1$. We also remeber that if $a=0$, we have that our fraction or ratio is $0$.
# $$
# \begin{align*}
# &\text{Fraction: } \frac{a}{b}, \text{ where } b \ne 0 \\ \\
# &\text{Ratio: } a:b , \text{ where we calculate } \frac{a}{a+b} \text{ and } \frac{b}{a+b} \\ \\
# &\;\;\;\text{ and } a+b \text{ is never equal to } 0
# \end{align*}
# \tag{Equation 1.6.1.1}
# It is important to understand the basics of fractions and ratios. The basics include the _mechanics_ of the parts of a fraction or a ratio and the comparison of different fractions and ratios.
# We have seen that a fraction or a ratio has a numerator and a denominator. Note that in the case of ratios we also use the terms divided or antecedent for the numerator and the term consequent for the denominator. A fraction describes a count of parts of a whole. By parts of the whole, we are referring to the denominator. The numerator describes how many of those parts we count.
# Consider any shape, such as a square or a circle. We can divide a shape into equal parts. That would be $b$ equal parts. If $b=2$ then we have divided the shape into halves. If $b=4$, then we have divided the shape into $4$ equally sized parts called quarters. This is the value of the denominator.
# The numerator, $a$, counts the number of $b$-parts of the whole. In __Figure 1.6.1.1__, we have divided the whole (of a circle) into $b=4$ equal parts (the denominator) and we consider $a=3$ of those $b=4$ parts (the numerator).
# In[102]:
pyplot.pie(
[1, 1, 1, 1],
colors=['red', 'red', 'red', 'lightgray'],
wedgeprops={"edgecolor":"k",'linewidth': 1}
)
pyplot.title('Figure 1.6.1.1')
pyplot.show()
# In __Figure 1.6.1.2__, we visualize the difference between three eights and seven eights, seen in __Equation 1.6.1.2__. We keep $b=8$ fixed and compare $a=3$ to $a=7$.
# $$
# \frac{3}{8} < \frac{7}{8}
# \tag{Equation 1.6.1.2}
# $$
# In[103]:
fig, ax = pyplot.subplots(1, 2)
ax[0].pie(
[1, 1, 1, 1, 1, 1, 1, 1],
colors=['red', 'red', 'red', 'lightgray', 'lightgray', 'lightgray', 'lightgray', 'lightgray'],
wedgeprops={"edgecolor":"k",'linewidth': 1}
)
ax[1].pie(
[1, 1, 1, 1, 1, 1, 1, 1],
colors=['red', 'red', 'red', 'red', 'red', 'red', 'red', 'lightgray'],
wedgeprops={"edgecolor":"k",'linewidth': 1}
)
ax[0].set_title('Figure 1.6.1.2')
pyplot.show()
# With a good understanding of the parts of a fraction or a ratio, we consider the _mechanics_. By the mechanics we mean, changes in the values of $a$ or $b$, where we consider a change in one, while keeping the other constant.
# If we keep $b$ constant and we increase $a$, we are counting more of the parts. The actual _overall_ value that the fraction or ratio describes gets bigger. If we decrease $a$, then we have fewer parts, and the overall value gets smaller.
# In __Figure 1.6.1.3__, we visualize the difference between three eighths (on the left) and three fifths (on the right), seen in __Equation 1.6.1.3__.
# $$
# \frac{3}{8} < \frac{3}{5}
# \tag{Equation 1.6.1.3}
# $$
# In[104]:
fig, ax = pyplot.subplots(1, 2)
ax[0].pie(
[1, 1, 1, 1, 1, 1, 1, 1],
colors=['red', 'red', 'red', 'lightgray', 'lightgray', 'lightgray', 'lightgray', 'lightgray'],
wedgeprops={"edgecolor":"k",'linewidth': 1}
)
ax[0].set_title('Figure 1.6.1.3')
ax[1].pie(
[1, 1, 1, 1, 1],
colors=['red', 'red', 'red', 'lightgray', 'lightgray'],
wedgeprops={"edgecolor":"k",'linewidth': 1}
)
pyplot.show()
# It is clear that $3$ parts of $8$ must be smaller than $3$ parts of $5$. In essence, keeping the numerator constant, the final values increases when the denominator get smaller and decreases when the demominator gets bigger.
# Understanding the mechanics, leads us to comparing fractions and ratios. When is one smaller or larger than the other?
# To answer this question, we must have equal $b$ parts (equal denominators). In __Equation 1.6.1.3__ we made such a comparison. We can make a direct comparison by _equalling_ $b=8$ and $b=5$. The easiest way to do this is to start by considering multiplying both by $1$. The number $1$ can be written as a fraction, in many ways, as we see in __Equation 1.6.1.4__, as long as $b \ne 0$.
# $$
# 1 = \frac{1}{1} = \frac{2}{2} = \frac{1206}{1206} = \frac{b}{b}
# \tag{Equation 1.6.1.4}
# $$
# Multiplying by $1$ does not change a number. All we have to do is write $1$ as a fraction. We do so my choosing the _opposite_ value of $b$, shown in __Equation 1.6.1.5__, remembering that we multiply numerator by numerator and denominator by denominator, we now have equal parts $b=40$.
# $$
# \begin{align*}
# &\frac{3}{8} \text{ vs. } \frac{3}{5} \\ \\
# &\frac{3}{8} \times \frac{5}{5} \text{ vs. } \frac{3}{5} \times \frac{8}{8} \\ \\
# &\frac{3 \times 5}{8 \times 5} \text{ vs. } \frac{3 \times 8}{5 \times 8} \text{ (see section 1.6.2 below)} \\ \\
# &\frac{15}{40} \text{ vs. } \frac{21}{40}
# \end{align*}
# \tag{Equation 1.6.1.5}
# $$
# Clearly $15 < 21$ and three eighths is smaller than three fifths.
# The `sympy` package makes such a comparison easy. We use the `Rational` function to create the fractions and the `>` and `<` symbols (which are comparison operators) to compare the fractions. We print the two fractions to the screen to see the use of the `Rational` function.
# In[105]:
# Print the rational three-eighths
sympy.Rational(3, 8)
# In[106]:
# Print the rational three-eighths
sympy.Rational(3, 8)
# In the code cell below, we ask the question: _Is three eighths smaller than three fifths?_ Note the use of the less than `<` comparison operation.
# In[107]:
sympy.Rational(3, 8) < sympy.Rational(3, 5)
# The `sympy` package returns `True` to indicate that three eighths is smaller than three fifths.
# Depending on the propblem, we can multiply both sides by $1$ expressed in smaller values for the numerator and denominator. The next problem shows the intuition.
# __Problem 1.6.1.1__ Compare the two fractions in __Equation 1.6.1.6__ and determine which is larger.
# $$
# \frac{3}{8} \text{ vs. } \frac{1}{4}
# \tag{Equation 1.6.1.6}
# $$
# The results is shown in __Equation 1.6.1.7__.
# $$
# \begin{align*}
# &\frac{3}{8} \text{ vs. } \frac{1}{4} \\ \\
# &\frac{3}{8} \times \frac{1}{1} \text{ vs. } \frac{1}{4} \times \frac{2}{2} \\ \\
# &\frac{3}{8} \text{ vs. } \frac{2}{8} \\ \\
# &\frac{3}{8} > \frac{2}{8} \\ \\
# &\frac{3}{8} > \frac{1}{4} \text{ (original problem)}
# \end{align*}
# \tag{Equation 1.6.1.7}
# $$
# Clearly, three eights is larger than two eights and therefor three-eights is larger than a quarter.
# We use `sympy` again to verify the result. Note that in this case we use the larger than `>` comparison operator.
# In[108]:
sympy.Rational(3, 8) > sympy.Rational(1, 4)
# If we use the less than `<` comparison operator (keeping the order of the fractions the same), we get `False`.
# In[109]:
sympy.Rational(3, 8) < sympy.Rational(1, 4)
# __Problem 1.6.1.1__ Is $\frac{21}{43}$ larger than $\frac{51}{100}$?
# We multiply both sides with $1$ as represented in __Equation 1.6.1.8__.
# $$
# \begin{align*}
# &\frac{21}{43} \times \frac{100}{100} \text{ vs. } \frac{51}{100} \times \frac{43}{43} \\ \\
# &\frac{2100}{4300} \text{ vs. } \frac{2193}{4300} \\ \\
# &\frac{2100}{4300} < \frac{2193}{4300}
# \end{align*}
# \tag{Equation 1.6.1.8}
# $$
# We note that $\frac{21}{43}$ is not larger than $\frac{51}{100}$. In fact, it is smaller. We confirm this with code below.
# In[110]:
sympy.Rational(21, 43) > sympy.Rational(51, 100)
# We do not require the `Rational` function to create the fractions. We can simply use the `/` symbol to create the fractions and the great than `>` or less than `<` comparison operators as required.
# In[111]:
(21 / 43) > (51 / 100)
# __Homework 1.6.1.1__ Is $\frac{3}{13}$ smaller than $\frac{7}{30}$?
# ### 1.6.2 Multiplication of fractions
# The multiplication of fractions is quite simple. In __Equation 1.6.2.1__, we see two fractions, where each of $a,b,c$, and $d$ are integers, and neither $b$ nor $d$ are equal to $0$.
# $$
# \frac{a}{b} \times \frac{c}{d} = \frac{a \times c}{b \times d}
# \tag{Equation 1.6.2.1}
# $$
# __Equation 1.6.2.2__ shows an example of multiplying two fractions.
# $$
# \frac{3}{11} \times \frac{7}{20}
# \tag{Equation 1.6.2.2}
# $$
# We use `sympy` to solve the problem.
# In[112]:
sympy.Rational(3, 11) * sympy.Rational(7, 20)
# Below, we calculate the same product using simple Python mathematical notation.
# In[113]:
(3 / 11) * (7 / 20)
# The result is a numerical approximation.
# Note that `sympy` will automatically simplify any fraction. In __Equation 1.6.2.3__ we see such a simplification where five tenths is eqal to a half.
# $$
# \frac{5}{10} = \frac{1}{2}
# \tag{Equation 1.6.2.3}
# $$
# Below, we see that `sympy` simplifies the fraction.
# In[114]:
sympy.Rational(5, 10)
# ### 1.6.3 The least common multiple
# We have seen that comparisons of fractions are easier when the denominator is the same. Arithmetic with fraction is in general easier when the denominators are the same. In fact, in the case of addition and subtraction the denominators have to be the same. To do this, we need to find the least common multiple.
# In __Equation 1.6.3.1__, we see the addition of two fractions. We rewrite each fraction by multiplying it by $1$, written as a fraction where the numerator and denominator are the denominator of the _other fraction_.
# $$
# \begin{align*}
# \frac{3}{4} + \frac{5}{6} &= \left( \frac{3}{4} \times \frac{6}{6} \right) + \left( \frac{5}{6} \times \frac{4}{4} \right) \\ \\
# &= \frac{18}{24} + \frac{20}{24}
# \end{align*}
# \tag{Equation 1.6.3.1}
# $$
# In __Equation 1.6.3.2__, we see an alternative way to solve __Equation 1.6.3.1__.
# $$
# \begin{align*}
# \frac{3}{4} + \frac{5}{6} &= \left( \frac{3}{4} \times \frac{3}{3} \right) + \left( \frac{5}{6} \times \frac{2}{2} \right) \\ \\
# &= \frac{9}{12} + \frac{10}{12}
# \end{align*}
# \tag{Equation 1.6.3.2}
# $$
# We have recognized that $12$ is the least common multiple between $4$ and $6$. Both $4$ and $6$ divide $12$. We can use the `lcm` function from the `sympy` package to find the least common denominator.
# In[115]:
sympy.lcm(4, 6)
# We still have to determine if the solutions in __Equation 1.6.3.1__ and in __Equation 1.6.3.2__ are the same. To do this, we see the rule of addition of fractions in __Equation 1.6.3.3__. Fractions can only be added (or subtracted) if they have the same denominator. The addition of two fractions with the same denominator is shown in __Equation 1.6.3.3__ where $c \ne 0$ and $a,b,$ and $c$ are integers.
# $$
# \frac{a}{c} + \frac{b}{c} = \frac{a+b}{c}
# \tag{Equation 1.6.3.3}
# $$
# Our two solutions are shown in __Equation 1.6.3.4__.
# $$
# \frac{38}{24} \text{ and } \frac{19}{12}
# \tag{Equation 1.6.3.4}
# $$
# These two fractions are indeed the same. In __Equation 1.6.3.5__ we simplify thirty-eight twenty-fourths by multiplying it by $1$ in the form of a half over a half.
# $$
# \begin{align*}
# &\frac{38}{24} \times \frac{\frac{1}{2}}{\frac{1}{2}} = \frac{\frac{38}{2}}{\frac{24}{2}} = \frac{19}{12}
# \end{align*}
# \tag{1.6.3.5}
# $$
# We can use `sympy` to verify this.
# In[116]:
sympy.Rational(38, 24)
# This brings us to an important topic for the least common multiple. We can _break down_ or rewrite any integer into the multiplication of other interegers. In more advanced algebra, we learn that these _other integers_ (if the _break down_ is complete) are all prime numbers (numbers that are only divisible by $1$ and themselves). We see an example in __Equation 1.6.3.6__, where we consider the integers that multiply to get $18$ and $9$, with $18 = 2 \times 3 \times 3$ and $9 = 3 \times 3$.
# $$
# \begin{array}{cccc}
# 18: & 2 & 3 & 3 \\
# 9: & & 3 & 3
# \end{array}
# \tag{Equation 1.6.3.6}
# $$
# We have taken care to write similar numbers in the same columns. In __Equation 1.6.3.7__, we add a new line by _bringing down_ or representing all the numbers.
# $$
# \begin{array}{cccc}
# 18: & 2 & 3 & 3 \\
# 9: & & 3 & 3 \\
# - & - & - & - \\
# & 2 & 3 & 3
# \end{array}
# \tag{1.6.3.7}
# $$
# If we multiply the numbers in the new row in __Equation 1.6.3.7__, we find the least common multiple of $18$ and $9$, which is $2 \times 3 \times 3 = 18$. So, instead of __Equation 1.6.3.8__, we can do __Equation 1.6.3.9__.
# $$
# \begin{align*}
# \text{Longer method: } \\ \\
# \frac{4}{9} + \frac{11}{18} &= \left( \frac{4}{9} \times \frac{18}{18} \right) + \left( \frac{11}{18} \times \frac{9}{9} \right) \\ \\
# &= \frac{72}{162} + \frac{99}{162} \\ \\
# &= \frac{171}{192}
# \end{align*}
# \tag{Equation 1.6.3.8}
# $$
# $$
# \begin{align*}
# \text{Shorther method: } \\ \\
# \frac{4}{9} + \frac{11}{18} &= \left( \frac{4}{9} \times \frac{2}{2} \right) + \left( \frac{11}{18} \right) \\ \\
# &= \frac{8}{18} + \frac{11}{18} \\ \\
# &= \frac{19}{18}
# \end{align*}
# \tag{1.6.3.9}
# $$
# We recognized that we do not have to perform the multiplication by rewriting $1$ as a fraction using the _other_ fraction's denominator. Since $18$ was the least common multiple, we only have to consider how to change the $9$ into an $18$ and that was mutiplying by $2$.
# __Problem 1.6.3.1__ Find the least common multiple of $12$ and $15$.
# The process of _breaking down_ or factoring an integer can be tedious. For the relatively small integers that we deal with in the life sciences, we can start by dividing repeatedly by $2$, then $3$, then $5$, and so on, using the primes in an iterative process. Remember, that primes are only divisible by $1$ and themselves. We do so for $12$ and $15$ in __Equation 1.6.3.10__
# $$
# \begin{array}{ccccc}
# 12: & 2 & 2 & 3 & \\
# 15: & & & 3 & 5 \\
# - & - & - & - & - \\
# & 2 & 2 & 3 & 5
# \end{array}
# \tag{Equation 1.6.3.10}
# $$
# We have that $2 \times 2 \times 3 \times 5 = 60$. This is smaller than $12 \times 15 = 180$. We can use the `lcm` function from the `sympy` package to verify our result.
# In[117]:
sympy.lcm(12, 15)
# __Homework 1.6.3.1__ Find the least common multiple of $13$ and $17$.
# __Problem 1.6.3.2__ Add the fractions in __Eqiation 1.6.3.11__.
# $$
# \frac{5}{12} + \frac{7}{15}
# \tag{Equation 1.6.3.11}
# $$
# We have to _turn_ $12$ into $60$ and $15$ into $60$. This is simple and is shown in __Equation 1.6.3.12__.
# $$
# \frac{60}{12} = 5, \; \; \frac{60}{15} = 4
# \tag{Equation 1.6.3.12}
# $$
# We have found the way to rewrite $1$ as fractions for this problem. The solution is shown in __Equation 1.6.3.13__.
# $$
# \begin{align*}
# \frac{5}{12} + \frac{7}{15} &= \left( \frac{5}{12} \times \frac{5}{5} \right) + \left( \frac{7}{15} \times \frac{4}{4} \right) \\ \\
# &= \frac{25}{60} + \frac{28}{60} \\ \\
# &= \frac{25 + 28}{60} \\ \\
# &= \frac{53}{60}
# \end{align*}
# \tag{Equation 1.6.3.13}
# $$
# We use `sympy` to confirm the result.
# In[118]:
sympy.Rational(5, 12) + sympy.Rational(7, 15)
# __Homework 1.6.3.2__ Add the fractions in __Equation 1.6.3.14__.
# $$
# \frac{7}{12} + \frac{5}{6}
# \tag{Equation 1.6.3.14}
# $$
# ### 1.6.4 Remainders
# A fraction does not change in value if we divide the numerator and the denominator by the same value. This is shown in __Equation 1.6.4.1__.
# $$
# \frac{p}{q} = \frac{\frac{p}{k}}{\frac{q}{k}}, \text{ where } p,k \ne 0
# \tag{Equation 1.6.4.1}
# $$
# In __Equation 1.6.4.2__, we note the equality between six-eights and a three-quarters.
# $$
# \frac{6}{8} = \frac{\frac{6}{2}}{\frac{8}{2}} = \frac{3}{4}
# \tag{Equation 1.6.4.2}
# $$
# In the case of six-eigths, we can divide the numberator and the denominator by the same number (and hence not change the value of the fraction). In this case, we can divide both by $2$ to get theee quarters. To do the simplification, we made use of the fact that $2$ divides both $6$ and $8$.
# We could not divide $3$ and $4$ by another integer that divides both $3$ and $4$. By divides in this case, we mean, division without a remainder, or then, a remainder of $0$.
# Consider, though, dividing $11$ by $4$. We know that $4$ does not divide $10$. If we divide $11$ by $4$ and still require this division to produce a whole number, we can at best get $2$ with a remainder of $3$. We see this in __Equation 1.6.4.3__.
# $$
# \frac{11}{4} = 2 \text{ with a remainder of } 3
# \tag{Equation 1.6.4.3}
# $$
# We have that $4$ divides into $11$ twice, with a remainder of $3$ since $2 \times 4 = 8$ and $8 + 3 = 12$. We can write this in more egneral terms as shown in __Equation 1.6.4.4__.
# $$
# \begin{align*}
# &11 = 2 \times 4 + 3 \\ \\
# &b = c a + r
# \end{align*}
# \tag{Equation 1.6.4.4}
# $$
# If $r=0$, we say that $a$ divides $b$ and $b=ca$, or then, $b$ is a constant multiple of $a$.
# __Problem 1.6.4.1__ Caclulate the remainder when $13$ is divided by $2$.
# We note that $2$ goes into $13$ a total of $6$ times. Since $6 \times 2 = 12$, we have a remainder of $1$. In __Equation 1.6.4.5__ we see the calculation. In the last line we also see a common notation for the remainder, where we state that $13$ divided by $2$ is $6$ and a half.
# $$
# \begin{align*}
# &\frac{13}{2} = 6 \text{ with a remainder of } 1 \\ \\
# &\frac{13}{2} = 6 \, \frac{1}{2}
# \end{align*}
# \tag{Equation 1.6.4.5}
# $$
# The `Mod` function in `sympy` returns the remainder and we use it below to verify the result.
# In[119]:
sympy.Mod(13, 2)
# __Homework 1.6.4.1__ What is the remainder when we divide $125$ by $7$.
# ## 1.7 Trigonometric functions (optional)
# In this section, we take a brief look at basic trigonometry. The trigonomtery that we explore here concenrs the right triangled triangle. A right triangle is a triangle with one angle of $90^{\circ}$.
# __Figure 1.7.1__ shows a right triangle with the angle $\theta$. The sides are named in relation to the angle $\theta$.
# In[120]:
# Use matplotlib to plot a triangle
fig, ax = pyplot.subplots()
ax.plot([0, 1, 0, 0], [0, 0, 1, 0], 'k')
ax.set_title('Figure 1.7.1')
pyplot.xlim(-1, 1.5)
pyplot.ylim(-0.5, 1.5)
pyplot.vlines(0, 0, 1, color='black')
pyplot.gca().spines['top'].set_visible(False)
pyplot.gca().spines['right'].set_visible(False)
pyplot.gca().spines['bottom'].set_visible(False)
pyplot.gca().spines['left'].set_visible(False)
pyplot.text(0.25, -0.125, 'adjacent', fontsize=12, color='black')
pyplot.text(-0.5, 0.5, 'opposite', fontsize=12, color='black')
pyplot.text(0.55, 0.5, 'hypoteneuse', fontsize=12, color='black')
pyplot.text(0.75, 0.047, r'$\theta$', fontsize=12, color='black')
pyplot.xticks([])
pyplot.yticks([])
pyplot.show()
# The trigonometric functions sine, cosine, tangent, cosine, secant, and cotangent are defined in __Equation 1.7.1__.
# $$
# \begin{align*}
# &\text{Sine: } \sin{\theta} = \frac{\text{opposite}}{\text{hypotenuse}} \\ \\
# &\text{Cosine: } \cos{\theta} = \frac{\text{adjacent}}{\text{hypotenuse}} \\ \\
# &\text{Tangent: } \tan{\theta} = \frac{\text{opposite}}{\text{adjacent}} \\ \\
# &\text{Cosecant: } \csc{\theta} = \frac{\text{hypotenuse}}{\text{opposite}} \\ \\
# &\text{Secant: } \sec{\theta} = \frac{\text{hypotenuse}}{\text{adjacent}} \\ \\
# &\text{Cotangent: } \cot{\theta} = \frac{\text{adjacent}}{\text{opposite}}
# \end{align*}
# \tag{Equation 1.7.1}
# $$
# It is common to use $\pi$ when dealing with trigonometric functions. We can express $pi$ as a numerical approximation using the `numpy` package and as an exact value using the `sympy` package.
# In[121]:
numpy.pi
# In[122]:
sympy.pi
# There are two common measures of any angle $\theta$. We can use degrees or radians. Degrees span a circle from $0^{\circ}$ to $360^{\circ}$. Radians span a circle from $0$ to $2\pi$. We can convert between degrees and radians using __Equation 1.7.2__.
# $$
# \begin{align*}
# &\text{Degrees to radians: } \theta \times \frac{\pi}{180} \\ \\
# &\text{Radians to degrees: } \theta \times \frac{180}{\pi}
# \end{align*}
# \tag{Equation 1.7.2}
# $$
# We can also use code to convert between degrees and radians. We use the `radians` function from the `numpy` package to convert degrees to radians. We use the `degrees` function from the `numpy` package to convert radians to degrees.
# In[123]:
numpy.radians(180)
# In[124]:
numpy.degrees(numpy.pi)
# __Problem 1.7.1__ Calculate the sine of $\pi$ radians.
# The `sympy` package contains functions for all the trigonometric functions. These functions use angles is radians. We use the `sin` function to calculate the sine of $\pi$.
# In[125]:
# Calculate the sime of pi radians
sympy.sin(sympy.pi)
# __Problem 1.7.2__ Calculate the cosine of $\frac{\pi}{2}$ radians.
# We use the `cos` function from the `sympy` package to calculate the cosine.
# In[126]:
# calculate the cosine of a half pi radians
sympy.cos(sympy.pi / 2)
# In[ ]:
# __Problem 1.7.3__ Calculate the tangent of $\frac{\pi}{6}$.
# We use the `tan` function from the `sympy` package to calculate the tangent.
# In[128]:
# calculate the tangent of pi over 6
sympy.tan(sympy.pi / 6)