Ir al contenido
wannacode

Broadcasting: el número, la fila y la columna

En resumen: El broadcasting es la regla con la que NumPy calcula con arreglos de formas distintas: un número, una fila o una columna se estira a la forma de la tabla cuando los tamaños encajan.

Problema

«¿Cuánto vendió cada producto cada semana?»

El gerente te da las unidades vendidas en las últimas 4 semanas de tres productos: pan blanco, leche fresca y manzanas. Una tabla con 4 filas (las semanas) y 3 columnas (los productos). Los precios son 1,20 €, 1,60 € y 2,10 €.

Quiere los ingresos en una tabla del mismo tipo: semana por producto. Con listas de Python son dos bucles for anidados, fila por fila y celda por celda.

En NumPy los cálculos actúan sobre todo el arreglo. Pero aquí hay dos arreglos con formas distintas: una tabla de 4 × 3 y una fila de 3 precios. ¿Cómo multiplicas uno por otro?

La idea

El número y el arreglo

Empieza por el caso más simple. El proveedor sube todos los precios un 20 %. products['price'].to_numpy() da la columna de precios como arreglo de NumPy, y se multiplica por un solo número:

Pythondatos: las tablas de la tienda

25 precios por un número dan 25 precios nuevos: 1,20 € pasa a 1,44 €, y el juego de cuchillos de 39 € pasa a 46,80 €. El número se «estira» a la forma del arreglo: como si junto a cada precio hubiera un 1.2.

Este estiramiento se llama broadcasting. El número es solo el primer caso. Lo mismo pasa con una fila y una tabla.

La idea

La fila y la tabla

Las unidades son una tabla (4, 3), los precios son una fila (3,). Multiplícalos directamente:

Pythondatos: las tablas de la tienda

La fila de precios se repite para cada una de las 4 semanas, y cada celda se multiplica por el precio de su columna. La primera semana: 30 panes a 1,20 € son 36 €, 22 leches a 1,60 € son 35,20 €, 14 kg de manzanas a 2,10 € son 29,40 €. Ni un solo for.

¿Cuándo estira NumPy y cuándo se detiene? Hay una regla:

  • Las formas se comparan de derecha a izquierda. (4, 3) y (3,): primero 3 y 3, luego 4 y el lugar vacío.
  • Los tamaños deben ser iguales, o uno de ellos debe ser 1. Un lugar vacío cuenta como 1.
  • El 1 se estira al otro tamaño. Aquí la fila (3,) pasa a (1, 3) y se estira a (4, 3).
Más precisamente, para curiosos

NumPy no copia la fila 4 veces en la memoria. Lee los mismos 3 precios otra vez para cada fila. Por eso el broadcasting es rápido y ligero en memoria, incluso cuando la tabla tiene millones de filas.

El número de la tarjeta anterior es un arreglo de forma (), sin tamaños. Se le ponen unos delante hasta que es (1,) o (1, 1), y entonces se estira. La regla es la misma.

Míralo

Cómo encaja la fila en la tabla

Aquí está el mismo cálculo despacio: la tabla de 4 × 3, la fila de 3 precios, el estiramiento hacia abajo y la multiplicación celda por celda.

import numpy as np
# unidades en 4 semanas: pan blanco, leche fresca, manzanas
units = np.array([[30, 22, 14],
[26, 25, 18],
[34, 20, 12],
[28, 27, 20]])
prices = np.array([1.2, 1.6, 2.1])
revenue = units * prices
print(units.shape, prices.shape, revenue.shape)
revenue
Paso a paso, en palabras
  1. units es una tabla (4, 3): las unidades de 4 semanas, una columna por producto.
  2. prices es una fila (3,): 1,20 €, 1,60 €, 2,10 €. Un precio por producto.
  3. Las formas se comparan desde la derecha: 3 y 3 son iguales. A la izquierda de (3,) no hay nada, eso cuenta como 1 y la fila pasa a (1, 3).
  4. El 1 se estira a 4: la fila de precios se repite para cada semana y pasa a (4, 3).
  5. Ahora celda por celda. La primera semana: 30 × 1,2 = 36, 22 × 1,6 = 35,2, 14 × 2,1 = 29,4.
  6. revenue es (4, 3), como units: 12 ingresos de una multiplicación, sin un solo for.

Pruébalo

¿Encajan las formas?

1. units tiene la forma (4, 3) y prices tiene la forma (3,). ¿Qué forma tiene units * prices?
2. units tiene la forma (4, 3) y week_bonus tiene la forma (4,). ¿Qué da units + week_bonus?
3. ¿Qué par de formas encaja?

La idea

Cuando las formas no encajan

Ahora el gerente pregunta otra cosa: qué producto vendió por encima del promedio de la semana en qué semana. Tienes que restar a cada fila la media de esa misma fila.

units.mean(axis=1) da las unidades medias de cada semana (axis viene de la lección anterior: el tamaño que desaparece). Son 4 números. Réstalos de la tabla:

Pythondatos: las tablas de la tienda

Las medias son 22, 23, 22 y 25, con la forma (4,). NumPy compara desde la derecha: 3 de la tabla contra 4 de las medias. No son iguales y ninguno es 1, así que se detiene: operands could not be broadcast together with shapes (4,3) (4,).

El error es útil. Una fila de 4 números de verdad no puede tenderse sobre 3 columnas.

La idea

La columna y la tabla

Las 4 medias tienen que ser una columna: un número por fila, estirado hacia la derecha a lo largo de los productos. Una columna tiene la forma (4, 1). Hay dos maneras de conseguirla:

  • week_mean[:, None]: None en segundo lugar añade un tamaño 1;
  • week_mean.reshape(4, 1): lo mismo, con la forma escrita.
Pythondatos: las tablas de la tienda

A la derecha ahora hay 3 y 1 (el 1 se estira a 3), luego 4 y 4. El resultado es una tabla (4, 3): en la tercera semana el pan está 12 unidades por encima del promedio, y las manzanas 10 por debajo.

En la segunda semana todo tuvo un 10 % de descuento, y en la cuarta un 20 %. discount tiene el descuento de cada semana. Guarda en after los ingresos después del descuento, una tabla de 4 × 3.

Pythondatos: las tablas de la tienda

La idea

La trampa silenciosa: 3 × 3 en lugar de 3

Un error de NumPy se ve enseguida. Es más peligroso cuando las formas encajan y el resultado es otra cosa.

Un colega te manda los precios nuevos, copiados de una columna de Excel. El arreglo es una columna (3, 1), no una fila (3,). Restas los precios viejos para ver cuánto subieron:

Pythondatos: las tablas de la tienda

Esperas 3 diferencias y obtienes 9. La columna (3, 1) se estiró hacia la derecha, la fila (3,) se estiró hacia abajo, y cada precio nuevo se restó de cada precio viejo. Solo la diagonal (0.1, 0 y 0.2) es la diferencia que quieres. Los demás números no significan nada, y NumPy no dice nada: las formas cumplen la regla.

Por eso, después de cada cálculo con formas distintas, imprime .shape. Si esperas (3,) y ves (3, 3), algo se estiró sin que lo pidieras.

Pruébalo

Cuánto subieron los precios

new_prices es una columna (3, 1) con los precios nuevos. Guarda en change cuánto subió el precio de cada producto: 3 números, forma (3,).

Pythondatos: las tablas de la tienda

Ejercicio

Cuánto más con los precios nuevos

De vuelta con el gerente. Quiere saber cuánto más habría ingresado cada producto cada semana si los precios nuevos ya hubieran estado en vigor.

units son las unidades (4, 3), prices los precios viejos (3,), new_prices los precios nuevos como columna (3, 1). Guarda en extra cuánto más altos habrían sido los ingresos: una tabla de 4 × 3, semana por producto.

Pythondatos: las tablas de la tienda

Test

Ponte a prueba

1. ¿Qué hace prices * 1.2 cuando prices es un arreglo de 25 precios?
2. week_mean tiene la forma (4,) y units tiene la forma (4, 3). ¿Cómo restas a cada semana su propia media?
3. old tiene la forma (3,) y new tiene la forma (3, 1). ¿Qué da new - old?
4. ¿Cuál es la manera más rápida de pillar un estiramiento silencioso a una tabla de 3 × 3?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·