Lesson 07 - Additional List Topics

The following topics are discussed in this notebook:

  • List functions and methods.
  • Slicing.
  • Lists of lists.
  • Copying lists.

List Functions

Python has several functions that can take lists as inputs. We will present four such functions here.

  • len() returns the number of items in the list.
  • sum() returns the sum of the elements in a list. This only works on numerical lists.
  • max() returns the largest item in the list.
  • min() returns the smallest item in the list.

We will illustrate the use of these functions using the two lists below.

In [1]:
names = ['beth', 'drew', 'fred', 'chad', 'anna', 'emma']
sales = [126, 81, 135, 114, 163, 92]
In [2]:
# Find the length of the lists.
print(len(names))
print(len(sales))
6
6
In [3]:
# Sum the sales list.
print(sum(sales))
711
In [4]:
# Find min and max of names list.
print(min(names))
print(max(names))
anna
fred
In [5]:
# Find min and max of sales list.
print(min(sales))
print(max(sales))
81
163

List Concatenation and Replication

As with strings, we can use the operators + and * to concatenate and replicate lists.

  • When + appears between two lists, the expression will be evaluated as a new list that contains the elements from both lists. The elements in the list on the left of + will appear first, and the elements on the right will appear last.

  • When * appears between a list and an integer, the expression will be evaluated as a new list that consists of several copies of the original list concatenated together. The number of copies is set by the integer.

In [6]:
letter_list = ['A', 'B', 'C']
number_list = [1, 2, 3]

print(letter_list + number_list)
print(number_list * 4)
['A', 'B', 'C', 1, 2, 3]
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

Sorting Lists

Python provides us with a few tools for sorting and reordering the elements of a list. In this section, we will discuss the following functions: the sorted() function, the sort() method, and the reverse() method.

sorted() Function

We can use sorted() to obtain a sorted version of the list. Note that this does not alter the order of the original list. It creates list which contains the same elements of the original list, but in sorted order. We can then store this new list in a variable, print it, or provide it to another function.

In [7]:
listA = [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]
listA_asc = sorted(listA)

print('Sorted List:  ', listA_asc)
print('Original List:', listA)
Sorted List:   [1, 2, 3, 4, 4, 5, 6, 6, 7, 9]
Original List: [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]

The sorted() function sorts items in ascending order by default. We can change this behavior by setting an optional parameter named reverse. This parameter accepts a Boolean value, and is set equal to False by default. If reverse is set to True, then the list will be sorted in descending order.

In [8]:
listA_desc = sorted(listA, reverse=True)

print('Sorted List:  ', listA_desc)
print('Original List:', listA)
Sorted List:   [9, 7, 6, 6, 5, 4, 4, 3, 2, 1]
Original List: [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]

The sort() Method

As mentioned above, the sorted() function does not alter the order of the original list, but instead creates a new list. There are occasions when we only intend to work with a sorted version of the list, and would thus perfer to perform the sort on the original list itself, rather than creating a new list. One option would be to use sorted(), and then store the results back into the variable that held the original list. However, a simpler approach would be to use the sort() list method.

The sort() method for lists will sort the elements of a list in ascending order. The method does not create any new lists, but instead rearranges the elements of the orignal list. This is referred to as "in place" sorting.

In [9]:
listB = [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]
listB.sort()
print(listB)
[1, 2, 3, 4, 4, 5, 6, 6, 7, 9]

Like the sorted() function, the sort() method has an optional reverse parameter that can be used to sort the list in descending order.

In [10]:
listC = [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]
listC.sort(reverse=True)
print(listC)
[9, 7, 6, 6, 5, 4, 4, 3, 2, 1]

sorted() vs sort()

The fact that sort() performs an in-place sort while sorted() creates a new list illustrates a difference between methods and lists. Methods can have permission to directly alter the value of the object that they are working on, while other types of function generally do not.

You might be asking how to decide which of these sorting functions to use if you need to sort a list. The answer essentially boils down to whether or not you need to preserve the original list, or if you only intend to ever work with the sorted version of the list. If you need need to preserve the original order, then you should sort using sorted(). However, if don't need the original ordering and will only work with the sorted version of the list, then you should use sort().

The reverse() Method

Lists also have a reverse() method when reverses the order of a list. This re-ordering is performed on the elements of the original list, and does not create any new lists. That is, it is done "in place".

In [11]:
listD = [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]
listD.reverse()
print(listD)
[2, 5, 4, 6, 3, 7, 4, 1, 9, 6]

Note that reverse() does not sort the list in descending order. It simply reverses the current order of the list. We saw previously that we can obtain an in-place sort of a list in descending order by specifying reverse=True when using the sort() method. We could also call the sort() method on a list to first sort the list in ascending order, and then call the reverse() method on the list to put the list into descending order. However, this would require two lines of code. We illustrate this technique in the cell below.

In [12]:
listE = [6, 9, 1, 4, 7, 3, 6, 4, 5, 2]
listE.sort()
listE.reverse()
print(listE)
[9, 7, 6, 6, 5, 4, 4, 3, 2, 1]

Finding Index of List Elements

Lists have an index() method that accepts a single parameter. The method will return the index of the first occurrence of the supplied value within the list. If the value does not appear in the list, the method will produce an error.

The cell below creates a randomly generate list of 30 elements. Run this cell as is.

In [13]:
import random
random.seed(1)
rand_list = random.choices(range(0,50), k=30)
print(rand_list)
[6, 42, 38, 12, 24, 22, 32, 39, 4, 1, 41, 21, 38, 0, 22, 36, 11, 47, 45, 1, 1, 27, 46, 19, 10, 21, 1, 11, 21, 24]

The value 1 appears multiple times in this list. The code in the cell below will return the index of the first occurrence of 1.

In [14]:
rand_list.index(1)
Out[14]:
9

The value 2 does not appear in the list. The code below will result in an error.

In [15]:
rand_list.index(2)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-15-53064462ac96> in <module>
----> 1 rand_list.index(2)

ValueError: 2 is not in list

Working with Data Stored in Parallel Lists

Occasionally, we will need to use multiple lists to store different types of data collected for several people or objects. When doing so, the lists are typically created so that entries of different lists at the same position provide different pieces of information all relating to the same entity.

As an example, the cell below creates a list names that contains the names of 10 people as well as a list called ages that stores the ages of the same 10 people. The two lists correspond in the sense that for each index i, the age of the person with name name[i] is equal to age[i].

In [16]:
names = ['Ivan', 'Dawn', 'Eric', 'Fred', 'Anna', 'Beth', 'Chad', 'Judy', 'Gary', 'Hana']
ages = [19, 61, 56, 26, 40, 38, 49, 57, 17, 13]

When we store data in "parallel" lists, Python will not be aware that the lists are meant to be related. The index() method can be useful for working with lists of this type.

Assume that we wish to determine the names and ages of the youngest and oldest people in these lists. We could certainly get the answer by manually inspecting the contents of the list, but that would be tedious to do if the lists were very long. The index() method can be used to develop a programmatic solution to this task.

In the cell below, we will write code to obtain and print the following information:

  • The name, age, and index for the youngest individual whose information is stored in the lists.
  • The name, age, and index for the oldest individual whose information is stored in the lists.
In [17]:
# Obtain information about youngest person.
min_age = min(ages)
idx_min = ages.index(min_age)
min_name = names[idx_min]

# Obtain information about oldest person.
max_age = max(ages)
idx_max = ages.index(max_age)
max_name = names[idx_max]

# Print results.
print('The youngest person is ', min_name, '. Their age is ', min_age, 
      '. Their information is stored at index ', idx_min, '.', sep='')

print('The oldest person is ', max_name, '. Their age is ', max_age, 
      '. Their information is stored at index ', idx_max, '.', sep='')
The youngest person is Hana. Their age is 13. Their information is stored at index 9.
The oldest person is Dawn. Their age is 61. Their information is stored at index 1.

Slicing Lists

Occasionally, we will need to need to work with a portion of a list, rather than the entire list. Slicing is a method of creating a sublist of consecutive elements drawn from another list. Assume that myList is a Python list, and let i and j be integers.

  • myList[i:j] will generate a sublist of myList that begins with the element at index i and contains all elements up to but not including the element at index j.
  • myList[i:] will generate a sublist of myList that begins with the element at index i and contains all later elements.
  • myList[:j] will generate a sublist of myList that contains all elements up to but not including the element at index j.
In [18]:
# Simple Slicing Example
simpleList = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

print(simpleList[3:7])
print(simpleList[5:])
print(simpleList[:8])
[6, 8, 10, 12]
[10, 12, 14, 16, 18, 20]
[0, 2, 4, 6, 8, 10, 12, 14]

We can also use negative indices when specifying a slice of a list.

In [19]:
# Print the last 6 elements of simpleList
print(simpleList[-6:])
[10, 12, 14, 16, 18, 20]
In [20]:
# Print all but the first two and last two elements of simpleList
print(simpleList[2:-2])
[4, 6, 8, 10, 12, 14, 16]

Example: Quarterly Sales

CompCo is an company that manufactures laptop computers. The list sales below provides CompCo's total monthly sales for each month of 2016, measured in US dollars. The results are listed in order of month.

In [21]:
sales = [52353, 43724, 32191, 34914, 44671, 37139, 49341, 
         57139, 55104, 45193, 52167, 61294]

CompCo needs to calculate their quarterly sales for each quarter in 2016.

Define four new variables Q1, Q2, Q3, and Q4 in the next cell.

  • Q1 contains the total sales for Quarter 1 (January, February, March).
  • Q2 contains the total sales for Quarter 2 (April, May, June).
  • Q3 contains the total sales for Quarter 3 (July, August, September).
  • Q4 contains the total sales for Quarter 4 (October, November, December).

Use a combination of slicing and the sum() function.

In [22]:
Q1 = sum(sales[0:3])
Q2 = sum(sales[3:6])
Q3 = sum(sales[6:9])
Q4 = sum(sales[9:])

Print the variables Q1, Q2, Q3, and Q4.

In [23]:
print(Q1)
print(Q2)
print(Q3)
print(Q4)
128268
116724
161584
158654

Lists of Lists

It is possible for the elements of a list to be lists themselves. Let's consider a simple example.

In [24]:
metaList = [ 
    [4, 2], 
    ['a', 'b', 'c', 'd'], 
    [1.1, 2.2, 3.3] 
]

Notice that metalist is a list that contains three elements, each of which is also a list.

  • metaList[0] is a list of two int values, [4, 2].
  • metaList[1] is a list of four str, ['a', 'b', 'c', 'd'].
  • metaList[2] is a list of three float values, [1.1, 2.2, 3.3].

We can access elements of the inner lists by using two sets of square braces and two indices.

For example, since metaList[1] is the list ['a', 'b', 'c', 'd'], we can access the str 'c' with metaList[1][2].

In [25]:
print(metaList[1][2])
c

Example: Working with a List of Lists

In the cell below, we have created lists for 9 midwest states. Each state list contains the names of the cities in that state with a population of 100,000 or greater.

In [26]:
MO_list = ['Independence', 'Kansas City', 'Springfield', 'St. Louis']
IL_list = ['Aurora', 'Chicago', 'Joliet', 'Napierville', 'Peoria', 'Rockford', 'Springfield']
AR_list = ['Little Rock']
KS_list = ['Kansas City', 'Olathe', 'Overland Park', 'Topeka', 'Wichita']
IA_list = ['Cedar Rapids', 'Des Moines']
NE_list = ['Lincoln', 'Omaha']
OK_list = ['Norman', 'Oklahoma City', 'Tulsa']
TN_list = ['Chattanooga', 'Clarksville', 'Knoxville', 'Memphis', 'Nashville']
KY_list = ['Lexington', 'Louisville']

We will now create a list that contains each of these 9 lists as its elements.

In [27]:
state_list = [MO_list, IL_list, AR_list, KS_list, IA_list, 
              NE_list, OK_list, TN_list, KY_list]

Without referring directly to any of the 9 original lists, print out a list of the cities in Kansas with a population of 100,000 or greater.

In [28]:
print(state_list[3])
['Kansas City', 'Olathe', 'Overland Park', 'Topeka', 'Wichita']

Without referring directly to any of the 9 original lists, print out a list of the cities in Nebraska with a population of 100,000 or greater.

In [29]:
print(state_list[5])
['Lincoln', 'Omaha']

Using only the list cities_100K, print the element 'St. Louis'.

In [30]:
print(state_list[0][3])
St. Louis

Using only the list cities_100K, print the element 'Joliet'.

In [31]:
print(state_list[1][2])
Joliet

Copying Lists

Assume that var1 is a variables of type int, float, str, or bool. We have seen before that if we create a new variable var2 and set its initial value to be equal to that of var1, then var2 will initially contain the same value as var1, but will be its own distinct variable whose value can be changed independent of var1. This is illustrated in the following two cells.

In [32]:
var1 = 37
var2 = var1 
print(var1)
print(var2)
37
37
In [33]:
var2 = "blah"
print(var1)
print(var2)
37
blah

The situation is different for lists, however. If we have a list called list1 and we set list2 to be equal to list1, then list1 and list2 will be two different names of the same list. Any changes made to list1 will also effect list2 and vice versa.

In [34]:
list1 = [3, 8, 2]
list2 = list1
print(list1)
print(list2)
[3, 8, 2]
[3, 8, 2]
In [35]:
list2.append(37)
print(list1)
print(list2)
[3, 8, 2, 37]
[3, 8, 2, 37]

What if we want to actually create a new, entirely separate, copy of an already existing list? It turns out that Python provides two ways of doing a such.

  1. We may use the copy() method of the list that we wish to duplicate.
  2. We can return a copy of a list by using slicing.

Let's see first see how to duplicate a list using the copy() method.

In [36]:
dup1 = list1.copy()
print(list1)
print(dup1)
[3, 8, 2, 37]
[3, 8, 2, 37]
In [37]:
dup1.append(42)
print(list1)
print(dup1)
[3, 8, 2, 37]
[3, 8, 2, 37, 42]

We will now see how to duplicate a list by using slicing.

In [38]:
dup2 = list1[:]
print(list1)
print(dup2)
[3, 8, 2, 37]
[3, 8, 2, 37]
In [39]:
dup2.remove(8)
print(list1)
print(dup2)
[3, 8, 2, 37]
[3, 2, 37]

Example: Copying and Sorting Lists

A list called Avengers is provided in the cell below. Add code to accomplish the following tasks:

  1. Create two new copies of the list, one called Avengers_Asc and one called Avengers_Desc.
  2. Sort Avengers_Asc in ascending alphabetical order.
  3. Sort Avengers_Desc in descending alphabetical order.
  4. Print all three lists.
In [40]:
Avengers = ['Capt. America', 'Black Widow', 'Iron Man', 'Hulk', 'Thor', 'Hawkeye']

Avengers_Asc = Avengers.copy()
Avengers_Desc = Avengers.copy()

Avengers_Asc.sort()
Avengers_Desc.sort(reverse=True)

print(Avengers)
print(Avengers_Asc)
print(Avengers_Desc)
['Capt. America', 'Black Widow', 'Iron Man', 'Hulk', 'Thor', 'Hawkeye']
['Black Widow', 'Capt. America', 'Hawkeye', 'Hulk', 'Iron Man', 'Thor']
['Thor', 'Iron Man', 'Hulk', 'Hawkeye', 'Capt. America', 'Black Widow']
In [41]:
Avengers_Asc = sorted(Avengers)
Avengers_Desc = sorted(Avengers)
Avengers_Desc.reverse()

print(Avengers)
print(Avengers_Asc)
print(Avengers_Desc)
['Capt. America', 'Black Widow', 'Iron Man', 'Hulk', 'Thor', 'Hawkeye']
['Black Widow', 'Capt. America', 'Hawkeye', 'Hulk', 'Iron Man', 'Thor']
['Thor', 'Iron Man', 'Hulk', 'Hawkeye', 'Capt. America', 'Black Widow']