Saltar al contenido

Indice / Practicas / EJ-3.3

EJ-3.3 · Asistencia con denominador variable

Practica de: 3.4 Mascaras booleanasDificultad: A

Treinta alumnos, doce sesiones, y un porcentaje que parece una división. La parte difícil no es el numerador: es darse cuenta de que el denominador no es 12 para todos.

Qué hay que hacer

A partir de asistencia_sesiones.csv (30 filas × 12 sesiones), producir asistencia_porcentaje.csv con una fila por alumno: cuántas sesiones tiene registradas, cuántas presencias, cuántas ausencias, cuántas celdas sin registro, y el porcentaje.

Archivos

ELI5Explicado facil

Si un alumno faltó a una clase, eso cuenta en su contra. Si el profesor no tomó asistencia ese día, no.

Las dos situaciones se ven casi iguales en la planilla - una celda que no dice «P» - y son completamente distintas. Contar la segunda como falta es inventar un dato en contra de alguien.

Las cinco celdas que no dicen ni P ni A

AlumnoSesiónDiceQué esQué se hace
L271708pUna presencia, en minúsculaNormalizar: cuenta como presencia
L272403PRESENTEUna presencia, escrita enteraNormalizar: cuenta como presencia
L270305(vacío)No se tomó asistenciaSin registro: sale del numerador y del denominador
L272810-ÍdemSin registro
L271101XUn estado que nadie sabe interpretarSin registro: no se puede adivinar

Dos se recuperan y tres no. Y esa distinción - entre escrito distinto y no escrito - es todo el ejercicio.

Lo que cambia el denominador

Tres alumnos, tres porcentajes distintos
AlumnoPresenciasDividiendo por 12Dividiendo por lo registrado
L27031083,3 %90,9 % (sobre 11)
L2711975,0 %81,8 % (sobre 11)
L2728433,3 %36,4 % (sobre 11)

Hasta 7,6 puntos de diferencia, sobre un umbral de aprobación que suele estar en el 75 %. A L2703 y a L2711 los cruza de lado.

La trampa del orden

L2703 y L2728 se llaman los dos Fabián Bravo

No es un duplicado que haya que resolver: son dos legajos distintos, y por lo tanto dos alumnos. Lo único que obliga es a ordenar por legajo, porque estudiante no es único y no daría un orden total.

Es la lección 2.2 apareciendo donde no se la esperaba.

El invariante

assert (salida["presencias"] + salida["ausencias"]
        == salida["sesiones_registradas"]).all()
assert (salida["sesiones_registradas"] + salida["sin_registro"] == 12).all()

Los dos tienen que valer en las 30 filas. Es la versión por fila del invariante de la lección 1.6: si no cierra, hay una celda que no fue clasificada.

Cómo saber si te salió

python comparar.py mi_asistencia.csv asistencia_porcentaje.csv

Antes de arrancar, mirá los datos

asistencia_sesiones.csv (entrada) · 30 filas, se muestran 5
legajoestudiantesesion_01sesion_02sesion_03sesion_04sesion_05sesion_06sesion_07sesion_08sesion_09sesion_10sesion_11sesion_12
L2701Rocío DuartePPAPPAPAAPAA
L2702Yamila EscobarPPPPAAPPPPPP
L2703Fabián BravoAPPPvacioPPPPPPP
L2704Pablo LunaAPPPPPPPPPAA
L2705Valeria VidalPPPPPPPAPPPP

El contrato de salida

asistencia_porcentaje.csv · 30 filas

ColumnaTipoQue lleva
legajotextoEl legajo tal cual vino
estudiantetextoEl nombre tal cual vino. OJO: se repite, no sirve como clave
sesiones_registradasenteroPresencias mas ausencias: el DENOMINADOR del porcentaje
presenciasenteroCuantas veces asistio
ausenciasenteroCuantas veces falto, con registro
sin_registroenteroCuantas celdas no son un estado utilizable
porcentajedecimal100 * presencias / sesiones_registradas, 1 decimal

Orden de las filas: legajo ascendente. Los 30 legajos son distintos; el nombre NO, asi que ordenar por estudiante no daria un orden total. Indice: no se escribe: to_csv(index=False)

Que hacer con cada defecto

DefectoDondeAccionAdonde va
sinonimos de presente escritos distinto2 celdas: 'p' en L2717 sesion_08 y 'PRESENTE' en L2724 sesion_03NORMALIZAR: NORMALIZAR: se pasa a mayusculas y se aceptan P, PRESENTE y PRES como presencia. Son registros validos escritos con otra convencionasistencia_porcentaje.csv, cuentan como presencia
celda vacia1 celda: L2703 sesion_05NO NO es una ausencia: es una sesion sin registro. Sale del numerador Y del denominadorasistencia_porcentaje.csv, columna sin_registro
el token '-'1 celda: L2728 sesion_10igual igual que la celda vacia: sin registroasistencia_porcentaje.csv, columna sin_registro
un estado fuera del dominio1 celda: 'X' en L2711 sesion_01sin sin registro. No se puede adivinar si esa X queria decir presente o ausenteasistencia_porcentaje.csv, columna sin_registro
dos alumnos con el mismo nombreL2703 y L2728 se llaman los dos Fabian Bravono no se corrige ni se fusiona: son dos legajos distintos y por lo tanto dos alumnos. Solo obliga a ordenar por legajoasistencia_porcentaje.csv, dos filas
Orden de comprobacion

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

  1. normalizar cada celda a mayusculas y sin espacios
  2. clasificar en presente, ausente, o sin registro
  3. contar los tres por alumno
  4. el porcentaje se calcula sobre presencias + ausencias, NO sobre 12

La normalizacion va primero, o la 'p' minuscula y el 'PRESENTE' se pierden como sin registro y dos alumnos quedan con un punto menos. Y el denominador sale del conteo, no de la cantidad de columnas: es la unica decision que cambia tres filas del resultado.

Conteos clave

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

filas de entrada30
celdas de asistencia360
asistencia_porcentaje.csv30
alumnos con menos de 12 sesiones registradas3
celdas sin registro3
celdas normalizadas2
por motivoausente: 2, dominio_desconocido: 1

Invariantes

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

len(asistencia) == 30
asistencia['legajo'].is_unique
asistencia['legajo'].is_monotonic_increasing
not asistencia.isna().any().any()
(asistencia['presencias'] + asistencia['ausencias'] == asistencia['sesiones_registradas']).all()
(asistencia['sesiones_registradas'] + asistencia['sin_registro'] == 12).all()
asistencia['sin_registro'].sum() == 3
(asistencia['sesiones_registradas'] < 12).sum() == 3
asistencia['porcentaje'].between(0, 100).all()
asistencia['presencias'].sum() == 285
not asistencia['estudiante'].is_unique
PISTASSi te trabas, abri esto
  • El porcentaje se divide por sesiones_registradas, no por 12. Son tres alumnos los que cambian, y para uno de ellos la diferencia es de 7,6 puntos.
  • Normalizar con .strip().upper() ANTES de comparar recupera la 'p' y el 'PRESENTE'. Sin eso, dos alumnos pierden una presencia cada uno.
  • Una 'X' no es una ausencia: es un estado que nadie sabe interpretar. Contarla como falta seria inventar un dato en contra del alumno.
  • El invariante sesiones_registradas + sin_registro == 12 tiene que valer en las 30 filas. Es la version por fila del invariante de la leccion 1.6.

Así tiene que quedar

asistencia_porcentaje.csv (objetivo) · 30 filas, se muestran 5
legajoestudiantesesiones_registradaspresenciasausenciassin_registroporcentaje
L2701Rocío Duarte1266050.0
L2702Yamila Escobar12102083.3
L2703Fabián Bravo11101190.9
L2704Pablo Luna1293075.0
L2705Valeria Vidal12111091.7
El orden de las operaciones

Normalizar (.strip().upper()) va antes de clasificar. Si se clasifica primero, la p minúscula y el PRESENTE caen en «sin registro» y dos alumnos más pierden una presencia cada uno - sin que nada avise.