Phy 485 Notes 2025
Phy 485 Notes 2025
By L. C. Moffat
Physics Department
Faculty of Science
University of Botswana
List of Figures 3
List of Tables 4
1
1.2.9 File handling (reading/ writing files) . . . . . . . . . . . . . . . 50
1.3 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2 NUMERICAL METHODS 53
2.1 Iterative Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.1.1 Bisection method . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.1.2 Newton-Raphson (or Newton’s) method . . . . . . . . . . . . 58
2.1.3 Secant method . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.2 Solutions of Integral Eqns. . . . . . . . . . . . . . . . . . . . . . . . . . 63
2.2.1 Trapezoidal rule . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2.2.2 Simpson’s rule . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.3 Solutions of Diff. Equations . . . . . . . . . . . . . . . . . . . . . . . . 69
2.1 Illustration of the bisection method for finding the root of a function
f (x). Notice that after each step (iteration N) the interval is halved, i.e.,
(b − a) /2N . If the root r is found within a tolerance ϵ (i.e., |ri − ri−1 | <
ϵ) the number of iterations N can be determined from (b − a)/2N < ϵ. 54
3
LIST OF FIGURES LIST OF FIGURES
5
INTRODUCTION TO COMPUTER
1
PROGRAMMING & THE PYTHON
LANGUAGE
1.1 Introduction
1.1.1 What is Computer Programming?
Before you understand what computer programming is, you need to understand what
a computer program is. A computer program can be used for almost anything. It can
be used to
• help doctors cure diseases, e.g., in ultrasounds, X-rays, CT scans, etc.
• manufacture self-driving cars
• design robots that perform special tasks like diffusing a bomb, or performing
non-invasive surgery
• make very fun games (e.g., simulation, puzzles, action, combat, etc.).
• create office applications like Word, Excel or PowerPoint.
• design websites (e.g., using JavaScript, HTML, CSS, Dreamweaver, Google Web
Designer, Webflow, PHP, SQL, XML, etc.).
• develop graphics for the web or for movie making, etc.
6
CHAPTER 1. INTRODUCTION TO COMPUTER PROGRAMMING & THE
1.1 Introduction PYTHON LANGUAGE
Examples:
(i) machine language - consists of a series of zeroes and ones. It can be executed
directly by the computer.
(ii) assembly language (also called assembly, ASM or symbolic machine code) -
unlike machine language which consists entirely of numbers, assembly lan-
guage allows the programmer to use mnemonics & symbols and it cannot
be directly executed by the computer. It requires an assembler to convert
Figure 1.1: Computer programming languages. Depending on the level of abstraction, two major
divisions exist: low-level and high-level programming languages. Low-level programming languages
are the oldest and relate to a specific architecture and hardware of a particular type of computer. High-
level languages are not restricted to a specific computer architecture, they are portable.
Examples: (i) BASIC (updated to Visual Basic by Microsoft) (ii) C (iii) C++ (an
extension of C) (iv) COBOL (v) Fortran (vi) Java (vii) PHP (viii) Python (ix) R
(x) MATLAB, etc.
In the PHY485 (Microcomputing for Physical Sciences) course, we will use the Python
programming language to complete basic tasks assigned in practical exercises and as-
signments.
Preprocessor Compiler
Source file Preprocessed code Object file
Linker
(.cpp) (.i) (.o or .obj) Executable
extension of the Libraries (.exe)
generated file (.dll)
Examples of languages that are compiled include BASIC, C/C++, C#, Objective-
C, Fortran, Java, COBOL, Julia, Ada, Pascal, etc.
• through an interpreter - an interpreter is a program that interprets instructions
of a program one instruction at a time into commands that are to be carried out
by the computer as it happens. Examples of languages that are interpreted include
(b) Runtime errors (Exceptions) - these occur while the program is running and
the interpreter/compiler has no way of knowing about them at parsing stage,
e.g., division by zero, using an undefined variable, overflow (limited storage
space for generated data), underflow (number smaller than what the device can
handle), opening a file that does not exist, etc.
(c) Logical errors - they are a type of runtime error that simply produces a wrong
output. Examples include using wrong input data, multiplying instead of di-
viding, adding instead of subtracting, etc. These errors are harder to find in a
program than syntax and runtime errors.
In general, the errors become more difficult to find and fix as you move down the
above list.
IDEs, like PyCharm, bundle Python with their installation. In Linux environments,
users can use the Python shell with any text editor, such as vi, Emacs, or gedit. For
scientific computing, additional packages like NumPy, SciPy, Matplotlib and Pandas
are commonly used.
In Python, everything — functions, modules, strings, and files — is an object. All objects
have associated methods, accessed using the syntax
object.methodname(parameters). For example, fruits.append("apple") adds "apple"
to the list fruits, where fruits might be defined as fruits = [`mango', `orange',
`banana', `pear', `peach'].
As mentioned, Python is an object-oriented programming language. OOP is a program-
ming style based on self-contained units called objects, which have unique attributes
and behaviors. Consider an everyday example, such as a bicycle. It has attributes (e.g.,
size, color) that represent data and behaviors (e.g., pedaling, braking) that represent
functions. OOP enables programmers to organize code in a more modular, reusable,
and manageable way by focusing on objects with specific characteristics and actions.
That’s it! This is a complete, working Python program. We will use it as an entry
point into the basic elements of a Python program. Let us explain it line-by-line:
1. The # symbol indicates that everything following it on the same line is a comment
and is ignored by the interpreter. To create a multi-line comment, you can either
begin each line with # (since Python doesn’t have explicit multi-line comment
syntax) or use a multi-line string literal (enclosed in triple quotes), for example:
"""This is a comment""". This works because Python ignores string literals
that are not assigned to a variable. Comments are crucial for explaining what
your code does, so use them liberally.
2. print(`Hello world!'): This is the core of the program. print() is a function.
Functions are reusable blocks of code that perform specific tasks. The print()
function’s job is to display output to the console (your screen). `Hello world!'
is a string literal. A string is a sequence of characters (letters, numbers, symbols)
enclosed in quotation marks (either single ` or double "). In this case, it’s the text
we want to display.
There are two main ways to run the Python program described above:
• Save the code to a file with a name ending in .py. Then, open a terminal (Com-
mand Prompt on Windows or a Linux shell) and type python filename.py,
Hello, world!
followed by pressing Enter. The output will be displayed on
your screen.
Note that you can also run the saved .py file directly from your Python IDE if
it provides that functionality.
• Use an interactive interpreter (available in many Python IDEs or directly via a
Python shell). Open a Python terminal, type print(`Hello, world!') at the
Hello, world!
>>> prompt, and press Enter. The output will be displayed
immediately below the command.
Note that all these keywords are in lowercase, except False, None and True,
which are capitalised.
Identifiers
In Python, the name given to a variable, function, class, module, or other object
is called an identifier. An identifier should be purposeful, clearly indicating the
entity’s function. It should also be descriptive; for example, n might be a poor
choice for a variable representing the number of students taking PHY485. A
name like num_students would be much better. There are several rules to follow
when naming identifiers:
• they cannot be Python keywords.
• they can only consist of letters (A-Z, a-z), numbers (0-9), and the under-
score character (_). They cannot contain any other symbols or whitespace.
• an identifier must begin with a letter or an underscore; it cannot start with
a number.
Note that there are specific conventions associated with underscores:
– A single leading underscore (_variable) indicates that a variable is in-
tended for internal use within a module or class.
– A double leading underscore (__variable) makes it more difficult to
override a variable in a subclass (due to name mangling).
– A single trailing underscore (variable_) is used to avoid naming conflicts
with Python keywords (e.g., import_ to avoid conflict with the import
keyword).
– A single underscore by itself (_) has several meanings in Python. It can
be used (i) to store the result of the last executed expression in a Python
shell, (ii) to ignore a variable or list of variables when unpacking a list or
a tuple, (iii) as a counter variable in loops especially when the counter
is not needed in the future and (iv) as digit separator in numbers for
better understanding (e.g., 100_000_000 for 100 million).
Note that Python lists and tuples are ordered collection of variables indexed
by integers. The difference between these two are that whereas ‘lists’ allow
insertion, deletion, and substitution of elements ‘tuples’ do not. ‘tuples’ are
like read only sequences and they are represented by parentheses. ‘Lists’ on
the other hand are represented by square brackets. Both ‘lists’ and ‘tuples’
The following code segment, on the other hand, will not produce an error since
correct indentation has been used.
Program 1.3: Good indentation.
1 i f True :
2 p r i n t ( ‘ I am l e a r n i n g i n d e n t a t i o n . ’ )
3
The standard practice is to use four spaces per indentation level. Most editors
automatically insert spaces when the Tab key is pressed after the colon symbol.
Consistent indentation is critical; mixing tabs and spaces will result in an error.
Indentation is required in constructs like:
• Conditionals (if, elif, else).
2+3=5
– Raw strings - they are useful when you want to include backslashes (\) in
your string without them being interpreted as escape characters. They can
be used in regular expressions, file paths and string literals. For instance,
in Matplotlib, raw strings (r"") are commonly used to format text using
LaTeX-style math expressions. LaTeX requires \ (e.g., \alpha for α), which
might be misinterpreted by Python without raw strings. Program 1.10 is a
code segment that shows how raw strings can be used in Matplotlib.
Program 1.10: Raw strings example.
1 p l t . p l o t ( x , y , l a b e l = r " $y = \ s i n ( x ) $ " )
2
Note that "Enter your name: " will be displayed first on the screen when you
run it. The second line will be printed after you type your name.
By default, input() returns a string. This means that if you want a numerical in-
put such as an integer or float, you will have to convert the received input string
into those data types. For example, to convert to an integer one would write age
= int(input("Enter your age: ")) or height = float(input("Enter your
height in meters: ")) for the case of a float. The input() function can also
handle multiple inputs in a single line. This is done using the function split(),
i.e., x, y = input("Enter two numbers: ").split(). You can then con-
vert them to the desired data type. Program 1.12 shows different ways you can
deal with multiple input from a user:
Program 1.12: How to receive multiple user input.
1 num1 , num2 , num3 , num4 = input ( " E n t e r f o u r numbers : " ) . s p l i t ( )
2
3 # c o n v e r t eac h number t o an i n t e r g e r
4 num1 = i n t ( num1 )
5 num2 = i n t ( num2 )
6 num3 = i n t ( num3 )
7 num4 = i n t ( num4 )
8
9 # ALTERNATIVELY, you can combine t h e above i n s t r u c t i o n s i n t o one l i n e a s
f o l l o w s u s i n g t h e f u n c t i o n map ( )
10 num1 , num2 , num3 , num4 = map( int , input ( " E n t e r f o u r numbers : " ) . s p l i t ( ) )
11
12 p r i n t ( " The numbers e n t e r e d a r e : " , num1 , num2 , num3 , num4 )
13
14 # you can a l s o c r e a t e a i n t e g e r l i s t ( a r r a y ) out o f t h e s e numbers u s i n g t h e
function l i s t ( )
15 numbers = l i s t (map( int , input ( " E n t e r f o u r numbers : " ) . s p l i t ( ) ) )
16
17 p r i n t ( numbers )
18
Data types
A data type refers to the type of data that a variable can hold. In Python, there is
no need to explicitly declare the variable’s type before using it. Python automatically
determines the data type during runtime or upon assignment. This differs from some
languages like C++ and Fortran where you have to explicitly declare the type of a
variable before using it.
Python provides a diverse range of built-in data types, broadly classified into five
major groups (refer to Table 1.1). These data types serve as the fundamental build-
ing blocks for constructing intricate data structures and executing various operations
within Python programs. Data types can also be categorized based on their proper-
ties, such as numeric and non-numeric types. Numeric types handle numerical val-
ues, while non-numeric types encompass strings, sequences (lists and tuples), mappings
(dictionaries), sets, and booleans. Another key property distinguishes between mutable
and immutable data types. Mutable data types allow for modification of their values
after creation (e.g., lists, dictionaries, sets), whereas immutable data types remain un-
changed once created (e.g., integers, floats, strings, tuples, frozensets).
Operators
An operator is a special symbol that tells a compiler or interpreter to perform a specific
operation on one or more operands to produce a final result. There are several types of
built-in operators in Python (see, Python Software Foundation, n.d.; TutorialsPoint,
2025), and these are:
• Arithmetic operators
+ addition
− subtraction
* multiplication
/ division
% modulus or remainder
// floor division
** exponent
= equals assignment
+= addition assignment (note that a+=b =⇒ a = a + b)
−= subtraction assignment
*= multiplication assignment
/= division assignment
%= modulus or remainder
//= floor division assignment e.g., a //= 6
**= exponent assignment e.g., a **= 4
• Comparison Operators
• Logical Operators
• Bitwise Operators
Bitwise operators modify variables considering the bit patterns that represent
the values they store.
• Identity Operators
These compare the memory locations of two objects.
0 0 0 0 0 0 1 0 (10>>2) = 2 0 0 1 0 1 0 0 0 (10<<2) = 40
Vacated bit positions Vacated bit positions
filled with zeros. filled with zeros.
1 1 1 1 1 0 0 0 (-1<<3) = -8 1 1 1 1 1 1 1 1 (-1>>3) = -1
Vacated bit positions Vacated bit positions
filled with zeros. filled with ones!
Figure 1.4: Example showing how bitwise operators modify variables using their bit patterns
considering an 8-bit representation. Note that (10 >> 2) means shift 10 by two bits to the right. The
other important thing concerns shifting of negative numbers. A 2’s complement is used instead of 1’s
complement (this is beyond the scope of this course!!). For positive numbers, the smallest positive
number is the smallest binary value, i.e., 00000000 = 0. Negative numbers always start with a 1.
Thus, the smallest negative number is the largest binary value, e.g., 11111111 = −1, 11111110 = −2,
11111101 = −3, 11111100 = −4, etc.
Recall that in arithmetic certain operations are performed before others, for exam-
ple, in an expression containing multiplication and addition, multiplication will take
precedence. The same is true for Python operators.
Table 1.2 summarises the order of precedence and associativity for Python operators.
Operators with the highest precedence appear at the top of the table while those with
the lowest precedence appear at the bottom. Examples are also included.
In general, operators in Python are normally left-associative meaning they are applied
from left to right as can be seen in table 1.2. For example, 2×3+4 will have multiplica-
tion performed first followed by addition giving a result of 10. For right-associativity
consider the example a=b=5; in this example the value of b will first be set to 5 and
subsequently the result of this operation, 5, will be assigned to a. Note that the order
of precedence can be overridden with the inclusion of parentheses as in 2 × (3 + 4) if
14 is the intended result. More examples are shown in table 1.2.
So each statement above will be executed after the preceding one, i.e., execution will
start with statement 1, followed by statement 2 and so on.
In the selection control structure program flow is directed by choice, that is, execution of
the next instruction follows from the selection made. Examples include the following:
1. Simple-alternative decision format
Consider the following examples
(a) Two alternatives
Program 1.13: Two alternatives.
1 i f l a b t i m e _ u s e == 0 :
2 p r i n t ( " I l i k e s p e n d i n g t i m e on s o c i a l media . " )
Test
Action 1 Action 2
Test
Action
3 else :
4 p r i n t ( "My Python programming s k i l l s a r e i m p o r t a n t . " )
5
Any of the statements in the two examples can be compound; that is, they
can contain a sequence of statements. The statements are indented to indi-
cate that they belong to the "if" or "else" block. This means that all indented
lines of code following the colon (:) belong to the same block of code and
will be executed together.
2. Multiple-alternative decision format
The two alternatives can be extended by following the if with an else if such that
the choices get expanded. This is commonly referred to as nesting - nesting allows
for more multiple-selection functionality. The length of the if...else result is now
what can be termed as multiple-alternative decision format, that is, there exist a
series of choices to make like in the awarding of grades after an examination.
(a) The if...else if...else (nested if ) statement
The following gives the general structure of the nested if statement
if condition_1:
statement_1
elif condition_2:
statement_2
..
.
elif condition_n:
statement_n
else
statement_n-1
Example program 1.15 illustrates how you can use an if...else if...else state-
ment in a Python program. The program allows the user to select an arith-
Notice that the underscore (_) symbol is used after the last case. This serves
as a wildcard, matching any value entered by the user that does not match
any of the specified case patterns in the match-case statement. The match-
case statement provides the same functionality as the if-else if-else statement
but in a more concise and potentially more readable way, especially for
more complex conditional logic.
Pitfalls to Watch
if...else
(i) Readability and maintenance issues for deeply nested and complex blocks.
(ii) Incorrect indentation.
match-case
In the while loop as long as the condition is met, the block of statements
will repeatedly be executed. The looping conditions of a while loop can
also be performed using the for loop statement.
Here is an example:
Program 1.18: Use a <while> loop to display "Hello, world!" four times.
1 i = 0 # initialize i
2
3 while i < 4 :
4 p r i n t ( " Hello , world ! " )
5 i = i + 1
The above program first checks whether the condition is met, then it prints
the statement “Hello, world!" to the screen. In this case provided the i has
been initialised as “i = 0", it will print the statement “Hello, world!" four
times each on a new line.
In loops, you can use the break, continue, and pass keywords to modify
the loop’s behavior. The break statement completely terminates the loop,
regardless of whether there are more iterations remaining. Program ex-
ecution continues from the line of code immediately following the loop.
The continue statement, in contrast, skips the remaining code within the
current iteration of the loop and immediately moves to the next iteration,
allowing you to exclude specific elements or conditions from processing.
The pass statement is a placeholder that does nothing. It is typically used
as a placeholder for code not yet implemented or to avoid syntax errors in
empty loops or conditionals. Below are examples:
Program 1.19: Skipping specific iterands in a sequence.
1 total = 0
2 primes = [ 2 , 3 , 5 , 7 , 11]
3 f o r v a l u e in p r i m e s :
4 i f v a l u e == 5 :
5 continue # skip value 5
6 t o t a l += v a l u e
7
multiple times within a program to perform a specific task. The general syntax for
defining a function in Python is:
def function_name(parameter1, parameter2, ...):
statements
where:
- def is a keyword used to define a function.
- function_name is the identifier used to call the function.
- parameters are variables that receive input values when the function is called. A
function can have multiple parameters as needed.
- statements form the body of the function, containing the logic to be executed.
For example, suppose we want to multiply two numbers. We can define a function as
follows:
Program 1.22: Function to calculate the product of two numbers.
1 def m u l t i p l i c a t i o n ( a , b ) :
2 " " " M u l t i p l i e s two numbers and r e t u r n s t h e r e s u l t . " " "
3 return a ∗ b
4
5 z = m u l t i p l i c a t i o n (4 , 5) # function c a l l
6 p r i n t ( f " The r e s u l t i s : { z } " )
In program 1.22, the function multiplication() returns a single value, the product
of two numbers, using the return keyword. The text in line 2 is a docstring, a comment
describing the function’s purpose. The variables a and b are parameters, while the values
4 and 5 are arguments passed to the function during a function call in line 5. The order
of arguments in the function call must match the order of parameters in the function
definition; such arguments are called positional arguments.
Scope of variables in a function
A function can access variables from two different scopes: local and global (McKinney,
2018). This is illustrated in the example below:
Program 1.23: Demonstrating local and global variables.
1 a = 10 # Global v a r i a b l e
2
3 def m u l t i p l i c a t i o n ( ) :
5, 6, 7
Modules
Most libraries (also referred to as modules) are not included in Python’s standard in-
stallation. They are developed by third parties and provide additional functionality
through collections of functions and tools. To use these libraries, you must first in-
stall them on your computer using Python’s package manager, pip. You can do this
by writing ‘pip install <module>’ on the command prompt (in Windows) or
terminal (Linux or MacOS), where module refers to the required module. If you are
using the Anaconda distribution, many popular libraries are pre-installed or can be in-
stalled automatically through its package manager, conda. After installation, you can
import a library into your working environment using the import statement. For
example, to calculate the square root of a number, you need the Numerical Python
(NumPy) library, which provides the sqrt() function. You can import NumPy
with ‘import numpy’. Once imported, you can use the square root function as
numpy.sqrt(number). Alternatively, you can import the sqrt() function di-
rectly with ‘from numpy import sqrt’. This allows you to use the function more
concisely as sqrt(number).
The following code demonstrates how to import a module and use its functions:
Program 1.28: Importing the NumPy module for mathematical operations.
1 import numpy
2
3 a = numpy . a r r a y ( [ 1 , 2 , 6 , 9 ] )
4 b = numpy . s q r t ( a ) # t a k e t h e s q u a r e r o o t o f e v e r y e l e m e n t i n ’ a ’
5
6 print ( b )
[1 1.41421356 2.44948974 3]
import statements are typically placed at the beginning of every script to prevent
errors and enhance code organization and readability. Modules can be categorized
into three types: built-in, third-party, and custom modules. There are several ways to
import modules:
• importing the whole module - e.g., ‘import math’. Every function then has
to have the math. prefix, e.g., math.sqrt(4). The advantage is that this
approach prevents name conflicts.
• importing a specific function or class - e.g., ‘from math import sqrt’. You
can then use the function directly without the math. prefix, e.g., sqrt(4).
• importing with an alias - e.g., ‘import numpy as np’, e.g., np.arange(5).
• importing all functions from a module - e.g., ‘from math import ∗ ’. You
can use the functions without the math. prefix as in the second bullet point
above. However, this is not recommended because it can lead to name conflicts
and reduced code readability.
Table 1.3 lists common modules used in academic research and numerical computing
for data analysis, statistical and time series analysis, modeling and simulations, visual-
ization, and machine learning.
Table 1.3: Common Python modules in academic research and numerical computing.
When importing modules, it is often more convenient to use aliases to reduce clutter in
your code. This involves assigning shorter or more meaningful names to the imported
modules. Here are some common aliases adopted by the Python community:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import statsmodels as sm
Therefore, using np.sqrt() clearly indicates that you’re referring to the square root
function within the NumPy library. This convention improves code readability and
maintainability, especially when working with multiple libraries or collaborating with
other developers.
[10, 2, 3, 4, 5, 6]
1920
1, 2, 3, 4, 5
(e) List comprehensions - List comprehensions provide a concise way to create lists
in Python. They are faster and more readable than traditional loops. The general
syntax of a list comprehension is:
new_list = [expression for item in iterable if condition]
Examples:
Program 1.33: Python list comprehension without a condition.
1 s q u a r e s = [ x ∗ ∗ 2 f o r x in range ( 1 0 ) ]
2 print ( squares )
3
[0, 2, 4, 6, 8]
[1, 2, 3, 4, 5]
NumPy arrays support element-wise operations, which are significantly more effi-
cient than looping over lists. Some of the basic operations that can be performed
with NumPy arrays include: arithmetic operations, aggregation functions (such as sum,
mean, max and mean), vectorisation, broadcasting and more. Common numerical op-
erations using NumPy are demonstrated in program 1.40.
Program 1.40: Common numerical operations with NumPy.
1 import numpy a s np
2
3 # vectorisation
4 a = np . a r r a y ( [ 1 , 2 , 3 ] ) # c r e a t e a v e c t o r o f t h r e e numbers
5 r e s u l t = np . s i n ( a ) # a p p l y s i n e f u n c t i o n t o e ach e l e m e n t
6
7 # c r e a t i n g r a n g e s and s e q u e n c e s
8 np . l i n s p a c e ( 0 , 1 0 , 5 ) # c r e a t e 5 e q u a l l y s p a c e d numbers from 0 t o 10
9 np . a r a n g e ( 0 , 1 0 , 2 ) # c r e a t e v a l u e s from 0 t o 10 with s t e p s i z e 2
10
11 # d o t p r o d u c t and m a t r i x o p e r a t i o n s
12 A = np . a r r a y ( [ [ 1 , 2 ] , [ 3 , 4 ] ] ) # 2D a r r a y
13 B = np . a r r a y ( [ [ 5 , 6 ] , [ 7 , 8 ] ] ) # a n o t h e r 2D a r r a y
14 d o t _ p r o d u c t = np . d o t ( A, B ) # d o t p r o d u c t o f t h e 2D a r r a y s
15
16 # summation and a g g r e g a t i o n
17 a r r = np . a r r a y ( [ [ 1 , 2 , 3 ] , [ 4 , 5 , 6 ] ] )
18 np . sum ( a r r ) # sum o f a l l e l e m e n t s
19 np . sum ( a r r , a x i s = 0 ) # column − w i s e sum ( i . e . , 1 s t d im e ns i on )
20 np . sum ( a r r , a x i s = 1 ) # row− w i s e sum ( i . e . , 2nd d im e ns i o n )
21
22 # c r e a t i n g multi − dimensional a r r a y s
23 a r r a y _ 3 d = np . a r r a y ( [ [ [ 1 , 2 , 3 ] ,
24 [4 , 5 , 6] ,
25 [7 , 8 , 9]] ,
26
27 [ [ 1 0 , 11 , 12] ,
28 [13 , 14 , 15] ,
29 [ 1 6 , 1 7 , 1 8 ] ] ] ) # c r e a t e a 3D a r r a y ( 2 x3x3 t e n s o r )
30 s u m _ a l o n g _ a x i s = np . sum ( a r r a y _ 3 d , a x i s = 0 ) # sum a l o n g t h e f i r s t a x i s ( a x i s = 0)
31
familiar syntax for those who are already acquainted with MATLAB, simplifying the
creation of various plot types: scatter plots, bar charts, histograms, statistical plots (e.g.,
box plots, errorbar plots, violin plots), gridded data plots, heatmaps, image plots, pie charts,
stem plots, 3D plots, and so on. To use ‘pyplot’, we import it as follows: ‘import
matplotlib.pyplot as plt’. The following programs demonstrate how to use
the Matplotlib module to create some of these plots and customize their appearance.
10
10
0
50 60 70 80 90
10
8
Y-axis Label
Dataset 1
Dataset 2
10
For seamless addition of legends to plots make sure you include a label for each plot
you make. Otherwise, you will be forced to add the labels manually for each plot in
the legend function.
12
13 # Display the p l o t
14 p l t . show ( )
10
Saving plots
In Matplotlib, you can save figures using the ‘savefig()’ function. This function
takes the filename as the first argument and allows you to specify the file format by
including the extension in the filename. Supported file formats are as mentioned earlier.
Here is a basic example:
Program 1.47: Saving plots from Matplotlib.
1 import m a t p l o t l i b . p y p l o t a s p l t
2
3 # Sample d a t a
4 x = [1 , 2 , 3 , 4 , 5]
5 y = [2 , 3 , 5 , 7 , 11]
6
7 # Create a l i n e p l o t
8 plt . plot (x , y)
9
10 # Save t h e p l o t
11 p l t . s a v e f i g ( ’ l i n e _ p l o t . png ’ )
12
13 # Display the p l o t
14 p l t . show ( )
Note that the figure will be saved to the current folder (in MS Windows) or directory
(in Linux).
The with statement ensures the file is properly closed after writing. Each write()
operation adds content to the file, but it does not automatically add a newline. The user
can add a newline using the newline character (\n). If you do not want to overwrite
the existing data, you can use the append mode ("a") as shown below:
Program 1.49: Appending data to a file.
1 with open ( " example . t x t " , " a " ) a s f i l e :
2 f i l e . w r i t e ( " Appending a new l i n e . \ n " )
Reading a file in Python is very easy as well. This is done using the open() function
with read ("r") mode, e.g.:
Program 1.50: Reading a text file.
1 with open ( " example . t x t " , " r " ) a s f i l e :
2 content = f i l e . read ( )
3 print ( content )
4
5 # r e a d a f i l e l i n e by l i n e
6 with open ( " example . t x t " , " r " ) a s f i l e :
7 f o r l i n e in f i l e :
8 p r i n t ( l i n e . s t r i p ( ) ) # s t r i p f u n c t i o n removes e x t r a n e w l i n e c h a r a c t e r s , \ n
9
10 # read a l l l i n e s into a l i s t
11 with open ( " example . t x t " , " r " ) a s f i l e :
12 lines = file . readlines ()
13 print ( l i n e s ) # Returns a l i s t of s t r i n g s
14
15 with open ( " example . t x t " , " r " ) a s f i l e :
16 l i n e s = [ l i n e . s t r i p ( ) f o r l i n e in f i l e ] # removes n e w l i n e c h a r a c t e r
17 print ( l i n e s )
Files can contain structured data such as Comma Separated Values (CSV). Python pro-
vides the csv module and the pandas library for such files. Programs 1.51 and 1.52
demonstrate how you can write and read to a CSV file, respectively, using the csv
module.
Program 1.51: Writing data to a CSV file.
1 import c s v
2
3 # w r i t i n g d a t a t o a CSV f i l e
4 with open ( " d a t a . c s v " , "w" , n e w l i n e = " " ) a s f i l e :
5 writer = csv . writer ( f i l e )
6 w r i t e r . writerow ( [ "Name" , " Age " , "Town" ] )
7 w r i t e r . writerow ( [ " Tshenolo " , 2 3 , " L o b a t s e " ] )
8 w r i t e r . writerow ( [ " T l o t l o " , 2 2 , " Gabane " ] )
Using the pandas module is even more convenient to use to read and save data to CSV
files. This is demonstrated below in program 1.53.
Program 1.53: Reading and saving CSV data using pandas.
1 import p a n d a s a s pd
2
3 # r e a d a CSV f i l e with p a n d a s
4 d f = pd . r e a d _ c s v ( " d a t a . c s v " )
5 print ( df )
6
For large numerical datasets, the NumPy library provides efficient methods for saving
and loading data. The program below demonstrates how this is done:
Program 1.54: Saving and loading data using NumPy.
1 import numpy a s np
2
3 # saving data to a t e x t f i l e
4 d a t a = np . a r r a y ( [ [ 1 , 2 , 3 ] , [ 4 , 5 , 6 ] ] )
5 np . s a v e t x t ( " d a t a . t x t " , d a t a )
6
7 # l o a d i n g d a t a from a t e x t f i l e
8 l o a d e d _ d a t a = np . l o a d t x t ( " d a t a . t x t " )
9 print ( loaded_data )
10
11 # s a v i n g d a t a t o a b i n a r y . npy f i l e
12 np . s a v e ( " d a t a . npy " , d a t a )
13
14 # l o a d i n g d a t a from a b i n a r y . npy f i l e
15 l o a d e d _ d a t a = np . l o a d ( " d a t a . npy " )
16 print ( loaded_data )
Note that using binary .npy files is more efficient for large numerical datasets than text
files.
1.3 Conclusion
In this chapter, we introduced the fundamentals of computer programming and the
Python language, equipping you with the basic skills to write and execute code. These
skills form the foundation for solving computational problems in the physical sciences.
In the next chapter, we will apply programming to numerical methods, focusing on
techniques for solving mathematical problems that lack exact solutions. Specifically,
we will explore iterative methods, the numerical solution of integral equations, and
the numerical solution of differential equations. Using Python, we will implement
these techniques to solve simple mathematical problems efficiently.
Pn (x) = a0 + a1 x + a2 x2 + · · · + an xn (an ̸= 0) .
This polynomial has at least one zero. An analytical solution to this polynomial can be
found only if n < 5, for example, it is easy to find the roots of a quadratic equation
(equation with n = 2) analytically as
√
−b ± b2 − 4ac
x= .
2a
For n ≥ 5, it is impossible to find such an analytical formula and hence the use of special
root-finding methods. They are iterative in nature. The idea behind these methods is
to find the value r such that f (r) = 0 for a function f (x) continuous on a predefined interval
[a, b]. Two important features associated with iterative methods are convergence and
stopping criterion. Each of these is discussed below under each individual method
(for more information, see Burden & Faires (2011, chapter 2) and Cheney & Kincaid
(2013, chapter 3)).
54
2.1 Iterative Methods CHAPTER 2. NUMERICAL METHODS
f (x) f (b)
f (r )
x
0 a r b
f (x) = 0
f (a)
Figure 2.1: Illustration of the bisection method for finding the root of a function f (x). Notice
that after each step (iteration N) the interval is halved, i.e., (b − a) /2N . If the root r is found
within a tolerance ϵ (i.e., |ri − ri−1 | < ϵ) the number of iterations N can be determined from
(b − a)/2N < ϵ.
⇒ N > 28,
i.e., the required number of iterations N should be greater than 28. Notice that the
program uses three functions: one defining the equation to solve, another defining the
bisection algorithm and the other defining the main function. This makes it easy to
follow and debug the program. The other thing to note is that instead of the while
loop shown in the algorithm above the program uses the do-while loop, so we force it
to perform the first iteration no matter what.
Program 2.1: The bisection method to solve one of the roots of the equation y = 2x2 +2x−12.
1 def f ( x ) :
2 # Function f
3 y = 2 ∗ x ∗ ∗ 2 + 2 ∗ x − 12 # This e q u a t i o n h a s two r e a l r o o t s : 2 & −3
4 return y
5
6 def b i s e c t i o n ( a , b , e p s i l o n , N) :
7 # Function b i s e c t i o n
8 print ( f " { ’ i ’: >6} | { ’ r ’: >10} | { ’ f ( r ) ’: >14} | { ’ Error | b − a | / 2 ’ : > 1 0 } " )
9 p r i n t ( " −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− " )
10
11 i = 0
12
13 while i <= N:
14 error = ( b − a ) / 2.0
15 r = a + error
16
17 print ( f " { i : >6} | { r : > 1 0 . 8 f } | { f ( r ) : > 1 4 . 8 f } | { abs ( e r r o r ) : > 1 0 . 8 f } " )
18
19 i f f ( r ) == 0 or abs ( e r r o r ) < e p s i l o n :
20 p r i n t ( f " The r o o t v a l u e i s { r : . 8 f } a f t e r { i + 1} i t e r a t i o n s . " )
21 return
22
23 if f ( a ) ∗ f ( r ) > 0:
24 a = r
25 else :
26 b = r
27
28 i += 1
29
30 p r i n t ( " \ nMethod f a i l e d s i n c e t h e e r r o r " , abs ( e r r o r ) , " i s s t i l l g r e a t e r than " )
31 print ( f " epsilon = { epsilon }. " )
32 p r i n t ( " \ n I n c r e a s e t h e number o f i t e r a t i o n s N and t r y a g a i n . " )
33
34 def main ( ) :
35 # F u n c t i o n main
36 N = i n t ( input ( " E n t e r t h e maximum number o f i t e r a t i o n s , N: \ n " ) )
37 a = f l o a t ( input ( " E n t e r t h e lower l i m i t of the search i n t e r v a l , a : \ n" ) )
38 b = f l o a t ( input ( " E n t e r t h e upper l i m i t of the search i n t e r v a l , b : \ n" ) )
39 e p s i l o n = f l o a t ( input ( " E n t e r t h e tolerance , epsilon : \ n" ) )
40
41 # N = 50
42 # a = −1
43 # b = 2.5
44 # e p s i l o n = 1e −8
45
46 b i s e c t i o n ( a , b , e p s i l o n , N)
47
48 i f __name__ == " __main__ " : # i t i s b e s t p r a c t i c e i n Python t o u s e t h i s c o n s t r u c t
49 main ( )
f (x0 )
x1 = x0 −
f ′ (x0 )
f (xi )
xi+1 = xi −
f ′ (xi )
until a sufficiently accurate value is reached. Algorithm 3 shows how the method is
implemented in a computer. A graphical representation of how the method is imple-
mented is shown in figure 2.2.
f (r ) = 0
x
0 r xn+1 xn
Figure 2.2: Illustration of the Newton-Raphson method for finding an isolated root of a
function f (x). xn+1 is a better approximation than xn for the root r of f (x).
The following tolerance formats can be used for the Newton-Raphson method:
(i) |xi − xi−1 | < ϵ
|xi −xi−1 |
(ii) |xi |
< ϵ, xi ̸= 0
′ h2 ′′
f (x0 ) + hf (x0 ) + f (x0 ) + · · · = 0.
2
To determine h from this expansion, we consider only the first two terms of the Taylor
series, such that
f (x0 ) + hf ′ (x0 ) = 0.
f (x0 )
h=− .
f ′ (x0 )
f (x0 )
x1 = x0 + h = x0 − .
f ′ (x0 )
f (x + h) − f (x)
f ′ (x) = lim ,
h→0 h
for small h,
f (x + h) − f (x)
f ′ (x) ≈ .
h
f (xn−1 ) − f (xn )
f ′ (x) ≈ .
xn−1 − xn
This is the secant method, thus named because the term multiplying f (xn ) is the
inverse of the slope of a secant line to the graph of f (x) (see figure 2.3). Algorithm 4
shows how the secant method is implemented.
f (x)
secant line
f (r ) = 0
x
0 r xn+1 xn xn-1
Figure 2.3: Illustration of the Secant method for finding an approximation to the root of a
function f (x).
The secant method has two advantages over Newton’s method in that:
(i) No differentiation is required.
(ii) Only one function is evaluated at each step once the computations have started.
Example
Therefore, we can use the bisection method to approximate the root. Table 2.1 shows
the results of the three methods in approximating the root of f (x).
Table 2.1: Comparison of the bisection, Newton-Raphson and secant methods in approximating
the root of the function f (x) = e − sin 2 , x ∈ [0.4, 0.5]. The tolerance used in all the
−x πx
methods is ∈= 1 × 10−8 .
From table 2.1 it is clear that the Newton’s method is the fastest of the three followed
by the secant method while the bisection comes in last.
We will now look at solutions of integral equations. In this section we will consider
the trapezoidal and Simpson’s rules.
Z b
f (x) dx
a
to a given degree of accuracy. Several methods exist that can be used to find an ap-
proximate solution to this integral to a desired precision. Amongst these methods are
the trapezoidal and the Simpson’s rules which are the focus of the discussion here.
f (x1)
f (x0)
x
a = x0 b = x1
Figure 2.4: Illustration of the trapezoidal rule for finding an approximate solution to the defi-
Rb
nite integral a f (x) dx.
The trapezoidal rule works by approximating the curve between (x0 , f (x0 )) and (x1 , f (x1 ))
with a trapezium (see figure 2.4) and calculating its area. The area is given by base ×
average height, i.e.,
1
(x1 − x0 ) × [f (x0 ) + f (x1 )] or
2
1
h [f (a) + f (b)] where h = (b − a) .
2
If we divide the region into n trapezoids, we can then add the areas of these trapezoids
to obtain the total area under the curve in the region [a, b]. The parameter h will
then be defined by h = (b − a) /n. The easier option is to divide the region into equal
small sub-regions which then gives rise to what is known as the compound or composite
trapezoidal rule:
" n−1
#
b
b − a 2 ′′
Z
h X
I= f (x) dx = f (a) + 2 f (xi ) + f (b) − h f (µ),
a 2 i=1 | 12 {z }
error term
for some µ in [a, b]. The following shows how the trapezoidal rule can be implemented
in a program.
Z b
h a+b
f (x) dx = f (a) + 4f + f (b) , or
a 3 2
Z x2
h b−a
f (x) dx = [f (x0 ) + 4f (x1 ) + f (x2 )] where h = .
x0 3 2
f (x)
x
a = x0 x1 b = x2
Figure 2.5: Illustration of the Simpson’s rule for finding an approximate solution to the definite
Rb
integral a f (x) dx.
n
Z b 2
∼ hX
I= f (x) dx = [f (x2j−2 ) + 4f (x2j−1 ) + f (x2j )] , or
a 3 j=1
n n
Z b 2
−1 2
h X X b−a 4 4
I= f (x) dx = f (a) + 2 f (x2j ) + 4 f (x2j−1 ) + f (b) − h f (µ),
a 3 j=1 j=1 |180 {z }
error term
for some µ in the interval [a, b]. Just like in the composite trapezoidal rule, h is now
h = (b − a) /n for (n) an even integer. Algorithm 6 shows implementation of the
Rb
composite Simpson’s rule to approximate the integral I = a f (x) dx:
The trapezoidal and Simpson’s rules form part of a class of numerical integration rules
known as the closed Newton-Cotes formulae with h = (xn − x0 ) /n. The following is
a list of the formulae:
• n = 1: Trapezoidal rule
Z x1
h
f (x) dx = [f (x0 ) + f (x1 )]
x0 2
• n = 2: Simpson’s rule
Z x2
h
f (x) dx = [f (x0 ) + 4f (x1 ) + f (x2 )]
x0 3
Note that Newton-Cotes formulae are considered open if the end points are not in-
cluded in the nodes used to approximate the integral, that is, x0 = a+h and xn = b−h,
therefore h = (b − a) /n + 2. Here are some examples -
(1) The trapezoidal rule for a function f on the interval [0, 2] is
Z 2
f (x) dx ≈ f (0) + f (2) , and
0
Z 2
1
f (x) dx ≈ [f (0) + 4f (1) + f (2)] .
0 3
The results to three places for some elementary functions are summarised in table 2.2.
Note that Simpson’s rule gives better results compared to trapezoidal rule, furthermore
it gives exact solutions for any polynomial of degree 3 or less.
Table 2.2: Numerical integration of some elementary functions using trapezoidal and Simp-
son’s rules.
√
f (x) x2 x4 1/ (x + 1) 1 + x2 sin x ex
Exact value 2.667 6.400 1.099 2.958 1.416 6.389
Trapezoidal 4.000 16.000 1.333 3.326 0.909 8.389
Simpson’s 2.667 6.667 1.111 29.64 1.425 6.421
′ dy
y = = f (z, y) , for a ≤ z ≤ b,
dz
subject to the initial condition
y (a) = α.
Examples
Differential problems are used to model problems in science and engineering that in-
volve the change of some variable with respect to another. The majority of these
problems require the solution to an initial-value problem, i.e., solution to a differential
equation that satisfies a given initial condition.
The Initial-value Problem
dy
= f (z, y) , a ≤ z ≤ b, y (a) = α,
dz
is said to be a well-posed problem if:
(i) a unique solution, y (z), to the problem exists.
(ii) for any ϵ > 0, there exists a positive constant k (ϵ) with the property that, when-
ever |ϵ0 | < ϵ and δ (z) is continuous with |δ (z)| < ϵ on [a, b], a unique solution,
g (z), to the problem
dg
= f (z, g) + δ (z) , a ≤ z ≤ b, g (a) = α + ϵ0 ,
dz
Moffat/PHY485 - Microcomputing for Physical Sciences notes 70
2.3 Differential Equations CHAPTER 2. NUMERICAL METHODS
exists with
|g (z) − y (z)| < k (ϵ) ϵ, for all a ≤ z ≤ b.
The problem in (ii) above is called a perturbed problem associated with the original
problem in (i). Numerical methods will always be concerned with solving a perturbed
problem, since any error introduced in the representation will result in a problem of
this type.
Let our solution function y be represented by its Taylor series
′ 1 2 ′′ 1 ′′′ 1 1
y (z + h) = y (z)+hy (z)+ h y (z)+ h3 y (z)+ h4 y 4 (z)+· · ·+ hn y n (z)+· · ·
2! 3! 4! n!
′
y = f (z, y) , a ≤ z ≤ b, y (a) = ya ,
then
′
y (z + h) ≈ y (z) + hy (z) , that is,
y (z + h) = y (z) + hf (z, y (z))
can be used to step from z = a to z = b with n steps of size h = (b − a) /n. Euler’s
method can thus be expressed as
wi ≈ y (zi )
w0 = y (z0 ) = α
wi+1 = wi + hf (zi , wi ) , for i = 1, 2, . . . , N − 1.
Algorithm 7 approximates the solution of the initial-value problem using Euler’s method:
A pseudocode with prescribed values for N, a, b and α (= y (a) orw) is shown in algo-
rithm 8.
Note that to use the program for pseudocode 8 a code for the function f (z, w) is
needed, an example of which is shown in program 2.2.
Runge-Kutta methods
The Runge-Kutta methods have the accuracy of the higher-order Taylor series methods
without the determination of the derivatives. The Runge-Kutta methods are classified
by the order (number of terms/ functions to be evaluated). The Runge-Kutta method
of order 2 has two forms: the modified Euler and Heun’s methods.
(i) Modified Euler Method
It evaluates
w0 = α
h
wi+1 = wi + [f (zi , wi ) + f (zi+1 , wi + hf (zi , wi ))] , for i = 1, 2, 3 . . . , N − 1.
2
w0 = α
h 2 2
wi+1 = wi + f (zi , wi ) + 3f zi + h, wi + hf (zi , wi ) , for i = 1, 2, 3 . . . , N − 1.
4 3 3
Higher orders of the Runge-Kutta methods are available, with order 4 being the most
commonly used method:
w0 = α
k1 = hf (zi , wi )
h 1
k2 = hf zi + , wi + k1
2 2
h 1
k3 = hf zi + , wi + k2
2 2
k4 = hf (zi + 1, wi + k3 )
1
wi+1 = wi + (k1 + 2k2 + 2k3 + k4 ) , for i = 1, 2, 3 . . . , N − 1.
6
The use of k1 , k2 , k3 and k4 is to eliminate the need for successive nesting in the second
variable of f (z, y). Implementation of the RK4 method to approximate the solution
of the initial-value problem
′
y = f (z, y) , a ≤ z ≤ b, y (a) = α
77
3.2 Distributions CHAPTER 3. DATA REDUCTION AND ERROR ANALYSIS
3.2 Distributions
3.2.1 Empirical Distributions
Suppose we have gathered measurements of a parameter x, represented as
xi = {x1 , x2 , · · · , xN } . (3.1)
Organising these data points can unveil underlying patterns. One common method
for visualising data is through a histogram. It is constructed by grouping or arrang-
ing measurements or data points by intervals. For example, a grouping of imaginary
PHY485 students’ Test 1 marks are shown in the form of a histogram in figure 3.1.
The test marks are grouped into intervals 0−9, 10−19, 20−29, 30−39, 40−49, 50−59,
60 − 69 and 70 − 79. The intervals form the horizontal axis and numbers or frequency
form the vertical axis. A histogram provides valuable information on the characteristics
of the data such as central tendency (characterised by the mean and the median), disper-
sion (characterised by the range, the standard deviation and the variance) and general shape.
6
Frequency
0
0 20 40 60 80
PHY485 Test Marks
Figure 3.1: Histogram showing imaginary test marks for PHY485 students.
N
1 X
x= xi , (3.2)
N i=1
is the sum of all observations divided by number of observations. The arithmetic mean
is
(i) often used as an estimate of the population mean µ for the underlying theoretical
distribution.
(ii) sensitive to outliers (extreme values).
Median
x
e = x(N +1)/2 , (3.3)
x-value in the middle of the data. It is
(i) often used as an alternative measure of central tendency.
(ii) also affected by outliers, but not to the same extent as the arithmetic mean. How-
ever, their absolute values do not influence it, only the position.
Mode
Mode is another important measure of central tendency. It is the most frequent value
of the data. Data has no mode if no value appears more frequent than any of the values.
A unimodal frequency distribution is one with one mode, a bimodal is one with two
modes and lastly a frequency distribution with many modes is called multimodal. Just
like the median the mode is not highly influenced by outliers.
Measures of Dispersion
Range
Range is the difference between the highest and lowest values and it is given by
Standard deviation is the most useful measure of dispersion. It is the average deviation
of each data point from the mean. It is expressed as
v
u N
u 1 X
s=t (xi − x)2 (3.5)
N − 1 i=1
Note that equation 3.5 gives the standard deviation of a sample or empirical distribu-
tion and it is often used as an estimate of the population standard deviation σ. When
calculating the population standard deviation N is used instead of N − 1 in the denom-
inator.
Variance
This is another important measure of dispersion. It is simply the square of the standard
variation. It is defined by
N
1 X
2
s = (xi − x)2 . (3.6)
N − 1 i=1
The variance is commonly used in many applications instead of the standard deviation.
pi if x = xi ,
f (x) = P (X = x) = (3.7)
0 otherwise.
In this equation, x is any of the six numbers and it represents the probability that
X = x. We call this function the probability mass function (PMF) since the outcome is
discrete. Clearly, the sum of all probabilities is unity, i.e.,
N
X
f (xi ) = 1. (3.8)
i=1
We may also be interested in knowing the probability of getting less than or equal to
any of the numbers on the dice. Writing that as F (x) = P (X ≤ x) defines what we
call the cumulative distribution function (CDF) (see figure 3.2).
Cumulative Probability
Probability
0 0
1 2 3 4 5 6 1 2 3 4 5 6
Outcome Outcome
(a) (b)
Figure 3.2: An example of the probability distribution functions for a discrete random vari-
able. Figure (a) shows the probability of any outcome occuring when throwing a dice. The
corresponding cumulative distribution is shown in (b).
Suppose now that you are looking at the height of all women in the world. The global
average height of adult women is about 165 cm with about 95% of heights falling
within two standard deviations of the mean (i.e., within the interval of 1̃51 - 179 cm).
Between any two values, let us say 163 cm and 164 cm, there are an infinite number
of heights, e.g., 163.25 cm, 163.25678 cm, 163.2567896 cm , etc. If we use X the
same way as was used above in the case of discrete probability distribution, there are
an infinite number of values X can assume in this case and the probability of selecting
any one specific value is zero. The best thing one can do is get a value that is very close
to the exact one. Because of this situation, height is considered a continuous variable
and we now consider ranges of values (such as P (a < X < b), P (X > c), etc.) instead
Z +∞
f (x) dx = 1. (3.9)
−∞
Its cumulative distribution function F (x) (that is, the probability that the random vari-
able X will take a value less than or equal to x) is given by
Z x
F (x) = P (X ≤ x) = f (t) dt f or − ∞ < x < ∞. (3.10)
−∞
0.5 0.5
0 0
140 165 190 140 165 190
Height (cm) Height (cm)
(a) (b)
Figure 3.3: An example of a probability density function (PDF) f (x) showing the heights of all
women in the world in (a) and its cumulative distribution function (CDF) in (b).
Note that when x → ∞, the value of F (x) approaches unity (or equals to 1). Precisely,
a PDF specifies the probability of a random variable, call it X, falling within a particular
range of values, say x = a and x = b. The probability is given by
Z b
P (a < X < b) = f (x) dx. (3.11)
a
Following from the definitions given in equations 3.10 and 3.11, we can conclude the
following:
Uniform Distribution
A uniform distribution is a distribution with a constant probability. Its corresponding
PDF is defined as
1
f (x) = = const, (3.13)
N
with a cumulative distribution function F (x) = x/N . The variable x in this case has
any of N possible values, that is, it is random.
Binomial Distribution
The binomial distribution gives the discrete probability of x successes out of N ex-
periments or trials, with probability p of success in any given trial. Its PDF is defined
as
N x
f (x) = p (1 − p)N −x (3.14)
x
with a cumulative distribution function given by
x
X N
F (x) = pi (1 − p)N −i (3.15)
i
i=0
where
N N!
N
Cr = = (3.16)
r r! (N − r)!
is the binomial coefficient. For the binomial distribution the mean µ = N p and the
variance σ 2 = N p (1 − p). Examples of results that can be modelled by a binomial
distribution include the probability of obtaining a tail when tossing a coin, probability
of a drug to cure a disease, the percentage chance of an adult Motswana suffering
from a specific disease and the average percentage of defective computer chips at a
manufacturing plant. The outcome of such trials are said to be dichotomous, you
either get a success or fail.
Poisson Distribution
Is an approximation to the binomial distribution for the special case where the number
of trials N → ∞ and the success probability p → 0 with one single parameter λ = N p.
It characterises events with extremely low occurrence, e.g., floods and storms. The
probability density function is defined as
e−λ λx
f (x) = , (3.17)
x!
and the cumulative distribution function is given by
x
X e−λ λi
F (x) = . (3.18)
i=0
i!
The parameter λ describes both the mean and the variance of this distribution, i.e., the
variance equals the mean of the distribution.
Normal (Gaussian) Distribution
This is used when the mean is the most frequent and most likely value. It is also an
approximation for the binomial distribution for the special case where p = 0.5 (sym-
metric) and N → ∞. Its PDF is defined by
" 2 #
1 1 x−µ
f (x) = √ exp − , (3.19)
σ 2π 2 σ
and the cumulative distribution function is
" 2 #
∞
y−µ
Z
1 1
F (x) = √ exp − dy, (3.20)
σ 2π −∞ 2 σ
Figure 3.4 shows a sketch of a probability density function for a normal distribution.
2σ σ mean σ 2σ
Figure 3.4: Bell shape of a normal distribution. Note that 68% of the x values lie between ±1
s.d. from the mean, 95% of the x values lie between ±2 s.d. from the mean and 99.7% of the
x values lie between ±3 s.d. from the mean.
When the mean is 0 and standard deviation is 1 we have what is known as standard
normal distribution with a PDF described by
2
1 x
f (x) = √ exp − . (3.21)
2π 2
The normal distribution has been found to characterise many physical events, for in-
stance, test marks for PHY112 take the form of a normal distribution.
k
X (Oi − Ei )2
χ2 = , (3.22)
i=1
Ei
where Oi is the observed number of events in a category/ class or frequency bin, Ei
is the expected number according to some known theoretical distribution (or fitting
function) and k is the number of categories (or frequency bins). The null hypothesis
(i.e., that Oi ’s are drawn from the population represented by Ei ’s) can be rejected if χ2
has a large value.
The χ2 -test is often compared to a χ2 distribution with df = k − 1 degrees of freedom.
For example, let us say we want to perform a goodness of fit test on an example problem
involving number of absentees in five consecutive lectures of PHY485. We expect 6
students to be absent for each of the 5 lectures. Observed and expected number of
absentees for the five consecutive lectures is shown in the table of figure 3.5 together
with the corresponding χ2 value. We want to test the hypothesis that the two come
from the same theoretical
Figure 3.5: Observed and Expected absentees for five consecutive lectures for PHY485.
distribution (e.g., χ2 distribution), i.e., the null hypothesis, with a 5% chance of in-
dependence (alternative hypothesis). In order to do this we need two parameters, the
degrees of freedom df and the critical χ2 value. For this particular data, df = 4 and the
critical χ2 value is 9.49. The critical χ2 value is obtained from standard χ2 distribution
tables, an extract of which is shown in figure 3.6. Using this information we find that
our χ2 value is 9.17. Since this value is below the critical value, we cannot reject the
null hypothesis! Figure 3.7 summarises this scenario.
The next subsection highlights a way of incorporating errors in meausured quantities
in the computation of the final error in the dependent variable.
Figure 3.6: Extract of χ2 distribution table with the critical value shown for our PHY485
example.
Figure 3.7: Principles of a χ2 -test. This is for the example of absentee numbers in five con-
secutive PHY485 lectures. The alternative hypothesis that the observed and the expected dis-
tributions are independent is rejected since the χ2 value is below the critical value.
whose uncertainties are uncorrelated (note that errors can be correlated, one example
is in nutritional epidemiology where errors in the estimation of the intake of different
nutrients are correlated), the final result can be estimated from
2 2
∂z ∂z
σz2 = σx2 + σy2 + · · · (3.25)
∂x ∂y
Table 3.1: Uncertainties of specific mathematical operations. Note that a and b are constants.
Operation z σz
1 X
µ = x̄ ≡
xi , (3.26)
N
where x̄ is the mean of the sample, and its standard deviation or standard error is ex-
pressed as
σ s
σµ = √ ≈ √ , (3.27)
N N
with s representing the standard deviation of the sample.
y = ax + b (3.28a)
δy = y − (ax + b) . (3.28b)
If the measurement errors are normally distributed, the least-squares principle allows
the manipulation of the above equations to determine the values of a and b. The least-
squares criterion states that for N measurements, we can find the values of a and b that
minimize
XN N
X
2
(δyi ) = [yi − (axi + b)]2 = M. (3.29)
i=1 i=1
To obtain a minimum, we set the first derivative of M with respect to a and b to zero
to obtain
N
∂M X
= −2 [yi − (axi + b)] xi = 0, and (3.30a)
∂a i=1
N
∂M X
= −2 [yi − (axi + b)] = 0 (3.30b)
∂b i=1
Equation 3.30 yields two simultaneous equations from which a and b are determined
to be
N N
P PN PN
i=1 yi xi − i=1 yi i=1 xi
a= PN 2 PN 2 , and (3.31a)
N i=1 xi − x
i=1 i
PN PN PN PN
x2i yi − xi y i xi
b= i=1
PN
i=1
P i=1
i=1
2 or (3.31b)
2 N
N i=1 xi − i=1 xi
= y − ax (3.31c)
P 2
N
i=1 (x i − x) (y i − y)
R 2 = PN 2 PN 2
. (3.32)
i=1 (x i − x) i=1 (y i − y)
Note that R2 is defined in the range 0 ≤ R2 ≤ 1 with R2 ≈ 1 indicating a good fit and
R2 ≈ 0 indicating that the fit does not significantly follow the trend of the data better
than the mean of the data.
The variance on the data points or variable y is given as the sum of the squares of the
residuals, i.e.,
PN
(δyi )2
s2y = i=1
. (3.33)
N −2
Note that the standard deviation sy represents the distribution of δy values about the
best-fit line.
Since a and b are derived from yi and xi , we expect these values to have deviations that
can be expressed in terms of the measured variables. The standard deviations of a and
b are given as
v
u N
sa = sy u
t PN P 2 (3.34a)
2 N
N i=1 xi − i=1 xi
v
u PN
x2i
(3.34b)
u i=1
sb = sy u PN P 2
N
t
2
N i=1 xi − i=1 xi
)
sin(
)
cos(
Figure 3.8: A schematic of a car uniformly accelerating from rest down a slope.
Thus, with the standard deviations of the slope a and the intercept b, we can indicate
regions of confidence using the attributes of the Normal Distribution.
As an example, consider a car uniformly accelerating down a slope, such as in fig-
ure 3.8. In the absence of friction, the car travels a distance s (t) = 21 g sin (θ) t2 from
rest. Suppose the angle of the incline is 30◦ and a first year student sets up an ex-
periment to measure the distance traveled after every second from rest. The student
records the distance traveled s (t) as 0, 3.31, 9.79, 15.05, 40.00, 62.01, 90.24, 120.17,
156.96, 200.09 and 245.25 in meters. We can write a program in Python that applies
a linear least-squares fit (with the line defined by y = ax + b - where y = s (t), x = t2 ,
a = 12 g sin (θ) and b = 0) to this data to help the student find the values of a and b
and their associated errors. ‘g’ is then easily computed from the slope of the line, i.e.,
2a
g = sin(θ) . This is illustrated in the program 3.1 below. ‘g’ is found to be ≈9.88 m/s2
and the correlation coefficient is R2 = 0.9972, approximately equals to 1, indicating an
excellent fit (see figure 3.9 for an illustration of this).
Program 3.1: Least-squares fit program for a car moving down an inclined plane.
1 """
2 lsq_demo . py
3
4 Description :
5 Uses l i n e a r l e a s t − s q u a r e s method t o f i n d t h e b e s t f i t t i n g l i n e t o a d a t a s e t .
6
7 Method :
8 Assuming t h e b e s t − f i t l i n e i s d e f i n e d by y = ax + b with r e s i d u a l s
9 d e l _ y = y − ( ax + b ) , we can minimize t h e s q u a r e s o f t h e r e s i d u a l s by
10 c a l c u l a t i n g t h e i r p a r t i a l d e r i v a t i v e s with r e s p e c t t o a and b t h e n s e t t i n g
11 t h a t t o z e r o . This r e s u l t s i n two s i m u l t e n e o u s e q u a t i o n s i n t h e unknown
12 p a r a m e t e r s a and b from which we can e a s i l y d e t e r m i n e them .
13
14 Author :
15 @ 2 0 22 , L . C. Moffat , U n i v e r s i t y o f Botswana
16 Version 1.0 12/02/2025
17
18 Language :
19 Python
20 """
21 import math
22
23 def main ( ) :
24 # Data p o i n t s
25 x = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 1 0 ] # Time i n s e c o n d s
26 y = [0 , 3.31 , 9.79 , 15.05 , 40.00 , 62.01 , 90.24 , 120.17 , 156.96 , 200.09 , 245.25]
# Distance in meters
27
28 N = len ( y ) # Length o f a r r a y y
29
30 # Square the time v e c t o r
31 xx = [ x i ∗ ∗ 2 f o r x i in x ]
32
33 # I n i t i a l i z e sums
34 xsum = sum ( xx ) # Sum o f x i
35 ysum = sum ( y ) # Sum o f y i
36 x2sum = sum ( x i ∗ ∗ 2 f o r x i in xx ) # Sum o f x i ^2
37 xysum = sum ( xx [ i ] ∗ y [ i ] f o r i in range (N) ) # Sum o f x i ∗ y i
38
39 # C a l c u l a t e sums f o r R^2
40 xxb = sum ( ( xx [ i ] − xsum / N) ∗ ∗ 2 f o r i in range (N) ) # Sum o f ( x i − x _ b a r ) ^2
41 yyb = sum ( ( y [ i ] − ysum / N) ∗ ∗ 2 f o r i in range (N) ) # Sum o f ( y i − y _ b a r ) ^2
42 xxbyyb = sum ( ( xx [ i ] − xsum / N) ∗ ( y [ i ] − ysum / N) f o r i in range (N) ) # Sum o f
( x i − x_bar ) ∗ ( yi − y_bar )
43
44 # C a l c u l a t e s l o p e ( a ) and i n t e r c e p t ( b )
45 a = (N ∗ xysum − xsum ∗ ysum ) / (N ∗ x2sum − xsum ∗ ∗ 2 )
46 b = ( x2sum ∗ ysum − xsum ∗ xysum ) / (N ∗ x2sum − xsum ∗ ∗ 2 )
47
48 # C a l c u l a t e R^2
49 R2 = ( xxbyyb ∗ ∗ 2 ) / ( xxb ∗ yyb )
50
51 # C a l c u l a t e f i t t e d y v a l u e s and e r r o r s
52 y _ f i t = [ a ∗ xx [ i ] + b f o r i in range (N) ]
53 e r r o r = [ y [ i ] − y _ f i t [ i ] f o r i in range (N) ]
54 del_y2sum = sum ( e ∗ ∗ 2 f o r e in e r r o r )
55
56 # C a l c u l a t e v a r i a n c e and s t a n d a r d d e v i a t i o n s
57 y _ v a r = del_y2sum / (N − 2 ) # V a r i a n c e i n f i t t e d y v a l u e s
58 s a = math . s q r t ( y _ v a r ) ∗ math . s q r t (N / (N ∗ x2sum − xsum ∗ ∗ 2 ) ) # S t a n d a r d
deviation in a
59 s b = math . s q r t ( y _ v a r ) ∗ math . s q r t ( x2sum / (N ∗ x2sum − xsum ∗ ∗ 2 ) ) # S t a n d a r d
deviation in b
60
61 # Print r e s u l t s
62 p r i n t ( f " { ’ Time ( s ) ’ : < 1 0 } { ’ D i s t a n c e ( o b s e r v e d ) ’ : < 2 0 } { ’ D i s t a n c e ( f i t t e d ) ’ : < 2 0 } { ’
Error ’: <10} " )
63 print ( " −" ∗ 60)
64 f o r i in range (N) :
65 p r i n t ( f " {x [ i ] : < 1 0 . 2 f } {y [ i ] : < 2 0 . 2 f } { y _ f i t [ i ] : < 2 0 . 2 f } { a b s ( e r r o r [ i ] ) : < 1 0 . 2 f } " )
66
67 p r i n t ( f " \ nThe l i n e a r f i t l i n e i s o f t h e form : y = { a : . 4 f }x + {b : . 4 f } " )
68 p r i n t ( f " \ nThe c o r r e l a t i o n c o e f f i c i e n t R^2 = {R2 : . 4 f } " )
69 p r i n t ( f " \ nThe v a r i a n c e i n y i s s ^2 = { y _ v a r : . 4 f } m" )
70 print ( f " \ nStandard d e v i a t i o n s : s a = { s a : . 4 f } , sb = { sb : . 4 f }" )
71 p r i n t ( f " \ nTherefore , a c c e l e r a t i o n due t o g r a v i t y g = { ( a ∗ 2 / 0 . 5 ) : . 4 f } m/ s ^2 " )
72
73 i f __name__ == " __main__ " :
74 main ( )
140
120
100
80
60
40
20
0
0 10 20 30 40 50 60 70 80 90 100
Time2 (s 2)
Figure 3.9: Least-squares fit of distance versus time-squared plot of a car uniformly accelerat-
ing from rest down a slope.
The topic of linear least-squares is discussed in detail in books by Devore (2009) and
Walpole & Myers (1993) and also online by Wikipedia Contributors (2024), Weisstein
(n.d.) and others. If you are interested to learn more, and especially for your projects,
consult those texts.
Burden, R. & Faires, J. D., 2011. Numerical Analysis, Cengage Learning, 9th edn.
Cheney, E. & Kincaid, D., 2013. Numerical Mathematics and Computing, International
Edition, Cengage Learning, 7th edn.
Colestock, J., 2017. Python - Control Flow, https://www.youtube.com/watch?v=
RpoUAGp7Pcc&t=11s&ab_channel=JamesColestock, Last Accessed: 2025-02-03.
Devore, J. L., 2009. Probability and Statistics for Engineering and the Sciences, Brooks/Cole,
Cengage Learning, 8th edn.
IEEE Spectrum, 2024. The top programming languages 2024, https://spectrum.
ieee.org/transportation-2024, Last Accessed: 2024-12-29.
Johansson, R., 2015. Numerical Python: Scientific Computing and Data Science Applications
with Numpy, Scipy and Matplotlib, Apress, Urayasu, Chiba, Japan, 1st edn.
McKinney, W., 2018. Python for Data Analysis: Data Wrangling with Pandas, NumPy,
and IPython, O’Reilly Media, Sebastopol, CA, 2nd edn.
Python Software Foundation, n.d. Built-in Types, https://docs.python.org/3/
library/stdtypes.html#ranges, Last Accessed: 2025-01-12.
Sweigart, A., 2019. Automate the Boring Stuff with Python: Practical Programming for
Total Beginners, No Starch Press, San Francisco, CA, 2nd edn.
95
References