Vectorización
En resumen: La vectorización en NumPy es calcular con todo el arreglo a la vez, sin bucle: aritmética, comparaciones, máscaras con & y | y elecciones con np.where.
Problema
Un bucle para cada pregunta
El gerente quiere los precios con IVA. Justo después pregunta cuántos productos cuestan más de 5 €. Con una lista, cada pregunta es un bucle nuevo:
Funciona: 1,20 € pasa a 1,44 €, y 8 productos están por encima de 5 €. Pero para cada pregunta siguiente escribes otro for, otra variable vacía y otro contador. La pregunta misma se pierde entre las líneas.
La idea
Cálculos con todo el arreglo
Con un arreglo escribes el cálculo una vez, para todos los números:
prices * 1.2 multiplica cada uno de los 25 precios. El bucle está dentro de NumPy, en C. Esto se llama una operación vectorizada: actúa sobre todo el arreglo, no sobre un solo número. El juego de cuchillos de 39 € pasa a 46,80 €.
+, -, *, / y ** funcionan así, y también funciones como np.round(array, 2), que redondea cada número a 2 decimales.
La idea
Arreglo con arreglo
Dos arreglos de la misma longitud se calculan número a número: el primero con el primero, el segundo con el segundo. En order_items.csv cada línea tiene una cantidad y un precio unitario. El valor de la línea es uno por el otro:
La primera línea son 4 unidades a 7,90 €, es decir, 31,60 €. Sin un solo bucle tienes el valor de las 152 líneas. .sum() las suma: 1963,50 €. A los resúmenes como sum llegamos en la lección 3.
Pruébalo
Cuánto vale el stock
Pon en value el valor del stock de cada producto: su precio por la cantidad en almacén.
Solución
import pandas as pd
products = pd.read_csv('products.csv')
prices = products['price'].to_numpy()
stock = products['stock'].to_numpy()
value = prices * stock
valuevalue = prices * stock da 25 valores. La banitsa y los pepinos son 0: están agotados. La tarjeta de regalo son 19 980 € de los 22 542,30 € totales. Es el marcador 999 de la primera lección. Por eso compruebas cada cálculo echando un vistazo a los datos.
La idea
Comparación y máscara
Una comparación también está vectorizada. prices > 5 no da un True o False, sino uno para cada precio:
Un arreglo de True y False así se llama una máscara. Puesta entre corchetes, se queda solo con los lugares con True. Y .sum() de una máscara cuenta los Trues, porque en una suma True es 1 y False es 0:
8 precios son mayores que 5 €: la misma respuesta que con el bucle, en una línea. La máscara del stock también sirve para otro arreglo de la misma longitud: los nombres de los productos agotados son la banitsa y los pepinos.
La idea
Dos condiciones: &, | y ~
Los precios de 2 € a 5 € son dos condiciones juntas. En Python normal escribirías and. Con arreglos and no funciona:
and pregunta «¿todo esto es verdad?» y quiere un solo True o False. El arreglo tiene 25. NumPy no adivina si quieres decir «todos» o «al menos uno», así que se detiene con un error.
Para las máscaras hay tres signos que actúan número a número:
&es «y»: las dos condiciones son verdaderas;|es «o»: al menos una es verdadera;~es «no»: da la vuelta a la máscara.
9 precios van de 2 € a 5 €. Por debajo de 1 € o por encima de 20 € están 0,80 €, 0,90 €, 24,90 € y 39 €. Los precios de los productos que no están en stock son 2,50 € y 2,80 €.
Míralo
La trampa de los paréntesis
Cada condición alrededor de & y | va entre paréntesis. Si los omites, Python calcula & antes que >= y <=, es decir, empieza con 2 & prices:
& entre un número y precios decimales no tiene sentido, así que obtienes un TypeError sobre bitwise_and. La regla es: cada condición entre paréntesis, (prices >= 2) & (prices <= 5).
Más precisamente, para curiosos
En Python &, | y ~ son operaciones bit a bit sobre enteros. Por eso ligan más fuerte que las comparaciones: 2 & 6 > 1 es (2 & 6) > 1. NumPy los tomó para las máscaras porque and, or y not no se pueden redefinir para arreglos: siempre quieren un solo True o False. Lo mismo verás con los filtros de pandas.
Pruébalo
Barato y en stock
¿Cuántos productos cuestan menos de 3 € y están en stock (stock mayor que 0)? Pon la cantidad en count.
Solución
import pandas as pd
products = pd.read_csv('products.csv')
prices = products['price'].to_numpy()
stock = products['stock'].to_numpy()
count = ((prices < 3) & (stock > 0)).sum()
countcount = ((prices < 3) & (stock > 0)).sum() da 12. Hay 14 productos por debajo de 3 €, pero dos están agotados: la banitsa a 2,50 € y los pepinos a 2,80 €. Con and el código falla con un ValueError, y sin los paréntesis con un TypeError.
La idea
Una cosa o la otra: np.where
Una máscara elige qué números se quedan. A veces quieres todos, pero distintos según la condición. np.where(máscara, si_sí, si_no) toma una cosa u otra para cada lugar:
Donde el stock es 0 obtienes «agotado», si no «en stock». En lugar del texto también puede haber arreglos: np.where(prices > 10, prices * 0.9, prices) rebaja solo los precios caros un 10 %.
Ejercicio
La lista de precios con IVA
De vuelta con el gerente: los precios con IVA y una promoción.
Pon en with_vat los precios con IVA (por 1,2), redondeados a 2 decimales. Pon en over_10 cuántos de ellos superan los 10 €.
Solución
import numpy as np
import pandas as pd
prices = pd.read_csv('products.csv')['price'].to_numpy()
with_vat = np.round(prices * 1.2, 2)
over_10 = (with_vat > 10).sum()
over_10with_vat = np.round(prices * 1.2, 2) y over_10 = (with_vat > 10).sum() da 7. Sin IVA, 6 productos superan los 10 €. El séptimo es el queso kashkaval: 9,50 € con IVA pasa a 11,40 €. Si olvidas .sum(), lo que queda es la máscara misma, no la cantidad.
Promoción: los productos de más de 10 € son un 10 % más baratos, y los demás conservan el precio viejo. Pon la lista de precios nueva completa (25 precios) en promo.
Solución
import numpy as np
import pandas as pd
prices = pd.read_csv('products.csv')['price'].to_numpy()
promo = np.where(prices > 10, prices * 0.9, prices)
promopromo = np.where(prices > 10, prices * 0.9, prices). Cambian seis precios: el juego de cuchillos pasa de 39 € a 35,10 €, el café en grano de 14,90 € a 13,41 €. Los otros 19 son iguales. prices[prices > 10] * 0.9 da solo los seis precios nuevos y pierde el resto, y prices * 0.9 rebaja todo.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: La primera tabla · El arreglo de NumPy
- Continúa en: Resúmenes y valores faltantes · Broadcasting: el número, la fila y la columna · Series y DataFrame · Seleccionar: [], loc e iloc
- Ejercicios sobre el tema: NumPy