Hecho por Samuel Ochoa, 21/02/2020
La UMC publicó los índices académicos acumulados (IAA) del período 2019-02 el 19 de febrero de 2020. Como desafío y práctica, decidí realizar este análisis estadístico de los índices académicos como una libreta interactiva de Jupyter. El código utiliza Python 3 con las librerías NumPy, Matplotlib y Pandas.
Si no quiere leer todo el documento, puede saltar directamente a las conclusiones.
Los índices académicos fueron publicados como documentos PDF, disponibles en este enlace. No garantizo que el enlace funcione en un futuro. Debido a que los documentos PDF no están en un formato legible para máquinas, utilicé la herramienta Tabula para extraer las tablas de datos como archivos CSV y LibreOffice Calc para cambiar los decimales a puntos en lugar de comas y corregir los errores de conversión (algunas letras en la columna del IAA).
Los archivos CSV resultantes están disponibles aquí:
A continuación se muestra el código en Python para convertir estos archivos CSV a dataframes y series de Pandas para su posterior análisis estadístico:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# Códigos y nombres de carreras
carreras = {
'ADM': 'Licenciatura en Administración',
'INGAMB': 'Ingeniería Ambiental',
'INGINF': 'Ingeniería Informática',
'INGM': 'Ingeniería Marítima',
'TUR': 'Licenciatura en Turismo'
}
# Dataframes originales
dfs = {carrera: pd.read_csv('IAA_' + carrera + '.csv',
header = 0, index_col = 0)
for carrera in carreras.keys()}
# Series de índices académicos solamente
iaa = {carrera: dfs[carrera]['IAA'] for carrera in carreras.keys()}
# Dataframe con los IAA de cada carrera
df_iaa = pd.DataFrame(iaa)
Primero se hace una descripción estadística de la población estudiantil completa. A continuación se crea una función describir_iaa
para describir brevemente una o varias series de índices académicos, y se aplica esta función a los IAA de la población estudiantil agrupados en una sola serie de datos:
def contar_reg(serie):
"""Cantidad de estudiantes regulares en la serie de IAA"""
return serie[serie >= 12].count()
def contar_pro(serie):
"""Cantidad de estudiantes en probatorio en la serie de IAA"""
return serie[serie < 12].count()
def porcentaje_reg(serie):
"""Porcentaje de estudiantes regulares en la serie de IAA"""
return contar_reg(serie)*100.0/serie.count()
def porcentaje_pro(serie):
"""Porcentaje de estudiantes en probatorio en la serie de IAA"""
return contar_pro(serie)*100.0/serie.count()
def moda_max(serie):
"""Mayor valor de la moda de la serie de IAA"""
return serie.mode().max()
def describir_iaa(df):
"""Descripción estadística de un dataframe con una
o varias series de IAA."""
return df.agg([
# Cantidades: total, regulares, probatorio
'count', contar_reg, contar_pro,
# Porcentajes: regulares, probatorio
porcentaje_reg, porcentaje_pro,
# IAA máximo y minimo
'max', 'min',
# Medidas de tendencia central
'mean', 'median', moda_max,
# Medidas de dispersión
'std', 'kurt', 'skew']).round(2)
def describir_carrera(carrera):
"""Descripción estadística de una sola carrera."""
return describir_iaa(pd.DataFrame(df_iaa[carrera]))
# Serie con todos los IAA unidos
iaa_general = pd.DataFrame(pd.concat(iaa.values()))
# Descripción estadística
describir_iaa(iaa_general)
La descripción estadística nos provee la siguiente información, en orden:
Este análisis global no es muy útil por sí solo, por lo que proseguimos a comparar la distribución de los índices académicos por carrera. A continuación se crea y aplica una función para graficar un histograma de la distribución de los estudiantes por IAA en cada carrera:
def histograma_iaa(titulo,
lista_carreras = carreras.keys(),
frec_relativa = False,
divisiones = 10,
pos_leyenda = 'best'):
"""Grafica un histograma de la distribución de IAA
de las carreras dadas."""
# Preparar gráfica
fig, ax = plt.subplots()
ax.grid(axis='y', alpha=0.75)
ax.set_xlabel('IAA')
if frec_relativa:
ax.set_ylabel('Frecuencia relativa (%)')
else:
ax.set_ylabel('Frecuencia absoluta')
ax.set_title(titulo)
# Extraer índices de carreras seleccionadas
indices_carreras = [iaa[carrera] for carrera in lista_carreras]
# Preparar ponderaciones para el caso de frecuencia relativa
porcentajes = [carrera.apply(lambda x: 100.0/carrera.size)
for carrera in indices_carreras]
# Graficar histograma
n, bins, patches = ax.hist(
x = indices_carreras,
bins = divisiones,
range = (0,20),
weights = porcentajes if frec_relativa else None,
rwidth = 0.95,
alpha = 0.7,
label = lista_carreras)
ax.set_xticks(bins)
# Agregar leyenda y mostrar figura
ax.legend(loc = pos_leyenda)
plt.show()
def histograma_carrera(carrera, fr = False):
"""Grafica un histograma de la distribución de IAA
de una sola carrera."""
histograma_iaa(carreras[carrera],
lista_carreras = [carrera],
frec_relativa = fr)
histograma_iaa('Distribución de IAA por carrera',
pos_leyenda = 'upper left')
Como el histograma muestra magnitudes absolutas, lo principal que se nota es la enorme disparidad entre la cantidad de estudiantes de Ingeniería Marítima y el resto de las carreras. Para poder apreciar mejor las diferencias en la distribución de índices académicos usaremos frecuencias relativas, es decir, compararemos los porcentajes de estudiantes en cada intervalo de IAA por carrera:
histograma_iaa('Distribución de IAA por carrera',
frec_relativa = True)
Esta gráfica nos permite realizar algunas observaciones preliminares que comprobaremos más adelante:
Los histogramas anteriores son una manera simple de visualizar la distribución de los índices académicos en intervalos. Sin embargo, la manera en que se dividen los datos puede ofuscar la forma real de la distribución.
Para apreciar mejor la distribución real de los datos, tenemos que hallar una aproximación de su función de densidad de probabilidad, la cual desconocemos. Una técnica para lograr esto es la llamada kernel density estimation (KDE) (estimación de densidad del núcleo). En resumen, la técnica consiste en ponderar los datos usando una función positiva llamada kernel (núcleo) y un parámetro positivo llamado ancho de banda que determina la suavidad de la curva.
La librería Pandas provee una función para graficar la KDE de un conjunto de datos usando una función de núcleo Gaussiana y un ancho de banda calculado automáticamente. A continuación se crea y aplica una función para generar este tipo de gráficas para los IAA de las diferentes carreras:
def distribucion_iaa(titulo,
lista_carreras = carreras.keys(),
promedio = [],
mediana = [],
moda = []):
"""Grafica la distribución aproximada de IAA
de las carreras dadas."""
# Preparar gráfica
fig, axes = plt.subplots()
# Extraer índices de carreras seleccionadas
indices_carreras = df_iaa.filter(items = lista_carreras)
# Graficar distribución aproximada
indices_carreras.plot.kde(title = titulo,
grid = True,
legend = True,
xlim = (0, 20),
xticks = [2*x for x in range(11)],
ax = axes)
# Graficar medidas de tendencia central de cada carrera
for carrera in promedio:
mu = indices_carreras[carrera].mean()
axes.axvline(mu, color = 'r', linestyle = '--',
label = f'{carrera}: μ = {round(mu, 2)}')
for carrera in mediana:
md = indices_carreras[carrera].median()
axes.axvline(md, color = 'g', linestyle = '--',
label = f'{carrera}: md = {round(md, 2)}')
for carrera in moda:
mo = moda_max(indices_carreras[carrera])
axes.axvline(mo, color = 'b', linestyle = '--',
label = f'{carrera}: mo = {round(mo, 2)}')
axes.legend()
plt.show()
def distribucion_carrera(carrera):
"""Grafica la distribución aproximada de IAA
de una sola carrera."""
distribucion_iaa(carreras[carrera],
lista_carreras = [carrera],
promedio = [carrera],
mediana = [carrera],
moda = [carrera])
distribucion_iaa('Distribución de IAA por carrera')
A partir de esta gráfica podemos reiterar y corregir nuestras observaciones preliminares antes de comprobarlas en el análisis por carrera:
Lo primero que hacemos es mostrar una previsualización de la tabla de datos, con los cinco índices académicos más altos y los cinco más bajos:
dfs['ADM'].sort_values(by = 'IAA', ascending = False)
Segundo, una breve descripción estadística de esta tabla:
describir_carrera('ADM')
Luego, un histograma del número de estudiantes por IAA en diez subintervalos:
histograma_carrera('ADM')
Por último, la distribución aproximada de los IAA y sus medidas de tendencia central:
distribucion_carrera('ADM')
Lo primero que hacemos es mostrar una previsualización de la tabla de datos, con los cinco índices académicos más altos y los cinco más bajos:
dfs['INGAMB'].sort_values(by = 'IAA', ascending = False)
Segundo, una breve descripción estadística de esta tabla:
describir_carrera('INGAMB')
Luego, un histograma del número de estudiantes por IAA en diez subintervalos:
histograma_carrera('INGAMB')
Por último, la distribución aproximada de los IAA y sus medidas de tendencia central:
distribucion_carrera('INGAMB')
Lo primero que hacemos es mostrar una previsualización de la tabla de datos, con los cinco índices académicos más altos y los cinco más bajos:
dfs['INGINF'].sort_values(by = 'IAA', ascending = False)
Segundo, una breve descripción estadística de esta tabla:
describir_carrera('INGINF')
Luego, un histograma del número de estudiantes por IAA en diez subintervalos:
histograma_carrera('INGINF')
Por último, la distribución aproximada de los IAA y sus medidas de tendencia central:
distribucion_carrera('INGINF')
Lo primero que hacemos es mostrar una previsualización de la tabla de datos, con los cinco índices académicos más altos y los cinco más bajos:
dfs['INGM'].sort_values(by = 'IAA', ascending = False)
Segundo, una breve descripción estadística de esta tabla:
describir_carrera('INGM')
Luego, un histograma del número de estudiantes por IAA en diez subintervalos:
histograma_carrera('INGM')
Por último, la distribución aproximada de los IAA y sus medidas de tendencia central:
distribucion_carrera('INGM')
Lo primero que hacemos es mostrar una previsualización de la tabla de datos, con los cinco índices académicos más altos y los cinco más bajos:
dfs['TUR'].sort_values(by = 'IAA', ascending = False)
Segundo, una breve descripción estadística de esta tabla:
describir_carrera('TUR')
Luego, un histograma del número de estudiantes por IAA en diez subintervalos:
histograma_carrera('TUR')
Por último, la distribución aproximada de los IAA y sus medidas de tendencia central:
distribucion_carrera('TUR')
A continuación se presentan la distribución aproximada de los IAA por carrera (de nuevo) y una tabla con la descripción estadística de cada carrera:
distribucion_iaa('Distribución de IAA por carrera')
describir_iaa(df_iaa)
A partir de estos datos podemos realizar las siguientes observaciones:
Entonces, se puede concluir que: