> Mastering 2D Arrays in Python: Complete Guide to Matrix Creation & Operations | Hands-on Video Tutorials & Exercises on PythonLib

THEORY AND PRACTICE

  • Input and Output Data
    • Tasks
  • Conditions
    • Tasks
  • For Loop
    • Tasks
  • Strings
    • Tasks
  • While Loop
    • Tasks
  • Lists
    • Tasks
  • Two-Dimensional Arrays
    • Tasks
  • Dictionaries
    • Tasks
  • Sets
    • Tasks
  • Functions and Recursion
    • Tasks

Lesson 7. Two-dimensional arrays in python

Python two-dimensional arrays: structure, data, typical representations

What are two-dimensional arrays in Python

A two-dimensional array is a data structure organized as a table, consisting of rows and columns. Each item in this table has two indexes: one for a row and one for a column.

# Example of a 2x3 table (2 rows, 3 columns)
table = [
    [1, 2, 3],
    [4, 5, 6]
]
                        

Two-dimensional arrays are used in game development to create game fields, in image processing to work with pixels, as well as when working with any tabular data.

Two-dimensional arrays as lists of lists

In Python, two-dimensional arrays are most often implemented as lists whose elements are other lists. Each nested list is a single row of the array.

# Each line is a separate list
shelf1 = [10, 20, 30]
shelf2 = [40, 50, 60]
box = [shelf1, shelf2]
print(box)  # [[10, 20, 30], [40, 50, 60]]
                        

This structure is ideal for storing data where two-dimensional organization is important, for example, for lesson schedules by day of the week and by hour. It is important to keep in mind the order of the indexes: the row index is indicated first, and then the column index.

Key differences between two-dimensional arrays and one-dimensional arrays

A one-dimensional array or list is a simple linear sequence of elements. A two-dimensional array has a more complex, tabular structure.

# One-dimensional - just a list
line = [1, 2, 3, 4, 5]

# Two-dimensional - list of listings
cinema = [
    [1, 2, 3], # first row
    [4, 5, 6] # second row
]
                        

One-dimensional lists are useful for simple sequences such as a shopping list, while two-dimensional arrays are indispensable for tasks that require data structuring along two axes, for example, when planning by time and category.

Applications of two-dimensional arrays

Matrices in mathematical calculations

In linear algebra and scientific calculations, two-dimensional arrays are used to represent matrices, which are the basis for many complex calculations.

matrix = [
    [1, 2],
    [3, 4]
]
                        

Image representation

A digital image is essentially a two-dimensional array where each element (pixel) stores color information.

# Simple 3x3 black and white 
image = [
    [0, 1, 0], # 0 = black, 1 = white
    [1, 1, 1],
    [0, 1, 0]
]
                        

Ways to create two-dimensional arrays in Python

Creating a static (predefined) array

Manually declaring an array

If all the values of the array are known in advance, it can be declared directly in the code.

menu = [
    ["porridge", "soup", "cutlet"], # Monday
    ["omelet", "borscht", "fish"], # Tuesday
    ["cereal", "salad", "chicken"]    # wednesday
]
						
Example: A tic-tac—toe playing field

A common example of a static ad is the creation of the initial state of the playing field.

game_field = [
    [" ", " ", " "],
    [" ", " ", " "],
    [" ", " ", " "]
]
                        

Creating an empty or initialized array

Using list generators

To create arrays of a given size filled with initial values (for example, zeros or None), it is convenient to use list generators.

# Creating an empty 3x4 table (3 rows, 4 columns) filled with zeros
rows, cols = 3, 4
empty_table = [[0 for j in range(cols)] for i in range(rows)]
print(empty_table)  # [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
                        

The importance of deep copying

When creating arrays, shallow copying of strings should be avoided, as this can lead to undesirable consequences when changing an element in one row affects all the others.

# WRONG: all strings refer to the same object
bad_table = [[0] * 3] * 2
bad_table[0][0] = 5
print(bad_table) # [[5, 0, 0], [5, 0, 0]] - both lines have changed!

# CORRECT: each row is an independent object
good_table = [[0 for _ in range(3)] for _ in range(2)]
good_table[0][0] = 5
print(good_table) # [[5, 0, 0], [0, 0, 0]] - only the first one has changed
                        

Filling an array with a single value

List generators also make it easy to fill the entire array with the same value.

# Fill the entire 2x3 table with the number 7
value = 7
rows, cols = 2, 3
table = [[value for j in range(cols)] for i in range(rows)]
print(table)  # [[7, 7, 7], [7, 7, 7]]
                        

Creating an array from a delimited string

Separation by spaces and newlines

Data for a two—dimensional array is often received as a single line, where the lines are separated by the character \n, and the elements are separated by spaces.

text = "1 2 3\n4 5 6\n7 8 9"
lines =text.split('\n') # split into lines
table = []
for line in lines:
    row = line.split(' ') # split into elements
table.append(row)
print(table) # [['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9']]
                        

Converting string elements to numbers

After splitting the string, all the elements will have a string type. For mathematical operations, they must be converted to numbers.

text = "1 2 3\n4 5 6"
lines = text.split('\n')
table = []
for line in lines:
    row = [int(x) for x in line.split(' ')] # turning strings into numbers
    table.append(row)
print(table)  # [[1, 2, 3], [4, 5, 6]]
                        

This method is often used when reading data from files or processing user input.

Working with elements of a two-dimensional array

Reading and changing elements by indexes

Reading values by indexes [row][col]

A specific element is accessed by specifying its row and column indexes. Remember that indexing in Python starts at 0.

cinema = [
    ["free", "busy", "free"],
    ["busy", "free", "busy"]
]

# We check the place in the second row, the third place
# Row 1 index (second row), column 2 index (third place)
seat = cinema[1][2]
print(seat) # "occupied"
                        

Index value change

You can change the value of any element of the array in the same way.

game = [
    [".", ".", "."],
    [".", ".", "."],
    [".", ".", "."]
]

# Put a cross in the center of the field (indexes 1, 1)
game[1][1] = "X"
print(game)
# Output:
# [['.', '.', '.'],
# ['.', ' X', '.'],
# ['.', '.', '.']]
						

Access to rows and columns

Accessing the whole string

To get the entire row, it is enough to specify only its index.

grades = [
    [5, 4, 3], # mathematics
    [4, 5, 5], # Russian language
    [3, 4, 4] # physics
]

math_grades = grades[0] # all math grades (first line)
print(math_grades)  # [5, 4, 3]
                        

Accessing an entire column

Getting a column requires iterating through all rows of the array and extracting the element with the desired column index from each row.

grades = [
    [5, 4, 3], # grades for 1, 2, 3 subjects
    [4, 5, 5],
    [3, 4, 4]
]

# All grades in the second subject (column index 1)
second_subject_grades = [row[1] for row in grades]
print(second_subject_grades)  # [4, 5, 4]
                        

Secure access and error handling

IndexError exception handling using try-except

When trying to access a non-existent index, the IndexError error occurs. It can be intercepted using the try-except block.

table = [[1, 2], [3, 4]]

try:
    value = table[5][0] # there is no such row
    print(value)
except IndexError:
    print("There is no element with such indexes in the table!")
						

Preliminary check of the array boundaries

A more reliable way is to check the indexes before accessing the element. This allows you to avoid exceptions and control the logic of the program.

def safe_get(table, row, col):
    # Check that the row and column index are within acceptable limits
    if 0 <= row < len(table) and 0 <= col < len(table[0]):
        return table[row][col]
    else:
        return None# or any other default value

table = [[1, 2], [3, 4]]
print(safe_get(table, 1, 1))  # 4
print(safe_get(table, 5, 0))  # None
                        

Iteration and traversal of two-dimensional arrays

Simple line-by-line iteration

The easiest way to get around is to iterate through all the rows of the array.

bookshelf = [
    ["War and Peace", "Anna Karenina"],
    ["Harry Potter", "The Hobbit"],
    ["Sherlock Holmes", "Dracula"]
]

for shelf in bookshelf:
    print("There are books on the shelf:", shelf)
						

Iterating over all elements through nested loops

Nested loops are used to access each individual element: the outer loop iterates through the rows, and the inner loop iterates through the elements in the current row.

house = [
    ["sofa", "table", "chair"],
    ["bed", "closet"],
    ["stove", "refrigerator", "sink"]
]

for room in house:
    for item in room:
        print("Item found:", item)
						

Access to indexes using range()

If element indexes are needed during iteration, you can use the range() function in combination with len().

storage = [
    ["hammer", "saw"],
    ["nails", "screws", "dowels"],
    ["paint", "brush"]
]

for i in range(len(storage)):
    for j in range(len(storage[i])):
        print(f"Shelf {i}, drawer {j}: {storage[i][j]}")
						

Using the enumerate() function to get indexes and values

A more "pythonic" and convenient way to get both indexes and values is to use the enumerate() function.

data = [
    [10, 20],
    [30, 40, 50],
    [60]
]

for i, row in enumerate(data):
    for j, value in enumerate(row):
        print(f"Position ({i},{j}): {value}")
						

Basic operations with two-dimensional arrays

Matrix transposition

Transpose is an operation in which rows and columns of a matrix are swapped.

Transposing via list comprehension

This is a compact and efficient way to perform transposition.

original = [
    [1, 2, 3],
    [4, 5, 6]
]

transposed = [[row[i] for row in original] for i in range(len(original[0]))]
for i in transposed:
    print(i)
                        

Transposition using the zip() function

The zip() function in combination with the decompression operator * provides an even more elegant way.

original = [
    [1, 2, 3],
    [4, 5, 6]
]

#zip() returns the tuple iterator
transposed_tuples = list(zip(*original))
print(transposed_tuples)  # [(1, 4), (2, 5), (3, 6)]

# If the output needs lists, not tuples
transposed_lists = [list(col) for col in zip(*original)]
print(transposed_lists) # [[1, 4], [2, 5], [3, 6]]
                        

Searching for the maximum value

Finding the maximum in each row

You can easily find the maximum element in each row by applying the max() function to each nested list.

heights = [
    [170, 180, 165],
    [175, 190, 160],
    [185, 155, 172]
]

for i, row in enumerate(heights):
    max_height = max(row)
print(f"In row {i} the maximum value is {max_height}")
						

Search for the absolute maximum in the entire array

To find the maximum element in the entire two-dimensional array, you can use a nested generator.

heights = [
    [170, 180, 165],
    [175, 190, 160],
    [185, 155, 172]
]

overall_max = max(max(row) for row in heights)
print(f"Maximum value in the entire array: {overall_max}")
                        

Calculating the sum of all elements

The sum of all elements can be found by summing the sum of the elements of each row.

wallets = [
    [100, 50, 200],
    [75, 300, 25],
    [150, 90]
]

total = sum(sum(row) for row in wallets)
print(f"Sum of all items: {total}")
                        

Checking the elements for compliance with the conditions

These operations are widely used in data analysis, statistics, and quality control.

Checking for a certain type of values

The any() function allows you to check whether the condition is met for at least one element of the array.

Example: Searching for negative numbers
temperatures = [
    [5, -2, 10],
    [15, 8, -5],
    [20, 12, 18]
]

has_negative = any(temp < 0 for row in temperatures for temp in row)
print("There are negative numbers in the array:", has_negative) # True
                        

Counting the number of elements satisfying the condition

The sum() function, combined with a generator expression, allows you to count the number of elements for which the condition is true (since True is interpreted as 1, and False as 0).

Example: Calculating grades
exam_results = [
    [5, 4, 3, 5],
    [4, 5, 5, 4],
    [3, 4, 5, 3]
]

fives_count = sum(grade == 5 for row in exam_results for grade in row)
print(f"Number of ratings '5': {fives_count}")
						

Generating two-dimensional arrays

Generating a matrix based on a formula

List generators allow you to create matrices whose element values are calculated using a given formula based on their indexes.

# Multiplication table 5x5
rows, cols = 5, 5
multiplication_table = [[(i + 1) * (j + 1) for j in range(cols)] for i in range(rows)]
# Print the third line (multiplication by 3)
print(multiplication_table[2]) # [3, 6, 9, 12, 15]
						

Matrix generation using conditions

Conditional operators can be embedded in generators to create complex structures.

#8x8 chessboard (0 and 1)
chessboard = [[1 if (i + j) % 2 == 0 else 0 for j in range(8)] for i in range(8)]
print(chessboard)
                        

Commonly used matrix types

Null matrix

A matrix with all elements equal to zero.

# 4x4 matrix of zeros
zeros = [[0 for j in range(4)] for i in range(4)]
print(zeros)
                        

The unit matrix

is a square matrix with units on the main diagonal and all other elements equal to zero.

# 3x3 matrix
identity = [[1 if i == j else 0 for j in range(3)] for i in range(3)]
print(identity) # [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
                        

Diagonal matrix

A square matrix in which all elements outside the main diagonal are zero.

# Matrix with specified diagonal numbers 
diagonal_values = [2, 4, 6]
size = len(diagonal_values)
diagonal_matrix = [[diagonal_values[i] if i == j else 0 for j in range(size)] for i in range(size)]
print(diagonal_matrix)  # [[2, 0, 0], [0, 4, 0], [0, 0, 6]]
                        

Additional features: conversion to other data structures

Converting to a list of tuples

Sometimes it is necessary to convert array strings into immutable tuples.

table = [
    [1, 2, 3],
    [4, 5, 6]
]

tuple_list = [tuple(row) for row in table]
print(tuple_list)  # [(1, 2, 3), (4, 5, 6)]
                        

Getting a set of unique elements

To get all the unique values from a two-dimensional array, it can be "expanded" into a one-dimensional sequence and transformed into a set.

table = [
    [1, 2, 3],
    [2, 3, 4],
    [3, 4, 5]
]

unique_elements = set(element for row in table for element in row)
print(unique_elements)  # {1, 2, 3, 4, 5}
                        

This operation is useful for analyzing the uniqueness of the data and preparing it for further processing. It should be remembered that when converting to a set, the original order of the elements is lost.