Hola! Empezamos por lo que ya sabes 🌸
Te voy a hacer una promesa antes de nada: en este libro no hay ni un ejemplo de peras y manzanas. Todo sale del mismo archivo de ventas que usamos en los otros libros, porque lo que quiero es que veas que la matemática ya estaba ahí, metida en lo que haces todos los días, y nadie te lo había dicho con ese nombre.
Vamos a abrirlo.
Abre el archivo y mira la forma
import pandas as pd import numpy as np df = pd.read_csv('ventas-miss-yera.csv') print('shape:', df.shape)
shape: (3037, 14)
Ese (3037, 14) es lo primero que quiero que mires. Son
3.037 filas y 14 columnas. Y eso, dicho en el idioma de este
libro, es una matriz de 3.037 por 14.
Ya está. Ese es el concepto entero. Una matriz es una tabla de números con filas y columnas 🙂
Nos quedamos con los números
Digo "de números" a propósito, porque tu archivo tiene columnas de texto como la ciudad y el canal. Una matriz de verdad es solo números, así que nos quedamos con tres columnas:
for c in ('unidades', 'monto', 'satisfaccion'): df[c] = pd.to_numeric(df[c], errors='coerce') X = df[['unidades', 'monto', 'satisfaccion']].dropna().to_numpy() print('shape:', X.shape, '| dtype:', X.dtype, '| ndim:', X.ndim)
shape: (2238, 3) | dtype: float64 | ndim: 2
Fíjate en el to_numeric. Sin esa línea el monto
llega como texto, porque el archivo trae suciedad a propósito, y entonces
dtype sale object en vez de float64. Eso
lo trabajamos entero en el capítulo de auditoría de tipos del libro de machine
learning; acá solo lo arreglamos para poder seguir.
El ndim: 2 es la palabra técnica: una matriz tiene
dos dimensiones. Una sola columna sería de una dimensión y se
llama vector, que es el capítulo que viene. Tres dimensiones sería un tensor, y
eso lo vas a ver cuando llegues a las imágenes en Deep learning.
Cómo se escribe esto en un paper
Cuando abras cualquier libro de machine learning te vas a topar con esto:
X es una tabla de n filas por d columnas, y todo lo que hay dentro son números reales
Y ahora ya lo puedes leer. La X es tu tabla. La n son tus 2.238 ventas. La d son tus 3 columnas. Lo de ℝ solo dice que dentro hay números reales, o sea números con decimales, no palabras.
| Símbolo | Qué es |
|---|---|
| X | la matriz entera, tu tabla de datos |
| n | cuántas filas tiene, o sea cuántos ejemplos |
| d | cuántas columnas tiene, o sea cuántas características |
| xij | un número suelto: el de la fila i, columna j |
Y una cosa que a mí me costó y por eso te la digo: en machine learning casi siempre las filas son los ejemplos y las columnas son las características. No es una ley del universo, es un acuerdo. Pero si te encuentras un paper donde está al revés, te lo va a decir.
Sacar un pedazo, que es lo que vas a hacer todo el tiempo
print('una venta entera X[0] :', np.round(X[0], 2)) print('todos los montos X[:,1] :', np.round(X[:5, 1], 2), '...') print('un solo numero X[0,1] :', X[0, 1])
una venta entera X[0] : [ 10. 480.37 4. ] todos los montos X[:,1] : [ 480.37 524.9 154.83 2480.84 379.4 ] ... un solo numero X[0,1] : 480.37
Tres formas de meter la mano en la misma tabla:
X[0]te da la fila completa. Una venta: 10 unidades, 480.37 soles, satisfacción 4.X[:, 1]te da la columna completa. Los dos puntos quieren decir "todas las filas".X[0, 1]te da un número. Fila 0, columna 1.
el elemento que está en el cruce: primero se dice la fila, después la columna, siempre en ese orden
El orden importa y es el que se te va a olvidar: primero la fila, después la columna. Siempre. Si alguna vez tu código explota con un "index out of bounds", nueve de cada diez veces es que los cambiaste de sitio.
Cuánto es una matriz, de verdad
print('filas x columnas =', X.shape[0], 'x', X.shape[1], '=', X.size, 'numeros') print('cuanto ocupa en memoria:', X.nbytes, 'bytes')
filas x columnas = 2238 x 3 = 6714 numeros cuanto ocupa en memoria: 53712 bytes
6.714 números. 53 kilobytes. Nada.
Te pongo el dato porque cuando llegues a Deep learning vas a ver matrices de otro tamaño, y quiero que tengas con qué comparar. Una imagen de 224 por 224 en color son 150.528 números por foto. Un lote de 32 fotos son casi cinco millones. Ahí es donde empieza a importar que esto sean matrices y no bucles 🐣
La traspuesta, que es mirarla al revés
print('X.T shape:', X.T.shape) print('X.T[1][:5]:', np.round(X.T[1][:5], 2))
X.T shape: (3, 2238) X.T[1][:5]: [ 480.37 524.9 154.83 2480.84 379.4 ]
La traspuesta gira la tabla: lo que era una columna pasa a ser una fila. Se escribe con esa T pequeñita arriba:
la traspuesta cambia filas por columnas, así que el elemento que estaba en la fila 2 columna 5 pasa a estar en la fila 5 columna 2
Y no es un truco de programador, es una operación con nombre propio que vas a ver constantemente. Cuando en Deep learning veas W⊤, ya sabes qué está pasando: alguien necesitaba que las dimensiones encajaran para poder multiplicar, y eso lo vemos en el capítulo de multiplicación de matrices.
Tres matrices que vas a ver por todos lados
Hay tres formas de matriz con nombre propio. Te las presento ahora porque cuando aparezcan en un capítulo más adelante quiero que ya las reconozcas.
print(np.eye(3))
[[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]]
Esa es la identidad. Unos en la diagonal y ceros en todo lo demás. Es el 1 de las matrices: multiplicar por ella no cambia nada. Cuando llegues al capítulo de la inversa la vas a necesitar, porque la inversa se define justo como "la que multiplicada por la original te da la identidad".
print(np.zeros((2, 3)))
[[0. 0. 0.] [0. 0. 0.]]
La de ceros. Parece inútil y no lo es: así es como se arranca casi cualquier acumulador, y así empiezan los pesos de algunas capas en deep learning.
print(np.diag([2., 0.5, 10.]))
[[ 2. 0. 0. ] [ 0. 0.5 0. ] [ 0. 0. 10. ]]
Una diagonal. Solo tiene números en la diagonal. Esta la vas a ver en el capítulo de autovalores, y también es lo que hay por debajo cuando escalas cada columna por un número distinto.
Operar con toda la tabla de una vez
Esto es lo que hace que NumPy valga la pena, y es donde mucha gente que viene de Excel dice "ah, ya".
print(np.round(X[:2], 2)) print(np.round(X[:2] * 2, 2)) print(np.round(X[:2] + 100, 2))
[[ 10. 480.37 4. ] [ 10. 524.9 3. ]] [[ 20. 960.74 8. ] [ 20. 1049.8 6. ]] [[110. 580.37 104. ] [110. 624.9 103. ]]
Sin un solo bucle. Escribes X * 2 y se multiplica cada número de
la tabla. Eso se llama operación elemento a elemento, y es lo
que hace que entrenar una red sobre millones de números sea posible.
Y ojo con esto, que es la trampa del capítulo que viene:
X * 2 no es multiplicar matrices. Es otra cosa
completamente distinta y tiene otro símbolo. Lo vemos en su momento 🐣
El eje, que es lo que más se confunde
Cuando resumes una tabla tienes que decir en qué dirección. Y aquí es donde todo el mundo se equivoca al menos una vez:
print('media de cada COLUMNA (axis=0):', np.round(X.mean(axis=0), 2)) print('media de cada FILA (axis=1):', np.round(X.mean(axis=1)[:5], 2), '...') print('sin axis (todo junto) :', round(X.mean(), 2))
media de cada COLUMNA (axis=0): [ 11.68 803.39 3.02] media de cada FILA (axis=1): [164.79 179.3 56.28 829.28 127.13] ... sin axis (todo junto) : 272.69
Mira los tres resultados y cuál tiene sentido:
- axis=0 da tres números, uno por columna. La venta media es de 11.68 unidades y 803.39 soles, con satisfacción media de 3.02. Esto significa algo.
- axis=1 da un número por venta, y es el promedio de mezclar unidades con soles y con satisfacción. Eso no significa nada. Sumar soles con estrellas no es una cantidad de nada.
- sin axis promedia los 6.714 números juntos. Peor todavía.
La regla para acordarte: axis=0 recorre las filas hacia abajo y te deja un número por columna. Es el que vas a querer el 90% de las veces, porque las columnas son las que tienen unidades comparables.
El error de forma, que te va a pasar
Te lo enseño ahora para que cuando te salga ya sepas leerlo:
a = X[:, :2] b = X[:, :3] print('a.shape', a.shape, ' b.shape', b.shape)
a.shape (2238, 2) b.shape (2238, 3)
Y ahora las sumas:
a + b
ValueError: operands could not be broadcast together with shapes (2238,2) (2238,3)
Dos tablas con distinto número de columnas no se pueden sumar. Obvio dicho así, pero cuando llevas cuarenta líneas de código y una de ellas cambió la forma sin que te dieras cuenta, este error aparece muy lejos de donde estuvo la culpa.
El hábito que te va a salvar horas: cuando algo no cuadre,
imprime los .shape antes de tocar nada más. En mi
experiencia, la mitad de los errores de un proyecto de machine learning son de
forma, no de matemática.
Lo que te llevas
- Una matriz es una tabla de números. Tu archivo de ventas ya lo era.
- Se escribe X, tiene n filas y d columnas, y en machine learning las filas son los ejemplos.
X[fila, columna], en ese orden y no al revés..shapete dice la forma y es lo primero que hay que mirar cuando algo no cuadra.- La traspuesta X⊤ cambia filas por columnas.
Comprueba que se entendió
Comprueba que lo tienes
Tienes X con forma (2238, 3) y escribes X.mean(axis=0). ¿Cuántos números te salen y qué es cada uno?
- Tres números: el promedio de cada columna
- 2.238 números: el promedio de cada venta
- Un solo número: el promedio de toda la tabla
- Depende de si la matriz tiene decimales
Ejercicios
1. La forma antes que nada
Antes de hacer ninguna cuenta, mira la forma. Es el hábito que más errores te va a ahorrar.
print('X :', X.shape) print('X.T :', X.T.shape) print('X[0] :', X[0].shape) print('X[:, 1] :', X[:, 1].shape)
X : (2238, 3) X.T : (3, 2238) X[0] : (3,) X[:, 1] : (2238,)
Mira las dos últimas: (3,) y (2238,), con esa coma
suelta. Eso quiere decir una sola dimensión, o sea que al sacar
una fila o una columna ya no tienes una matriz, tienes un vector. Ese es el
capítulo que viene.
2. Traspón dos veces y mira qué pasa
Sin correrlo, ¿qué crees que sale? Después córrelo.
print(np.array_equal(X.T.T, X))
True
Girar la tabla dos veces te devuelve la de partida. Obvio dicho así, pero es una propiedad con nombre y la vas a usar para simplificar expresiones largas: (X⊤)⊤ = X.
3. Saca la última fila sin saber cuántas hay
Los índices negativos cuentan desde el final.
print('ultima fila :', np.round(X[-1], 2)) print('lo mismo, feo :', np.round(X[X.shape[0] - 1], 2))
ultima fila : [ 17. 993.57 4. ] lo mismo, feo : [ 17. 993.57 4. ]
Las dos dan lo mismo. La primera se lee mejor y no se rompe si mañana el archivo tiene más ventas.
4. El error que vas a cometer
Pide una columna que no existe y lee el error entero, que para eso está.
try: print(X[0, 7]) except IndexError as e: print('IndexError:', e)
IndexError: index 7 is out of bounds for axis 1 with size 3
El mensaje te lo dice todo si sabes leerlo: axis 1 son las
columnas, size 3 es que hay tres. Pediste la número 7. Cuando
esto te salga en un proyecto de verdad, mira el .shape antes de
tocar nada más.
Preguntas frecuentes
¿Qué es una matriz?
Una tabla de números con filas y columnas. Tu archivo de ventas ya es una: cada fila es una venta y cada columna una cosa medida.
¿Qué son las filas y las columnas de una matriz?
Las filas son los casos y las columnas las variables. Cuando alguien dice que una matriz es "de 3037 por 14", el primer número son las filas y el segundo las columnas, siempre en ese orden.
¿Qué es la matriz identidad?
La que tiene unos en la diagonal y ceros en todo lo demás. Multiplicar por ella no cambia nada, igual que multiplicar por uno, y sirve para comprobar si una inversa está bien calculada.
¿Qué es la transpuesta de una matriz?
La misma matriz con las filas puestas como columnas. Se usa mucho más de lo que parece, porque es lo que hace que dos formas encajen para poder multiplicarlas.
Ya sabes qué pasa por debajo. Ahora que corra
Sabes qué mide una derivada, hacia dónde apunta un gradiente y por qué la regla de la cadena es lo que hace que una red aprenda. Eso no lo tiene la mayoría de la gente que entrena modelos 🎓
Lo que la matemática no te da es lo que pasa después: que el sistema corra solo, que alguien se entere cuando empiece a fallar y que puedas demostrar con números que sirve. Eso es el curso de ingeniería de IA en producción.
Son cuatro semanas y sales con un sistema tuyo, sobre un proceso real de tu trabajo. Con lo que acabas de leer vas a entender por qué se toma cada decisión en vez de copiarla.
Y si por ahora te quedas con la teoría, tranqui: el capítulo de referencias te deja por dónde seguir sola 🌸
Practica este capítulo 📓
Todo el código de arriba en un cuaderno que corre de principio a fin, y los ejercicios con una celda vacía para que los hagas tú. Se abre en Google Colab de un clic y no hay que instalar nada. Donde veas %%revisa, escribe tu respuesta y el cuaderno te dice si te salió.
¿Prefieres trabajar en tu máquina? Bájate el cuaderno de práctica o el de soluciones. Todos están también en github.com/soymissyera/MissYeraEjercicios.