Ir al contenido
wannacode

Arrays bidimensionales

En resumen: Un array bidimensional (matriz) en Python es una lista de listas: cada elemento de la lista exterior es una fila, y los bucles anidados recorren todos los valores.

Problema

Tres semanas, tres listas

Una cafetería apunta cuántos cafés vende cada día laborable. Para una semana, la lista de la lección anterior es justo lo que necesitas. Para tres semanas haces tres listas:

week1 = [42, 38, 51, 47, 60]
week2 = [45, 40, 49, 52, 63]
week3 = [39, 44, 50, 48, 58]
tuesday = week1[1] + week2[1] + week3[1]
print(f"Martes: {tuesday}")
Salida
Martes: 122

Ahora el gerente pregunta cuánto se vendió todos los martes del último año. Son 52 semanas, así que 52 listas y una suma con 52 sumandos. Y un bucle no puede recorrer week1, week2, week3: son nombres distintos, no índices.

Los datos son una tabla: las semanas son filas y los días son columnas. Necesitas una lista que sea una tabla.

La idea

Una lista de listas: fila y columna

Una tabla en Python es una lista de listas: cada elemento de la lista exterior es una fila entera, y una fila es una lista normal de números. En Java y C# esto se llama array bidimensional, y a menudo matriz.

sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
]
print(sales[1][0])
print(f"{len(sales)} semanas, {len(sales[0])} días")
Salida
45
3 semanas, 5 días
  • Cada [ … ] interior es una fila. Las escribimos una debajo de otra para que la tabla se vea bien.
  • sales[1][0] es la fila con índice 1 (la segunda semana) y la columna con índice 0 (el lunes). Primero va la fila, luego la columna. Aquí también se cuenta desde 0.
  • len(sales) es la cantidad de filas. len(sales[0]) es la longitud de la primera fila, es decir, la cantidad de columnas.

sales[1] es una fila entera, una lista normal con 5 números. Por eso escribes dos corchetes separados [1][0]: primero obtienes la fila, luego el número que hay en ella.

sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
]
print(sales[1])
print(sales)
Salida
[45, 40, 49, 52, 63]
[[42, 38, 51, 47, 60], [45, 40, 49, 52, 63], [39, 44, 50, 48, 58]]

Una tabla vacía de 52 × 5 llena de ceros se hace fila por fila: 52 veces añades una fila nueva de 5 ceros.

table = []
for week in range(52):
table.append([0] * 5)
table[0][1] = 7
print(len(table), len(table[0]), table[0][1], table[1][1])
Salida
52 5 7 0
Más precisamente, para curiosos
  • Como las filas son listas separadas, pueden tener longitudes distintas. Una tabla así se llama irregular (jagged):
shifts = [
[8, 8],
[6, 6, 6],
[12],
]
print(len(shifts[1]), len(shifts[2]))
Salida
3 1

En ella, len(sales[0]) no es la longitud de todas las filas. Por eso en un bucle anidado es más seguro escribir len(sales[row]): la longitud de la fila que estás recorriendo en ese momento.

  • No hagas la tabla vacía con [[0] * 5] * 3. Así los tres lugares apuntan a una misma fila, como los dos nombres de una lista de la lección anterior. Un cambio en una fila se ve en todas:
table = [[0] * 5] * 3
table[0][1] = 7
print(table)
Salida
[[0, 7, 0, 0, 0], [0, 7, 0, 0, 0], [0, 7, 0, 0, 0]]

Míralo

Toda la tabla en la pantalla

Para pasar por cada celda necesitas dos bucles, uno dentro del otro. Los bucles anidados ya los conoces de las estrellas: el exterior elige la fila, el interior recorre las columnas de esa fila.

for week in range(len(sales)):
for day in range(len(sales[0])):
print(f"{sales[week][day]:5}", end="")
print()
Salida
42 38 51 47 60
45 40 49 52 63
39 44 50 48 58

El bucle exterior se ejecuta 3 veces, una por cada semana. En cada una de sus vueltas, el interior se ejecuta 5 veces. 15 celdas en total, en el orden en que lees una tabla: de izquierda a derecha, de arriba abajo.

:5 en la f-string imprime el número en un campo de 5 caracteres de ancho, alineado a la derecha. Así las columnas quedan una debajo de otra. end="" mantiene los números en una línea, y el print() vacío después de cada fila pasa a una nueva.

Pruébalo

¿Qué celda?

En el registro de notas, cada fila es un estudiante y cada columna es una asignatura.

¿Qué imprimirá el programa?

grades = [
[5, 3, 4, 6],
[3, 4, 6, 5],
[6, 2, 5, 4],
]
print(grades[1][2])
Respuesta

La idea

Sumas por filas y por columnas

¿Cuántos cafés se vendieron cada semana? Es la suma de cada fila. La suma empieza en 0 para cada fila nueva, por eso total está dentro del bucle exterior:

for week in range(len(sales)):
total = 0
for day in range(len(sales[0])):
total += sales[week][day]
print(f"Semana {week + 1}: {total}")
Salida
Semana 1: 238
Semana 2: 249
Semana 3: 239

sales = [
[42, 38, 51, 47, 60],
[45, 40, 49, 52, 63],
[39, 44, 50, 48, 58],
]
for week in range(len(sales)):
total = 0
for day in range(len(sales[0])):
total += sales[week][day]
print(f"Semana {week + 1}: {total}")
Paso a paso, en palabras
  1. La tabla: 3 semanas (filas) de 5 días (columnas).
  2. week = 0: total vuelve a empezar desde 0.
  3. El bucle interior recorre los días: 42 + 38 + 51 + 47 + 60 = 238.
  4. Se imprime «Semana 1: 238».
  5. week = 1: total vuelve a empezar desde 0.
  6. El bucle interior recorre los días: 45 + 40 + 49 + 52 + 63 = 249.
  7. Se imprime «Semana 2: 249».
  8. week = 2: total vuelve a empezar desde 0.
  9. El bucle interior recorre los días: 39 + 44 + 50 + 48 + 58 = 239.
  10. Se imprime «Semana 3: 239».

¿Cuánto se vendió el lunes, el martes y así sucesivamente? Es la suma de cada columna. La tabla es la misma, solo cambia el orden de los bucles: el día va fuera y las semanas van dentro. Lo terminarás en la siguiente tarjeta.

Qué bucle va fuera decide qué sumas. Los índices en sales[week][day] no se mueven: la fila siempre va primero.

Pruébalo

Suma por días

El bucle exterior recorre los días, el interior recorre las semanas. ¿Qué va en el hueco?

days = ["Lun", "Mar", "Mié", "Jue", "Vie"]
for day in range(len(sales[0])):
    total = 0
    for week in range(len(sales)):
        total += sales[][day]
    print(f"{days[day]}: {total}")

Pruébalo

Encuentra el error

La sala tiene 3 filas de 5 asientos: 1 está ocupado, 0 está libre. Hay 7 asientos libres, pero el programa dice 5. ¿En qué línea está el error?

Elige la línea con el error.

La idea

Reserva: las personas cuentan desde 1

Un espectador quiere la fila 3, asiento 2. Para él, la primera fila es la fila 1. Para la lista es la fila 0. Por eso restas 1 a los números que escribe una persona antes de usarlos como índices:

row = int(input())
seat = int(input())
if hall[row - 1][seat - 1] == 1:
print("El asiento está ocupado.")
else:
hall[row - 1][seat - 1] = 1
print(f"Reservado: fila {row}, asiento {seat}")
Entrada
3
2
Salida
Reservado: fila 3, asiento 2
Entrada
1
2
Salida
El asiento está ocupado.

hall es la sala de la tarjeta anterior. Una celda se cambia como una variable normal: hall[row - 1][seat - 1] = 1.

Más precisamente, para curiosos
  • Si alguien pide la fila 7, el índice 6 está fuera de la tabla y el programa se detiene con IndexError, igual que con una lista normal. Es más traicionero si pide la fila 0: el índice es -1, y en Python esa es la última fila. No hay error, y se reserva el asiento equivocado:
hall = [
[1, 1, 0, 0, 1],
[0, 1, 1, 1, 0],
[0, 0, 0, 1, 1],
]
try:
print(hall[7 - 1][2 - 1])
except IndexError as error:
print(f"IndexError: {error}")
print(hall[0 - 1])
Salida
IndexError: list index out of range
[0, 0, 0, 1, 1]

Así que comprueba los números antes de usarlos: 1 <= row <= len(hall), y lo mismo para el asiento.

  • Aquí cada número está en su propia línea. Cómo leer de una vez una línea entera de números (0 1 1 0) lo verás en la próxima lección, sobre el texto.

Ejercicio

Cálculos con una tabla

Resuelve los ejercicios en tu editor. Escribe las tablas directamente en el código.

1. Diagonales y filas. Para la tabla de 2 × 2 de abajo, halla la suma de la diagonal principal (de arriba a la izquierda hasta abajo a la derecha), la de la otra diagonal y las sumas de la primera y la segunda fila. En la diagonal principal, el índice de la fila y el de la columna son iguales. En la otra diagonal, la columna es n - 1 - row.

La tabla
1 2
3 4
Salida
Diagonal principal: 5
Diagonal secundaria: 5
Primera fila: 3
Segunda fila: 7

2. Gira la tabla. Las ventas están apuntadas por semanas (filas) y días (columnas). Contabilidad las quiere por días y semanas. Haz una tabla nueva en la que las filas se conviertan en columnas. Esto se llama trasponer. La tabla nueva tiene el tamaño columnas × filas, y la celda [r][c] pasa a [c][r].

La tabla
42 38 51
45 40 49
Salida
42 45
38 40
51 49

Ejercicio

Laberintos y tesoro

3. ¿Cuántos caminos hay? El laberinto es una tabla: 0 es un paso, 1 es una pared. Empiezas en la esquina de arriba a la izquierda y llegas a la de abajo a la derecha, moviéndote solo hacia la derecha o hacia abajo. ¿Cuántos caminos distintos hay?

El laberinto
0 0 0 0
0 1 0 0
0 0 0 1
1 0 0 0
Salida
Caminos: 3
Pista

Haz una segunda tabla ways del mismo tamaño. En ella escribe de cuántas maneras se llega a cada celda. A la salida se llega de 1 manera. A una pared se llega de 0 maneras. A cualquier otra celda se llega o desde arriba o desde la izquierda, así que la cantidad es la suma de la celda de arriba y la de su izquierda (si existen). Rellena la tabla fila por fila, de arriba abajo. La respuesta está en la última celda.

4. El camino más corto. Uno difícil. Ahora puedes moverte en las cuatro direcciones: arriba, abajo, izquierda y derecha. ¿Cuál es la menor cantidad de pasos para ir de la esquina de arriba a la izquierda a la de abajo a la derecha? Si no hay camino, imprime «No hay camino».

El laberinto
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
Salida
Camino más corto: 16 pasos
Pista

Lanza una «ola» desde la salida. Haz una tabla dist llena de -1 (todavía no hemos llegado a esa celda) y pon 0 en la salida. Después repite: busca todas las celdas cuyo dist es igual al paso actual, y en sus vecinas libres a las que todavía no hemos llegado escribe paso + 1. La ola crece un paso en cada vuelta. Para cuando llegue a la meta o cuando no haya cambiado nada (entonces no hay camino). Las cuatro direcciones son cómodas como dos listas: dr = [-1, 1, 0, 0] y dc = [0, 0, -1, 1]. La vecina en la dirección k está en la fila r + dr[k] y la columna c + dc[k].

Más ejercicios

  • Encuentra el tesoro. Un juego en un mapa que es una tabla: 0 es camino, 1 es obstáculo, 2 es el tesoro. El jugador empieza en la esquina de arriba a la izquierda. En cada movimiento, imprime el mapa (P para el jugador, # para un obstáculo, . para un camino) y lee una orden como número: 8 arriba, 2 abajo, 4 izquierda, 6 derecha (como en el teclado numérico). Si el movimiento lleva fuera del mapa o a un obstáculo, el jugador se queda en su sitio. Cuando pise el tesoro, imprime «¡Encontraste el tesoro en … movimientos!».
  • La fila con más ventas. Para la tabla sales, halla qué semana fue la más fuerte y qué día de la semana es el más fuerte de media.
  • Asientos libres en una fila. Para la sala hall, halla una fila que tenga al menos 3 asientos libres seguidos, e imprime el número de la fila y el del primer asiento.

Test

Ponte a prueba

1. ¿Qué es sales[1] en la tabla sales?
2. La tabla sales tiene 3 semanas (filas) y 5 días (columnas). ¿Qué da len(sales[0])?
3. ¿Cuál es la última celda de una tabla con 3 filas y 5 columnas?
4. Quieres la suma de una columna (un día en todas las semanas). ¿Qué índice cambia en el bucle interior?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·