Mathematical Operators#

Learning outcomes#

  • Develop familiarity with basic mathematical operations in Python.

  • Understand the consequences of doing maths on non-numerical data types.

  • Understand how to access some additional, more complex mathematical operations.

Prerequisites#

Arithmetic#

Python is extremely useful for numerical computing. There are seven principle mathematical operations that Python can do, the notation for which might be slightly different to what you’re used to (e.g. × is written as * in Python). Further mathematical operations (such as logarithms, trigonometry, and calculus) are available through imported modules and libraries (such as math).

These are the principal mathematical operations:

Operation

Mathematical Notation

Pythonic Notation

Addition

\(a + b\)

a + b

Subtraction

\(a - b\)

a - b

Multiplication

\(a \times b\)

a * b

Division

\(a \div b\)

a / b

Exponent

\(a ^ b\)

a ** b

Modulo

\(a \textrm{ mod } b\)

a % b

Floored Division

\(a // b\)

a // b

The modulo and floored division operations may be new to you.

  • Modulo computes the remainder from the division of two numbers. For example, 5 % 2 returns 1, and 19 % 4 returns 3.

  • Floored division is the partner to modulo, and returns only the integer part of the division. For example. 5 // 2 returns 2, and 19 // 4 returns 4.

  • Other, more complicated operations are also available through Python libraries.

Importantly, Python can only perform maths on variables of the data type integer or float. Maths performed on strings or lists will result in strange outcomes (see below for some examples).

A single line of code may have many mathematical operations. In this event, Python will follow the standard order of mathematical operations: you might know this as BODMAS. Use round brackets “()” when coding your formulas to avoid ambiguity.

Maths can be done both with numbers and with variable, as long as the variable is of the correct type (integer or float). Storing numbers as variables becomes extremely useful when they are being used often, and when they are needed to a large number of significant figures. For example, instead of writing out π or Avogadro’s number each time it is used, you can store it at the beginning and only call it by the name you have given it.

pi = 3.141592653589
avogadro = 6.02214e23

product = pi * avogadro * 4

The value of the variable product is now that of 3.14 × 6.022×10-23 × 4, which equals 7.567644313153783e+24 .

Note that to represent standard form, Python uses the symbol en instead of ×10n.

Assigning variables using maths

You can perform a mathematical calculation and assign it to a variable, which can be called inside a print statement.

mass_H = 1.008 # g mol-1
mass_O = 15.999 # g mol-1

mass_H2O = 2 * mass_H + mass_O
print("The mass of water is: " , mass_H2O, " g mol-1") 

The output of this code is:
The mass of water is: 18.015 g mol-1

Remember to separate objects in a print statement with commas, and always provide units for an output!


Using maths inside a print() statement

You can do mathematical calculations within the print statement.

mass_H = 1.008 # g mol-1
print("The mass of a hydrogen molecule is: ", 2 * mass_H, " g mol-1")

The output of this code is:
The mass of a hydrogen molecule is: 2.016 g mol-1

The value 2.016 has been calculated, but is not stored as a variable like it is in the previous example. If you wanted to do further calculations with the number 2.016, it would be better to store it as a variable.


Maths in an f-string

You can do mathematical calculations inside f-strings. Just like previously, we can store the results inside a variable and then call it from the f-string, or do the calculation within the f-string itself.

mass_C = 12.008 # g mol-1
mass_H = 1.008 # g mol-1
mass_methane = mass_C + 4 * mass_H

# Calling up a variable that has already been calculated
print(f"The mass of methane is: {mass_methane:.2f} g mol-1")

# Doing maths inside an f string
print(f"The mass of ethane is: {2 * mass_C + 6 * mass_H:.5f} g mol-1")

The output of this is:

The mass of methane is: 16.04 g mol-1
The mass of ethane is 30.06400 g mol-1

The syntax :.5f indicates that the calculation should be given to 5 decimal places. The number can be changed to any number of desired decimal places. More information on the formatting codes can be found here Most useful will be the f specifier (for floats to control number of decimal points) and the e specifier (for scientific notation).


Exercise: Addition and Subtraction

Using variables to store the atomic masses of hydrogen, carbon, oxygen, and nitrogen, write a program to calculate the molecular masses of the following species.

    1. Ethanol (C2H6O), doing the calculation inside a print() statement
    2. cyclohexanone (C6H10O), doing the calculation in a variable which you then print
    3. Nitrobenzene (C6H5NO2), using an f string

The atomic masses are:

H = 1.008
C = 12.011
O = 15.999
N = 14.007
Click to view answer

Your code should look something like this:

H = 1.008 # g mol-1
C = 12.011 # g mol-1
O = 15.999 # g mol-1
N = 14.007 # g mol-1

print("The mass of ethanol is: " , 2 * C + 6 * H + O, " g mol-1" )

mass_cyclohexanone = 6 * C + 10 * H + O
print("The mass of cyclohexanone is: " , mass_cyclohexanone, "g mol-1")
 
print(f"The mass of nitrobenzene is: {6 * C + H * 5 + N + 2 * O} g mol-1")

You should have got the output:
The mass of ethanol is: 46.069 g mol-1
The mass of cyclohexanone is: 98.145 g mol-1
The mass of nitrobenzene is: 123.111 g mol-1

We only have to define the masses once, and they can be reused for all three sums.


Exercise: Multiplication and Division

The interaction potential between two isolated ions in water, Na+ (ionic radius = 116 pm) and Cl- (ionic radius = 157 pm) is given by the following equation:

\( V(r) = \frac{q_{Na^+} \ q_{Cl^-}}{4 \ \pi \ \epsilon \ \epsilon_{0} \ r}\ \)

Where \(q_{Na^+}\) and \(q_{Cl^-}\) are the relative charges on the ions (equivalent to ± the charge on an electron, 1.602×10-19 Coulombs), \(r\) is the distance between ionic nuclei (sum of ionic radii given above) in m, \(\epsilon\) is the relative permittivity (\(\epsilon\) = 78.7 Fm-1 in water), and \(\epsilon_0\) is the permittivity of free space (\(\epsilon_0\) = 8.85×10-12 Fm-1).

By copying out and completing the code below, calculate the interaction potential of a sodium and chlorine ion in water using the above equation.

q_Na = 
q_Cl = 
pi = 3.1415
e = 
e_0 = 
r = 

interaction_potential = 

print(interaction_potential)
Click to view answer

Your code should look something like this:

q_Na = 1.602e-19 # Coulombs 
q_Cl = -1.602e-19 # Coulombs
e = 78.7 # F m-1
e_0 = 8.85e-12 # F m-1
r = 116e-12 + 157e-12 # m
pi = 3.1415 

interaction_potential = (q_Na*q_Cl)/(4 * pi * e * e_0 *r)

print(interaction_potential , " J")

You should have got the output: -1.074e-20 J

In this sum, we have used round brackets to avoid ambiguity with the top and bottom of the fraction.

Once we have defined each of the constants, it becomes very easy to use them again later in the code. One thing to always remember is to put units on your outputs!


Exercise: Exponents

The equilibrium constant of an equation

aA + bB ⇌ cC + dD

is given by

\( K_c = \frac{[C]^c \ [D]^d}{[A]^a \ [B]^b} \)

For the following equation at equilibrium, write a program which calculates the equilibrium constant to 1 decimal place. Work it out on a calculator and compare your answers.

2SO2(g) + O2(g) ⇌ 2SO3(g)

Concentrations at equilibrium:
[SO3] = 5.0 × 10−2
[O2] = 3.5 × 10−3
[SO2] = 3.0 × 10−3

Click to view answer

You should have got the output: 7.9e4

The answer you got from Python should match your calculator.

Think further: Double the value of SO3. What happens to the answer? What if we had lots of different concentrations we wanted to find the equilibrium constant for; how could we store the information to use later, or to plot in a graph? What if we wanted to identify what concentrations of reagents lead to the reverse reaction being favoured (equilibrium constant less than 1)? What if we wanted to do all of this at once, in one program?

We will learn how to do more powerful and complex analysis later on, using lists, for loops, and if statements in a few lesson’s time.


Mathematical operations on non-numerical data types#

In Python, you need to be careful with variable types. As alluded to in the introduction, using the wrong data type can lead to numerous unintended consequences. It can be made obvious with an error message when you try to run your code (e.g. TypeError or ValueError messages), or it could just output an incorrect answer without an error message (e.g. with string concatenation).

If you are getting errors in your code, it is often useful to check the data type of your variables using the function type().

If your variables are the wrong data types, you can convert between data types (as long as they are in the correct format) using the functions float(), int(), and str(). This is referred to as casting, e.g. casting an integer to a float variable.

    int_var = 1 #This declares an integer of 1
    float_var = float(int_var)  #This casts the integer, 1, to a float, 1.0,
Error message: TypeError

Many mathematical operations can only be performed on variables of the integer or float type, and cannot be performed on variables of the string type, even if that string is a number. Often, this will result in an error message popping up when you try to run it. Or, potentially worse, no error but unexpected behaviour.

print("3" / 2)

The code above results in the error: TypeError: unsupported operand type(s) for /: 'str' and 'int'.

This is a TypeError, which is raised when an operation is performed on an unsupported data type. When we put the number 3 in speech marks, we are telling Python that this is just plain text, not a number. To Python, it is as incomprehensible as writing answer = sausage / 2.

If we do accidentally try to use division on a string, the error message straight away tells us what has gone wrong.

However, it is not always this simple.


String concatenation

Run the two print statements below.

print("3" + "1")
print("3" * 5)

Instead of the mathematical answers 4 and 15, you get this:
31
33333

Instead of adding the numbers together, Python has added the two strings together in the first instance, or multiplied the same one multiple times in the second instance. This is called concatenation, and is an operation that can be performed not just on strings, but also other data types, like lists:

print([1, 2, 3] + [2, 2, 2])
print([1, 2, 3] * 3)

Which results in:
[1, 2, 3, 2, 2, 2]
[1, 2, 3, 1, 2, 3, 1, 2, 3]

The most important thing to realise here is that this is not an error in the behaviour of Python. So Python will not shout at you that something has gone wrong, and the printed line on your screen doesn’t give any indication that something is wrong. If you aren’t careful, you might print an output that looks right, but is in fact a mistake borne from unintentional concatenation.


Exercise: Maths errors

You are writing a program to calculate the mean of some data. Which of the below programs will result in an error, and why?

a)

a = 12.0
b = 5.1
c = 8.5
mean = a + b + c / 3
print(mean)

b)

a = "12.0"
b = "5.1"
c = "8.5"
mean = (a + b + c)/3
print(mean)

c)

a = "12.0"
b = "5.1"
c = "8.5"
mean = (int(a) + float(b) + float(c))/3
print(mean)
Click to view answer

The answer is all three will result in error! Try for yourself to confirm!

a) This will not produce an error message, but that does not mean there is not an error present. Due to maths order of operations, this code will produce the answer 19.93, which is clearly not a correct mean. To avoid ambiguity, the top of the fraction should be in round brackets (). It is important to only use round brackets in a sum, because other kinds of brackets are used to define other data types and would result in TypeErrors.

b) This will produce an error message. The variables here are defined as strings, and although these strings can be concatenated together using +, they cannot be divided using /. You will get the error message TypeError: unsupported operand type(s) for /: 'str' and 'int'.

c) This one looks correct, and it almost works! The problem in this code is int(a), which has resulted in the error message: ValueError: invalid literal for int() with base 10: '12.0'. Value errors occur when a function (in this case, the int() function) receives a value of the wrong type. The int() function can only take floats, or integer strings, as an input.

Here are two correct versions of this code:

a = 12.0
b = 5.1
c = 8.5
mean = (a + b + c) / 3
print(mean)
a = "12.0"
b = "5.1"
c = "8.5"
mean = (float(a) + float(b) + float(c))/3
print(mean)

Both versions result in the output: 8.53


More complex mathematical operations#

As mentioned previously, some mathematical functions are not native to Python. We must import them from another module or library in order to use them. One of the most basic and useful is the built-in Python module math (documentation can be found here) or cmath if dealing with complex numbers.

Quick note on terminology: a module is a file containing the functions we want to import. A library is a collection of modules. More information on importing modules can be found in the importing modules lesson, although this is not necessary to understand this section of the lesson.

It is always important to look up the documentation of the functions you are importing to use, as some can only take specific inputs. For example, in math, the value of pi is called without using brackets (just math.pi will call the correct value), and trigonometric functions will do their calculations using radians, not degrees!

Importing a specific function#

To access a specific function, we must import it from the module. Below, we import the base 10 logarithm function, log10() (more information on importing modules will be found in later lessons):

from module import function

The function can then be used throughout the rest of the program.

Importing a function from math

In this code, we want to import the function log10() to use when calculating pH from proton concentration using \( pH = -log_{10}[H^+]\).

from math import log10

conc_H = 1.0e-7

pH = log10(conc_H)
pH *= -1

print(pH)

The *= simply means to take the previous variable also called pH and multiply it by -1. Only the new value of pH can be called now, so only use this if you will no longer need to call the previous value of pH. Similarly, you can use +=, -=, and /=.


Importing pi and other constants

We can also import constants from math, such as pi, e, and even inf, infinity.

We import constants in the same way we import functions. The only difference is in how we use them. Constants are not the same as functions, and therefore do not need brackets.

from math import pi

circle_area = 4**2 * pi
print(circle_area)

Exercise: Importing a function

Using the relation

\( pH = -log_{10}[H^+]\)

Write a program that calculates the pH of a solution in which [H+] = 2.3 ×10-5 M.

Click to view answer

You should have got the output: pH = 4.64

Think further: Is this solution acidic or basic? We can work that out quite easily, but what if we had hundreds of values for hundreds of different solutions and wanted to identify only which ones were acidic? Later lessons will give you the tools to be able to do this.


Importing an entire module/library#

You can also import the entire library, giving you access to all the functions within it. You do this by writing simply:

import module

If you import the entire library, you can then call their functions in the following way:

answer = module.function()

Which tells us that we are referencing a function from the module named just after the full-stop (in this case, the module is called ‘module’). Referencing the module we are calling the function from is necessary because you often import lots of different modules and libraries, and you want to prevent errors caused by multiple different functions having the same name.

There are lots of libraries and modules which you can import, which serve different purposes, such as the scientific module NumPy, and the graphing library matplotlib.pyplot.

Importing the entire 'math' module

For example with the module math:

import math

# Calculate area of a circle using pi
radius = 4
circle_area = radius**2 * math.pi
 
# Calculate e^6
exp_answer = math.exp(6)

# Calculate cosine of 10 in radians
trig_ans = math.cos(10)

The math. part is very important if you are importing an entire library (not just a specific function). Without it, Python won’t know you are trying to call a function from the math module - it will think you have forgotten to define your own function! It gets especially confusing if you are importing many different libraries and modules, some of whom might use the same function name for a slightly different purpose. Python needs to know which one you are referring to!


Importing other modules

You can also import other modules and libraries, such as NumPy, SciPy, or MatPlotLib.pyplot.

For libraries with longer names, you can give them an alias which you can refer to instead of their full name. For example:

import numpy as np

# Calculate the numpy cosine of 0.78
ans = np.cos(0.78)
print(ans)

This is not recommended for modules with an already short name, such as math. If you give your modules extremely short names, you may well end up with other variables having the same name, causing errors!


Exercise: Import the module math and use multiple functions

A right-angled triangle sits inside a circle, with its hypotenuse equal to the diameter of the circle.

Write a program to calculate one of the angles, θ, of the triangle in degrees, given that the opposite edge of the triangle has length 2 cm and the circumference of the circle is 10 cm. Import the module math.

Useful information:

\(circle \ diameter = \frac{circumference}{\pi}\)

\(\theta = sin^{-1}(\frac{length\ of\ triangle\ edge\ opposite\ \theta}{length\ of\ triangle\ hypotenuse})\)

The math function for \(sin^{-1}\) is: asin().

The math function degrees() converts any value inside the brackets to degrees from radians.

Hint: The answer you should get is 38.9°.

Click to view answer
import math

circumference = 10 # cm
opposite_side = 2 # cm

diameter = 10 / math.pi # cm
hypotenuse = diameter # cm

theta_rads = math.asin(opposite_side/hypotenuse)
theta_degrees = math.degrees(theta_rads)

print(theta_degrees)

Since we know that the diameter and hypotenuse are equal to each other, we can easily set the variable ‘hypotenuse’, which will change when a new diameter is calculated. Note that we have used the function math.degrees() to convert our answer into degrees from radians.

Further Practice#

Question 1#

Find the velocity, \(v\), of a N2 molecule with a mass, \(m\), of 4.6×10-26 kg at a temperature, \(T\), of 293 K, given the following equation,

\[ v = \sqrt{\frac{3k_bT}{m}}, \]

where, \(k_b\) is 1.38×10−23 J/K.

Click to view answer
k_b = 1.38e-23
T = 293
m = 4.6e-26

v = ((3 * k_b * T) / m) ** (1 / 2)
print(f'velocity = {v:.1f} m/s')

You should have got the output:
velocity = 513.5 m/s

Remember there are many different ways of writing the same piece of code. Don’t worry about finding the ‘best’ solution at this point. As long as it works, you have succeeded!

Question 2#

The weak acid formic acid has a dissociation constant \(K_a\) that is related to the concentration of H+ ions by the following relation, where \(N\) is the amount of acid dissolved.

\[ K_a = \frac{[\textrm{H}^+]^2}{N - [\textrm{H}^+]}. \]

This can be rearranged to the form \(ax^2 + bx + c = 0\) and the value [H+] solved using the quadratic formula.

\[ [\textrm{H}^+]^2 + K_a[\textrm{H}^+] - K_aN = 0. \]

Write a program to calculate the concentration of H+ ions when \(K_a\) = 1.8 ×10-4 and \(N\) = 0.1 M. What pH is this?

Useful:
\( x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}. \)

Click to view answer
# Import all relevant libraries and functions at the beginning
from math import log10

# Define the variables we know
K_a = 1.8e-4
N = 0.1

# Assign a, b, and c by comparing the two forms
a = 1
b = K_a
c = -K_a * N

# Calculate the two potential answers of the quadratic formula
# To perform a square root, we are raising to the power of 1/2
H_conc_plus = (-b + (b ** 2 - 4 * a * c) ** (1 / 2)) / (2 * a)
H_conc_minus = (-b - (b ** 2 - 4 * a * c) ** (1 / 2)) / (2 * a)

print(f'H_conc_plus = {H_conc_plus:.5f} M')
print(f'H_conc_minus = {H_conc_minus:.5f} M')
 
# Find and print the pH
pH = log10(H_conc_plus) * -1
print(f'pH = {pH:.2f}')

You should have got the output:
H_conc_plus = 0.00415 M
H_conc_minus = -0.00433 M
pH = 2.38

The variable H_conc_minus can be disregarded, as it is not possible to have a negative concentration. It is important to run your code often as you write it, to identify if there are any errors, or if you are using the right values.

Even though we don’t use log10 until the end, we have imported it at the beginning of our code. This is good practice, as it both looks neater and reduces the likelihood of importing the same library or function multiple times.

When we are doing complicated maths, it is good to leave comments describing what is happening to make it easier for someone else to come along and edit it. Or for yourself when you come back to look at it after a long time!

Think further: Writing out the maths for the quadratic formula takes quite a long time, and it is easy to make a mistake. If we wanted to do multiple calculations with it, we would only want to write it out once. We could do this by defining our own function (covered later in the course), which would allow us to perform the calculation using only a shorthand, such as solve_quadratic(a, b, c).

Question 3#

The code below will not work. Identify why and correct it so that it does work. Run it yourself, and keep looking at your error message to figure out what is wrong. You should get an output of 4.86 cm.

# Find the length, x, of one side of a triangle using the cosine rule
 
math

side_b = "7" # cm
side_c = 5 cm
angle_A = 44 # degrees

x = side_b^2 + side_C**2 - (2 * side_b side_c * cos(angle_A)
x *= 1/2

print(x " cm")
Click to view answer

Here is the same code with the mistakes corrected. Did you find them all or did you have to look at the answer?

# Find the length, x, of one side of a triangle using the cosine rule

import math

side_b = 7 # cm
side_c = 5 # cm
angle_A = math.radians(44) # radians

x = side_b**2 + side_c**2 - (2 * side_b * side_c * math.cos(angle_A))
x **= 1/2

print(x , " cm")

Here are a list of the errors and their corrections, in the order they appear.

  • You must import math, not just write math.

  • The variable side_b should be an integer, not a string, or maths cannot be done on it. Remove the quotation marks from around the number to make it so.

  • The variable side_c should have its units in an inline comment, not just floating on their own.

  • angle_A is given in degrees, however the module math does trigonometry in radians. You can change degrees into radians using the math function math.radians().

  • The maths operator for exponents is ** not ^.

  • In the sum, the ‘c’ in side_C is capitalised when it shouldn’t be. Variables are case sensitive. Since side_c was defined the first time using a lowercase ‘c’, all following instances should also use a lowercase letter.

  • There is a missing multiplication symbol * between side_b and side_c.

  • ‘Cos’ is a function from the module ‘math’. Therefore, to use it we should do math.cos(angle_A), not just cos(angle_A). The function also only takes angles in radians, so make sure your input here is in radians, not degrees!

  • The sum is missing a closing bracket. All opened brackets must be closed in Python.

  • When doing the square root of x, the maths operator for exponents in this case should be **=, not *=.

  • There should be a comma between x and " cm" in the print statement.

You can see the importance of correct syntax!

Learning outcomes#

  • Develop familiarity with basic mathematical operations in Python, including +, -, *, /.

  • Understand how to access some additional, more complex mathematical function using the math module.

Summary#

  • Python’s basic maths uses the symbols +,-,*,/, **.

  • Use %, “modulo”, to find the remainder of a division, and //, “floored division” to return the integer part of a division.

  • Use order of operations (BODMAS/BIDMAS/PEMDAS) to ensure Python does the correct sum. Use round brackets (not any other type of brackets) to remove ambiguity.

  • Assign your calculation a variable to be able to call that value forward later.

  • Inside an f string, use :.3f to print your answer to 3 decimal places. Changing the number changes the number of decimal places displayed in the answer.

  • Use +=, -=, *=, etc. to change the value of a variable.

  • For more complex functions, import them from a library or module such as numpy or math. If you import the whole library, you must reference the library using modulename.function(), like in the calculation answer = math.sin(1).

  • Make sure to place the lines of code importing any functions or libraries at the beginning of the program.