December 4: Python Numeric and Mathematical Modules
Today is a bit less bad than yesterday: As part of the Python Standard Library traversal, after yesterday's extensive post about data types, today we're going to look at Python modules that take care of numbers and maths and make us very happy because we don't need to implement them ourselves. Or wait for NumPy to install.
Highlights
- The
numbers
module contains abstract base classes so you can check for features of given numbers usingisinstance
. - The Python documentation takes a stand for
tau
and againstpi
. Good documentation. -
cmath
provides mathematical operations and functions for complex numbers – it exists separately frommath
so that people aren't surprised when they runmath.sqrt(-1)
. 🤣 -
fractions.limit_denominator()
allows you to take a float and turn it into the number it was meant to be. Go teamfractions
. - The fact that
decimal
is singular andfractions
is plural will never cease to confuse me. - Apparently there's both
random.gauss()
andrandom.normalvariate()
??? -
statistics.NormalDist
creates a normal distribution either from a set of samples or the mean and a standard deviation.
numbers
The numbers
module contains abstract base classes, so you can check for features of given numbers using isinstance
.
These classes are Number
(the root class), Complex
(guarantees real
, imag
and conjugate()
), Real
, Rational
(guarantees numerator
and denominator
) and Integral
.
math
Here you'll find mathematical functions defined by the C standard. Those include basic functions like ceil()
and
floor()
, trunc()
, remainder()
and prod()
, isclose()
, factorial()
and comb()
(binomial coefficient),
gcd()
(greatest common divisor) and lcm()
(least common multiple, new in 3.9). Square root, logarithmic
functions and exponential/power functions, trig functions and conversion between degree and radians, plus some functions
I've never heard of. Additionally, you get pi
, e
, tau
plus advertisement for tau and against pi, inf
and nan
.
There's also a bit of floating point handling, as of 3.9: ulp()
returns the value of the least siginificant bit of a
given number ("unit in the last place"), and nextafter()
returns the next float value from a in direction b.
cmath
cmath
contains helper functions for dealing with complex numbers – exactly like math
, only that all return values
are always complex numbers, even when they could be expressed as real numbers. Use polar()
and rect()
to convert
between the two different coordinate representations. There are a ton of calculation functions: exp
, log
, log10
,
sqrt
, all the trigonometric functions, and isclose()
to see if two complex numbers are close to each other.
This module exists to avoid confusing the average math
module user, who would not expect math.sqrt(-1)
to actually
return a result. 🤣
decimal
Who wants floating point arithmetic that doesn't constantly try to screw you over? I do! decimal.Decimal
is here for
all of us.
Decimals
Decimals are immutable objects with a sign, a coefficient and an exponent. They generally follow float
norms in also
understanding about positive and negative zero and infinity. They are instantiated with a number, a string or a tuple.
You usually do not want to pass float
values, and instead go with their string representation. You can get the most
significant digit with .adjusted()
, get the value of e**x
with exp()
, run log10()
and ln()
, and run all sorts
of arithmetic operations – you'd choose to use the decimal
object methods to make sure you're dealing with the
precision you want. Run normalize()
if you want to make sure the number is as readable as possible. Use helper methods
like is_finite()
, is_nan()
, is_signed()
, is_zero()
and the like to learn more about a number.
Context
All decimal operations take place in a context, which you can change. The context specifies precision, rounding rules,
limits on exponents, flags indicating the results of operations, and trap enablers which determine whether signals are
treated as exceptions. You can use getcontext()
and setcontext()
to interact with it, or use the localcontext
context manager to change it just for a while. Read the docs for a list of rounding flags, trap/signal flags and other
attributes (like prec
) that you can change with the context.
Signals
Signals can be ignored, treated as information or as errors. They arise when "exceptional conditions arise" during computation, such as inexact operations, division by zero or overflows.
Working with threads
Every thread retains its own context object, so you can change your decimal context without influencing other threads.
fractions
You know how floats are evil and bad? Fractions are not evil and not bad! You can instantiate them from two numbers, or
a single number, or even a string. You can do all sorts of math on them and run as_integer_ratio()
(much more useful
than on float
s!). Even more useful: limit_denominator()
returns the closest fraction with a denominator smaller than
n=100000
, which is great for converting a float
to the number it was meant to be!
random
All random
generators share the same base generator, random.Random
, which you can instantiate if you need a separate
generator. Almost all functions rely on random.random()
(which returns values between 0 and 1), and then runs some
transformations on it. The generators are pseudo-random and should not be used for security purposes.
Bookkeeping functions
random.seed()
starts the generator with a seed or with system time (use for reproducibility). With getstate()
and
setstate()
, you can manipulate the generator status.
The main generator functions are randrange()
for integers, choice()
, choices()
, shuffle()
and sample()
for
sequences. For real-value distributions, there's the base random()
for the 0, 1 interval, randbytes()
and
randbits()
for n random bytes or bytes (new in 3.9), uniform()
for any interval, triangular()
for a weighted
interval, gauss()
and normalvariate()
and a lot of other distributions. Wish I knew enough math to appreciate it.
Alternative Generator
Normal random
methods use random.Random
as generator, but you can also use random.SystemRandom
, which uses
os.urandom()
if it is available.
statistics
This is basically our scientific calculator – not a competitor with NumPy, but making sure we can draw graphs and
conclusions. Functions support int
, float
, Decimal
and Fraction
.
The statistics package includes different sorts of mean()
and median()
calculations, plus mode
, multimode
and
quantile
functions. It also has measures for spread, such as stdev()
and variance()
. Additionally, there is a
NormalDist
class that creates a standard distribution from either a mean and a standard deviation (boring) or a set of
samples (woo-hoo!)