Indice / Practicas / EJ-6.4
EJ-6.4 · Tres tablas, validate y huerfanos
Tres tablas: estudiantes, cursos e inscripciones. Hay que juntarlas, apartar las inscripciones que apuntan a alguien que no existe, y encontrar a los que no se anotaron a nada. Con una trampa de clave que rompe el cruce si elegís la columna equivocada.
Qué hay que hacer
Producir tres archivos:
inscripciones_enriquecidas.csv- las 108 inscripciones válidas con el nombre del estudiante y del curso.estudiantes_sin_inscripcion.csv- los 8 que no aparecen en ninguna.inscripciones_huerfanas.csv- las 2 que referencian a un estudiante o a un curso que no existe.
ELI5Explicado facil
Tenés una lista de alumnos, una de materias y una de anotaciones. Cada anotación dice «el alumno 12 se anotó a la materia 3».
Hay una anotación que dice «el alumno 999», y no existe ningún alumno 999. Y hay ocho alumnos que no aparecen en ninguna anotación. Las dos cosas son información, no errores del programa, y hay que informarlas por separado.
Archivos
- Entrada ·
academico_estudiantes.csv - Entrada ·
academico_cursos.csv - Entrada ·
academico_inscripciones.csv - Objetivo ·
inscripciones_enriquecidas.csv - Objetivo ·
estudiantes_sin_inscripcion.csv - Objetivo ·
inscripciones_huerfanas.csv - Herramienta ·
comparar.py
La trampa: cuál columna es la clave
El padrón tiene id y legajo, y los dos parecen
identificar al estudiante. Solo uno lo hace: el legajo E007 está
repetido en dos estudiantes distintos (id 7 y id 8).
Si cruzás por legajo, las 9 inscripciones de esos dos estudiantes se
multiplican y el archivo sale con filas de más. El total sigue pareciendo razonable
- nadie cuenta 108 a mano - y las notas de esos alumnos aparecen dos
veces.
validate="m:1" convierte eso en una excepción antes de que llegue al
archivo.
Cómo saber si te salió
python comparar.py mi_enriquecidas.csv inscripciones_enriquecidas.csv
python comparar.py mi_sin.csv estudiantes_sin_inscripcion.csv
python comparar.py mi_huerfanas.csv inscripciones_huerfanas.csv
Antes de arrancar, mirá los datos
| id | legajo | nombre | correo |
|---|---|---|---|
| 1 | E001 | Javier Herrera | javier.herrera@uni.edu.ar |
| 2 | E002 | Pablo Díaz | pablo.diaz@uni.edu.ar |
| 3 | E003 | Ana Peña | ana.pena@uni.edu.ar |
| 4 | E004 | Lautaro Ruiz | lautaro.ruiz@uni.edu.ar |
| 5 | E005 | Inés Quiroga | ines.quiroga@uni.edu.ar |
El contrato de salida
inscripciones_enriquecidas.csv · 108 filas
| Columna | Tipo | Que lleva |
|---|---|---|
fila_origen | entero | Posicion en academico_inscripciones.csv, empezando en 0 |
estudiante_id | entero | La clave del estudiante |
legajo | texto | Del padron. OJO: no es unico |
estudiante | texto | El nombre del estudiante |
curso_id | entero | La clave del curso |
codigo | texto | El codigo del curso |
curso | texto | El nombre del curso |
creditos | entero | Del catalogo. Puede quedar vacio: un curso no los tiene cargados |
nota | decimal | Tal cual viene, redondeada a 2 decimales. Puede quedar vacia |
estudiantes_sin_inscripcion.csv · 8 filas
| Columna | Tipo | Que lleva |
|---|---|---|
estudiante_id | entero | El id del estudiante |
legajo | texto | Su legajo |
estudiante | texto | Su nombre |
correo | texto | Su correo |
inscripciones_huerfanas.csv · 2 filas
| Columna | Tipo | Que lleva |
|---|---|---|
fila_origen | entero | Posicion en el archivo de inscripciones |
estudiante_id | entero | Tal cual viene |
curso_id | entero | Tal cual viene |
nota | decimal | Tal cual viene |
columna | texto | Cual de las dos referencias no existe |
motivo | texto | Siempre huerfano |
Que hacer con cada defecto
| Defecto | Donde | Accion | Adonde va |
|---|---|---|---|
| inscripcion del estudiante 999, que no esta en el padron | 1 fila, la 107 | apartar apartar | inscripciones_huerfanas.csv con motivo huerfano y columna estudiante_id |
| inscripcion al curso 77, que no esta en el catalogo | 1 fila, la 108 | apartar apartar | inscripciones_huerfanas.csv con motivo huerfano y columna curso_id |
| el legajo E007 esta repetido en dos estudiantes distintos (id 7 e id 8) | 2 filas del padron | NO NO se toca. Es la trampa del ejercicio: hay que cruzar por id, que si es unico. Cruzar por legajo multiplicaria las 9 inscripciones de esos dos estudiantes | inscripciones_enriquecidas.csv, sin filas de mas |
| el par (estudiante 35, curso 5) aparece dos veces, con notas distintas | 2 filas | NO NO se deduplica: las dos son inscripciones y las dos se enriquecen. Por eso el orden necesita fila_origen | inscripciones_enriquecidas.csv |
| notas ausentes y creditos ausentes | 2 notas, 1 curso sin creditos | NO NO se imputan: este ejercicio cruza, no limpia | inscripciones_enriquecidas.csv, con el campo vacio |
Una fila puede tener mas de un problema. Se informa solo el primero de esta lista:
- agregar fila_origen a las inscripciones
- marcar como huerfana la que referencia a un estudiante inexistente
- marcar como huerfana la que referencia a un curso inexistente
- cruzar SOLO las validas contra estudiantes y contra cursos, por id, con validate='m:1'
- listar los estudiantes que no aparecen en ninguna inscripcion valida
Los huerfanos se apartan ANTES de cruzar: un how='left' los conservaria con las columnas del padron vacias, y despues serian indistinguibles de un dato que falta. Y validate='m:1' se pone en los dos merges: es lo que falla si alguien cruza por legajo en vez de por id.
Conteos clave
| inscripciones de entrada | 110 |
|---|---|
| estudiantes en el padron | 50 |
| cursos en el catalogo | 8 |
| inscripciones_enriquecidas.csv | 108 |
| inscripciones_huerfanas.csv | 2 |
| estudiantes_sin_inscripcion.csv | 8 |
| estudiantes con al menos una inscripcion valida | 42 |
| notas ausentes en las enriquecidas | 2 |
| por motivo | huerfano: 2 |
Invariantes
Comprobaciones que tu resultado tiene que cumplir. Corrilas vos mismo antes de comparar los archivos:
len(enriquecidas) + len(huerfanas) == 110
enriquecidas['fila_origen'].is_unique
huerfanas['fila_origen'].is_unique
42 + 8 == 50
enriquecidas['estudiante_id'].nunique() == 42
enriquecidas['estudiante_id'].isin(estudiantes['id']).all()
enriquecidas['curso_id'].isin(cursos['id']).all()
set(huerfanas['motivo']) == {'huerfano'}
enriquecidas['legajo'].notna().all()
PISTASSi te trabas, abri esto
- La clave del padron es id, no legajo. El legajo E007 esta en dos estudiantes distintos: cruzar por ahi multiplica 9 inscripciones.
- validate='m:1' en los dos merges convierte ese error en una excepcion. Sin el, el archivo sale con filas de mas y la cantidad parece razonable.
- Apartar los huerfanos ANTES de cruzar. Con how='left' quedarian adentro, con el nombre del estudiante vacio, y no habria forma de distinguirlos de un dato faltante.
- El par (35, 5) aparece dos veces y las dos son inscripciones legitimas del archivo: no se deduplican. Por eso el orden termina en fila_origen.
Así tiene que quedar
| fila_origen | estudiante_id | legajo | estudiante | curso_id | codigo | curso | creditos | nota |
|---|---|---|---|---|---|---|---|---|
| 28 | 1 | E001 | Javier Herrera | 3 | AL110 | Álgebra | 6 | 3.7 |
| 15 | 2 | E002 | Pablo Díaz | 2 | PR201 | Programación II | 8 | vacio |
| 71 | 2 | E002 | Pablo Díaz | 6 | IA401 | Inteligencia artificial | 15 | 5.0 |
| 88 | 2 | E002 | Pablo Díaz | 8 | ET100 | Ética profesional | vacio | 5.3 |
| 49 | 3 | E003 | Ana Peña | 2 | PR201 | Programación II | 8 | 3.4 |
| estudiante_id | legajo | estudiante | correo |
|---|---|---|---|
| 23 | E023 | Federico Quiroga | federico.quiroga@uni.edu.ar |
| 24 | E024 | Gabriela Acosta | gabriela.acosta@uni.edu.ar |
| 45 | E045 | Santiago Bravo | santiago.bravo@uni.edu.ar |
| 46 | E046 | Lucas Gutiérrez | lucas.gutierrez@uni.edu.ar |
| 47 | E047 | Carla Duarte | carla.duarte@uni.edu.ar |
| fila_origen | estudiante_id | curso_id | nota | columna | motivo |
|---|---|---|---|---|---|
| 107 | 999 | 1 | 8.0 | estudiante_id | huerfano |
| 108 | 1 | 77 | 7.0 | curso_id | huerfano |
Con how="left" las dos inscripciones huérfanas se quedarían adentro,
con el nombre del estudiante vacío. Y a partir de ahí son indistinguibles de
un estudiante cuyo nombre falta en el padrón, que es otro problema y se
arregla distinto.
Apartarlas primero deja el archivo enriquecido con la garantía de que todas sus
filas tienen nombre, y esa garantía se puede escribir como assert.
108 + 2 = 110: ninguna inscripción se perdió en el camino.
42 + 8 = 50: los estudiantes con al menos una inscripción válida más los que no tienen ninguna dan el padrón completo. Esa segunda cuenta es la que atrapa el error de contar sobre las inscripciones sin apartar los huérfanos.