Fundamentos de Manipulación de Arrays NumPy

NumPyBeginner
Practicar Ahora

Introducción

En este laboratorio, explorará los conceptos fundamentales de la manipulación de arrays de NumPy, centrándose específicamente en la distinción entre copias y vistas. Comprender esta diferencia es crucial para escribir código numérico eficiente y sin errores en Python. Escribirá y ejecutará scripts de Python para ver estos conceptos en acción.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 96%. Ha recibido una tasa de reseñas positivas del 97% por parte de los estudiantes.

La Diferencia Entre una Copia y una Vista

En NumPy, una copia es un array completamente nuevo con sus propios datos, mientras que una vista es una nueva forma de ver los mismos datos. Modificar una vista afectará al array original, pero modificar una copia no lo hará.

Veamos esto en la práctica. Crearemos un array, luego haremos una vista y una copia de él. Luego modificaremos ambos y observaremos el efecto en el array original.

Primero, abra el archivo main.py desde el explorador de archivos de la izquierda. Luego, reemplace su contenido con el siguiente código:

import numpy as np

## --- Parte 1: Modificar una Vista ---
print("--- Modifying a View ---")
## Crear un array original
original_array_view = np.array([1, 2, 3, 4, 5])
print(f"Original array: {original_array_view}")

## Crear una vista del array
view_array = original_array_view.view()
## Modificar el primer elemento de la vista
view_array[0] = 99
print(f"View after modification: {view_array}")
print(f"Original array after modifying the view: {original_array_view}\n")


## --- Parte 2: Modificar una Copia ---
print("--- Modifying a Copy ---")
## Crear otro array original
original_array_copy = np.array([10, 20, 30, 40, 50])
print(f"Original array: {original_array_copy}")

## Crear una copia del array
copy_array = original_array_copy.copy()
## Modificar el primer elemento de la copia
copy_array[0] = 999
print(f"Copy after modification: {copy_array}")
print(f"Original array after modifying the copy: {original_array_copy}")

Ahora, guarde el archivo y ejecútelo desde la terminal para ver la salida.

python main.py

Debería ver la siguiente salida. Observe cómo el array original cambió cuando se modificó su vista, pero permaneció sin cambios cuando se modificó su copia.

--- Modifying a View ---
Original array: [1 2 3 4 5]
View after modification: [99  2  3  4  5]
Original array after modifying the view: [99  2  3  4  5]

--- Modifying a Copy ---
Original array: [10 20 30 40 50]
Copy after modification: [999  20  30  40  50]
Original array after modifying the copy: [10 20 30 40 50]

Esto demuestra la diferencia fundamental: una vista está vinculada a los datos originales, mientras que una copia es completamente independiente.

Rebanado de un Array - Creación de una Vista

Una operación muy común en NumPy es el rebanado (slicing), que se utiliza para seleccionar un rango de elementos de un array. El rebanado básico siempre crea una vista del array original. Esta es una característica clave para la eficiencia de la memoria, pero debe tener en cuenta que modificar la rebanada alterará los datos originales.

Probemos esto. Borre el contenido de main.py y agregue el siguiente código:

import numpy as np

## Crear un array del 0 al 9
original_array = np.arange(10)
print(f"Original array: {original_array}")

## Crear una rebanada del array (elementos del índice 2 al 4)
array_slice = original_array[2:5]
print(f"Slice of the array: {array_slice}")

## Modificar el primer elemento de la rebanada
print("Modifying the first element of the slice to 100...")
array_slice[0] = 100

## Imprimir el array original de nuevo para ver el cambio
print(f"Original array after modification: {original_array}")

Guarde el archivo y ejecútelo en la terminal.

python main.py

Su salida será:

Original array: [0 1 2 3 4 5 6 7 8 9]
Slice of the array: [2 3 4]
Modifying the first element of the slice to 100...
Original array after modification: [  0   1 100   3   4   5   6   7   8   9]

Como puede ver, cambiar array_slice[0] también cambió original_array[2]. Esto confirma que la rebanada era una vista, no una copia.

Indexación Avanzada - Creación de una Copia

Mientras que el rebanado básico crea vistas, la indexación avanzada siempre crea una copia. La indexación avanzada implica pasar una lista, tupla u otro array de índices para seleccionar elementos. Dado que los elementos seleccionados pueden no estar en un bloque contiguo de memoria, NumPy crea un nuevo array (una copia) para contenerlos.

Contrastemos esto con el paso anterior. Borre main.py e inserte este código:

import numpy as np

## Crear un array del 0 al 9
original_array = np.arange(10)
print(f"Original array: {original_array}")

## Usar indexación avanzada para seleccionar los elementos en los índices 1, 3 y 5
indexed_array = original_array[[1, 3, 5]]
print(f"Indexed array: {indexed_array}")

## Modificar el primer elemento del nuevo array
print("Modifying the first element of the indexed array to 100...")
indexed_array[0] = 100

## Imprimir el array original de nuevo para ver si cambió
print(f"Original array after modification: {original_array}")

Guarde el archivo y ejecute el script.

python main.py

La salida será:

Original array: [0 1 2 3 4 5 6 7 8 9]
Indexed array: [1 3 5]
Modifying the first element of the indexed array to 100...
Original array after modification: [0 1 2 3 4 5 6 7 8 9]

Esta vez, el array original permanece sin cambios. indexed_array era una copia, por lo que las modificaciones en él no afectaron a original_array.

Identificación de Copias y Vistas con .base

A veces no es obvio si una operación devolvió una vista o una copia. NumPy proporciona una forma fiable de comprobarlo: el atributo .base de un array.

  • Si un array es una vista, su atributo .base apuntará al objeto array original al que pertenece.
  • Si un array es una copia, su atributo .base será None.

Usemos esto para confirmar nuestros hallazgos de los pasos anteriores. Borre main.py y agregue el siguiente código:

import numpy as np

## Crear un array original
original_array = np.arange(10)
print(f"Original array: {original_array}\n")

## Crear una vista usando rebanado
view_slice = original_array[2:5]
print(f"Slice (view): {view_slice}")
## Comprobar si la rebanada es una vista del array original
print(f"Is the slice a view? {view_slice.base is original_array}\n")

## Crear una copia usando indexación avanzada
copy_indexed = original_array[[1, 3, 5]]
print(f"Indexed (copy): {copy_indexed}")
## Comprobar si el array indexado es una copia
print(f"Is the indexed array a copy? {copy_indexed.base is None}")

Guarde el archivo y ejecútelo desde la terminal.

python main.py

Obtendrá la siguiente salida, que confirma programáticamente que la rebanada es una vista y el array indexado es una copia.

Original array: [0 1 2 3 4 5 6 7 8 9]

Slice (view): [2 3 4]
Is the slice a view? True

Indexed (copy): [1 3 5]
Is the indexed array a copy? True

El atributo .base es una herramienta invaluable para depurar y asegurar que su código se comporta como se espera.

Resumen

En este laboratorio, ha aprendido la diferencia crítica entre una copia y una vista de un array de NumPy. Ha practicado la creación de ambas y ha observado cómo las modificaciones afectan a los datos originales.

  • Ha aprendido que métodos como .view() y el rebanado básico crean vistas, que son eficientes en memoria pero pueden provocar efectos secundarios no deseados si se modifican.
  • Ha aprendido que métodos como .copy() y la indexación avanzada crean copias, que son independientes del array original.
  • Finalmente, ha aprendido a utilizar el atributo .base para comprobar de forma definitiva si un array es una vista o una copia.

Dominar estos conceptos es un paso clave para escribir aplicaciones numéricas robustas y eficientes con NumPy.