Rows and Columns with Lists
Overview
A table is a list of lists, and a column is what you pull out of it.
What You Will Learn in This Lesson
By the end of this lesson, you will be able to:
- Store a small table as a list of row lists.
- Read one row, one cell, and one column out of it.
- Build a column with a comprehension over the rows.
- Explain the weakness of position-based rows that the next lesson fixes.
Why This Matters
Every table you will ever analyse — a CSV, a spreadsheet, a DataFrame — is rows and columns. Building one out of plain lists first shows exactly what a table is underneath, so when pandas hands you df["score"] you know it is doing the comprehension in this lesson for you, and why it needed column names to do it.
Step 1: A table is a list of rows
The simplest possible table: an outer list, holding one inner list per row, each inner list holding the values in a fixed order.
One index for a row, two for a cell
The first index picks the row. The second picks a position inside that row. Row first, then column — the same order a spreadsheet uses.
table = [
["Ada", 92, "maths"],
["Grace", 88, "computing"],
["Alan", 79, "maths"],
]
print(table[0]) # the first row
print(table[0][1]) # the second cell of the first row
print(table[2][2]) # the third cell of the third row
The shape of the table
len(table) is the number of rows. len(table[0]) is the number of columns, assuming every row has the same length — an assumption real files break more often than you would hope.
table = [["Ada", 92, "maths"], ["Grace", 88, "computing"]]
print("rows:", len(table))
print("columns:", len(table[0]))
Mini Practice #1: Find the cell
Try It YourselfPrint Alan’s subject using two indexes, then print the number of columns in the table.
Step 2: A column is a comprehension
There is no column object in a list of lists. To work on one, you build a new list out of the same position in every row.
Pulling one position out of every row
[row[1] for row in table] reads as “the second value of each row”. That is what a column is.
table = [["Ada", 92], ["Grace", 88], ["Alan", 79]]
scores = [row[1] for row in table]
names = [row[0] for row in table]
print(names)
print(scores)
Summarise the column, not the table
Once the column is a list of numbers, every summary from the last lesson works on it.
table = [["Ada", 92], ["Grace", 88], ["Alan", 79]]
scores = [row[1] for row in table]
print(sum(scores) / len(scores))
A column with a condition
Adding an if keeps only some rows while taking the column — the names of everyone who studies maths, say.
table = [["Ada", 92, "maths"], ["Grace", 88, "computing"], ["Alan", 79, "maths"]]
maths = [row[0] for row in table if row[2] == "maths"]
print(maths)
Mini Practice #2: Two columns
Try It YourselfBuild names and scores as columns, then print the name of the student with the highest score using scores.index.
Step 3: The weakness: positions mean nothing
This representation works, and it has one serious problem.
row[1] does not say what it is
You have to remember that position 1 is the score. Six months later, or for someone else reading the code, row[1] is a mystery.
row = ["Ada", 92, "maths"]
print(row[1]) # the score -- but only if you remember
A new column moves everything
Insert an age column after the name and every row[1] now reads the age. Nothing raises; the average is just the average age. The next lesson fixes this by giving every value a name.
table = [["Ada", 36, 92], ["Grace", 45, 88]]
# Written before the age column existed. Still runs. Now wrong.
scores = [row[1] for row in table]
print(sum(scores) / len(scores))
Predict, then check: Trace one row at a time
Which names will remain, and in what order? Trace the condition for each row before reading the answer.
table = [["Zoe", 0, "art"], ["Ada", 85, "maths"], ["Kai", 70, "art"]]
names = [row[0] for row in table if row[2] == "art"]
print(names)
Show the output and explanation
['Zoe', 'Kai']
The condition uses column 2, while the result takes column 0. Zoe still belongs to art even though her score is zero. A filter keeps the original order unless you explicitly sort it.
Try one change: Change Zoe’s subject to maths, then add a new art row. Predict the new list.
Go deeper: Trace both indexes separately
A table stored as nested lists has two levels. The first index chooses a row; the second chooses a value within that row. Keep the column layout consistent across every row, because numeric positions carry no field names.
Trace the example
Predict the output before opening the explanation.
rows = [["Ada", 8], ["Grace", 10], ["Lin", 6]]
print(rows[1][0])
scores = [row[1] for row in rows]
print(sum(scores))
Show the output and trace
Grace 24
rows[1] is ["Grace", 10], then [0] selects Grace from that row. The comprehension selects position 1 from every row, producing [8, 10, 6]. Summing this column gives 24.
A mistake to watch for
rows[1] selects a row, not the second column. A shorter row can raise IndexError; a row with swapped fields may instead produce misleading data. Check row lengths and field meaning before extracting a column.
Transfer challenge
Add ["Mo", 12] as a new row. Extract the names into one list and calculate the average score from the score column.
Change the example below to solve the challenge. Predict the result, run your code, and compare. This practice is ungraded.
Compare with a worked solution
rows = [["Ada", 8], ["Grace", 10], ["Lin", 6], ["Mo", 12]]
print([row[0] for row in rows])
scores = [row[1] for row in rows]
print(sum(scores) / len(scores))
Run both versions. Explain any difference in their output, then change one input and predict the new result.
Step 4: When to Use It
Small, fixed-shape tables
A handful of rows whose column order you control — test data, a few results typed into a script.
scores = [row[1] for row in table]
Data that arrives as positions
csv.reader and many APIs hand back rows as lists; a comprehension is how you get a column out of them.
When Not to Use It
Do not keep list rows for anything long-lived or shared. The moment a column is added or reordered, every row[1] silently reads the wrong thing — switch to dictionaries.
End-of-Lesson Exercises
Exercise 1: Pull out a column
Pull the second column out of table into a list called scores, then print it.
One value per row, always at the same position. A comprehension over the rows is the shortest way to say that.
Exercise 2: A column, filtered
Write names_in(table, subject) returning the names, in table order, of the rows whose third column equals subject. Each row is [name, score, subject].
The subject is at position 2 and the name at position 0: [row[0] for row in table if row[2] == subject].