1.4 · NumPy¶
Objetivos. Al terminar este capítulo podrás: crear e inspeccionar arrays; indexarlos y filtrarlos con máscaras booleanas; calcular estadísticas por fila o columna; generar números aleatorios reproducibles; distinguir copias de vistas; transponer y multiplicar matrices; y usar broadcasting para operar arrays de distinto tamaño.
Evidencia de logro. Transformarás y resumirás una tabla de ventas de TechShop usando arrays, máscaras y broadcasting, justificando cada operación.
Contexto y motivación¶
Los datos tabulares son, por debajo, números en bloque. NumPy es la capa fundamental que da a Python una estructura rápida y cómoda para trabajar con esos bloques: el array. Pandas (cap. 1.5) y scikit-learn (unidades 3–7) se construyen sobre él: un DataFrame de Pandas guarda sus columnas como arrays, y scikit-learn espera los datos en arrays.
Aprender NumPy no es un trámite: es entender cómo se representan los datos por debajo. Casi todos los errores de rendimiento y de lógica que verás más adelante (una operación lenta, una matriz mal alineada, un cambio que "se propaga solo") se explican con los conceptos de este capítulo.
Vocabulario¶
| Término | Significado |
|---|---|
| Array | Estructura de NumPy: colección de elementos del mismo tipo, en 1D, 2D, etc. |
| dtype | Tipo de datos de un array (int64, float64…) |
| Máscara | Array de booleanos que selecciona elementos |
| Eje (axis) | Eje por el que se agrega la operación: axis=0 atraviesa las filas y devuelve una entrada por columna; axis=1 atraviesa las columnas y devuelve una entrada por fila |
| Vista | Array que comparte memoria con el original |
| Copia | Array independiente, con su propia memoria |
| Broadcasting | Regla que permite operar arrays de formas distintas |
Prerrequisitos¶
Haber leído los capítulos 1.1 a 1.3 y tener el entorno preparado (Entorno de trabajo).
Ruta de estudio
El núcleo obligatorio para la práctica es crear arrays, inspeccionar su
forma y tipo, indexar y filtrar con máscaras, calcular estadísticas,
generar datos reproducibles, distinguir vistas de copias y usar
broadcasting. La indexación avanzada, el producto matricial, np.where,
las operaciones de conjuntos y la combinación de arrays se trabajan como
ampliación guiada o demostración cuando el ritmo del grupo lo permita.
Orden de los ejemplos
Los bloques de esta página están pensados para ejecutarse de arriba abajo. Algunos reutilizan imports y arrays definidos en el bloque anterior; si copias uno por separado, incluye también su preparación. En un cuaderno marimo, cada celda debe mostrar esas dependencias.
1. Listas de Python vs. arrays de NumPy¶
Una lista de Python guarda referencias a objetos (cada elemento puede ser de un tipo distinto y vive en un lugar distinto de la memoria). Un array de NumPy agrupa elementos del mismo tipo y permite almacenarlos de forma compacta; muchos arrays son contiguos en memoria. Esa diferencia tiene dos consecuencias prácticas: operar es más simple y más rápido.
import numpy as np
lista = [1, 2, 3, 4, 5]
arr = np.array(lista)
print("lista * 2:", [x * 2 for x in lista]) # hay que hacer un bucle
print("array * 2:", arr * 2) # se aplica a todo el array
Salida esperada:
En NumPy arr * 2 multiplica cada elemento sin escribir ningún bucle: es
una operación vectorizada. Tú escribes la operación una vez y NumPy la
aplica a todos los elementos a la vez.
¿Por qué es más rápido? Porque el bucle for x in lista se ejecuta en
Python, instrucción a instrucción, con toda su sobrecarga. La operación
vectorizada se ejecuta en C, un lenguaje mucho más cercano a la máquina, y
además sobre datos contiguos en memoria (que la CPU puede leer en bloque). Es la
misma razón por la que sumar a mano 1000 números es más lento que usar una
calculadora.
import time
n = 1_000_000
numeros = list(range(n))
t0 = time.perf_counter()
suma_lista = sum(x ** 2 for x in numeros) # bucle Python
t_lista = time.perf_counter() - t0
t0 = time.perf_counter()
suma_numpy = (np.arange(n) ** 2).sum() # vectorizado
t_numpy = time.perf_counter() - t0
print("sumas iguales:", suma_lista == suma_numpy)
print(f"tiempos medidos: lista={t_lista:.4f} s, numpy={t_numpy:.4f} s")
Qué observar (la salida cambia en cada equipo): el programa muestra
sumas iguales: True y dos tiempos medidos. Los valores concretos dependen del
equipo y de la carga del sistema; no se evalúa que coincidan con una cifra
concreta, sino la proporción entre ambos.
time.perf_counter() funciona como un cronómetro: restar dos lecturas da los
segundos transcurridos. En general, NumPy suele ser más rápido:
el bucle en Python es el cuello de botella y NumPy ejecuta la operación
vectorizada en C por debajo.
Regla práctica
En NumPy (y en Pandas) evita los bucles siempre que exista una operación vectorizada. Piensa en "qué quiero hacerle a todo el array", no en "qué le hago a cada elemento uno a uno".
2. Crear arrays¶
import numpy as np
print(np.zeros(3)) # inicializar con ceros
print(np.ones(4)) # inicializar con unos
print(np.arange(0, 10, 2)) # secuencia con paso (como range)
print(np.linspace(0, 1, 5)) # 5 valores equiespaciados entre 0 y 1
Salida esperada:
zerosyonessirven para inicializar (crear un contenedor que luego rellenarás).arange(0, 10, 2)genera 0, 2, 4, 6, 8 (el10no se incluye, igual querange).linspace(0, 1, 5)genera 5 valores equiespaciados entre 0 y 1, incluyendo ambos extremos. Es lo que usas cuando te da igual el paso y te importa el número de puntos.
2.1 Arrays 2D (matrices)¶
matriz = np.array([[1, 2, 3],
[4, 5, 6]])
print(matriz)
print("shape:", matriz.shape, "· ndim:", matriz.ndim,
"· size:", matriz.size, "· dtype:", matriz.dtype)
Salida esperada:
Interpreta los cuatro atributos, porque los usarás constantemente:
shape= (filas, columnas) → (2, 3): dos filas y tres columnas.ndim= número de dimensiones (2: es una tabla).size= número total de elementos (6 = 2 × 3).dtype= tipo de datos (int64).
2.2 Tipos de datos¶
print(np.array([1, 2, 3], dtype=float)) # forzar float64
print(np.array([1, 2, 3]).astype(float)) # convertir después
Salida esperada:
Elegir bien el tipo
Un array de enteros no admite decimales. NumPy promueve automáticamente
a float en operaciones como np.array([1, 2, 3]) / 2 → [0.5 1. 1.5],
pero si guardas un 2.5 en un array int, se trunca a 2 sin avisar.
Cuando trabajes con importes, precios o medias, usa float64 desde el
principio. El tipo correcto evita resultados silenciosamente equivocados.
3. Indexación y slicing¶
arr = np.array([1, 2, 3, 4, 5])
print(arr[0], arr[-1], arr[1:4]) # primer, último y rango [1,4)
print(arr[::-1]) # invertido
Salida esperada:
Recuerda la regla de Python: arr[1:4] va del índice 1 al 3 (el 4 no
se incluye). Los índices negativos cuentan desde el final (-1 es el
último). El truco [::-1] recorre el array hacia atrás, es decir, lo
invierte.
En 2D usamos [fila, columna]:
matriz = np.array([[1, 2, 3], [4, 5, 6]])
print(matriz[0, 1]) # fila 0, columna 1
print(matriz[:, 1]) # toda la columna 1
print(matriz[1, :2]) # fila 1, columnas 0 y 1
Salida esperada:
: solo significa "todo ese eje": matriz[:, 1] es "todas las filas,
columna 1". Memoriza este patrón porque en Pandas y en ML lo usarás a diario:
fila, columna, y : para "todo".
4. Operaciones y máscaras booleanas¶
Las operaciones aritméticas son elemento a elemento:
precios = np.array([12.5, 8.0, 25.0, 5.5, 30.0])
print(precios + 1)
print(np.round(precios * 0.21, 2)) # IVA 21 %
Salida esperada:
precios + 1 suma 1 a cada precio y precios * 0.21 calcula el IVA de
cada precio a la vez. No hay bucle: la operación se "reparte" por todo
el array.
Comparar produce una máscara booleana, y esa máscara selecciona elementos:
print(precios > 10) # la máscara
print(precios[precios > 10]) # los precios que la cumplen
print(precios[(precios >= 8) & (precios <= 20)]) # entre 8 y 20
print(precios[(precios < 8) | (precios > 20)]) # fuera de 8-20
print(precios[~(precios < 10)]) # negación
Salida esperada:
Una máscara es un array de True/False con la misma forma que el
original. Al usarla dentro de los corchetes (precios[mascara]), NumPy se
queda con los elementos donde la máscara vale True. Es la forma idiomática
de "filtrar" en NumPy, y el patrón tabla[condición] se repite idéntico en
Pandas.
Paréntesis obligatorios
Para combinar condiciones no uses and/or (fallan con arrays). Usa
& (y), | (o), ~ (no) y rodea cada comparación con paréntesis:
(precios >= 8) & (precios <= 20). Sin paréntesis, Python evalúa en otro
orden y obtienes un error o un resultado incorrecto.
5. Estadísticas y ejes¶
tabla = np.array([[10, 20, 30],
[40, 50, 60]])
print(tabla.mean()) # media de todo
print(tabla.mean(axis=0)) # media por columna (reduce filas)
print(tabla.mean(axis=1)) # media por fila (reduce columnas)
print(tabla.sum(axis=0)) # suma por columna
print(round(tabla.std(), 2), round(tabla.var(), 2)) # desviación y varianza
Salida esperada:
Aquí está la parte que más cuesta al principio: qué significa axis.
axis=0significa "recorre las filas": el resultado tiene una entrada por columna (media de la columna 0 = (10+40)/2 = 25, etc.).axis=1significa "recorre las columnas": una entrada por fila (media de la fila 0 = (10+20+30)/3 = 20).
Truco para recordarlo: axis=0 "aplasta" hacia abajo (por columnas),
axis=1 "aplasta" hacia la derecha (por filas). Si dudas, dibuja la flecha
sobre la tabla.
La varianza (291,67) es la media de las distancias al cuadrado a la media; la desviación típica (17,08) es su raíz cuadrada, y se mide en las mismas unidades que los datos. Cuanto mayores, más dispersos están los valores.
std() de NumPy y de pandas no coinciden por defecto
NumPy calcula por defecto la desviación poblacional (ddof=0, divide
entre n), mientras que pandas usa la muestral (ddof=1, divide entre
n - 1), como viste en 1.2. Con los
mismos datos obtendrás números ligeramente distintos. Si el valor importa,
escríbelo explícitamente: tabla.std(ddof=1).
6. Números aleatorios reproducibles¶
rng = np.random.default_rng(42) # generador moderno con semilla 42
print(rng.integers(0, 10, size=5)) # enteros en [0, 10)
print(np.round(rng.random(5), 3)) # decimales en [0, 1)
print(np.round(rng.normal(0, 1, 5), 3)) # normal media 0, desv. 1
print(np.round(rng.uniform(1, 10, 5), 3)) # uniforme en [1, 10)
Salida esperada:
[0 7 6 4 4]
[0.697 0.094 0.976 0.761 0.786]
[-0.017 -0.853 0.879 0.778 0.066]
[8.405 4.991 3.045 5.991 1.574]
Los números aleatorios sirven para simular datos (generar ventas de prueba, repartir clientes en grupos, etc.). Las distribuciones más usadas:
integers(0, 10): enteros en [0, 10) — el 10 no entra.random(5): decimales en [0, 1).normal(media, desv): campana de Gauss (la mayoría de valores cerca de la media).uniform(a, b): todos los valores entreaybigual de probables.
La semilla (42) fija el generador: con la misma semilla obtienes los
mismos números siempre. Es como elegir "la partida guardada" del azar.
Salida esperada:
default_rng, no el método antiguo
Usa np.random.default_rng(seed). El método antiguo
np.random.seed(...) sigue existiendo, pero está desaconsejado: es global
(afecta a todo el programa) y menos robusto. Con default_rng creas tu
propio generador, local e independiente.
7. Guardar y cargar arrays¶
np.save("ventas.npy", precios) # guarda en formato binario .npy
cargado = np.load("ventas.npy") # lo recupera
print(cargado)
Salida esperada:
Este bloque crea el archivo ventas.npy en tu carpeta de trabajo: esa es su
comprobación visible (además de la línea impresa). .npy guarda el array en
binario, con su dtype y su forma, de modo que al cargarlo vuelve exacto
(sin pérdidas de precisión como podría ocurrir al pasar por texto). Para
varios arrays a la vez usa np.savez.
8. Copias vs. vistas (y el atributo .base)¶
Esta sección explica uno de los comportamientos más sorprendentes de NumPy, y es la causa de bugs muy difíciles de cazar si no lo conoces.
Un slicing devuelve una vista: comparte memoria con el original (es una "ventana" al mismo dato). Una máscara booleana o un fancy indexing devuelven una copia: un array nuevo e independiente.
a = np.array([1, 2, 3, 4, 5])
vista = a[1:4]
vista[0] = 99
print(a) # ¡el original cambió!
print("¿vista comparte memoria?", vista.base is a)
Salida esperada:
Al hacer vista = a[1:4] no creamos datos nuevos: vista es una ventana
a los elementos 1, 2 y 3 de a. Por eso, modificar vista[0] modifica
también a. Es útil (evita copiar datos grandes) pero hay que saberlo.
b = np.array([1, 2, 3, 4, 5])
seleccion = b[b > 2] # máscara → copia
seleccion[0] = 100
print(b) # el original NO cambia
print("¿seleccion comparte memoria?", seleccion.base is b)
Salida esperada:
La selección con máscara crea un array nuevo (no hay forma de
"ventanear" elementos no contiguos). Por eso cambiarla no afecta a b.
Cómo saberlo en el código
arr.base devuelve el array del que depende: si es None, arr es dueño
de su memoria (copia); si apunta a otro array, es una vista. Úsalo cuando
dudes de por qué un cambio "se propaga" o no. Regla práctica: el slicing
es vista; la selección con máscara o con índices es copia.
9. Fancy indexing¶
Podemos seleccionar con arrays de enteros:
mat = np.array([[10, 11, 12],
[20, 21, 22],
[30, 31, 32]])
print(mat[[0, 2]]) # filas 0 y 2
print(mat[[0, 2], [1, 0]]) # pares (fila,columna): (0,1) y (2,0)
Salida esperada:
"Fancy indexing" es seleccionar con listas/arrays de índices en vez de
un solo índice o un rango. mat[[0, 2]] toma las filas 0 y 2 completas;
mat[[0, 2], [1, 0]] toma los pares (fila 0, columna 1) y (fila 2, columna
0). Recuerda de la sección anterior: el resultado es una copia, no una
vista.
10. Transposición y producto matricial¶
A = np.array([[1, 2],
[3, 4],
[5, 6]])
print(A.T) # transpuesta
print(A * 2) # producto ELEMENTO a ELEMENTO
print(A.T @ A) # producto MATRICIAL (con @)
Salida esperada:
- Transponer (
A.T) intercambia filas por columnas: una matriz de 3×2 pasa a 2×3. A * 2es el producto elemento a elemento.A.T @ Aes el producto matricial: cada celda(i, j)es la suma de los productos de la filaipor la columnaj. El resultado es 2×2.
No confundas * con @
A * Bmultiplica elemento a elemento y admite broadcasting si las formas son compatibles; no exige siempre la misma forma.A @ Bes el producto matricial (filas × columnas; las dimensiones internas deben coincidir).A.T @ A(matriz de Gram, cuadrada y simétrica) aparece muchísimo en ML: resume cómo se relacionan las columnas deAentre sí, y es la base de métodos como la regresión lineal que verás en la Unidad 4.
11. np.where: localizar y condición vectorizada¶
np.where hace dos cosas según cuántos argumentos le pases. La más habitual es
localizar posiciones; la menos habitual es usarlo como condición
vectorizada (un if/else aplicado a todo el array).
11.1 Localizar con np.where (lo más habitual)¶
Si le pasas solo la condición, devuelve las posiciones que la cumplen. Es la forma vectorizada de preguntar "¿dónde están los elementos que cumplen esto?".
ventas = np.array([120, 45, 300, 80, 250])
indices = np.where(ventas >= 100) # localizar las posiciones
print(indices)
print(ventas[indices]) # y recuperar sus valores
Salida esperada:
np.where(condición) devuelve una tupla de arrays de índices: en 1D es
una tupla con un único array ([0, 2, 4]), que son las posiciones donde la
condición es True. Con esos índices puedes recuperar los valores
(ventas[indices]), contarlos (len(indices[0]) → 3) o marcar filas en
otra estructura.
En un array 2D devuelve dos arrays: uno con las filas y otro con las
columnas. Si prefieres directamente los pares (fila, columna), usa
np.argwhere(condición).
11.2 Condición vectorizada con np.where (menos habitual)¶
Con tres argumentos (condición, valor_si_verdad, valor_si_falso), np.where
devuelve un array nuevo en el que cada posición toma un valor u otro según se
cumpla la condición. Es como un if/else aplicado de una sola vez a todos los
elementos, útil para clasificar o sustituir valores sin escribir un bucle:
Salida esperada:
np.where(condición, valor_si_verdad, valor_si_falso) es el "si/entonces"
aplicado a todo el array a la vez, sin bucle: cada posición mira su
condición y elige uno de los dos valores. Es la versión vectorizada del
if/else que usarías en un bucle.
12. Operaciones de conjuntos¶
hoy = np.array([101, 102, 103, 104]) # productos vistos hoy
ayer = np.array([102, 104, 105]) # productos vistos ayer
print(np.intersect1d(hoy, ayer)) # en ambos días
print(np.union1d(hoy, ayer)) # en alguno de los dos
print(np.setdiff1d(hoy, ayer)) # solo hoy
print(np.isin(hoy, [101, 105])) # ¿está en la lista?
print(np.setxor1d(hoy, ayer)) # en uno u otro, no en ambos
print(np.unique(np.array([101, 101, 102]))) # sin repetidos
Salida esperada:
Estas funciones responden a preguntas típicas de negocio: ¿qué productos se
compraron ambos días (intersect1d)? ¿cuáles solo hoy
(setdiff1d)? ¿está este id en la lista (isin)? Te ahorran escribir
bucles de comparación, que son lentos y propensos a errores.
isin, no in1d
Usa np.isin. La función antigua np.in1d está desaconsejada en las
versiones modernas de NumPy.
13. Manipular la forma¶
m = np.arange(12)
print(m.reshape(3, 4)) # 12 elementos → matriz 3x4
print(m.reshape(3, -1).shape) # -1 = "calcúlalo tú"
Salida esperada:
reshape reorganiza los mismos datos con otra forma (el número de elementos
debe coincidir: 12 = 3×4). El -1 es un comodín: "calcula esta dimensión
para que cuadre". Es muy útil cuando sabes cuántas filas (o columnas)
quieres y no quieres hacer la cuenta a mano.
ravel() intenta devolver una vista cuando el orden de los datos lo permite;
si no puede, crea una copia. flatten() crea siempre una copia:
m2 = np.arange(12).reshape(3, 4)
aplanado = m2.ravel()
aplanado[0] = 999
print(m2[0, 0]) # 999: ravel compartía memoria
m3 = np.arange(12).reshape(3, 4)
copia = m3.flatten()
copia[0] = 999
print(m3[0, 0]) # 0: flatten creó una copia
m4 = np.arange(12).reshape(3, 4)[:, ::2] # vista no contigua
aplanado_no_contiguo = m4.ravel()
print(np.shares_memory(m4, aplanado_no_contiguo)) # False: ravel tuvo que copiar
Salida esperada:
ravel() comparte memoria cuando devuelve una vista, por lo que modificar su
resultado puede modificar el array original. En otras situaciones, como la
vista no contigua del ejemplo, devuelve una copia. Si necesitas garantizar un
resultado independiente, usa flatten() o ravel().copy().
14. Combinar y dividir¶
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
print(np.concatenate([x, y])) # uno detrás de otro
print(np.vstack([x, y])) # apilados como filas
print(np.hstack([x, y])) # apilados como columnas
print(np.split(np.arange(9), 3)) # dividir en 3 trozos iguales
Salida esperada:
[1 2 3 4 5 6]
[[1 2 3]
[4 5 6]]
[1 2 3 4 5 6]
[array([0, 1, 2]), array([3, 4, 5]), array([6, 7, 8])]
concatenatepega arrays uno tras otro (misma dimensión).vstackapila verticalmente (cada array se convierte en una fila).hstackapila horizontalmente (columnas).splitdivide en trozos iguales (el tamaño debe ser divisible).
15. Broadcasting¶
NumPy permite operar arrays de formas distintas cuando una de las dimensiones es 1 (o coincide). Se "estira" la dimensión pequeña:
mat = np.array([[1, 2, 3],
[4, 5, 6]])
print(mat + 10) # escalar → se aplica a todos
print(mat + np.array([[10], [20]])) # columna → se suma por filas
print(mat * np.array([1, 10, 100])) # fila → se multiplica por columnas
Salida esperada:
Qué está pasando en cada caso:
mat + 10: el escalar 10 se "estira" a la forma dematy se suma a cada celda.mat + columna (2×1): la columna[10, 20]se repite por las 3 columnas; así, la primera fila suma 10 y la segunda suma 20.mat * fila (3,): la fila[1, 10, 100]se repite por las 2 filas y multiplica cada columna por su factor (columna 0 ×1, columna 1 ×10, columna 2 ×100).
Broadcasting es lo que hace que "sumar una columna a toda la tabla" se escriba en una línea, sin bucles.
La regla
Dos formas son compatibles si, comparadas de derecha a izquierda, cada
dimensión es igual o una de las dos es 1. Un escalar es compatible
con cualquier forma. Ej.: (2, 3) con (1, 3) ✓; (2, 3) con (3,) se
alinea como (1, 3) ✓; (2, 3) con (2,) ✗; (2, 3) con (2, 2) ✗.
Si no cuadran, NumPy lanza un error de broadcasting y te toca ajustar las
formas.
Errores frecuentes¶
- Usar
and/orcon máscaras en lugar de&/|(y sin paréntesis). - Creer que
arr[1:4]incluye el índice 4. - Confundir
*(elemento a elemento) con@(matricial). - Modificar una "copia" pensando que era vista (o al revés).
- Usar
np.random.seedglobal en lugar dedefault_rng. - Esperar que
np.splitdivida cuando el tamaño no es múltiplo exacto del número de trozos. - Olvidar que los
dtypeenteros truncan los decimales.
Práctica de transferencia¶
TechShop guarda las ventas de una semana así (filas = días, columnas = [unidades, importe]):
import numpy as np
rng = np.random.default_rng(7)
unidades = rng.integers(10, 100, size=7)
importe = unidades * rng.uniform(20, 80, size=7)
ventas = np.column_stack([unidades, np.round(importe, 2)])
print(ventas)
- Calcula el importe medio y el número total de unidades vendidas.
- Crea una máscara que seleccione los días con más de 50 unidades y muestra esos días.
- Añade una columna nueva con el "precio medio por unidad" de cada día (importe / unidades) usando broadcasting o división vectorizada.
- Calcula la media de cada columna con
axis=0y explica qué representa cada resultado. - Comprueba con
.basesiventas[:, 0]es vista o copia, y explica la diferencia práctica.
Qué debes comprobar: ventas.shape es (7, 2) y, tras añadir la columna del
punto 3, (7, 3); el precio medio por unidad de cada día queda entre 20 y 80,
porque así se generaron los datos. Si sale fuera de ese rango, revisa qué
columna divides entre cuál.
Producto evaluable¶
Cuaderno 02_numpy.py con:
- Los ejercicios 1–5 anteriores, cada uno con su celda de código y una celda Markdown que interprete el resultado.
- Una celda final que genere, con
default_rngy una semilla fija, 1000 valores simulados de "importe de pedido" y muestre su media, mediana y desviación típica.
Formato de entrega: cuaderno marimo ejecutado, con conclusiones al final.
Resumen y referencia rápida¶
| Tarea | Función/patrón |
|---|---|
| Crear | np.array, zeros, ones, arange, linspace |
| Inspeccionar | shape, ndim, size, dtype |
| Indexar | arr[i], arr[1:4], mat[fila, col] |
| Filtrar | arr[mascara], combinando con &, |, ~ |
| Estadísticas | mean, median, std, var, sum, min, max |
| Aleatorios | default_rng(seed), integers, random, normal, uniform |
| Guardar/cargar | np.save / np.load |
| Copia vs. vista | .base (vista → apunta al original; copia → None) |
| Transponer/multiplicar | .T, @ (matricial), * (elemento a elemento) |
| Localizar / si-entonces vectorizado | np.where(cond) / np.where(cond, a, b) |
| Conjuntos | unique, intersect1d, union1d, setdiff1d, isin, setxor1d |
| Forma | reshape, ravel (vista si puede), flatten (copia) |
| Combinar/dividir | concatenate, vstack, hstack, split |
| Broadcasting | operar formas compatibles (dimensión 1 o igual) |
Siguiente: 1.5 · Pandas.