https://github.com/johnny-godoy/laboratorios-mds/blob/main/lab%204/laboratorio_4.ipynb
¶pandas
. En esta segunda parte se incluye adicionalmente agregaciones, concatenaciones, merge y trabajo con strings.plotly
.pandas
.plotly
para obtener información gráfica del dataset.Nota: El laboratorio deberá ser desarrollado sin el uso indiscriminado de iteradores nativos de python (aka "for", "while"). La idea es que aprendan a exprimir al máximo las funciones optimizadas que nos entrega
pandas
, las cuales vale mencionar, son bastante más eficientes que los iteradores nativos sobre DataFrames.
import collections
from IPython.display import display
import numpy as np
import pandas as pd
import plotly.figure_factory as ff
import plotly.graph_objects as go
import plotly.express as px
Para este laboratorio deberán continuar el Análisis Exploratorio de datos sobre el conjunto students_grades
, el cual contiene una caracterización sobre el rendimiento y otros atributos de cada alumno de la Universidad de la Cachaña .
Ya finalizado en análisis inicial, ud. y su equipo le entregaron a Don Caguayo (rector de la Universidad de la Cachaña) tanto los resultados del análisis como también la base de datos limpia y lista para ser almacenada. Dada la ingente cantidad de los datos, el equipo de TI de la universidad resolvió separar el dataset en dos bases de datos distintas (lo que según argumentan ellos, permitiría hacer agregaciones de forma más eficiente).
Gracias a la excelente labor de ud. y su equipo en el análisis previo, el rector le solicita continuar el trabajo con una nueva batería de análisis. Por este motivo, la sección de TI les entrega nuevamente los datos. Sin embargo, argumentan que dada una escazes de personal, solo le entregarán dumps (copias) de cada base de datos y su equipo deberá unir las bases de datos. Los datos se encuentran en los siguiente archivos .json
: students_grades_1.json
y students_grades_2.json
.
Por ende, ud. y su equipo deciden que la primera tarea se centrará en cargar estos datos y unirlos.
No se preocupe por la limpieza ni transformar el tipo de datos de las columnas, ni tampoco transformar a notas chilenas, recuerde que anteriormente ya se encargo de este tema.
df_grades = pd.concat((pd.read_json("data/students_grades_1.json"),
pd.read_json("data/students_grades_2.json")))
df_grades
names | gender | race/ethnicity | parental level of education | lunch | test preparation course | math score | reading score | writing score | |
---|---|---|---|---|---|---|---|---|---|
0 | Rita Courtney | female | group B | some high school | standard | none | 3.22 | 3.76 | 3.76 |
1 | Charles Linstrom | male | group A | bachelor's degree | standard | completed | 5.80 | 5.68 | 5.86 |
2 | Brian Young | male | group C | some high school | standard | none | 5.38 | 4.96 | 4.78 |
3 | Howard Jimenez | male | group E | some high school | standard | completed | 5.86 | 5.50 | 5.56 |
4 | Wayne Wilson | male | group B | some high school | standard | completed | 6.64 | 6.16 | 6.22 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
470 | Richard Young | male | group D | high school | standard | none | 5.14 | 5.50 | 5.26 |
471 | Wanda Russell | female | group B | high school | free/reduced | completed | 2.38 | 3.64 | 3.16 |
472 | Marina Zeigler | female | group C | bachelor's degree | free/reduced | completed | 4.96 | 5.44 | 5.86 |
473 | Laurie Carter | female | group B | some high school | standard | completed | 4.24 | 4.66 | 4.72 |
474 | Amanda Perez | female | group A | high school | standard | completed | 5.08 | 5.80 | 5.56 |
875 rows × 9 columns
Preocupado por la dificultad que representa el graficar correctamente las notas, el rector le solicita implementar distintas alternativas de visualización.
Para esto, genere un boxplot, un displot, un histograma con un gráfico marginal de caja y un histograma con el ramo como faceta de fila que permitan visualizar las notas.
Luego, responda las siguientes pregunta:
¿Existe una diferencia notable entre las notas?
¿Cuál de los gráficos mostrados cree que es adecuado para mostrarle al rector? ¿Y a los padres? ¿Y a un centro de estudios educativos? ¿Por qué?. Base sus respuestas en lo visto en la clase de visualizaciones como también en lo que usted y su equipo consideren correcto.
Hint: Para elaborar el histograma, puede que le sea de utilidad hacer un
melt
del DataFrame, dejando como variables los ramos y valores las notas. Por otra parte, visiten la documentación para generar los gráficos.
scores = [f"{subject} score" for subject in ("math", "reading", "writing")]
melted_frame = df_grades.melt(value_vars=scores, var_name="subject", value_name="score")
melted_frame
subject | score | |
---|---|---|
0 | math score | 3.22 |
1 | math score | 5.80 |
2 | math score | 5.38 |
3 | math score | 5.86 |
4 | math score | 6.64 |
... | ... | ... |
2620 | writing score | 5.26 |
2621 | writing score | 3.16 |
2622 | writing score | 5.86 |
2623 | writing score | 4.72 |
2624 | writing score | 5.56 |
2625 rows × 2 columns
Gráfico de Caja:
px.box(melted_frame, x="subject", y="score", color="subject")
Distplot:
ff.create_distplot([df_grades[score] for score in scores], scores, show_hist=False, show_rug=False)
Histograma con Boxplots:
px.histogram(melted_frame, x="score", color="subject", marginal='box', barmode="group", nbins=14)
Histograma con Faceta:
px.histogram(melted_frame, x="score", facet_row="subject", color="subject", nbins=14)
Justifique:
El rector, basado en su experiencia, cree fuertemente que el nivel educacional y la etnia de los padres influyen en las notas que obtienen sus hijos. Como científicos de datos, ud. y su equipo creen que deben encontrar evidencia para confirmar o refutar la hipótesis del rector.
Para esto, deciden generar dos análisis: una tabla de resumen por una parte y gráficos de caja por otro.
Para generar la tabla de resumen:
GPA
(grade point average).[x] Hacer una simplificación a través de un mapeo (investigar el método map()
) de la variable parental level of education
según la siguiente conversión:
some high school -> school
some college -> school
high school -> school
bachelor's degree -> college
associate's degree -> college
master's degree -> postgraduate
Los resultados de este mapeo deben ser guardados en la columna simple parental level of education
.
race/ethnicity
y simple parental level of education
para obtener el promedio de las notas. race/ethnicity
y simple parental level of education
para obtener un conteo de los alumnos en cada grupo y agregarlos como una nueva fila count.Utilizar la tabla de resultados esperados como guía para desarrollar este punto.
def percentage(x):
return f"{100*x.count()/len(df_resumen):.2f} %"
df_resumen = df_grades.copy()
df_resumen["GPA"] = df_resumen[scores].mean(axis=1)
replacement_dict = collections.defaultdict(lambda: 'school')
replacement_dict["bachelor's degree"] = "college"
replacement_dict["associate's degree"] = "college"
replacement_dict["master's degree"] = "postgraduate"
df_resumen["simple parental level of education"] = df_resumen["parental level of education"].map(replacement_dict)
df_resumen.drop("parental level of education", axis=1, inplace=True)
grouped = df_resumen.groupby(["race/ethnicity", "simple parental level of education"])
aggregators = {score: "mean" for score in scores}
aggregators["GPA"] = "mean"
aggregators["names"] = "count"
resumen_final = grouped.agg(aggregators).round(2).reset_index()
resumen_final.rename(columns={"names": "count"}, inplace=True)
resumen_final["percentage"] = resumen_final["count"].apply(lambda x: f"{100*x/len(df_resumen):.2f} %")
resumen_final
race/ethnicity | simple parental level of education | math score | reading score | writing score | GPA | count | percentage | |
---|---|---|---|---|---|---|---|---|
0 | group A | college | 4.74 | 5.00 | 4.89 | 4.88 | 24 | 2.74 % |
1 | group A | postgraduate | 4.69 | 5.23 | 5.35 | 5.09 | 2 | 0.23 % |
2 | group A | school | 4.57 | 4.73 | 4.56 | 4.62 | 51 | 5.83 % |
3 | group B | college | 5.07 | 5.26 | 5.19 | 5.18 | 54 | 6.17 % |
4 | group B | postgraduate | 4.91 | 5.69 | 5.55 | 5.38 | 5 | 0.57 % |
5 | group B | school | 4.69 | 4.89 | 4.76 | 4.78 | 107 | 12.23 % |
6 | group C | college | 5.02 | 5.37 | 5.35 | 5.25 | 102 | 11.66 % |
7 | group C | postgraduate | 4.92 | 5.14 | 5.10 | 5.06 | 15 | 1.71 % |
8 | group C | school | 4.76 | 5.02 | 4.92 | 4.90 | 155 | 17.71 % |
9 | group D | college | 5.11 | 5.25 | 5.25 | 5.20 | 70 | 8.00 % |
10 | group D | postgraduate | 5.22 | 5.54 | 5.73 | 5.50 | 20 | 2.29 % |
11 | group D | school | 5.02 | 5.13 | 5.11 | 5.09 | 149 | 17.03 % |
12 | group E | college | 5.54 | 5.45 | 5.45 | 5.48 | 52 | 5.94 % |
13 | group E | postgraduate | 5.54 | 6.03 | 5.89 | 5.82 | 6 | 0.69 % |
14 | group E | school | 5.40 | 5.31 | 5.16 | 5.29 | 63 | 7.20 % |
Resultado Esperado
race/ethnicity | simple parental level of education | math score | reading score | writing score | GPA | count | percentage | |
---|---|---|---|---|---|---|---|---|
0 | group A | college | 4.74 | 5 | 4.89 | 4.88 | 24 | 2.74 % |
1 | postgraduate | 4.69 | 5.23 | 5.35 | 5.09 | 2 | 0.23 % | |
2 | school | 4.57 | 4.73 | 4.56 | 4.62 | 51 | 5.83 % | |
3 | group B | college | 5.07 | 5.26 | 5.19 | 5.18 | 54 | 6.17 % |
4 | postgraduate | 4.91 | 5.69 | 5.55 | 5.38 | 5 | 0.57 % | |
5 | school | 4.69 | 4.89 | 4.76 | 4.78 | 107 | 12.23 % | |
6 | group C | college | 5.02 | 5.37 | 5.35 | 5.25 | 102 | 11.66 % |
7 | postgraduate | 4.92 | 5.14 | 5.1 | 5.06 | 15 | 1.71 % | |
8 | school | 4.76 | 5.02 | 4.92 | 4.9 | 155 | 17.71 % | |
9 | group D | college | 5.11 | 5.25 | 5.25 | 5.2 | 70 | 8.0 % |
10 | postgraduate | 5.22 | 5.54 | 5.73 | 5.5 | 20 | 2.29 % | |
11 | school | 5.02 | 5.13 | 5.11 | 5.09 | 149 | 17.03 % | |
12 | group E | college | 5.54 | 5.45 | 5.45 | 5.48 | 52 | 5.94 % |
13 | postgraduate | 5.54 | 6.03 | 5.89 | 5.82 | 6 | 0.69 % | |
14 | school | 5.4 | 5.31 | 5.16 | 5.29 | 63 | 7.2 % |
Ahora, implemente un gráfico de caja en donde se muestre el GPA con respecto al nivel educacional y que la variable de color sea la etnicidad y luego comente.
px.box(df_resumen, x="simple parental level of education", y="GPA",
color="race/ethnicity")
- ¿Hay alguna diferencia entre los grupos graficados tanto para el nivel educacional de los padres como también para la etnicidad?
- ¿Este gráfico permite hacer facilmente un análisis conjunto de estas dos variables de forma sencilla?
Justifique:
Mientras le notificaba por videollamada los resultados de sus descubrimientos a Don Caguayo, un exaltado practicante del area de TI entra a la reunión y les informa que ha encontrado una nueva base de datos que cuenta con las notas de dos asignaturas (en escala chilena): historia y ciencias. Para más remate, antes de huir, el practicante les cuenta que este dataframe lamentablemente contiene nuevamente los alumnos de los registros corruptos que ud. y su equipo filtraron en el análisis anterior.
El rector (evidentemente molesto por la situación) les ruega incluir estos datos (vaciados en el archivo other_grades.csv) al estudio original(students_grades.csv
).
Para esto, carge el archivo other_grades.csv
y busque la forma de unir ambos DataFrames, de tal manera que las columnas de history score
y science score
se anexen al final del DataFrame original. NO LIMPIE LOS DATOS, si no que explore los distintos tipos de merge para encontrar el mas situable para su situación (y así evitar buscar duplicados).
To-Do
other_grades.csv
df_grades
con other_grades.csv
usando outer join y explique el resultado.df_grades
con other_grades.csv
usando left join y explique el resultado.df_grades
con other_grades.csv
usando right join y explique el resultado.df_grades
con other_grades.csv
usando inner join y explique el resultado.Hint: Puede explicar los resultados del merge a través de la cantidad de filas resultantes y los valores que estas contienen.
other_grades = pd.read_csv("data/other_grades.csv")
other_grades
names | science score | history score | |
---|---|---|---|
0 | Pam Otoole | 57 | 4.0 |
1 | Diane Olson | 48 | 4.8 |
2 | Sarah Flannery | 72 | 6.1 |
3 | Stanley Chappell | 47 | 3.7 |
4 | Arnold Dodson | 60 | 5.2 |
... | ... | ... | ... |
995 | Melanie Acevedo | 79 | 6.3 |
996 | Christopher Wall | 62 | 4.0 |
997 | Margaret Spannaus | 47 | 4.0 |
998 | Vickie Sellers | 47 | 5.7 |
999 | Kari Mitchell | 53 | 6.2 |
1000 rows × 3 columns
def joiner(how):
return df_grades.merge(other_grades, on="names", how=how)
joined_dfs = {how: joiner(how) for how in ("outer", "left", "right", "inner")}
for how, df in joined_dfs.items():
print(f"{how} join")
display(df)
outer join
names | gender | race/ethnicity | parental level of education | lunch | test preparation course | math score | reading score | writing score | science score | history score | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Rita Courtney | female | group B | some high school | standard | none | 3.22 | 3.76 | 3.76 | 25 | 3.8 |
1 | Charles Linstrom | male | group A | bachelor's degree | standard | completed | 5.80 | 5.68 | 5.86 | 56 | 5.2 |
2 | Brian Young | male | group C | some high school | standard | none | 5.38 | 4.96 | 4.78 | 58 | 3.8 |
3 | Howard Jimenez | male | group E | some high school | standard | completed | 5.86 | 5.50 | 5.56 | 72 | 5.0 |
4 | Wayne Wilson | male | group B | some high school | standard | completed | 6.64 | 6.16 | 6.22 | 94 | 4.6 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
995 | Leona Markow | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 39 | 3.8 |
996 | Maxine Gulbranson | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 38 | 3.9 |
997 | Jeane Budds | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 43 | 3.9 |
998 | Nancy Ramos | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 54 | 6.2 |
999 | Andrea Mcdavid | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 40 | 4.2 |
1000 rows × 11 columns
left join
names | gender | race/ethnicity | parental level of education | lunch | test preparation course | math score | reading score | writing score | science score | history score | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Rita Courtney | female | group B | some high school | standard | none | 3.22 | 3.76 | 3.76 | 25 | 3.8 |
1 | Charles Linstrom | male | group A | bachelor's degree | standard | completed | 5.80 | 5.68 | 5.86 | 56 | 5.2 |
2 | Brian Young | male | group C | some high school | standard | none | 5.38 | 4.96 | 4.78 | 58 | 3.8 |
3 | Howard Jimenez | male | group E | some high school | standard | completed | 5.86 | 5.50 | 5.56 | 72 | 5.0 |
4 | Wayne Wilson | male | group B | some high school | standard | completed | 6.64 | 6.16 | 6.22 | 94 | 4.6 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
870 | Richard Young | male | group D | high school | standard | none | 5.14 | 5.50 | 5.26 | 62 | 4.2 |
871 | Wanda Russell | female | group B | high school | free/reduced | completed | 2.38 | 3.64 | 3.16 | 16 | 3.6 |
872 | Marina Zeigler | female | group C | bachelor's degree | free/reduced | completed | 4.96 | 5.44 | 5.86 | 59 | 4.1 |
873 | Laurie Carter | female | group B | some high school | standard | completed | 4.24 | 4.66 | 4.72 | 43 | 4.3 |
874 | Amanda Perez | female | group A | high school | standard | completed | 5.08 | 5.80 | 5.56 | 61 | 5.3 |
875 rows × 11 columns
right join
names | gender | race/ethnicity | parental level of education | lunch | test preparation course | math score | reading score | writing score | science score | history score | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Pam Otoole | female | group B | bachelor's degree | standard | none | 5.32 | 5.32 | 5.44 | 57 | 4.0 |
1 | Diane Olson | female | group C | some college | standard | completed | 5.14 | 6.40 | 6.28 | 48 | 4.8 |
2 | Sarah Flannery | female | group B | master's degree | standard | none | 6.40 | 6.70 | 6.58 | 72 | 6.1 |
3 | Stanley Chappell | male | group A | associate's degree | free/reduced | none | 3.82 | 4.42 | 3.64 | 47 | 3.7 |
4 | Arnold Dodson | male | group C | some college | standard | none | 5.56 | 5.68 | 5.50 | 60 | 5.2 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
995 | Melanie Acevedo | female | group E | master's degree | standard | completed | 6.28 | 6.94 | 6.70 | 79 | 6.3 |
996 | Christopher Wall | male | group C | high school | free/reduced | none | 4.72 | 4.30 | 4.30 | 62 | 4.0 |
997 | Margaret Spannaus | female | group C | high school | free/reduced | completed | 4.54 | 5.26 | 4.90 | 47 | 4.0 |
998 | Vickie Sellers | female | group D | some college | standard | completed | 5.08 | 5.68 | 5.62 | 47 | 5.7 |
999 | Kari Mitchell | female | group D | some college | free/reduced | none | 5.62 | 6.16 | 6.16 | 53 | 6.2 |
1000 rows × 11 columns
inner join
names | gender | race/ethnicity | parental level of education | lunch | test preparation course | math score | reading score | writing score | science score | history score | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | Rita Courtney | female | group B | some high school | standard | none | 3.22 | 3.76 | 3.76 | 25 | 3.8 |
1 | Charles Linstrom | male | group A | bachelor's degree | standard | completed | 5.80 | 5.68 | 5.86 | 56 | 5.2 |
2 | Brian Young | male | group C | some high school | standard | none | 5.38 | 4.96 | 4.78 | 58 | 3.8 |
3 | Howard Jimenez | male | group E | some high school | standard | completed | 5.86 | 5.50 | 5.56 | 72 | 5.0 |
4 | Wayne Wilson | male | group B | some high school | standard | completed | 6.64 | 6.16 | 6.22 | 94 | 4.6 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
870 | Richard Young | male | group D | high school | standard | none | 5.14 | 5.50 | 5.26 | 62 | 4.2 |
871 | Wanda Russell | female | group B | high school | free/reduced | completed | 2.38 | 3.64 | 3.16 | 16 | 3.6 |
872 | Marina Zeigler | female | group C | bachelor's degree | free/reduced | completed | 4.96 | 5.44 | 5.86 | 59 | 4.1 |
873 | Laurie Carter | female | group B | some high school | standard | completed | 4.24 | 4.66 | 4.72 | 43 | 4.3 |
874 | Amanda Perez | female | group A | high school | standard | completed | 5.08 | 5.80 | 5.56 | 61 | 5.3 |
875 rows × 11 columns
Justificación:
df_grades
es decir, solamente aquellos de este frame están en el resultado, y tienen sus notas de historia/ciencia definidas si y solamente sí se encuentran en other_grades
other_grades
, es decir, solamente aquellos de este frame están en el resultado, y tienen su género/raza/etc definidos solamente si aparecen en df_grades
Cabe destacar sin embargo que todos los nombres en df_grades
están en other_grades
:
df_grades.names.isin(other_grades.names).all()
True
Pero no viceversa:
other_grades.names.isin(df_grades.names).all()
False
En particular, si tenemos que $A\subseteq B$, entonces:
Genere dos visualizaciones extras que encuentre interesantes (y no triviales) con estos datos y explique sus resultados. Agrupe los atributos que estime convenientes.
To-Do:
NOTA: No utilice historia ni ciencias, son notas generadas aleatoriamente.
px.histogram(df_resumen, x="GPA", color="gender",
marginal='box', barmode="group", nbins=14)
Este gráfico representa la distribución de GPA según género.
px.box(df_resumen, x="GPA", color="test preparation course")
Este gráfico busca entender la distribución de GPA según si completaron o no la prueba de preparación. Poren imbalance de datos, se prefiere un gráfico de cajas a un histograma.
Eso ha sido todo para el lab de hoy, recuerden que el laboratorio tiene un plazo de entrega de una semana y que los días de atraso no se pueden utilizar para entregas de lab solo para tareas. Cualquier duda del laboratorio, no duden en contactarnos por mail o U-cursos.