Saltar al contenido

Indice / Practicas / EJ-3.1

EJ-3.1 · Estadisticas de una columna con NaN

Practica de: 3.6 Estadistica y NaNDificultad: B

Once medidas sobre dos columnas de notas. La parte que importa no es calcularlas: es separar lo que falta de lo que es imposible, y que las dos categorías sumen.

Qué hay que hacer

A partir de estudiantes_notas.csv (40 filas), producir notas1_estadisticas.json con dos claves - nota_1 y nota_3 - y para cada una: cuántas hay, cuántas faltan, cuántas están fuera de rango, cuántas quedan válidas, y siete medidas sobre esas válidas.

Archivos

ELI5Explicado facil

Es la ficha de un análisis clínico: no alcanza con el valor, hace falta el rango de referencia y cuántas muestras se procesaron.

Acá igual: un promedio de 6,33 no dice nada si no viene con «sobre 38 de 40 notas, y estas 2 se descartaron por imposibles».

Las dos columnas están elegidas a propósito

ColumnaAusentesFuera de rangoQué enseña
nota_102 (12,5 y 10,5) Que una columna sin un solo hueco igual puede tener datos imposibles
nota_351 (−1,0) Que las dos cosas son independientes y se cuentan por separado

Si tu n_validas de nota_1 da 40, no chequeaste el rango. La media cambia de 6,59 a 6,33, y las dos son números correctos de cosas distintas.

La trampa que no avisa

Una de las cinco celdas ausentes de nota_3 dice NaN

float("NaN") no falla: devuelve nan. Si convertís antes de filtrar los tokens, esa celda entra al array como un número, y después np.mean devuelve nan para la columna entera.

El orden correcto es: primero mirar si el texto está en NULOS = {"", "-", "N/A", "NaN", "null"}, y recién después convertir.

El invariante

assert n_ausentes + n_fuera_de_rango + n_validas == n_total

Tiene que dar 40 en las dos columnas. Es el mismo invariante de la lección 1.6, aplicado a celdas en vez de a filas: si no cierra, hay una nota que no fue ni contada ni descartada.

Cómo saber si te salió

python comparar.py mis_estadisticas.json notas1_estadisticas.json

comparar.py compara JSON recursivamente y te dice la ruta exacta, por ejemplo nota_3.p25.

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_estadisticas.json · 2 filas

ColumnaTipoQue lleva
n_totalenteroFilas del archivo: 40 en las dos columnas
n_ausentesenteroCuantas celdas son un token de ausente o no convierten a numero
n_fuera_de_rangoenteroDe las presentes, cuantas caen fuera de 0 a 10
n_validasenteroLas que quedan: n_total - n_ausentes - n_fuera_de_rango
minimadecimalEl minimo de las validas, 2 decimales
p25decimalPercentil 25 de las validas
medianadecimalPercentil 50 de las validas
p75decimalPercentil 75 de las validas
maximadecimalEl maximo de las validas
mediadecimalEl promedio de las validas
desviodecimalDesvio estandar POBLACIONAL de las validas: ddof=0, que es el default de NumPy

Orden de las filas: No hay filas: es un objeto con dos claves. sort_keys=True fija el orden alfabetico en los dos niveles, para que dos soluciones correctas den el mismo archivo. Indice: no aplica: es un JSON, no una tabla. Con pandas la equivalencia seria to_csv(index=False)

Que hacer con cada defecto

DefectoDondeAccionAdonde va
tokens de ausente en nota_35 celdas: '', 'N/A', '-' (x2) y 'NaN'convertir convertir a NaN y contarlos en n_ausentes. NO participan de ninguna medidanotas1_estadisticas.json, nota_3.n_ausentes = 5
el token 'NaN'1 celda de nota_3tratarlo tratarlo como ausente. float('NaN') NO falla: devuelve nan, asi que si se convierte antes de filtrar, entra igual y arruina la medianotas1_estadisticas.json, contado dentro de los 5 ausentes
notas fuera del rango 0 a 102 en nota_1 (12.5 y 10.5) y 1 en nota_3 (-1.0)descartar descartar de las medidas y contarlas aparte. El rango lo fija el reglamento de la materia, no los datosnotas1_estadisticas.json, n_fuera_de_rango
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 no convierta
  2. contar los NaN: eso es n_ausentes
  3. de los presentes, contar los que caen fuera de 0 a 10: eso es n_fuera_de_rango
  4. las once medidas se calculan sobre las validas, que son las restantes

Las tres categorias son excluyentes y tienen que sumar el total. Si se calcula la media sobre 'los presentes' en vez de sobre 'las validas', nota_1 da 6.59 en vez de 6.33: la diferencia son las dos notas imposibles.

Conteos clave

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

filas de entrada40
notas1_estadisticas.json2
nota_1: validas38
nota_3: validas34
por motivoausente: 5, fuera_de_rango: 3

Invariantes

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

todas['nota_1']['n_total'] == 40
todas['nota_3']['n_total'] == 40
todas['nota_1']['n_validas'] == 38
todas['nota_3']['n_validas'] == 34
for c in ('nota_1', 'nota_3'): n_ausentes + n_fuera_de_rango + n_validas == n_total
0 <= minima <= p25 <= mediana <= p75 <= maxima <= 10
0 <= media <= 10
desvio >= 0
todas['nota_1']['n_ausentes'] == 0
todas['nota_3']['n_ausentes'] == 5
PISTASSi te trabas, abri esto
  • nota_1 no tiene ningun ausente y aun asi pierde dos notas: son 12.5 y 10.5. Si tu n_validas de nota_1 da 40, no chequeaste el rango.
  • El desvio va con ddof=0, que es el default de NumPy (a.std()). pandas usa ddof=1 por defecto: si comparas contra .std() de una Series, no van a coincidir.
  • Las tres categorias tienen que sumar 40 en las dos columnas. Es el mismo invariante de la leccion 1.6, aplicado a celdas en vez de a filas.
  • sort_keys=True en el json.dump: sin eso, dos soluciones correctas producen archivos distintos y no hay nada que comparar.

Así tiene que quedar

notas1_estadisticas.json (objetivo) · 2 de 1 registros
{
  "nota_1": {
    "desvio": 1.95,
    "maxima": 10.0,
    "media": 6.33,
    "mediana": 6.05,
    "minima": 3.0,
    "n_ausentes": 0,
    "n_fuera_de_rango": 2,
    "n_total": 40,
    "n_validas": 38,
    "p25": 4.7,
    "p75": 7.8
  },
  "nota_3": {
    "desvio": 2.15,
    "maxima": 9.9,
    "media": 6.23,
    "mediana": 6.05,
    "minima": 3.2,
    "n_ausentes": 5,
    "n_fuera_de_rango": 1,
    "n_total": 40,
    "n_validas": 34,
    "p25": 4.12,
    "p75": 7.8
  }
}
El desvío: ddof=0

Es el default de NumPy (a.std()) y es el que pide el contrato. pandas usa ddof=1 por defecto: si comparás contra el .std() de una Series, no van a coincidir, y no es un error de nadie.

Es exactamente la clase de detalle que un contrato tiene que fijar, porque las dos respuestas son legítimas.

sort_keys=True

Sin eso, dos soluciones correctas producen archivos con las claves en distinto orden y no hay nada que comparar.