Saltar al contenido

Indice / Practicas / EJ-3.2

EJ-3.2 · Normalizar por z-score con broadcasting

Practica de: 3.5 BroadcastingDificultad: M

Tres evaluaciones con dificultades distintas. Un 7 en la más difícil no vale lo mismo que un 7 en la más fácil, y el z-score es lo que las vuelve comparables.

Qué hay que hacer

A partir de estudiantes_notas.csv (40 filas), producir notas1_normalizadas.csv: las tres notas de cada estudiante expresadas en desvíos respecto de su propia evaluación, más el promedio de esos tres.

Archivos

ELI5Explicado facil

Es lo que hace un profesor cuando dice «un 6 en mi parcial es como un 8 en el de la otra cátedra».

El z-score pone esa frase en un número: a cuántos desvíos del promedio de su examen está cada nota. Después sí se pueden sumar.

El detalle de broadcasting que hace al ejercicio

La matriz va (40, 3), y la media con axis=0

Una fila por estudiante, una columna por evaluación. La media por evaluación se calcula con axis=0 y tiene shape (3,): alineada a la derecha contra (40, 3), ese 3 coincide con el 3 y funciona sin reshape ni keepdims.

Es el caso «fácil» de la lección 3.5, y está elegido a propósito: el promedio por fila de la última columna es el otro caso, y ahí el axis=1 vuelve a aparecer.

El orden importa más de lo que parece

Las notas imposibles se descartan antes de calcular la media

nota_1 tiene un 12,5 y un 10,5. Si participan del cálculo, la media y el desvío de esa columna quedan corridos, y los 38 z restantes salen todos mal - sin que nada falle.

Es el mismo enmascaramiento de la lección 3.7, aplicado a la referencia en vez de al criterio: el valor que había que detectar contamina la medida con la que se lo iba a detectar.

El chequeo de treinta segundos

Cada columna z tiene que dar media 0 y desvío 1. Es lo primero que hay que mirar: si una da 0,4, esa columna se normalizó con la referencia equivocada.

for c in ["z_nota_1", "z_nota_2", "z_nota_3"]:
    print(c, salida[c].mean(), salida[c].std(ddof=0))

Cómo saber si te salió

python comparar.py mis_normalizadas.csv notas1_normalizadas.csv

Antes de arrancar, mirá los datos

estudiantes_notas.csv (entrada) · 40 filas, se muestran 5
legajoestudiantenota_1nota_2nota_3
L2601Karina Acosta9.74.78.6
L2602Santiago Ramos6.03.84.2
L2603Carla Luna8.13.63.8
L2604Lucas Núñez7.37.43.4
L2605Tamara Torres9.59.57.8

El contrato de salida

notas1_normalizadas.csv · 40 filas

ColumnaTipoQue lleva
legajotextoEl legajo tal cual vino
estudiantetextoEl nombre tal cual vino
z_nota_1decimal(nota_1 - media de nota_1) / desvio de nota_1, 3 decimales. Vacio si la nota no es usable
z_nota_2decimalIdem para nota_2
z_nota_3decimalIdem para nota_3
z_promediodecimalEl promedio de los z que ese estudiante tenga, ignorando los ausentes. 3 decimales

Orden de las filas: legajo ascendente. Los 40 legajos son distintos, asi que el orden es total. Indice: no se escribe: to_csv(index=False)

Que hacer con cada defecto

DefectoDondeAccionAdonde va
tokens de ausente5 celdas de nota_3van van a NaN y no participan de la media ni del desvio de su columna. La celda z queda vacianotas1_normalizadas.csv, celda vacia
notas fuera del rango 0 a 104 celdas: 12.5 y 10.5 en nota_1, 1 en nota_2 y -1.0 en nota_3van van a NaN igual que los ausentes. Una nota imposible no puede participar de la media que despues sirve de referencianotas1_normalizadas.csv, celda vacia
estudiante sin ninguna nota usable0 estudiantes en este archivoz_promedio z_promedio queda vacio. La comprobacion va igual, porque nanmean sobre una fila toda NaN emite RuntimeWarning y devuelve nannotas1_normalizadas.csv
Orden de comprobacion

Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:

  1. convertir a float mandando a NaN los tokens de ausente y lo que caiga fuera de 0-10
  2. calcular media y desvio POR COLUMNA, con axis=0 y las funciones nan*
  3. restar y dividir: el broadcasting de (40, 3) con (3,) funciona sin reshape
  4. el promedio por fila se calcula sobre los z ya normalizados, con axis=1

El rango se valida ANTES de calcular la media: si el 12.5 entra, la media y el desvio de nota_1 quedan corridos y los 38 z restantes salen todos mal. Es el mismo enmascaramiento de la leccion 3.7, aplicado a la referencia en vez de al criterio.

Conteos clave

Si estos numeros no te dan, algo se desvio antes de que llegues a comparar los archivos.

filas de entrada40
notas1_normalizadas.csv40
celdas z vacias9
notas usables111
por motivoausente: 5, fuera_de_rango: 4

Invariantes

Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:

len(normalizadas) == 40
normalizadas['legajo'].is_unique
normalizadas['legajo'].is_monotonic_increasing
normalizadas[['z_nota_1','z_nota_2','z_nota_3']].isna().sum().sum() == 9
normalizadas['z_promedio'].notna().all()
abs(normalizadas['z_nota_1'].mean()) < 0.005
abs(normalizadas['z_nota_2'].mean()) < 0.005
abs(normalizadas['z_nota_3'].mean()) < 0.005
abs(normalizadas['z_nota_1'].std(ddof=0) - 1) < 0.005
normalizadas[['z_nota_1','z_nota_2','z_nota_3']].abs().max().max() < 4
PISTASSi te trabas, abri esto
  • La media va con axis=0: una por evaluacion, no una por estudiante. Con shape (3,) el broadcasting contra (40, 3) funciona sin reshape ni keepdims.
  • El desvio va con ddof=0 (el default de NumPy). Con ddof=1 los z quedan todos multiplicados por un factor y ninguno coincide.
  • Las notas fuera de rango se descartan ANTES de calcular la media. Si el 12.5 participa, los 38 z de esa columna salen corridos y nada avisa.
  • Cada columna z tiene que dar media 0 y desvio 1. Es el chequeo mas rapido: si una da 0.4, esa columna se normalizo con la referencia equivocada.

Así tiene que quedar

notas1_normalizadas.csv (objetivo) · 40 filas, se muestran 5
legajoestudiantez_nota_1z_nota_2z_nota_3z_promedio
L2601Karina Acosta1.732-0.6021.1010.744
L2602Santiago Ramos-0.169-1.079-0.943-0.73
L2603Carla Luna0.91-1.185-1.129-0.468
L2604Lucas Núñez0.4990.829-1.3150.004
L2605Tamara Torres1.6291.9420.731.434
Las 9 celdas vacías son información

5 vienen de los tokens de ausente de nota_3 y 4 de notas fuera de rango. Una celda z vacía significa «esta nota no era usable», y eso no es un hueco del resultado: es el resultado.

z_promedio, en cambio, tiene valor en las 40 filas, porque ningún estudiante se quedó sin las tres notas.

ddof=0, otra vez

El desvío va con el default de NumPy. Con ddof=1 todos los z quedan multiplicados por el mismo factor y ninguno coincide con el objetivo: no es un error de lógica, es un parámetro que el contrato fija porque las dos opciones son defendibles.