Indexación y corte en NumPy: cómo recortar tensores con libertad

1. ¿Por qué la indexación/corte es tan importante?



Al trabajar con deep learning, se manipulan tensores con frecuencia.

  • Seleccionar solo los primeros pocos ejemplos de un batch
  • Extraer un canal específico (R/G/B) de una imagen
  • Cortar solo algunos pasos de tiempo en datos secuenciales
  • Obtener solo ciertas clases de las etiquetas

Todas estas tareas se reducen a "indexación e intersección".

Además, la sintaxis de indexación/corte de Tensor en PyTorch es casi idéntica a la de NumPy, por lo que dominarla en NumPy facilita mucho la escritura de código de deep learning.


2. Indexación básica: empezando con 1D

2.1 Indexación de un array 1D

import numpy as np

x = np.array([10, 20, 30, 40, 50])

print(x[0])  # 10
print(x[1])  # 20
print(x[4])  # 50
  • Los índices comienzan en 0
  • x[i] devuelve el elemento i‑ésimo

2.2 Indexación negativa

Para contar desde el final se usan índices negativos.

print(x[-1])  # 50 (último)
print(x[-2])  # 40

PyTorch funciona de la misma manera.


3. Corte básico: start:stop:step



El corte se escribe como x[start:stop:step].

x = np.array([10, 20, 30, 40, 50])

print(x[1:4])    # [20 30 40], 1 ≤ i < 4
print(x[:3])     # [10 20 30], desde el principio hasta 3
print(x[2:])     # [30 40 50], desde 2 hasta el final
print(x[:])      # copia completa

Al incluir step se puede establecer un intervalo.

print(x[0:5:2])  # [10 30 50], de 0 a 4 con paso 2
print(x[::2])    # [10 30 50], igual que arriba

En deep learning, por ejemplo, saltar cada dos pasos de tiempo o seleccionar muestras a intervalos regulares resulta muy útil.


4. Indexación en arrays 2D y superiores: filas y columnas

A partir de 2D, estamos esencialmente trabajando con matrices/batches, lo que ya suena a deep learning.

import numpy as np

X = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])  # shape: (3, 3)

4.1 Indexación de fila/columna individual

print(X[0])      # primera fila: [1 2 3]
print(X[1])      # segunda fila: [4 5 6]

print(X[0, 0])   # fila 1, columna 1: 1
print(X[1, 2])   # fila 2, columna 3: 6
  • X[i] → fila i‑ésima (array 1D)
  • X[i, j] → valor en fila i, columna j (escalar)

4.2 Corte de filas

print(X[0:2])    # filas 0 y 1
# [[1 2 3]
#  [4 5 6]]

print(X[1:])     # desde la fila 1 hasta el final
# [[4 5 6]
#  [7 8 9]]

Este patrón se usa con frecuencia en PyTorch para recortar solo los primeros o últimos ejemplos de un batch.

4.3 Corte de columnas

print(X[:, 0])   # todas las filas, columna 0 → [1 4 7]
print(X[:, 1])   # todas las filas, columna 1 → [2 5 8]
  • : significa todas las posiciones en esa dimensión
  • X[:, 0] indica todas las filas, columna 0

En deep learning:

  • En un array (batch_size, feature_dim) se puede extraer una característica específica con X[:, k].

5. 3D y superiores: batch × canal × altura × anchura

Tomemos como ejemplo datos de imagen.

# Supongamos (batch, height, width)
images = np.random.randn(32, 28, 28)  # 32 imágenes 28x28

5.1 Extraer una sola muestra

img0 = images[0]        # primera imagen, shape: (28, 28)
img_last = images[-1]   # última imagen

5.2 Usar solo una parte del batch

first_8 = images[:8]    # los primeros 8, shape: (8, 28, 28)

5.3 Cortar una región de la imagen (crop)

# Solo el centro 20x20
crop = images[:, 4:24, 4:24]  # shape: (32, 20, 20)

En PyTorch, con un tensor images_torch de forma (32, 1, 28, 28):

center_crop = images_torch[:, :, 4:24, 4:24]

El concepto de indexación/corte es prácticamente idéntico.


6. El corte suele ser una "vista"

Un punto clave:

El resultado de un corte suele ser una vista del array original. Es decir, no se copian datos, sino que se crea una ventana que mira al original.

x = np.array([10, 20, 30, 40, 50])
y = x[1:4]   # vista

print(y)     # [20 30 40]
y[0] = 999

print(y)     # [999  30  40]
print(x)     # [ 10 999  30  40  50]  ← el original también cambia!

Esta característica:

  • Ahorra memoria y es rápida
  • Pero puede provocar cambios accidentales en el original

Para crear una copia independiente se usa copy().

x = np.array([10, 20, 30, 40, 50])
y = x[1:4].copy()

y[0] = 999
print(x)  # [10 20 30 40 50], el original se mantiene

PyTorch tiene un concepto similar, por lo que comprender la diferencia entre vista y copia facilita la depuración.


7. Indexación booleana: filtrar por condición

La indexación booleana se usa para seleccionar solo los elementos que cumplen una condición.

import numpy as np

x = np.array([1, -2, 3, 0, -5, 6])

mask = x > 0
print(mask)      # [ True False  True False False  True]

pos = x[mask]
print(pos)       # [1 3 6]
  • x > 0 produce un array de True/False
  • x[mask] devuelve solo los índices donde la máscara es True

Ejemplo combinado:

X = np.array([[1, 2, 3],
              [4, 5, 6],
              [-1, -2, -3]])

pos = X[X > 0]
print(pos)  # [1 2 3 4 5 6]

En deep learning, patrones comunes incluyen:

  • Extraer solo muestras que cumplan una condición (por ejemplo, etiquetas específicas)
  • Calcular la pérdida solo sobre valores que cumplan una máscara

PyTorch funciona de manera casi idéntica:

import torch

x = torch.tensor([1, -2, 3, 0, -5, 6])
mask = x > 0
pos = x[mask]

8. Indexación con arrays/lists de enteros (Fancy Indexing)

Se pueden usar arrays o listas de enteros para extraer múltiples posiciones a la vez.

x = np.array([10, 20, 30, 40, 50])

idx = [0, 2, 4]
print(x[idx])  # [10 30 50]

En 2D también funciona.

X = np.array([[1, 2],
              [3, 4],
              [5, 6]])  # shape: (3, 2)

rows = [0, 2]
print(X[rows])  
# [[1 2]
#  [5 6]]

En deep learning, por ejemplo:

  • Seleccionar muestras aleatorias de un batch con un array de índices mezclados
  • Agrupar solo ciertas posiciones de etiquetas/predicciones para estadísticas

PyTorch también admite este estilo.


9. Patrones de indexación comunes

Desde la perspectiva de deep learning, los patrones habituales son:

import numpy as np

# (batch, feature)
X = np.random.randn(32, 10)

# 1) Los primeros 8 ejemplos
X_head = X[:8]              # (8, 10)

# 2) Una característica específica (ej. columna 3)
f3 = X[:, 3]                # (32,)

# 3) Solo índices pares
X_even = X[::2]             # (16, 10)

# 4) Muestras con etiqueta 1
labels = np.random.randint(0, 3, size=(32,))
mask = labels == 1
X_cls1 = X[mask]            # solo muestras con etiqueta 1

# 5) Mezclar aleatoriamente y dividir en train/val
indices = np.random.permutation(len(X))
train_idx = indices[:24]
val_idx = indices[24:]

X_train = X[train_idx]
X_val = X[val_idx]

Estos patrones se aplican casi igual en tensores de PyTorch. En resumen, dominar la indexación de NumPy equivale a poder manipular tensores de manera libre y eficiente.


10. Conclusión

Para recapitular:

  • Indexación: x[i], x[i, j], índices negativos
  • Corte: start:stop:step, :, indexación multidimensional (X[:, 0], X[:8], X[:, 4:8])
  • El corte suele ser una vista, por lo que el original puede cambiar → usar copy() si se necesita una copia
  • Indexación booleana: filtrar por condición (x[x > 0], X[labels == 1])
  • Indexación con arrays de enteros: seleccionar múltiples posiciones a la vez (x[[0,2,4]])

Con estos conceptos, podrás trabajar con tensores de PyTorch para recortar batches, seleccionar canales, aplicar máscaras y mezclar muestras de forma mucho más fluida.

image