Matrices en Python: Arrays Bidimensionales

Para construir este tablero digital, necesitamos crear una lista principal (el tablero) que contenga en su interior 8 sublistas (las filas), y cada una de esas sublistas debe albergar 8 elementos (EMPTY).

Enfoque 1: Construcción con Bucles Tradicionales

La primera forma de resolverlo combina un bucle clásico for en el exterior con una lista de comprensión en el interior:

board = []  # Inicializamos el contenedor principal vacío

for i in range(8):
    row = [EMPTY for i in range(8)]  # Crea una fila individual con 8 casillas vacías
    board.append(row)                # Inyecta la fila completa dentro del tablero

🔍 Análisis de la Estructura:

  • El bloque interno: Se ejecuta y genera una lista independiente: [EMPTY, EMPTY, ..., EMPTY].
  • El bloque externo: Se repite 8 veces. En cada vuelta, toma la lista que representa la fila actual y la añade al final de board.
  • Resultado neto: Obtenemos una colección que contiene $8 \times 8 = 64$ elementos en total.

Enfoque 2: Listas de Comprensión Anidadas (Matriz en una Línea)

Dado que las listas de comprensión en Python admiten la anidación (meter una estructura dentro de otra), podemos compactar todo el proceso anterior eliminando los bloques tradicionales y reduciéndolo a una sola línea de código sumamente elegante:

board = [[EMPTY for i in range(8)] for j in range(8)]

¿Cómo desarmar esta instrucción para el Examen PCEP?

Para leer correctamente una lista de comprensión anidada de fuera hacia dentro, sigue estas reglas:

  1. La Cláusula Externa (for j in range(8)): Es el motor principal del extremo derecho. Le ordena a Python: “Quiero repetir el proceso de generación un total de 8 veces (para crear las 8 filas del tablero)”.
  2. La Expresión de Datos Interna ([EMPTY for i in range(8)]): Se encuentra a la izquierda y actúa como el “molde” de datos. Determina qué se va a inyectar en cada una de esas 8 repeticiones. En este caso, el molde genera una sublista de 8 celdas rellenas con EMPTY.

Al ejecutarse, Python crea de forma nativa e instantánea una matriz. En álgebra y computación, una matriz es simplemente una estructura de datos indexada por filas y columnas.

Resumen

  • Una matriz o arreglo bidimensional en Python se implementa como una “lista de listas”.
  • En la sintaxis [[X for i in range(8)] for j in range(8)], la sección más externa (derecha) define el número de filas, mientras que la sección interna (izquierda) define el contenido y número de columnas de cada fila.
  • Esta técnica permite modelar estructuras complejas como tableros de juego, tablas de bases de datos, coordenadas de mapas o píxeles de imágenes.

Manipulación de Matrices: Acceso y Modificación de Elementos

Una vez que hemos construido nuestra matriz bidimensional (el tablero de ajedrez), el siguiente paso crucial es aprender a interactuar con sus casillas. Para acceder o modificar un elemento dentro de un arreglo bidimensional, necesitas proporcionar dos índices obligatorios encerrados en corchetes independientes:

  1. El primer índice (board[row]): Selecciona la fila horizontal con la que deseas trabajar (la sublista interna).
  2. El segundo índice (board[row][column]): Selecciona el elemento o columna vertical exacta dentro de esa fila.

⚠️ Regla de Oro Inquebrantable para el Test: La sintaxis es siempre [fila][columna]. Invertir este orden es un error conceptual crítico que te llevará a leer coordenadas completamente erróneas.

Posicionando Piezas: La Traspuesta de Índices Basada en Cero

Alineemos la lógica del ajedrez tradicional con las reglas de indexación de Python (que siempre empiezan en 0).

1. Las Cuatro Torres (Los Extremos del Tablero)

Las esquinas de la matriz representan los límites absolutos de nuestro arreglo de 8 x 8:

# Fila 0 (Primera fila superior)
board[0][0] = ROOK  # Esquina superior izquierda
board[0][7] = ROOK  # Esquina superior derecha

# Fila 7 (Última fila inferior)
board[7][0] = ROOK  # Esquina inferior izquierda
board[7][7] = ROOK  # Esquina inferior derecha

2. Colocar un Caballo en C4 (board[4][2])

Aquí es donde debes prestar mucha atención. En el ajedrez, la coordenada “C4” hace referencia a la columna C (la tercera columna) y a la fila 4.

  • ¿Por qué en Python se escribe board[4][2]? * Fila: Dependiendo de cómo mapees el tablero (de arriba a abajo), la fila 4 del juego corresponde al índice indexado 4 en tu matriz.
  • Columna: La letra C es la tercera columna del alfabeto (A, B, C). Al pasarla al sistema informático basado en cero, el índice de la columna C es exactamente el 2 (0 para A, 1 para B, 2 para C).
board[4][2] = KNIGHT  # Fila de índice 4, Columna de índice 2 (C)

3. Colocar un Peón en E5 (board[3][4])

Siguiendo la misma lógica matemática exacta:

  • La letra E es la quinta columna del tablero. En la indexación de Python, la quinta posición corresponde al índice 4 (0=A, 1=B, 2=C, 3=D, 4=E).
  • La fila se traduce en este caso al índice 3.
board[3][4] = PAWN  # Fila de índice 3, Columna de índice 4 (E)

Resumen de Control para el PCEP

  • La lectura y escritura en arreglos multidimensionales sigue estrictamente el patrón: matriz[indice_fila][indice_columna].
  • Olvidar que los índices comienzan en 0 es el error más común al calcular coordenadas espaciales en el examen.
  • Cada corchete realiza un paso de desempaquetado: board[0] te devuelve una lista completa (la fila), mientras que board[0][0] te devuelve el valor escalar guardado en la celda.

Matrices en la Vida Real: Aplicaciones Avanzadas y Análisis de Datos

Para entender verdaderamente el poder de un arreglo bidimensional, debemos salir de los tableros de juego y mirar un caso de ingeniería real.

Imagina que estás desarrollando el software para una estación meteorológica automatizada. El dispositivo registra la temperatura del aire cada hora, de forma ininterrumpida durante todo un mes. Si asumimos un mes estándar de 31 días, el sistema generará un total de 24 x 31 = 744 mediciones.

Diseñando la Estructura de Datos

  1. Tipo de dato: Usaremos números de punto flotante (float), ya que el termómetro tiene una precisión de \(0.1^\circ\text{C}\).
  2. Distribución espacial: Decidimos que cada fila representará un día completo (un contenedor de 24 elementos, uno por cada hora) y que la matriz principal agrupará las 31 filas del mes.

Utilizando una lista de comprensión anidada, inicializamos nuestra base de datos climática en cero:

temps = [[0.0 for h in range(24)] for d in range(31)]

Operación 1: Calcular la Temperatura Media del Mediodía

Nuestro primer objetivo estadístico es analizar el comportamiento climático a las 12:00 PM (el mediodía) a lo largo de todo el mes. Para lograrlo, debemos sumar las 31 mediciones correspondientes a esa hora y dividirlas entre 31.

⚠️ Trampa de Índice en el Test: Si la primera medición del día corresponde a la medianoche (00:00 h — índice 0), la hora 12 del día (las 12:00 PM) se encuentra exactamente en el índice 11.

# La matriz "temps" ya ha sido rellenada con datos reales por los sensores
total = 0.0

for day in temps:
    total += day[11]  # Extraemos el mediodía de la fila actual

average = total / 31
print("Temperatura media al mediodía:", average)

🧠 Análisis del Bucle (Clave de examen):

La variable de control day creada por el bucle for no es un número escalar, es una lista completa (una fila que representa un día). Por eso, para extraer el dato del mediodía, estamos obligados a indexarla directamente: day[11].

Operación 2: Encontrar el Máximo Absoluto Mensual

Si queremos descubrir cuál fue la temperatura más alta registrada en cualquier hora de cualquier día de todo el mes, no podemos escanear una sola columna; necesitamos realizar una búsqueda bidimensional completa. Esto nos obliga a anidar dos bucles for:

highest = -100.0  # Un valor inicial hipotéticamente muy bajo

for day in temps:         # Bucle externo: recorre los 31 días
    for temp in day:      # Bucle interno: recorre las 24 horas de ese día específico
        if temp > highest:
            highest = temp

print("La temperatura más alta fue:", highest)
  • Mecánica: El bucle externo extrae una fila (day), y el bucle interno desarma esa fila elemento por elemento (temp), contrastando las 744 mediciones contra nuestra variable de control.

Operación 3: Filtrado y Conteo Condicional

Por último, necesitamos auditar el clima para saber cuántos días del mes registraron un mediodía “caluroso” (temperatura estrictamente superior a \(20.0^\circ\text{C}\):

hot_days = 0

for day in temps:
    if day[11] > 20.0:
        hot_days += 1

print(hot_days, "días fueron calurosos.")
  • Mecánica: Volvemos a optimizar el rendimiento. Como solo nos interesa evaluar el mediodía, no necesitamos un bucle anidado; basta con un único bucle que apunte directamente al índice 11 de cada jornada.

Resumen

  • En el bucle for row in matrix:, la variable row hereda la estructura de una sublista completa.
  • Para procesar o buscar un elemento específico en toda la matriz de forma global, se requiere una estructura de bucles anidados (for dentro de for).
  • Ajustar correctamente los índices basados en cero (como traducir las 12:00 h al índice 11) evita errores lógicos de desviación en los cálculos de datos.

Aquí tienes la adaptación del cierre absoluto del módulo de listas. El Python Institute introduce aquí las matrices tridimensionales (3D) utilizando el famoso ejemplo del complejo hotelero. Este es el techo conceptual de las colecciones en el examen PCEP, diseñado para evaluar si eres capaz de rastrear coordenadas en tres ejes espaciales independientes ([edificio][piso][habitación]).

Matrices Tridimensionales: Rompiendo la Barrera del Espacio 3D

Python no impone ningún límite técnico a la profundidad de anidación de las listas. Puedes meter listas dentro de listas, dentro de otras listas, de forma indefinida. Para entender la utilidad de un arreglo tridimensional (3D), analicemos el caso de la gestión de un mega-complejo hotelero:

  • El hotel está compuesto por 3 edificios independientes.
  • Cada edificio tiene exactamente 15 pisos.
  • Cada piso cuenta con 20 habitaciones.

Diseñando la Estructura de Datos 3D

  1. Tipo de dato: Usaremos valores booleanos. Un valor True significará que la habitación está ocupada, y un valor False indicará que la habitación está libre (vacante).
  2. Anidación de bucles: Necesitamos tres dimensiones: el eje X (edificios), el eje Y (pisos) y el eje Z (habitaciones).

Combinando tres listas de comprensión concéntricas, damos vida a la estructura en una sola línea de código:

rooms = [[[False for r in range(20)] for f in range(15)] for t in range(3)]

🧠 Cómo Desglosar las Coordenadas para la Certificación:

Para leer u operar en este hipercubo de datos sin equivocarte, recuerda que los índices se resuelven estrictamente en orden de exterior a interior utilizando triples corchetes:

rooms[indice_edificio][indice_piso][indice_habitacion]
  • Primer índice (0 al 2): Selecciona el edificio.
  • Segundo índice (0 al 14): Selecciona el piso.
  • Tercer índice (0 al 19): Selecciona el número de habitación.

Operaciones Prácticas con Matrices 3D

Veamos cómo aplicar las reglas de indexación basadas en cero en escenarios reales propuestos por el curso:

1. Reservar una habitación (Escritura de datos)

Queremos alojar a una pareja de recién casados en el segundo edificio, en el décimo piso, habitación 14:

rooms[1][9][13] = True
  • Edificio 2 — Índice 1.
  • Piso 10 — Índice 9.
  • Habitación 14 — Índice 13.

2. Liberar una habitación (Cancelación de reserva)

Necesitamos dejar disponible la segunda habitación del quinto piso ubicada en el primer edificio:

rooms[0][4][1] = False
  • Edificio 1 — Índice 0.
  • Piso 5 — Índice 4.
  • Habitación 2 — Índice 1.

3. Auditoría de Disponibilidad (Búsqueda Condicional)

Queremos comprobar cuántas habitaciones vacías quedan disponibles en el piso 15 del tercer edificio:

vacancy = 0

# Fijamos los dos primeros índices y variamos secuencialmente el tercero
for room_number in range(20):
    if not rooms[2][14][room_number]:
        vacancy += 1

print("Habitaciones disponibles:", vacancy)
  • Mecánica: Bloqueamos el acceso directo al edificio de índice 2 (tercer edificio) y al piso de índice 14 (piso 15). El bucle for solo itera sobre el rango de las 20 habitaciones. La condición if not evalúa si la celda se encuentra en False (libre) para incrementar nuestro contador.

Resumen

  • Python permite anidar listas sin un límite teórico de profundidad.
  • La sintaxis para acceder a una estructura 3D requiere tres grupos de corchetes contiguos: lista[x][y][z].
  • Modificar un dato en una matriz 3D exige fijar de forma exacta las coordenadas de todos sus ejes exteriores antes de llegar al valor escalar final.

Aquí tienes la adaptación de la última sección de Key Takeaways (Puntos Clave). Este artículo funciona como el gran cierre del bloque de listas multidimensionales y de comprensión. El Python Institute condensa aquí las estructuras avanzadas que cerrarán el Módulo 3 y que aparecerán de forma masiva en el examen de certificación PCEP.

Resumen del artículo: Estructuras y Comprensión de Listas

Hemos llegado al final del estudio avanzado de las listas. A continuación, repasamos los tres pilares fundamentales de esta sección para asegurar que tienes el control absoluto de los conceptos antes de enfrentarte al test oficial.

1. Listas de Comprensión (List Comprehensions)

La lista de comprensión es una sintaxis elegante y puramente pythonica que te permite construir colecciones nuevas a partir de iterables existentes de forma concisa. La estructura general inquebrantable que debes memorizar es:

[expresión for elemento in iterable if condicional]

Esta única línea de código realiza exactamente la misma operación mecánica que este bloque tradicional de cuatro líneas:

for elemento in iterable:
    if condicional:
        expresión
  • Ejemplo de control: Generar una lista con los primeros cinco números naturales (del 0 al 4) elevados al cubo (N**3):
cubed = [num ** 3 for num in range(5)]
print(cubed)  # Salida: [0, 1, 8, 27, 64]

2. Matrices y Arreglos Bidimensionales (2D)

En Python, una matriz se modela metiendo listas dentro de otra lista. Para leer o modificar cualquier celda, se requiere el uso de dos índices contiguos: el primero selecciona la fila horizontal y el segundo la columna vertical (matrix[fila][columna]).

Table - a two-dimensional array
  • Ejemplo de control: Una tabla de expresiones de $4 \times 4$:
table = [[":(", ":)", ":(", ":)"],
         [":)", ":(", ":)", ":)"],
         [":(", ":)", ":)", ":("],
         [":)", ":)", ":)", ":("]]

print(table[0][0])  # Salida: ':(' (Fila 0, Columna 0)
print(table[0][3])  # Salida: ':)' (Fila 0, Columna 3)

3. Hipercubos y Arreglos N-Dimensionales

Python no limita la profundidad de la inclusión. Puedes anidar tantas listas como requiera tu lógica de negocio, dando vida a estructuras tridimensionales (3D), tetradimensionales (4D) o superiores. Para acceder al dato escalar final de una matriz de N dimensiones, necesitarás proporcionar exactamente N corchetes de indexación.

Cube - a three-dimensional array
  • Ejemplo de control: Un cubo de datos tridimensional de 3 x 3 x 3:
cube = [[[':(', 'x', 'x'],
         [':)', 'x', 'x'],
         [':(', 'x', 'x']],

        [[':)', 'x', 'x'],
         [':(', 'x', 'x'],
         [':)', 'x', 'x']],

        [[':(', 'x', 'x'],
         [':)', 'x', 'x'],
         [':)', 'x', 'x']]]

print(cube[0][0][0])  # Salida: ':(' (Cubo 0, Fila 0, Columna 0)
print(cube[2][2][0])  # Salida: ':)' (Cubo 2, Fila 2, Columna 0)