CURSO

Python para Ciencia de Datos

Ph.D. Antonio Escamilla P.
8 sections to go

Capítulo 2: NumPy - La Base del Análisis Numérico en Python

¿Qué es NumPy?

NumPy (Numerical Python) es la biblioteca fundamental para la computación científica en Python. Proporciona soporte para arrays y matrices multidimensionales, junto con una colección de funciones matemáticas de alto nivel para operar con estos arrays de manera eficiente.

¿Por qué NumPy es esencial para la ciencia de datos?

En este capítulo, exploraremos los fundamentos de NumPy y aprenderemos a utilizar sus capacidades para manipular datos numéricos de manera eficiente.

Logo de NumPy
El ecosistema de NumPy forma la base de la ciencia de datos en Python

Instalación y Primeros Pasos con NumPy

Antes de comenzar a trabajar con NumPy, necesitamos instalarlo. La forma más común es utilizar pip, el administrador de paquetes de Python:

# Instalar NumPy
pip install numpy

Si estás utilizando Anaconda, NumPy ya viene incluido. Para verificar la versión instalada, puedes ejecutar:

import numpy as np
print(np.__version__)
1.24.3
Por convención, NumPy se importa con el alias np. Esto hace que el código sea más legible y consistente con la comunidad de ciencia de datos.

Tu primer array de NumPy

Comencemos creando nuestro primer array de NumPy:

# Importar NumPy
import numpy as np

# Crear un array simple
arr = np.array([1, 2, 3, 4, 5])
print(arr)
print(type(arr))
[1 2 3 4 5]
<class 'numpy.ndarray'>

A diferencia de las listas de Python, los arrays de NumPy son homogéneos, lo que significa que todos los elementos deben ser del mismo tipo. Esto permite optimizaciones que hacen a NumPy mucho más rápido para operaciones numéricas.

Arrays en NumPy

Los arrays son la estructura de datos principal en NumPy. Podemos pensar en ellos como colecciones de elementos del mismo tipo organizados en una estructura de filas y columnas.

Creando Arrays

Existen múltiples formas de crear arrays en NumPy:

# Desde una lista
arr1 = np.array([1, 2, 3, 4])
print(f"Desde lista: {arr1}")

# Crear un array con ceros
arr2 = np.zeros(5)  # Array unidimensional con 5 ceros
print(f"Array de ceros: {arr2}")

# Crear un array con unos
arr3 = np.ones((2, 3))  # Array 2x3 de unos
print(f"Array de unos:\n{arr3}")

# Crear array con valores secuenciales
arr4 = np.arange(0, 10, 2)  # Valores de 0 a 10 (exclusivo) con paso 2
print(f"Array con arange: {arr4}")

# Crear array con valores espaciados linealmente
arr5 = np.linspace(0, 1, 5)  # 5 valores entre 0 y 1, espaciados uniformemente
print(f"Array con linspace: {arr5}")

# Crear array con valores aleatorios
arr6 = np.random.random((2, 2))  # Array 2x2 con valores aleatorios entre 0 y 1
print(f"Array aleatorio:\n{arr6}")

# Crear matriz identidad
arr7 = np.eye(3)  # Matriz identidad 3x3
print(f"Matriz identidad:\n{arr7}")
Desde lista: [1 2 3 4]
Array de ceros: [0. 0. 0. 0. 0.]
Array de unos:
[[1. 1. 1.]
[1. 1. 1.]]
Array con arange: [0 2 4 6 8]
Array con linspace: [0. 0.25 0.5 0.75 1. ]
Array aleatorio:
[[0.23651224 0.82170757]
[0.43459951 0.0494728 ]]
Matriz identidad:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]]

Dimensiones y Forma de Arrays

Los arrays de NumPy pueden tener múltiples dimensiones. Veamos cómo trabajar con ellas:

# Array unidimensional (vector)
vector = np.array([1, 2, 3, 4])
print(f"Vector: {vector}")
print(f"Dimensión: {vector.ndim}")
print(f"Forma: {vector.shape}")
print(f"Tamaño: {vector.size}")

# Array bidimensional (matriz)
matriz = np.array([[1, 2, 3], [4, 5, 6]])
print(f"\nMatriz:\n{matriz}")
print(f"Dimensión: {matriz.ndim}")
print(f"Forma: {matriz.shape}")  # (filas, columnas)
print(f"Tamaño: {matriz.size}")

# Array tridimensional (cubo)
cubo = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(f"\nCubo:\n{cubo}")
print(f"Dimensión: {cubo.ndim}")
print(f"Forma: {cubo.shape}")  # (planos, filas, columnas)
print(f"Tamaño: {cubo.size}")
Vector: [1 2 3 4]
Dimensión: 1
Forma: (4,)
Tamaño: 4

Matriz:
[[1 2 3]
[4 5 6]]
Dimensión: 2
Forma: (2, 3)
Tamaño: 6

Cubo:
[[[1 2]
[3 4]]

[[5 6]
[7 8]]]
Dimensión: 3
Forma: (2, 2, 2)
Tamaño: 8
La forma (shape) de un array es una tupla que indica el tamaño de cada dimensión. Por ejemplo, una forma de (2, 3) indica una matriz con 2 filas y 3 columnas.

Tipos de Datos en NumPy

NumPy admite diferentes tipos de datos numéricos, lo que permite optimizar el uso de memoria y mejorar el rendimiento:

# Tipos de datos en NumPy
arr_int = np.array([1, 2, 3], dtype=np.int32)
print(f"Array de enteros: {arr_int}, tipo: {arr_int.dtype}")

arr_float = np.array([1.0, 2.5, 3.7], dtype=np.float64)
print(f"Array de flotantes: {arr_float}, tipo: {arr_float.dtype}")

arr_bool = np.array([True, False, True])
print(f"Array de booleanos: {arr_bool}, tipo: {arr_bool.dtype}")

# Cambiar el tipo de datos (conversión)
arr_to_float = np.array([1, 2, 3], dtype=np.float32)
print(f"Enteros a flotantes: {arr_to_float}, tipo: {arr_to_float.dtype}")

arr_to_int = np.array([1.9, 2.8, 3.1], dtype=np.int64)
print(f"Flotantes a enteros: {arr_to_int}, tipo: {arr_to_int.dtype}")  # Nótese el truncamiento
Array de enteros: [1 2 3], tipo: int32
Array de flotantes: [1. 2.5 3.7], tipo: float64
Array de booleanos: [ True False True], tipo: bool
Enteros a flotantes: [1. 2. 3.], tipo: float32
Flotantes a enteros: [1 2 3], tipo: int64
Al convertir de flotantes a enteros, NumPy trunca los valores (elimina la parte decimal) en lugar de redondearlos. Si necesitas redondear, usa funciones como np.round() antes de la conversión.

Cambiar la forma de un Array

NumPy proporciona varias formas de cambiar la estructura de un array sin modificar sus datos:

# Cambiar forma con reshape
arr = np.arange(12)  # Array de 0 a 11
print(f"Array original: {arr}")

# Cambiar a matriz 3x4
matriz = arr.reshape(3, 4)
print(f"Matriz 3x4:\n{matriz}")

# Cambiar a matriz 2x2x3
cubo = arr.reshape(2, 2, 3)
print(f"Cubo 2x2x3:\n{cubo}")

# Dimensión desconocida (NumPy la calcula)
matriz_auto = arr.reshape(3, -1)  # 3 filas, columnas calculadas automáticamente
print(f"Matriz 3x?:\n{matriz_auto}")

# Aplanar un array multidimensional
arr_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
arr_flat = arr_3d.flatten()  # Crea una copia
print(f"Array aplanado: {arr_flat}")

# Alternativa a flatten (vista, no copia)
arr_ravel = arr_3d.ravel()
print(f"Array con ravel: {arr_ravel}")
Array original: [ 0 1 2 3 4 5 6 7 8 9 10 11]
Matriz 3x4:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Cubo 2x2x3:
[[[ 0 1 2]
[ 3 4 5]]

[[ 6 7 8]
[ 9 10 11]]]
Matriz 3x?:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Array aplanado: [1 2 3 4 5 6 7 8]
Array con ravel: [1 2 3 4 5 6 7 8]
reshape reorganiza los elementos de un array sin cambiar su contenido. La cantidad total de elementos debe permanecer igual antes y después del cambio de forma.

Indexación y Slicing en NumPy

Similar a las listas de Python, los arrays de NumPy admiten indexación y slicing, pero con capacidades avanzadas para trabajar con arrays multidimensionales.

Indexación Básica

En NumPy, la indexación y el slicing son herramientas poderosas para acceder y manipular subconjuntos de datos. La siguiente imagen ilustra diferentes operaciones de slicing en un array multidimensional, donde cada color representa un tipo diferente de selección:

Ejemplos de slicing en NumPy
Visualización de diferentes operaciones de slicing en arrays multidimensionales de NumPy
# Array unidimensional
arr = np.array([10, 20, 30, 40, 50])

# Acceso a elementos individuales
print(f"Primer elemento: {arr[0]}")
print(f"Último elemento: {arr[-1]}")

# Array bidimensional
matriz = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f"\nMatriz:\n{matriz}")

# Acceso a elementos en matriz
print(f"Elemento en fila 1, columna 2: {matriz[1, 2]}")  # Equivalente a matriz[1][2]
print(f"Última fila: {matriz[-1]}")  # Fila completa
Primer elemento: 10
Último elemento: 50

Matriz:
[[1 2 3]
[4 5 6]
[7 8 9]]
Elemento en fila 1, columna 2: 6
Última fila: [7 8 9]

Slicing en Arrays

El slicing en NumPy sigue la sintaxis inicio:fin:paso y funciona en todas las dimensiones:

# Slicing en array unidimensional
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

# Sintaxis: array[inicio:fin:paso]
print(f"Elementos del 2 al 5: {arr[2:6]}")
print(f"Elementos hasta el 4: {arr[:5]}")
print(f"Elementos desde el 6: {arr[6:]}")
print(f"Elementos con paso 2: {arr[1:8:2]}")
print(f"Array invertido: {arr[::-1]}")

# Slicing en array bidimensional
matriz = np.array([[1, 2, 3, 4],
                   [5, 6, 7, 8],
                   [9, 10, 11, 12]])

# Seleccionar submatriz (filas 0-1, columnas 1-3)
submatriz = matriz[:2, 1:3]
print(f"\nSubmatriz:\n{submatriz}")

# Seleccionar filas completas
filas_02 = matriz[[0, 2]]  # Filas 0 y 2
print(f"\nFilas 0 y 2:\n{filas_02}")

# Seleccionar columnas específicas
columna_1 = matriz[:, 1]  # Toda la columna 1
print(f"\nColumna 1: {columna_1}")

# Seleccionar diagonales
diagonal = np.diag(matriz)  # Diagonal principal
print(f"\nDiagonal principal: {diagonal}")
Elementos del 2 al 5: [2 3 4 5]
Elementos hasta el 4: [0 1 2 3 4]
Elementos desde el 6: [6 7 8 9]
Elementos con paso 2: [1 3 5 7]
Array invertido: [9 8 7 6 5 4 3 2 1 0]

Submatriz:
[[2 3]
[6 7]]

Filas 0 y 2:
[[ 1 2 3 4]
[ 9 10 11 12]]

Columna 1: [ 2 6 10]

Diagonal principal: [1 6 11]
A diferencia de las listas de Python, cuando haces slicing en arrays de NumPy, obtienes vistas en lugar de copias. Esto significa que modificar la vista modificará el array original. Si necesitas una copia independiente, usa el método copy().

Indexación Avanzada

NumPy permite formas sofisticadas de seleccionar elementos:

# Indexación con arrays de índices
arr = np.array([10, 20, 30, 40, 50])
indices = np.array([0, 2, 4])
print(f"Elementos en posiciones {indices}: {arr[indices]}")

# Indexación condicional (boolean masking)
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
mask = arr > 5  # Crear una máscara booleana
print(f"Máscara booleana: {mask}")
print(f"Elementos mayores que 5: {arr[mask]}")

# Forma más compacta
print(f"Elementos pares: {arr[arr % 2 == 0]}")

# Indexación con arrays booleanos en matrices
matriz = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f"\nMatriz:\n{matriz}")

# Seleccionar elementos mayores que 5
print(f"Elementos > 5: {matriz[matriz > 5]}")

# Reemplazar valores con condiciones
matriz_copia = matriz.copy()
matriz_copia[matriz_copia % 2 == 0] = 0  # Reemplazar pares con 0
print(f"\nMatriz con pares reemplazados:\n{matriz_copia}")
Elementos en posiciones [0 2 4]: [10 30 50]
Máscara booleana: [False False False False False True True True True True]
Elementos mayores que 5: [ 6 7 8 9 10]
Elementos pares: [ 2 4 6 8 10]

Matriz:
[[1 2 3]
[4 5 6]
[7 8 9]]
Elementos > 5: [6 7 8 9]

Matriz con pares reemplazados:
[[1 0 3]
[0 5 0]
[7 0 9]]

Operaciones Vectorizadas con NumPy

Una de las principales ventajas de NumPy es su capacidad para realizar operaciones vectorizadas, que son mucho más rápidas que los bucles explícitos en Python.

Operaciones Aritméticas

# Operaciones aritméticas con arrays
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])

# Operaciones elemento a elemento
print(f"a + b = {a + b}")  # Suma
print(f"a - b = {a - b}")  # Resta
print(f"a * b = {a * b}")  # Multiplicación elemento a elemento
print(f"a / b = {a / b}")  # División
print(f"a ** 2 = {a ** 2}")  # Potencia
print(f"a % 2 = {a % 2}")  # Módulo

# Operaciones con escalares
print(f"\na + 2 = {a + 2}")  # Suma con escalar
print(f"a * 3 = {a * 3}")  # Multiplicación por escalar

# Comparaciones
print(f"\na > 2: {a > 2}")
print(f"a == 2: {a == 2}")
print(f"a < b: {a < b}")

# Funciones trigonométricas, logarítmicas, etc.
angulos = np.array([0, np.pi/4, np.pi/2, np.pi])
print(f"\nsen(angulos) = {np.sin(angulos)}")
print(f"cos(angulos) = {np.cos(angulos)}")

valores = np.array([1, 10, 100, 1000])
print(f"log10(valores) = {np.log10(valores)}")
a + b = [ 6 8 10 12]
a - b = [-4 -4 -4 -4]
a * b = [ 5 12 21 32]
a / b = [0.2 0.33333333 0.42857143 0.5 ]
a ** 2 = [ 1 4 9 16]
a % 2 = [1 0 1 0]

a + 2 = [3 4 5 6]
a * 3 = [ 3 6 9 12]

a > 2: [False False True True]
a == 2: [False True False False]
a < b: [ True True True True]

sen(angulos) = [0. 0.70710678 1. 0. ]
cos(angulos) = [ 1.00000000e+00 7.07106781e-01 6.12323400e-17 -1.00000000e+00]
log10(valores) = [0. 1. 2. 3.]

Operaciones Estadísticas

# Operaciones estadísticas
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# Estadísticas básicas
print(f"Suma total: {np.sum(arr)}")
print(f"Media: {np.mean(arr)}")
print(f"Desviación estándar: {np.std(arr)}")
print(f"Mínimo: {np.min(arr)}")
print(f"Máximo: {np.max(arr)}")

# Estadísticas por eje
print(f"\nSuma por filas: {np.sum(arr, axis=1)}")
print(f"Media por columnas: {np.mean(arr, axis=0)}")
print(f"Mínimo por filas: {np.min(arr, axis=1)}")
print(f"Máximo por columnas: {np.max(arr, axis=0)}")

# Posición del mínimo y máximo
print(f"\nPosición del mínimo: {np.argmin(arr)}")
print(f"Posición del máximo: {np.argmax(arr)}")

# Acumulativas
print(f"\nSuma acumulativa: {np.cumsum(arr.ravel())}")
print(f"Producto acumulativo: {np.cumprod(arr[:, 0])}")  # Solo primera columna
Suma total: 45
Media: 5.0
Desviación estándar: 2.581988897471611
Mínimo: 1
Máximo: 9

Suma por filas: [ 6 15 24]
Media por columnas: [4. 5. 6.]
Mínimo por filas: [1 4 7]
Máximo por columnas: [7 8 9]

Posición del mínimo: 0
Posición del máximo: 8

Suma acumulativa: [ 1 3 6 10 15 21 28 36 45]
Producto acumulativo: [ 1 4 28]

Operaciones con Matrices

# Operaciones matriciales
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

# Multiplicación matricial (producto punto)
c1 = np.dot(a, b)
print(f"Producto punto (dot):\n{c1}")

# Sintaxis alternativa para producto matricial
c2 = a @ b  # Disponible desde Python 3.5
print(f"\nProducto matricial (@):\n{c2}")

# Producto elemento a elemento
c3 = a * b
print(f"\nProducto elemento a elemento (*):\n{c3}")

# Determinante
det_a = np.linalg.det(a)
print(f"\nDeterminante de a: {det_a}")

# Inversa
inv_a = np.linalg.inv(a)
print(f"\nInversa de a:\n{inv_a}")

# Verificación de la inversa (debería ser cercana a la identidad)
identity = np.dot(a, inv_a)
print(f"\nA × A⁻¹ (debería ser la identidad):\n{identity}")

# Resolver sistema de ecuaciones lineales: Ax = b
b_vec = np.array([1, 2])
x = np.linalg.solve(a, b_vec)
print(f"\nSolución a Ax = b: {x}")
print(f"Verificación: {np.dot(a, x)}")
Producto punto (dot):
[[19 22]
[43 50]]

Producto matricial (@):
[[19 22]
[43 50]]

Producto elemento a elemento (*):
[[ 5 12]
[21 32]]

Determinante de a: -2.0000000000000004

Inversa de a:
[[-2. 1. ]
[ 1.5 -0.5]]

A × A⁻¹ (debería ser la identidad):
[[1.00000000e+00 0.00000000e+00]
[8.88178420e-16 1.00000000e+00]]

Solución a Ax = b: [0. 0.5]
Verificación: [1. 2.]
NumPy tiene muchas más funciones para álgebra lineal, incluyendo descomposiciones (SVD, LU, QR), autovalores y autovectores. Estas operaciones son esenciales en machine learning y análisis de datos avanzado.

Funciones Universales (ufuncs) en NumPy

NumPy tiene integradas muchas funciones universales (ufuncs), que son esencialmente operaciones matemáticas que se pueden aplicar a todo el array de manera vectorizada. Estas funciones operan elemento por elemento y son extremadamente eficientes.

Las categorías principales de funciones universales incluyen:

Veamos algunos ejemplos de estas funciones universales en acción:

Funciones Matemáticas

# Crear un array de ejemplo
arr = np.arange(10)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Calcular la raiz cuadrada de cada elemento
np.sqrt(arr)
array([0. , 1. , 1.41421356, 1.73205081, 2. , 2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
# Calcular el exponencial (e^) de cada elemento
np.exp(arr)
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01, 5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03, 2.98095799e+03, 8.10308393e+03])
# Obtener el valor máximo como una función
np.max(arr)  # lo mismo arr.max()
9

Funciones Trigonométricas

# Calcular el seno de cada elemento
np.sin(arr)
array([ 0. , 0.84147098, 0.90929743, 0.14112001, -0.7568025 , -0.95892427, -0.2794155 , 0.6569866 , 0.98935825, 0.41211849])

Funciones Logarítmicas

# Calcular el logaritmo natural de cada elemento
np.log(arr)
/tmp/ipykernel_38414/1102247883.py:2: RuntimeWarning: divide by zero encountered in log np.log(arr)

array([ -inf, 0. , 0.69314718, 1.09861229, 1.38629436, 1.60943791, 1.79175947, 1.94591015, 2.07944154, 2.19722458])
Cuando se aplican ciertas funciones como log a valores no válidos (como 0), NumPy genera advertencias y produce resultados especiales como -inf (infinito negativo).

Funciones de Valor Absoluto

# Calcular el valor absoluto
np.abs(arr)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Las funciones universales de NumPy son altamente optimizadas y operan mucho más rápido que los bucles equivalentes en Python puro. Siempre que sea posible, utiliza estas funciones para operaciones elemento a elemento en tus arrays.

Estadísticas con NumPy

NumPy proporciona un conjunto robusto de funciones estadísticas que van más allá de las operaciones básicas como suma o media. Estas funciones son esenciales para el análisis exploratorio de datos y la comprensión de las distribuciones.

Medidas de Tendencia Central y Dispersión

# Crear datos de ejemplo
datos = np.array([23, 45, 12, 67, 34, 89, 24, 56, 78, 45, 34, 23])

# Medidas de tendencia central
print(f"Media: {np.mean(datos)}")
print(f"Mediana: {np.median(datos)}")
print(f"Percentil 25%: {np.percentile(datos, 25)}")
print(f"Percentil 75%: {np.percentile(datos, 75)}")

# Medidas de dispersión
print(f"\nDesviación estándar: {np.std(datos)}")
print(f"Varianza: {np.var(datos)}")
print(f"Rango: {np.ptp(datos)}")  # Peak to peak (max - min)
Media: 44.16666666666667
Mediana: 39.5
Percentil 25%: 23.75
Percentil 75%: 61.5

Desviación estándar: 23.30635734475076
Varianza: 543.1861111111112
Rango: 77

Correlación y Covarianza

# Crear dos conjuntos de datos
x = np.array([1, 2, 3, 4, 5])
y = np.array([5, 4, 2, 4, 5])

# Calcular coeficiente de correlación
correlacion = np.corrcoef(x, y)
print(f"Matriz de correlación:\n{correlacion}")
print(f"Coeficiente de correlación entre x e y: {correlacion[0, 1]}")

# Calcular covarianza
covarianza = np.cov(x, y)
print(f"\nMatriz de covarianza:\n{covarianza}")
print(f"Covarianza entre x e y: {covarianza[0, 1]}")
Matriz de correlación:
[[ 1. -0.15430335]
[-0.15430335 1. ]]
Coeficiente de correlación entre x e y: -0.15430334996209192

Matriz de covarianza:
[[2.5 -0.5 ]
[-0.5 1.7 ]]
Covarianza entre x e y: -0.5

Histogramas y Análisis de Frecuencias

# Generar datos aleatorios
datos_aleatorios = np.random.normal(loc=50, scale=10, size=1000)

# Crear un histograma
histograma, bins = np.histogram(datos_aleatorios, bins=10)

print("Histograma:")
print(f"Frecuencias: {histograma}")
print(f"Límites de bins: {bins}")

# Funciones auxiliares para análisis de frecuencias
valores_unicos, conteos = np.unique(np.round(datos_aleatorios), return_counts=True)
print(f"\nValores más frecuentes:")
# Mostrar los 5 valores más frecuentes
indices_ordenados = np.argsort(-conteos)[:5]
for i in indices_ordenados:
    print(f"Valor {valores_unicos[i]}: aparece {conteos[i]} veces")
Histograma:
Frecuencias: [ 20 55 117 178 223 195 129 57 18 8]
Límites de bins: [24.66393496 31.55754824 38.45116153 45.34477481 52.23838809 59.13200138
66.02561466 72.91922794 79.81284123 86.70645451 93.59977522]

Valores más frecuentes:
Valor 50.0: aparece 55 veces
Valor 51.0: aparece 54 veces
Valor 49.0: aparece 53 veces
Valor 53.0: aparece 49 veces
Valor 44.0: aparece 46 veces

Estadísticas a lo largo de Ejes Específicos

# Crear matriz de ejemplo
matriz = np.array([[1, 5, 9], 
                   [2, 6, 10], 
                   [3, 7, 11],
                   [4, 8, 12]])

print(f"Matriz original:\n{matriz}")

# Estadísticas por filas (axis=1)
print(f"\nMedia por filas: {np.mean(matriz, axis=1)}")
print(f"Desviación estándar por filas: {np.std(matriz, axis=1)}")

# Estadísticas por columnas (axis=0)
print(f"\nMedia por columnas: {np.mean(matriz, axis=0)}")
print(f"Desviación estándar por columnas: {np.std(matriz, axis=0)}")

# Valores acumulativos
print(f"\nSuma acumulativa a lo largo de las filas:\n{np.cumsum(matriz, axis=1)}")
print(f"Suma acumulativa a lo largo de las columnas:\n{np.cumsum(matriz, axis=0)}")
Matriz original:
[[ 1 5 9]
[ 2 6 10]
[ 3 7 11]
[ 4 8 12]]

Media por filas: [5. 6. 7. 8.]
Desviación estándar por filas: [3.26598632 3.26598632 3.26598632 3.26598632]

Media por columnas: [2.5 6.5 10.5]
Desviación estándar por columnas: [1.11803399 1.11803399 1.11803399]

Suma acumulativa a lo largo de las filas:
[[ 1 6 15]
[ 2 8 18]
[ 3 10 21]
[ 4 12 24]]
Suma acumulativa a lo largo de las columnas:
[[ 1 5 9]
[ 3 11 19]
[ 6 18 30]
[10 26 42]]
Las funciones estadísticas de NumPy pueden aplicarse a todo el array o a lo largo de ejes específicos usando el parámetro axis. Esto es particularmente útil para análisis de datos tabulares, donde las filas pueden representar observaciones y las columnas variables.

Ejercicios Prácticos

Notebooks interactivos en Google Colab donde podrás practicar los conceptos básicos de Python para ciencia de datos. Ideal para consolidar lo aprendido en este capítulo con ejercicios prácticos y ejemplos ejecutables

Referencias

Para aprender más sobre Numpy, recomendamos consultar estos recursos oficiales: