Chapter 18. Numerical Programming

In this chapter, you learn how to use Python to work with numbers. You've already seen some arithmetic examples, but after reading this chapter, you'll have a better understanding of the different ways you can represent numbers in Python, of how to perform mathematical computations, and of efficient ways of working with large numerical data sets.

Numerical code lies at the heart of technical software, and is used widely in science, engineering, finance, and related fields. Almost any substantial program does some nontrivial numerical computation, so it pays to be familiar with some of the contents of this chapter even if you are not working in one of these fields. For instance, if you are writing a script to analyze web logs, you might want to compute statistics on the rate of hits on your web server; if you are writing a program with a graphical user interface, you might need math functions to compute the coordinates of the graphics in your GUI.

Parts of this chapter require some understanding of math beyond simple arithmetic. Feel free to skip over these if you have forgotten the math being used. The last section of this chapter, which discusses numerical arrays, is technically more advanced than most of the material in this book, but it's important reading if you plan to use Python for handling large sets of numbers.

Designing software that performs complex numerical computation, known as numerical analysis, is both a science and an art. There are often many ways of doing a computation, and numerical analysis tells you which of these will produce an answer closest to the correct result. Things can get tricky, especially when working with floating-point numbers, because, as you will see, a floating-point number is merely an approximation of a real number. This chapter mentions numerical precision but doesn't go into the finer points, so if you are embarking on writing software that performs extensive floating-point computations, consider flipping through a book on numerical analysis to get a sense of the kind of problems you might run into.

In this chapter you learn:

  • The different data types that relate to numbers.

  • Some basic (and advanced) math operators.

  • How to perform mathematical equations on arrays.

  • How to work with the math and array modules.

Numbers in Python

A number, like any object in Python, has a type. Python has three basic numerical types. One of these, int, represents integers, and float represents floating-point numbers. The third numeric type, which is covered later in this chapter, represents complex floating-point numbers.

Integers

You've already seen the integer type, int. If you write an ordinary number in your program like 42, called a literal number, Python creates an int object for it:

>>> x = 42
>>> type(x)
<class 'int'>

You didn't have to construct the int explicitly, but you could if you want, like this:

>>> x = int(42)

You can also use the int constructor to convert other types, such as strings or other numerical types, to integers:

>>> x = int("17")
>>> y = int(4.8)
>>> print(x, y, x - y)
17 4 13

In the first line, Python converts a string representing a number to the number itself; you can't do math with "17" (a string), but you can with 17 (an integer). In the second line, Python converted the floating-point value 4.8 to the integer 4 by truncating it—chopping off the part after the decimal point to make it an integer.

When you convert a string to an int, Python assumes the number is represented in base 10. You can specify another base as the second argument. For instance, if you pass 16, the number is assumed to be hexadecimal:

gt;>> hex_number = "a1"
>>> print(int(hex_number, 16))
161

You can specify hexadecimal literals by prefixing the number with 0x. For example, hexadecimal 0xa1 is equivalent to decimal 161. Similarly, literals starting with just a 0 are assumed to be octal (base 8), so octal 0105 is equivalent to decimal 69. These conventions are used in many other programming languages, too.

Long Integers

What's the largest number Python can store in an int? Prior to Python 3.0, Python used at least 32 bits to represent integers, which meant that you could store numbers at least as large as 231−1 and negative numbers as small as −231. If you needed to store a larger number, Python provided the long type, which represented arbitrarily large integers.

For example, long before the search engine Google existed, mathematicians defined a googol, a one followed by 100 zeros. To represent this number in Python, you used to type out the hundred zeros, or you could have saved yourself the trouble by using the exponentiation operator, **:

>>> googol = 10 ** 100
>>> print(googol)
1000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000

The preceding was an example of a long integer. Starting in Python 3.0, the long type no longer exists; instead, int has been extended so that there is no limit to the size of an integer.

Floating-point Numbers

In Python, a floating-point number is represented by a float object. A floating-point number is only an approximation to a real number, so you may sometimes see results that look strange. For example:

>>> x = 1.1
>>> x
1.1000000000000001
>>> print(x)
1.1

What's going on here? You assigned to x the floating-point approximation to the number 1.1. The floating-point number that Python can represent that is closest to 1.1 is actually a tiny bit different, and Python is honest with you and shows this number when you ask for the full representation of x. When you print x, however, Python provides you with a "nice" depiction of the number, which doesn't show enough decimal places to illustrate the floating-point approximation.

Simply entering x at the command prompt prints what you would get by calling repr(x). Entering print x prints what you would get by calling str(x).

As with integers, you can use the float constructor to covert strings to numbers (but only in base 10). For example:

>>> x = float("16.4")

Very large and very small floating-point numbers are represented with exponential notation, which separates out the power of ten. A googol as a floating-point number would be 1e+100, which means the number 1 times ten raised to the power 100. The U.S. national debt at the time this was written, according to the Treasury Department website, was:

>>> debt = 11322188570453.51

Python prefers exponential notation to print a number this large:

>>> print(debt)
1.13221885705e+13

You can also enter literals with exponential notation.

Formatting Numbers

You can convert any Python number to a string using the str constructor. This produces the text that would be printed by the print statement, as a string object. For simple applications, this is adequate.

For better control of the output format, use Python's built-in string formatting operator, %.

Note that this has nothing to do with the remainder operator. If you use % after a string, that's the string formatting operator. If you use % between two numbers, you get the remainder operator.

Following are some details on formatting numbers. If you are familiar with the printf function in C, you already know much of the syntax for formatting numbers in Python.

To format an integer, use the %d conversion in the format string. For a floating-point number, use %f. If you use %d with a floating-point number or %f with an integer, Python will convert the number to the type indicated by the conversion. For example:

>>> print("%d" % 100)
100
>>> print("%d" % 101.6)
101

You probably didn't really notice, because it's so obvious, that Python formatted these integers in base 10. For some applications, you might prefer your output in hexadecimal. Use the %x conversion to produce this. If you use %#x, Python puts 0x before the output to make it look just like a hexadecimal literal value, like so:

>>> print("%#x" % 100)
0x64

Similarly, %o (that's the letter "o," not a zero) produces output in octal, and %#o produces octal output preceded by a 0.

For integers, you can specify the width (number of digits) of the output by placing a number after the % in the format string. If the number starts with 0, the output will be left-padded with zeros; otherwise, it will be padded with spaces. In the examples that follow, the output is surrounded with parentheses so you can see exactly what Python generates for the %d conversions:

>>> print("z is (%6d)" % 175)
z is (   175)
>>> print("z is (%06d)" % 175)
z is (000175)

When you format floating-point numbers, you can specify the total width of the output, and/or the number of digits displayed after the decimal place. If you want the output to have total width w and to display p decimal places, use the conversion %w.pf in the format string. The total width includes the decimal point and digits after the decimal point. Unlike converting a float to an integer value, Python rounds to the nearest digit in last decimal place:

>>> x = 20.0 / 3
>>> print("(%6.2f)" % x)
(  6.67)

If you omit the number before the decimal point, Python uses as much room as necessary to print the integer part and the decimal places you asked for:

>>> print("(%.4f)" % x)
(6.6667)

You can demand as many digits as you want, but remember that a float carries a limited precision and, therefore, contains information for only 16 digits or so. Python will add zero digits to fill out the rest:

>>> two_thirds = 2.0 / 3
>>> print("%.40f" % two_thirds)
0.6666666666666666300000000000000000000000

The number you see may be slightly different, because architectures handle the details of floating-point computations differently.

If you omit the number after the decimal point (or specify zero decimal places), Python doesn't show any decimal places and omits the decimal point, too:

>>> print("(%4.f)" % x)
(   7)

For example, the following function formats the ratio of its arguments, num and den, as a percentage, showing one digit after the decimal point:

>>> def as_percent(num, den):
...     if den == 0:
...         ratio = 0
...     else:
...         ratio = float(num) / den
...     return "%5.1f%%" % (100 * ratio)
...
>>> print("ratio = " + as_percent(6839, 13895))
ratio =  49.2%

One nice thing about this function is that it confirms that the denominator is not zero, to avoid division-by-zero errors. Moreover, look closely at the format string. The first % goes with the f as part of the floating-point conversion. The %% at the end is converted to a single % in the output: Because the percent symbol is used to indicate a conversion, Python requires you to use two of them in a format string if you want one in your output.

You don't have to hard-code the width or number of decimal places in the format string. If you use an asterisk instead of a number in the conversion, Python takes the value from an extra integer argument in the argument tuple (positioned before the number that's being formatted). Using this feature, you can write a function that formats U.S. dollars. Its arguments are an amount of money and the number of digits to use for the dollars part, not including the two digits for cents:

>>> def format_dollars(dollars, places):
...     return "$%*.2f" % (places + 3, dollars)
...
>>> print(format_dollars(499.98, 5))
$  499.98

In the format string, you use * instead of the total width in the floating-point conversion. Python looks at the argument tuple and uses the first value as the total width of the conversion. In this case, you specify three more than the desired number of digits for dollars, to leave room for the decimal point and the two digits for cents.

Even more options are available for controlling the output of numbers with the string formatting operator. Consult the Python documentation for details, under the section on sequence types (because strings are sequences) in the Python Library Reference.

Characters as Numbers

What about characters? C and C++ programmers are used to manipulating characters as numbers, because C's char type is just another integer numeric type. Python doesn't work like this, though. In Python, a character is just a string of length one, and cannot be used as a number.

Occasionally, you might need to convert between characters and their numeric values. Python provides the built-in function ord to convert a single character to its numeric code and the function asc to convert back from a numeric code to a character. The numeric code must be between 0 and 255.

Strictly speaking, this code is not ASCII, because ASCII only goes up to 127. However, the first 127 values converted by ord and asc are ASCII code values.

If you are a Usenet regular, you are probably familiar with the rot13 cipher. It's not particularly secure; all it does is rotate letters of the alphabet 13 positions forward, wrapping around from "z" to "a." Using chr and ord functions, it's not hard to implement in Python:

def rot13_character(character):
    # Look up codes for ends of the alphabet.
    a = ord('a')
    z = ord('z')
    A = ord('A')
    Z = ord('Z')

    code = ord(character)
    # Rotate lower-case characters.
    if a <= code <= z:
        code = a + (code - a + 13) % 26
    # Rotate upper-case characters.
    elif A <= code <= Z:
        code = A + (code - A + 13) % 26
    # Leave other characters alone.
    else:
        pass
    return chr(code)

def rot13(plaintext):
    # Loop over letters in the text.
    ciphertext = ""
    for character in plaintext:
        ciphertext += rot13_character(character)
    return ciphertext

The program is composed of two functions. The first, rot13_character, applies rot13 to a single character. If it's an uppercase or lowercase letter, it is rotated 13 places; otherwise, it is left alone. (In case you are not familiar with the remainder operator, %, it is described in the next section.) The main function, rot13, takes the message to be coded (the "plaintext") and creates the encoded message (the "ciphertext") by rotating each letter.

Type the preceding code into a module file named rot13.py. In Python, import the module and try it out:

>>> import rot13
>>> message = rot13.rot13("This is a TOP-SECRET encoded message.")
>>> print(message)
Guvf vf n GBC-FRPERG rapbqrq zrffntr.

rot13 has the nice property that it is its own inverse: To decode a rot13-encoded message, you just apply rot13 to it again:

>>> print(rot13.rot13(message))
This is a TOP-SECRET encoded message.

Mathematics

In addition to the usual complement of arithmetic operations, Python includes some handy built-in math functions, and a math module that provides other commonly used functions. Coverage of arithmetic operators may seem obvious, but you should also understand some subtle points about how Python handles certain numeric types.

Arithmetic

Python provides the normal arithmetic operators + (addition), - (subtraction), * (multiplication), and / (division) for numerical types. You can mix numerical types when using these operators; Python automatically chooses the more flexible type for the result:

>>> i = 10
>>> f = 6.54
>>> print(i + f)
16.54

When adding an integer, i, and a floating-point number f, Python chose a float for the result.

These operators all have special forms for updating the values of variables, written by adding an equals sign right after the operator. Instead of writing

>>> total = total + 6
>>> coefficient = coefficient / 2

you can simply write:

>>> total += 6
>>> coefficient /= 2

and so forth.

When dividing two integers, Python always uses an integer type for the result, unless the result is fractional, as is the case in the following example:

>>> print 10 / 3
3.33333333333

The exponentiation operator ** is used previously in examples. It, too, works for integer and floating-point values. The function that follows uses it to compute compounded interest. The function returns the amount of money you would have if you put starting_balance in a bank account with APR annual_rate and waited for years:

>>> def compound(starting_balance, annual_rate, years):
...     return starting_balance * ((1 + annual_rate) ** years)
...

Ten grand in the bank at 4 percent APR for a century yields:

>>> print(compound(10000, 0.04, 100))
505049.481843

That's half a million bucks. Start saving now.

Also useful is the remainder operator %. It's like floor division, but instead of returning the quotient, it returns the remainder. Using it, you can format a number of months into whole years and remaining months:

>>> def format_months(months):
...   print("%d years, %d months" % (months // 12, months % 12))
...
>>> format_months(100)
8 years, 4 months

Built-in Math Functions

A few very common mathematical functions are available as built-in functions. The simplest is abs, which returns the absolute value of a number. The number that abs returns is the same type as the number you pass it:

>>> print(abs(−6.5))
6.5

Also useful are min and max, which return the smallest or largest of several values. You can call them either with several numeric arguments or with a single argument that is a sequence (such as a list or tuple) of numbers. The values needn't all be the same type:

>>> print(min(6, 7, 2, 8, 5))
2
>>> print(max([0, 43.5, 19, 5, −6]))
43.5

The round function rounds a floating-point value to a specified number of digits. This is similar to the behavior you saw before in the %f conversions, except the result is not a string but rather another floating-point number with which you can perform further computations. Specify the number to round, and the number of decimal places you want to keep:

>>> print(round(1234.56789, 2))
1234.57

You can even specify a negative number of decimal places, which rounds to that multiple of 10:

>>> print(round(1234.56789, −2))
1200.0

Lastly, the sum function adds numbers in a sequence. Together with range, you can compute the sum of the first 100 positive integers:

>>> print(sum(range(1, 101)))
5050

Suppose in your Python programming class you got a 96 percent and 90 percent on the two homework assignments, a perfect score on the final project, and an 88 percent on the final exam. What's your average for the class? Of course, you would write a Python function to compute it. The function uses sum and computes the mean, or average, value of a sequence of numbers:

>>> def mean(numbers):
...     if numbers:
...         return float(sum(numbers)) / len(numbers)
...     else:
...         raise ValueError("no numbers specified")
...
>>> print(mean([96, 90, 100, 88]))
93.5

It's a good idea to make sure that the sequence of numbers isn't empty, to avoid dividing by zero. In this case, the function raises an exception if the sequence is empty.

The math module contains the standard transcendental functions listed here. All these functions take float arguments and return float values:

  • Square root: sqrt

  • Exponentiation: exp

  • Logarithms: log (natural logarithm), log10 (base 10 logarithm)

  • Trigonometric functions: sin, cos, and tan; arguments are in radians

  • Inverse trigonometric functions: asin, acos, and atan; results are in radians

  • Hyperbolic functions: sinh, cosh, and tanh

A few other useful math functions are included:

  • hypot(x, y) is equivalent to sqrt(x ** 2 + y ** 2)

  • atan2(x, y) is like atan(x / y) but gets the quadrant right and handles a zero denominator

  • floor and ceil are the standard floor and ceiling functions; their results are integers but represented as float values

The math package also contains the constants pi and e.

Here's some sample code that uses the math module. It will give you flashbacks to your freshman physics class. It's a function that computes the time of flight and range of a projectile launched into the air (such as a cannonball), neglecting friction. Examine it at least long enough to understand how the Python code works. Pay attention to how sin, cos, and pi are imported from math, which saves you from having to refer to them as math.sin and so on. It's a handy technique for commonly used functions. Note also how carefully the units used in the arguments and results are documented. Many failed rocket launches attest to the importance of this practice.

from math import sin, cos, pi

def trajectory(velocity, angle):
    """Compute time of flight and range of a projectile.

    For a projectile with initial 'velocity' in meters/sec launched at
    'angle' from horizontal in degrees, returns time of flight in sec
    and range in meters, neglecting friction."""

    # Gravitational acceleration in meters/sec^2.
    g = 9.8
    # Convert 'angle' to radians.
    angle = angle * pi / 180
    # Compute horizontal and vertical components of velocity.
    v_h = velocity * cos(angle)
    v_v = velocity * sin(angle)
    # Compute the time of flight and range.
    tof = 2 * v_v / g
    range = tof * v_h
    return tof, range

Suppose you throw a ball into the air at 40 m/sec (about 90 mph) at a 45° angle. How long will it stay in the air, and how far away will it land? Save the preceding code into a file named ballistic.py, and then call the function like this:

>>> from ballistic import trajectory
>>> tof, range = trajectory(40, 45)
>>> print("time of flight: %.1f sec, range: %.0f meters" % (tof, range))
time of flight: 5.8 sec, range: 163 meters

Complex Numbers

A complex number is the sum of a real number and an imaginary number. In case you need a refresher, an imaginary number is a multiple of the imaginary unit, which is the square root of −1. Mathematicians (and math teachers) usually use the symbol i for the imaginary unit, whereas engineers often use j.

In Python, an imaginary number is written as a number followed by j (with no intervening spaces):

>>> imaginary_number = 16j

To create a complex number, add (or take the difference of) a real number and an imaginary number:

>>> complex_number = 6 + 4j

Python stores the complex number as a single object, whose type is complex:

>>> print(complex_number)
(6+4j)
>>> print(type(complex_number))
<class 'complex'>

If you prefer, you can use the complex constructor to construct complex number objects. This assignment is equivalent to the preceding one:

>>> complex_number = complex(6, 4)

Let's make sure that 1j is really the imaginary unit:

>>> print(1j ** 2)
(−1+0j)

This verifies that j2 is in fact −1, and also demonstrates that the result of an arithmetic operation involving complex values is itself a complex, even if the result happens to be a real number (that is, has a zero imaginary part).

You can't write j by itself to represent the imaginary unit. You must write 1j. By itself, j represents the variable named "j."

Both the real and imaginary parts of a complex object are stored as floating-point values, even if you specified them as integers. Remember that 1/3 in Python returns zero? Not so for complex numbers:

>>> print((1+0j)/3)
(0.333333333333+0j)

Arithmetic works for complex numbers as you would expect, and you can mix int, float, and complex in the same expression:

>>> print(2 * (10 + 3j) * (6.5 − 4j) / (1 − 1j) + 30)
(127.5+56.5j)

A few other operations round out Python's handling of complex numbers. First, the mathematical operations Re and Im return the real and imaginary parts of a complex number, respectively. These are provided in Python by attributes named real and imag that every complex object has. The value of each is a float:

>>> x = 5 − 6j
>>> print(x.real)
5.0
>>> print(x.imag)
−6.0

You saw before that the built-in abs function returns the absolute value of an int, long, or double object. For complex numbers, it returns the magnitude, which is the square root of the sum of the squares of the real and imaginary parts. You can verify this by using the hypot function discussed previously:

>>> print(abs(x))
7.81024967591
>>> import math
>>> print(math.hypot(x.real, x.imag))
7.81024967591

Finally, every complex object has a method conjugate, which returns the complex conjugate. This is the complex number with the same real part and negated imaginary part. Keep in mind that whereas real and imag are attributes (you don't call them), conjugate is a method (you must call it):

>>> print(x.conjugate())
(5+6j)

The transcendental functions in the math package work only on and return float values. For instance, you can't actually take the square root of −1 to obtain 1j:

>>> print(math.sqrt(−1))
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
ValueError: math domain error

That's a shame, because square roots and most of the other functions in math can be defined on complex numbers, too. Fortunately, Python provides a parallel module named cmath, which contains versions of the same functions that operate on and return complex objects. Its version of the square root function can handle −1:

>>> import cmath
>>> print(cmath.sqrt(−1))
1j

Arrays

You've learned how to perform computations with individual numbers, be they integers, floating-point numbers, or even complex numbers. What if you want to perform computations on many numbers? A group of numbers is typically arranged into an array. In this section, you learn different ways of implementing arrays in Python.

Keep in mind that arrays may be multidimensional. If you arrange numbers in a row, you have a one-dimensional array. A vector in linear algebra is an example; another is a list of daily closing prices of your favorite stock. You can also arrange your numbers on a rectangular grid, to produce a two-dimensional array. A grayscale image is often represented as a two-dimensional array, where each value is the lightness of one pixel in the image. In some applications, you may want to arrange your numbers into higher-dimensional arrays as well.

You've already seen one technique for constructing arrays in Python, when you wrote the mean function earlier. That function takes a sequence of numbers (of arbitrary length) and computes the numbers' mean. You can think of this sequence of numbers as an array and can think of mean as a function that acts on an array. You can invoke the function with a list of numbers, but it works with any sequence type, including tuples. These built-in types are the simplest way of building arrays.

Let's take another example of a function that operates on an array. You already wrote a function that computes the mean of an array of numbers. Now write a function that computes the standard deviation. To remind you, the standard deviation is an indication of how much the numbers vary among themselves. If they're all almost the same, the standard deviation will be small, whereas if they are all over the place, the standard deviation will be large. The formula for the standard deviation that you will use is shown in Figure 18-1.

Figure 18-1

Figure 18.1. Figure 18-1

Here x1, ..., xN are the numbers in the array, μ is their mean, and N is the length of the array.

You could implement a standard deviation function several different ways. Here's one of them:

from math import sqrt

def stddev(numbers):
    n = len(numbers)
    sum = 0
    sum_of_squares = 0
    for number in numbers:
        sum += number
        sum_of_squares += number * number
    return sqrt(sum_of_squares / n - (sum / n) ** 2)

This function loops over the numbers to compute their sum of squares. Simultaneously, it computes their sum, because it needs that to compute the mean. The last line computes the standard deviation according to the preceding formula. You might have noticed that the function uses number * number when computing the sum of squares instead of number ** 2; that's because squaring a number by multiplying it by itself is faster than using the general exponentiation operator.

Watch stddev in action. Remember that it takes one argument, a sequence of numbers (not several numerical arguments):

>>> print(stddev((5.6, 3.2, −1.0, 0.7)))
2.50137462208

Think for a moment about some advantages and drawbacks of using lists of numbers for arrays:

  • The elements of a Python list need not be of the same type. You can create a list for which some elements are int, float, long, and double, or other objects like strings or even other sequences. For some applications, this is very handy. For instance, you may want to store None in a sequence to indicate that a value is not known. For other applications, it's important to make sure that all of the values in an array are of the same type. In that case, you'll have to write extra code to ensure this.

  • Lists are single-dimensional, which makes them natural for expressing one-dimensional arrays. You can create two-dimensional arrays as lists of lists and higher-dimensional arrays analogously, but this can get complicated.

  • Lists are a standard part of Python. They're always available (you don't even have to import a module), and you already know how to use them.

  • Lists can be pickled. That makes it easy to store your list in a file for later use.

  • Internally, Python represents each element in a list as a separate object. Therefore, if you have a list of a million numbers (not at all unusual in many fields), you force Python to keep track of 1,000,001 objects: the list itself and all of its elements. This both wastes a lot of memory and makes Python work pretty hard whenever you access or modify the array.

This last point is a major limitation in many types of numerical work. To address it, you can use one of two other array implementations that store numbers more efficiently.

The Array Module

The Python standard library has just the ticket: a module array for one-dimensional arrays of numbers. The array type in this module stores numbers all together in memory as one object subject to the constraint that all of them must be of the same type. The numerical types supported by array are not the same as Python's numeric types. (In fact, they correspond to the numerical types in the C language.) An array can store numbers equivalent to Python's int and float, as well as integers of other sizes, and floating-point numbers of other precisions. (An array can store long values, but not arbitrarily large ones, and cannot store complex values at all.)

When you create an array, you have to specify the numerical type to store in the array. The type is specified by a single character. To store numbers as Python int objects, use "l"; for float use "d". (Other options are available; see the documentation for the array module for a list of them.) If you don't specify anything else, you'll get an empty array:

>>> import array
>>> a = array.array("l")
>>> print(a)
array('l')

Generally, you can use an array object just as you would an ordinary list. You can insert, append, or delete elements, and the indexing syntax is the same. (Note that in versions of Python earlier than 2.4, an array object is somewhat more limited than a list object.) For example:

>>> a.append(15)
>>> a.extend([20, 17, 0])
>>> print(a)
array('l', [15, 20, 17, 0])
>>> a[1] = 42
>>> print(a)
array('l', [15, 42, 17, 0])
>>> del a[2]
>>> print(a)
array('l', [15, 42, 0])

You can also convert a list or tuple to an array object by passing it to the constructor:

>>> t = (5.6, 3.2, −1.0, 0.7)
>>> a = array.array("d", t)
>>> print(a)
array('d', [5.5999999999999996, 3.2000000000000002, −1.0, 0.69999999999999996])

Here again you see the approximate nature of floating-point values.

In fact, because an array object behaves very much like a list, you can pass it to the same stddev function you wrote previously, and it works just fine:

>>> print(stddev(a))
2.50137462208

If you ever need to convert back to an ordinary tuple or list, just pass the array to the tuple or list constructor:

>>> back_again = tuple(a)
>>> print(back_again)
(5.5999999999999996, 3.2000000000000002, −1.0, 0.69999999999999996)

Compared to lists, array objects have the following advantages and disadvantages:

  • All elements of an array are the same type.

  • Like a list, an array is one-dimensional.

  • The array module is part of Python's standard library (but don't forget to import it).

  • An array object cannot automatically be pickled.

  • An array object stores its values much more efficiently than a list of numbers does. However, computations on the numbers are generally not much faster, because computations are performed using Python's normal number objects.

Summary

In this chapter, you learned how to perform many kinds of numerical computations in Python. You experimented first with Python's built-in integer and floating-point number types and saw how to use Python's built-in arithmetic operations. Then you moved on to higher mathematics, using the special functions in the math module and Python's complex number type.

Finally, you learned two different ways of representing arrays of numbers: The simplest method is to use a list or tuple of numbers. For more efficient storage, use the array module included with Python.

The key things to take away from this chapter are:

  • A number, like any object in Python, has a type. Python has three basic numerical types. One of these, int, represents integers, and float represents floating-point numbers. The third numeric type represents complex floating-point numbers.

  • You can convert any Python number to a string using the str constructor. This produces the text that would be printed by the print statement, as a string object.

  • To format an integer, use the %d conversion in the format string. For a floating-point number, use %f. If you use %d with a floating-point number or %f with an integer.

  • Python provides the normal arithmetic operators + (addition), - (subtraction), * (multiplication), and / (division) for numerical types.

  • The array type in the array module stores numbers all together in memory as one object. (Note that they must all be the same type.)

  • The math module contains the functions: sqrt (square root), exp (exponentiation), log/log10 (natural logarithm and base 10 logarithm), sin, cos, and tan (trigonometric functions), asin, acos, and atan (inverse trigonometric functions), and the hyperbolic functions: sinh, cosh, and tanh.

Exercises

  1. Write a function that expresses a number of bytes as the sum of gigabytes, megabytes, kilobytes, and bytes. Remember that a kilobyte is 1024 bytes, a megabyte is 1024 kilobytes, and so on. The number of each should not exceed 1023. The output should look something like this:

    >>> print(format_bytes(9876543210))
    9 GB + 203 MB + 5 KB + 746 bytes
  2. Write a function that formats an RGB color in the color syntax of HTML. The function should take three numerical arguments: the red, green, and blue color components, each between zero and one. The output is a string of the form #RRGGBB, where RR is the red component as a value between 0 and 255, expressed as a two-digit hexadecimal number, and GG and BB likewise for the green and blue components.

    For example:

    >>> print(rgb_to_html(0.0, 0.0, 0.0)  # black)
    #000000
    >>> print(rgb_to_html(1.0, 1.0, 1.0)  # white)
    #ffffff
    >>> print(rgb_to_html(0.8, 0.5, 0.9)  # purple)
    #cc80e6
  3. Write a function named normalize that takes an array of float numbers and returns a copy of the array in which the elements have been scaled such that the square root of the sum of their squares is one. This is an important operation in linear algebra and other fields.

    Here's a test case:

    >>> for n in normalize((2.2, 5.6, 4.3, 3.0, 0.5)):
    ...   print("%.5f" % n,)
    ...
    0.27513 0.70033 0.53775 0.37518 0.06253
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.163.197