Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Scientific Computing with Python - Second Edition

You're reading from  Scientific Computing with Python - Second Edition

Product type Book
Published in Jul 2021
Publisher Packt
ISBN-13 9781838822323
Pages 392 pages
Edition 2nd Edition
Languages
Authors (3):
Claus Führer Claus Führer
Profile icon Claus Führer
Jan Erik Solem Jan Erik Solem
Olivier Verdier Olivier Verdier
View More author details

Table of Contents (23) Chapters

Preface Getting Started Variables and Basic Types Container Types Linear Algebra - Arrays Advanced Array Concepts Plotting Functions Classes Iterating Series and Dataframes - Working with Pandas Communication by a Graphical User Interface Error and Exception Handling Namespaces, Scopes, and Modules Input and Output Testing Symbolic Computations - SymPy Interacting with the Operating System Python for Parallel Computing Comprehensive Examples About Packt Other Books You May Enjoy References
Container Types

Container types are used to group objects together. The main difference between the different container types is the way individual elements are accessed and how operations are defined. In this chapter, we discuss container types such as lists, tuples, dictionaries, and sets and related concepts such as indexing techniques. More specialized containers such as pandas DataFrames will be presented in Chapter 4: Linear Algebra – Arrays, Chapter 5: Advanced Array Concepts, and Chapter 10: Series and DataFrames.

In particular, we will cover the following topics:

  • Lists
  • Arrays
  • Tuples
  • Dictionaries
  • Sets

3.1 Lists

In this section, we introduce lists  the most frequently used container datatype in Python. With lists, we can refer to several, even totally different, Python objects together.

A list is, as the name hints, a list of objects of any kind:

L = ['a', 20.0, 5]
M = [3,['a', -3.0, 5]]

The first list in this example contains a string, a float, and an integer object. The second list in this example, M, contains another list as its second item.

The individual objects are enumerated by assigning each element an index. The first element in the list gets index . This zero-based indexing is frequently used in mathematical notation. Consider as an example for zero-based indexing the usual indexing of coefficients of a polynomial.

The index allows us to access the following objects from the two lists defined in the preceding example:

L[1] # returns 20.0
L[0] # returns 'a'
M[1] # returns ['a',-3.0,5]
M...

3.1.1 Slicing

Like cutting a slice from a loaf of bread, lists can be cut into slices. Slicing a list between i and j creates a new list containing the elements starting at index i and ending just before j.

For slicing, a range of indexes has to be given. L[i:j] means create a list by taking all elements from L starting at L[i] until L[j-1]. In other words, the new list is obtained by removing the first i elements from L and taking the next j-i elements

Here, L[i:] means remove the  first elements and L[:i] means take only the first elements:

L = ['C', 'l', 'o', 'u', 'd', 's']
L[1:5] # remove one element and take four from there:
# returns ['l', 'o', 'u', 'd']

You may omit the first or last bound of the slicing:

L = ['C', 'l'...

Strides

When computing slices, you may also specify a stride, which is the length of the step from one index to the other. The default stride is 1.

Here is an example:

L = list(range(100))
L[:10:2] # [0, 2, 4, 6, 8]
L[::20] # [0, 20, 40, 60, 80]
L[10:20:3] # [10, 13, 16, 19]

Note that the stride may also be negative:

L[20:10:-3] # [20, 17, 14, 11]

It is also possible to create a new list that is reversed, using a negative stride:

L = [1, 2, 3]
R = L[::-1] # L is not modified
R # [3, 2, 1]

Alternatively, you might want to use the method reverse, which is explained in Section 3.1.4: List methods.

3.1.2 Altering lists

Typical operations on lists are the insertion and deletion of elements and list concatenation. With the slicing notation, list insertion and deletion become obvious; deletion is just replacing a part of a list with an empty list []:

L = ['a', 1, 2, 3, 4]
L[2:3] = [] # ['a', 1, 3, 4]
L[3:] = [] # ['a', 1, 3]

Insertion means replacing an empty slice with the list to be inserted:

L[1:1] = [1000, 2000] # ['a', 1000, 2000, 1, 3]

Two lists are concatenated by the plus operator + :

L = [1, -17]
M = [-23.5, 18.3, 5.0]
L + M # gives [1, -17, 23.5, 18.3, 5.0]

Concatenating a list n times with itself motivates the use of the multiplication operator *:

n = 3
n * [1.,17,3] # gives [1., 17, 3, 1., 17, 3, 1., 17, 3]
[0] * 5 # gives [0,0,0,0,0]

There are no arithmetic operations on a list, such as elementwise summation or division. For such operations, we use arrays; see Section 3.2: A quick glance...

3.1.3 Belonging to a list

You may use the keywords in and not in to determine whether an element belongs to a list or not, which is similar to  and  in mathematics:

L = ['a', 1, 'b', 2]
'a' in L # True
3 in L # False
4 not in L # True

3.1.4 List methods

Some useful methods of the list type are collected in the following Table 3.1:

Command Action
list.append(x) Add x to the end of the list.
list.extend(L) Extend the list by the elements of the list L.
list.insert(i,x) Insert x at position i.
list.remove(x) Remove the first item from the list whose value is x.
list.sort() Sort the items of the list.
list.reverse() Reverse the elements of the list.
list.pop() Remove the last element of the list.
Table 3.1: In-place methods of the datatype list

These methods are in-place operations, that is, they change the list directly.

Other methods, such as those given in Table 3.2, do not alter the list, but return some information or create a new list object:

Command Action
list.count(x) Count how often x appears in the list.
list.copy() Create a copy of the list.
Table 3.2: Methods...

In-place operations

Most methods that result in a list are in-place operations. These are operations that change a Python object directly without creating a new object of the same type. They can be best explained by looking at the following example, reverse:

L = [1, 2, 3]
L.reverse() # the list L is now reversed
L # [3, 2, 1]

Be aware of in-place operations. You might be tempted to write:

L=[3, 4, 4, 5]
newL = L.sort()

This is correct Python. But in-place operations return the value None and alter the list. Therefore, using the variable newL as if it was a (sorted) list, for example, as 

print(newL[0])

causes an error:

TypeError: 'NoneType' object is not subscriptable

Here, we demonstrate in-place list operations:

L = [0, 1, 2, 3, 4]
L.append(5) # [0, 1, 2, 3, 4, 5]
L.reverse() # [5, 4, 3, 2, 1, 0]
L.sort() # [0, 1, 2, 3, 4, 5]
L.remove(0) # [1, 2, 3, 4, 5]
L.pop() # [1, 2, 3, 4]
L.pop() # [1, 2, 3]
L.extend(['a','b','c&apos...

3.1.5 Merging lists  zip

A particularly useful function for lists is zip. It can be used to merge two given lists into a new list by pairing the elements of the original lists. The result is a list of tuples (see Section 3.3Tuples):

ind = [0,1,2,3,4]
color = ["red", "green", "blue", "alpha"]
list(zip(color,ind)) 
# gives [('red', 0), ('green', 1), ('blue', 2), ('alpha', 3)]

This example also demonstrates what happens if the lists have different lengths: the length of the zipped list is the shorter of the two input lists.

The function zip creates a special iterable object that can be turned into a list by applying the function list, as in the preceding example. See Section 9.3: Iterable objects for more details on iterable objects.

3.1.6 List comprehension

A convenient way to build up lists is by using the list comprehension construct, possibly with a condition inside. The syntax of a list comprehension is:

[<expr> for <variable> in <list>]

or more generally:

[<expr> for <variable> in <list> if <condition>]

Here are some examples:

L = [2, 3, 10, 1, 5]
L2 = [x*2 for x in L] # [4, 6, 20, 2, 10]
L3 = [x*2 for x in L if 4 < x <= 10] # [20, 10]

It is possible to have several for loops inside a list comprehension:

M = [[1,2,3],[4,5,6]]
flat = [M[i][j] for i in range(2) for j in range(3)] 
# returns [1, 2, 3, 4, 5, 6]

This is of particular interest when dealing with arrays; see Section 3.2A quick glance at the concept of arrays.

List comprehension is closely related to the mathematical notation for sets. Compare and L2 = [2*x for x in L]. One big difference though is that lists are ordered while sets aren't; see...

3.2 A quick glance at the concept of arrays

The NumPy package offers arrays, which are container structures for manipulating vectors, matrices, or even higher-order tensors in mathematics. In this section, we point out the similarities between arrays and lists. But arrays deserve a broader presentation, which will be given in Chapter 4: Linear Algebra  Arrays, and Chapter 5: Advanced Array Concepts.

Arrays are constructed from lists by the function array :

v = array([1.,2.,3.])
A = array([[1.,2.,3.],[4.,5.,6.]])

To access an element of a vector, we need one index, while an element of a matrix is addressed by two indexes:

v[2]     # returns 3.0
A[1,2]   # returns 6.0

At first glance, arrays are similar to lists, but be aware that they are different in a fundamental way, which can be explained by the following points:

  • Access to array data corresponds to that of lists, using square brackets and slices. But for arrays representing matrices...

3.3 Tuples

A tuple is an immutable list. Immutable means that it cannot be modified. A tuple is written as a comma-separated sequence of objects (a list without brackets). To increase readability, you often enclose a tuple in a pair of parentheses:

my_tuple = 1, 2, 3     # our first tuple
my_tuple = (1, 2, 3)   # the same
my_tuple = 1, 2, 3,    # again the same
len(my_tuple) # 3, same as for lists
my_tuple[0] = 'a'   # error! tuples are immutable

Omitting parentheses can have side effects; see the following example:

1, 2 == 3, 4 # returns (1, False, 4) 
(1, 2) == (3, 4) # returns False

The comma indicates that the object is a tuple:

singleton = 1,   # note the comma
len(singleton)   # 1
singleton = (1,) # this creates the same tuple

Tuples are useful when a group of values goes together; for example, they are used to return multiple values from functions. See Section 7.3Return values.

3.3.1 Packing and unpacking variables

 You may assign several variables at once by unpacking a list or tuple:

a, b = 0, 1 # a gets 0 and b gets 1
a, b = [0, 1] # exactly the same effect
(a, b) = 0, 1 # same
[a,b] = [0,1] # same thing

Use packing and unpacking to swap the contents of two variables: 

a, b = b, a

3.4 Dictionaries

Lists, tuples, and arrays are ordered sets of objects. Individual objects are inserted, accessed, and processed according to their place in the list. On the other hand, dictionaries are unordered sets of pairs. You access dictionary data by keys. 

3.4.1 Creating and altering dictionaries

For example, we may create a dictionary containing the data of a rigid body in mechanics, as follows:

truck_wheel = {'name':'wheel','mass':5.7,
               'Ix':20.0,'Iy':1.,'Iz':17.,
               'center of mass':[0.,0.,0.]}

A key/data pair is indicated by a colon, :. These pairs are comma-separated and listed inside a pair of curly brackets, {}.

Individual elements are accessed by their keys:

truck_wheel['name']   # returns 'wheel'
truck_wheel['mass']   # returns 5.7

New objects are added to the dictionary by creating a new key:

truck_wheel['Ixy'] = 0.0

Dictionaries are also used to provide parameters to a function (refer to Section 7.2: Parameters and arguments in Chapter 7, Functions, for further information).

Keys in a dictionary can be, among others...

3.4.2 Looping over dictionaries

There are mainly three ways to loop over dictionaries:

  • By keys:
for key in truck_wheel.keys():
    print(key) # prints (in any order) 'Ix', 'Iy', 'name',...

or equivalently:

for key in truck_wheel:
    print(key) # prints (in any order) 'Ix', 'Iy', 'name',...
  • By value:
for value in truck_wheel.values():
    print(value) 
    # prints (in any order) 1.0, 20.0, 17.0, 'wheel', ...
  • By item, that is, key/value pairs:
for item in truck_wheel.items():
    print(item) 
    # prints (in any order) ('Iy', 1.0), ('Ix, 20.0),...

Please consult Section 14.4Shelves for a special dictionary object for file access.

3.5 Sets

The last container object we introduce in this section is defined by the data type set

Sets are containers that share properties and operations with sets in mathematics. A mathematical set is a collection of distinct objects. Like in mathematics, in Python the elements of a set are also listed within a pair of braces.

Here are some mathematical set expressions:

And here are their Python counterparts:

A = {1,2,3,4}
B = {5}
C = A.union(B)   # returns{1,2,3,4,5}
D = A.intersection(C)   # returns {1,2,3,4}
E = C.difference(A)   # returns {5}
5 in C   # returns True

Sets contain an element only once, corresponding to the aforementioned definition:

A = {1,2,3,3,3}
B = {1,2,3}
A == B # returns True

Moreover, a set is unordered; that is, the order of the elements in the set is not defined:

A = {1,2,3}
B = {1,3,2}
A == B # returns True

Sets in Python can contain all kinds of immutable objects, that is, numeric objects, strings...

3.6 Container conversions

We summarize in the following Table 3.3 the most important properties of the container types presented so far. (Arrays will be treated separately in Chapter 4: Linear Algebra  Arrays):

Type

Access

Order

Duplicate Values

Mutability

List

By index

Yes

Yes

Yes

Tuple

By index

Yes

Yes

No

Dictionary

By key

No

Yes

Yes

Set

No

No

No

Yes

Table 3.3: Container types

As you can see in the previous table, there is a difference in accessing container elements, and sets and dictionaries are not ordered.

Due to the different properties of the various container types, we frequently convert one type to another (see Table 3.4):

Container Types

Syntax

List → Tuple

tuple([1, 2, 3])

Tuple → List

list((1, 2, 3))

List, Tuple → Set

set([1, 2]), set((1, ))

Set → List

list({1, 2 ,3})

Dictionary →...

3.7 Checking the type of a variable

The direct way to see the type of a variable is to use the command type:

label = 'local error'
type(label) # returns str
x = [1, 2] # list
type(x) # returns list

However, if you want to test for a variable to be of a certain type, you should use isinstance (instead of comparing the types with type):

isinstance(x, list) # True

The reason for using isinstance becomes apparent after having read about the concept of subclassing and inheritance in Section 8.5Subclassing and inheritance. In short, often different types share some common properties with some basic type. The classical example is the type bool, which is derived by subclassing from the more general type int. In this situation, we see how the command isinstance can be used in a more general way:

test = True
isinstance(test, bool) # True
isinstance(test, int) # True
type(test) == int # False
type(test) == bool...

3.8 Summary

In this chapter, you learned how to work with container types, mainly lists. It is important to know how to fill these containers and how to access and manage their content. We saw that there is access by position or by keyword.

We will meet the important concept of slicing again in the next chapter on arrays. These are specially designed containers for mathematical operations.

3.9 Exercises

Ex. 1: Execute the following statements:

L = [1, 2]
L3 = 3*L
  1. What is the content of L3?
  2. Try to predict the outcome of the following commands:
L3[0]
L3[-1]
L3[10]
  1. What does the following command do?
L4 = [k**2 for k in L3]
  1. Concatenate L3 and L4 to a new list L5.

Ex. 2: Use the range command and a list comprehension to generate a list with 100 equidistantly spaced values between 0 and 1.

Ex. 3: Assume that the following signal is stored in a list:

L = [0,1,2,1,0,-1,-2,-1,0]

What is the outcome of:

L[0]
L[-1]
L[:-1]
L + L[1:-1] + L
L[2:2] = [-3]
L[3:4] = []
L[2:5] = [-5]

Do this exercise by inspection only, that is, without using your Python shell.

Ex. 4: Consider the Python statements:

L = [n-m/2 for n in range(m)]
ans = 1 + L[0] + L[-1]

and assume that the variable m has been previously assigned an integer value. What is the value of ans? Answer this question without executing the statements...

lock icon The rest of the chapter is locked
You have been reading a chapter from
Scientific Computing with Python - Second Edition
Published in: Jul 2021 Publisher: Packt ISBN-13: 9781838822323
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}