Curso: Visualización para la analítica de datos - Componente Práctico Visualización de datos con R

Presentado por: Gabriel Elías Chanchí Golondrino

Grupo: 1

Código: 10301274

Presentado a: Prof. Dayana Alejandra Barrera Buitrago

Universidad Nacional Abierta y a Distancia

Fecha: 2 de Abril de 2025

Introducción

Este proyecto tiene como objetivo analizar el rendimiento académico de estudiantes en varias instituciones educativas de Colombia, considerando asignaturas, calificaciones, asistencias, y actividades realizadas. Mediante este análisis se busca identificar patrones de desempeño académico y explorar cómo factores como la asistencia o la carga de actividades influyen en los resultados. Además, se desarrollarán dashboards interactivos para facilitar la toma de decisiones por parte de administradores educativos.

Actividad 1: Preparación del Dataset y Análisis Exploratorio de Datos

1.1 Carga del dataset

Cargamos las librerías necesarias

options(repos = c(CRAN = "https://cloud.r-project.org"))
install.packages("ggplot2")
## package 'ggplot2' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
install.packages("dplyr")
## package 'dplyr' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
install.packages("fmsb")
## package 'fmsb' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
install.packages("wordcloud")
## package 'wordcloud' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
install.packages("waffle")
## package 'waffle' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
install.packages("readr")
## package 'readr' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
#Cargamos las librerías necesarias
library(readr)
library(ggplot2)
library(dplyr)

Cargamos ahora el dataframe desde el archivo

#Definimos la ruta del archivo
archivo<-"dataset fase 3.csv"
#Creamos el dataframe
df <- read.table(archivo, header = TRUE, sep = ",", fileEncoding = "latin1")
# Intento con read.table especificando un delimitador por espacio
head(df)
##   index id_estudiante   institucion asignatura calificacion_final asistencias
## 1     0             1        Inst B   Lenguaje                0.2          66
## 2     1             2 Institución D  Ciencias                 0.3          61
## 3     2             3     Instituto   Ciencias                4.5          72
## 4     3             4 Institucion E    Histori                0.7          65
## 5     4             5        Inst B   Ciencias                2.7          52
## 6     5             6 Institución C   Lenguaje                2.1          79
##   numero_actividades
## 1                 19
## 2                 18
## 3                 24
## 4                 23
## 5                 17
## 6                 14

1.2 Exploración inicial del dataset

El dataset está conformado por las siguientes columnas o atributos:

index: Corresponde al índice de cada registro del dataset.
id_estudiante: Corresponde al ídentificador asociado a cada estudiante.
intitucion: Corresponde al nombre de la institución donde estudia el estudiante.
asignatura: Corresponde a la asignatura que cursa el estudiante.
calificacion_final: Es la calificación que obtuvo el estudiante en la asignatura.
asistencias: Es el número de asistencias que tiene el estudiante en la asignatura que está cursando.
numero_actividades: Es el número de actividades desarrolladas por el estudiante.

1.3 Verificación de la calidad de los datos y análisis exploratorio

# Contar el número de valores NA por columna
na_por_columna <- colSums(is.na(df))

# Ver los resultados
na_por_columna
##              index      id_estudiante        institucion         asignatura 
##                  0                  0                  0                  0 
## calificacion_final        asistencias numero_actividades 
##                  2                  0                  0

Ahora procedemos a verificar visualmente los outliers de las columnas: “calificacion_final”, “asistencias” y “numero_actividades” mediante un diagrama de boxplot. Es posible apreciar como el atributo calificacion_final presenta outliers. Así mismo los atributos “asistencias” y “número de actividades” presentan inicialmente una buena distribución inicial y no presentan outliers.

#Creanos un boxplot para las tres columnas mencionadas
boxplot(df$calificacion_final, df$asistencias, df$numero_actividades,
        names = c("calificacion_final", "asistencias", "numero_actividades"),
        main = "Boxplot para identificar outliers",
        ylab = "Valor",
        col = c("lightblue", "lightgreen", "lightcoral"))
grid()

Apreciamos en detalle la columna calificacion_final, se aprecian además del outliers en 10, valores negativos y valores mayores a 5 por lo cual será necesario realizar procesos de imputación de datos.

#Creamos un boxplot para las tres columnas mencionadas
boxplot(df$calificacion_final,
        names = c("calificacion_final"),
        main = "Boxplot para identificar outliers - calificacion_final",
        ylab = "Valor",
        col = c("lightblue", "lightgreen", "lightcoral"))
grid()

Verificamos ahora si los nombres de las asignaturas son correctos, de tal modo que se aprecia que es necesario integrar nombres similares, como por ejemplo “Histori” e “Historia”, “” y “Arte”, “Lenguaje” y “Lenguage”, “Ciencias” y “Ciencias”.

#Creamos una tabla de frecuencia para la columna 'asignatura'
tabla_asignaturas <- table(df$asignatura)
#Imprimimos la tabla
tabla_asignaturas
## 
##                    Arte    Ciencias   Ciencias      Histori    Historia 
##         122         126         130         117         110         129 
##    Lenguage    Lenguaje Matematicas Matemáticas 
##         118         114         121         113

Verificamos ahora si los nombres de las instituciones son correctos, de tal manera que se aprecia que hay nombres que se pueden integrar, por ejemplo “A” e “Institución A”, “B” e “Institución B”. Así mismo como hay un nombre vacío es posible asignarle el nombre “Institución F”.

#Creamos una tabla de frecuencia para la columna 'asignatura'
tabla_institucion <- table(df$institucion)
#Verificamos la frecuencia de 'Ciencias '
tabla_institucion
## 
##                           A        Inst B Institución A Institución B 
##           128           122           136           158           118 
## Institución C Institución D Institucion E     Instituto 
##           144           118           142           134

1.4 Limpieza del dataset y selección de variables clave

Dado que hay valores negativos y mayores a 5, llegando hasta 6, se realizará la siguiente estrategia: - Los valores negativos se reemplazarán por cero. - Los valores entre 5 y 6 se reemplazarán por cinco.

#Modificamos la columna calificacion_final de acuerdo a las condiciones mencionadas
df$calificacion_final[df$calificacion_final < 0] <- 0  #Valores negativos a 0
df$calificacion_final[df$calificacion_final > 5 & df$calificacion_final <= 6] <- 5  #Valores entre 5 y 6 a 5
#Verificamos el resultado
head(df)
##   index id_estudiante   institucion asignatura calificacion_final asistencias
## 1     0             1        Inst B   Lenguaje                0.2          66
## 2     1             2 Institución D  Ciencias                 0.3          61
## 3     2             3     Instituto   Ciencias                4.5          72
## 4     3             4 Institucion E    Histori                0.7          65
## 5     4             5        Inst B   Ciencias                2.7          52
## 6     5             6 Institución C   Lenguaje                2.1          79
##   numero_actividades
## 1                 19
## 2                 18
## 3                 24
## 4                 23
## 5                 17
## 6                 14

Verificamos los cambios con un diagrama de boxplot, apreciando en este caso que solo resta corregir el outlier y los valores NA.

#Creanos un boxplot para las tres columnas mencionadas
boxplot(df$calificacion_final,
        names = c("calificacion_final"),
        main = "Boxplot para identificar outliers",
        ylab = "Valor",
        col = c("lightblue", "lightgreen", "lightcoral"))
grid()

En primer lugar antes de hacer la imputación de los valores NA, convertimos el outlier existente a valor NA.

#Calculamos los cuartiles y el IQR para la columna calificacion_final
Q1 <- quantile(df$calificacion_final, 0.25,na.rm = TRUE)  # Primer cuartil
Q3 <- quantile(df$calificacion_final, 0.75,na.rm = TRUE)  # Tercer cuartil
IQR <- Q3 - Q1  #Rango intercuartílico

#Calculamos los límites para los outliers
limite_inferior <- Q1 - 1.5 * IQR
limite_superior <- Q3 + 1.5 * IQR

#Reemplazamos el outlier por un valor NA
df$calificacion_final[df$calificacion_final < limite_inferior | df$calificacion_final > limite_superior] <- NA

Ahora procedemos con el cálculo de la media de la columna “calificacion_final” para realizar la imputación de los valores NA por dicho valor.

#Calculamos la media de la columna calificacion_final sin contar los NA
media_cf <- mean(df$calificacion_final, na.rm = TRUE)

#Reemplazamos los NA en la columna calificacion_final con la media calculada
df$calificacion_final[is.na(df$calificacion_final)] <- media_cf
head(df)
##   index id_estudiante   institucion asignatura calificacion_final asistencias
## 1     0             1        Inst B   Lenguaje                0.2          66
## 2     1             2 Institución D  Ciencias                 0.3          61
## 3     2             3     Instituto   Ciencias                4.5          72
## 4     3             4 Institucion E    Histori                0.7          65
## 5     4             5        Inst B   Ciencias                2.7          52
## 6     5             6 Institución C   Lenguaje                2.1          79
##   numero_actividades
## 1                 19
## 2                 18
## 3                 24
## 4                 23
## 5                 17
## 6                 14

Verificamos ahora que se haya realizado la imputación

#Contamos el número de valores NA por columna
na_por_columna <- colSums(is.na(df))

#Vemos los resultados
na_por_columna
##              index      id_estudiante        institucion         asignatura 
##                  0                  0                  0                  0 
## calificacion_final        asistencias numero_actividades 
##                  0                  0                  0

Realizamos el diagrama de boxplot para verificar gráficamente la imputación y la distribución de los valores en la columna calificacion_final. Se aprecia que la variable calificacion_final está lista para proceder con el análisis.

#Creanos un boxplot para las tres columnas mencionadas
boxplot(df$calificacion_final,
        names = c("calificacion_final"),
        main = "Boxplot para identificar outliers -  calificacion_final",
        ylab = "Valor",
        col = c("lightblue", "lightgreen", "lightcoral"))
grid()

Ahora procedemos con el ajuste de los nombres de las instancias en la columna asignaturas.

#Corregimos los errores en cada asignatura
df$asignatura <- gsub("Matematicas", "Matemáticas", df$asignatura)
df$asignatura <- gsub("\\bHistori\\b", "Historia", df$asignatura)
df$asignatura <- gsub("Ciencias ", "Ciencias", df$asignatura)
df$asignatura <- gsub("Lenguage", "Lenguaje", df$asignatura)
df$asignatura <- ifelse(df$asignatura == "", "Arte", df$asignatura)
head(df)
##   index id_estudiante   institucion asignatura calificacion_final asistencias
## 1     0             1        Inst B   Lenguaje                0.2          66
## 2     1             2 Institución D   Ciencias                0.3          61
## 3     2             3     Instituto   Ciencias                4.5          72
## 4     3             4 Institucion E   Historia                0.7          65
## 5     4             5        Inst B   Ciencias                2.7          52
## 6     5             6 Institución C   Lenguaje                2.1          79
##   numero_actividades
## 1                 19
## 2                 18
## 3                 24
## 4                 23
## 5                 17
## 6                 14

Verificamos ahora que se hayan ajustados los nombres de las asignaturas y se aprecia que los ajustes se realizaron de manera correcta.

# Crear una tabla de frecuencia para la columna 'asignatura'
tabla_asignaturas <- table(df$asignatura)

# Verificar la frecuencia de 'Ciencias '
tabla_asignaturas
## 
##        Arte    Ciencias    Historia    Lenguaje Matemáticas 
##         248         247         239         232         234

Ahora procedemos con el ajuste de los nombres de las instituciones

#Corregimos los errores en cada institución
df$institucion <- gsub("(?<!Institución )A", "Institución A", df$institucion, perl = TRUE)
df$institucion <- gsub("Inst B", "Institución B", df$institucion)
df$institucion <- gsub("Institucion E", "Institución E", df$institucion)
df$institucion <- ifelse(df$institucion == "", "Institución F", df$institucion)
head(df)
##   index id_estudiante   institucion asignatura calificacion_final asistencias
## 1     0             1 Institución B   Lenguaje                0.2          66
## 2     1             2 Institución D   Ciencias                0.3          61
## 3     2             3     Instituto   Ciencias                4.5          72
## 4     3             4 Institución E   Historia                0.7          65
## 5     4             5 Institución B   Ciencias                2.7          52
## 6     5             6 Institución C   Lenguaje                2.1          79
##   numero_actividades
## 1                 19
## 2                 18
## 3                 24
## 4                 23
## 5                 17
## 6                 14

Vertificamos ahora que se hayan realizado los ajustes

# Obtener valores únicos de la columna 'asignatura'
institucion_unicas <- unique(df$institucion)

# Mostrar el resultado
print(institucion_unicas)
## [1] "Institución B" "Institución D" "Instituto"     "Institución E"
## [5] "Institución C" "Institución F" "Institución A"

Ahora procedemos con la asignación de coordenadas de latitud y longitud, así como la ciudad correspondiente a las diferentes instituciones. En este caso se agregaron a las instituciones en orden las coordenadas correspondientes a las siguientes ciudades: Bogotá, Tunja, Armenia, Pereira, Manizales, Ibagué, Cali.

library(dplyr)
df <- df %>%
  mutate(
    latitud = case_when(
      institucion == "Institución A" ~ 4.7110,
      institucion == "Institución B" ~ 5.5353,
      institucion == "Institución C" ~ 4.5339,
      institucion == "Institución D" ~ 4.8087,
      institucion == "Institución E" ~ 5.0689,
      institucion == "Institución F" ~ 4.4389,
      institucion == "Instituto" ~ 3.4516,
      TRUE ~ NA_real_
    ),
    longitud = case_when(
      institucion == "Institución A" ~ -74.0721,
      institucion == "Institución B" ~ -73.3678,
      institucion == "Institución C" ~ -75.6811,
      institucion == "Institución D" ~ -75.6906,
      institucion == "Institución E" ~ -75.5174,
      institucion == "Institución F" ~ -75.2322,
      institucion == "Instituto" ~ -76.5319,
      TRUE ~ NA_real_
    ),
    ciudad = case_when(
      institucion == "Institución A" ~ "Bogotá",
      institucion == "Institución B" ~ "Tunja",
      institucion == "Institución C" ~ "Armenia",
      institucion == "Institución D" ~ "Pereira",
      institucion == "Institución E" ~ "Manizales",
      institucion == "Institución F" ~ "Ibagué",
      institucion == "Instituto" ~ "Cali",
      TRUE ~ NA_character_
    )
  )
head(df)
##   index id_estudiante   institucion asignatura calificacion_final asistencias
## 1     0             1 Institución B   Lenguaje                0.2          66
## 2     1             2 Institución D   Ciencias                0.3          61
## 3     2             3     Instituto   Ciencias                4.5          72
## 4     3             4 Institución E   Historia                0.7          65
## 5     4             5 Institución B   Ciencias                2.7          52
## 6     5             6 Institución C   Lenguaje                2.1          79
##   numero_actividades latitud longitud    ciudad
## 1                 19  5.5353 -73.3678     Tunja
## 2                 18  4.8087 -75.6906   Pereira
## 3                 24  3.4516 -76.5319      Cali
## 4                 23  5.0689 -75.5174 Manizales
## 5                 17  5.5353 -73.3678     Tunja
## 6                 14  4.5339 -75.6811   Armenia

A nivel de la selección de variables, se tomaran en este caso las variables: institucion, asignatura, calificacion_final, asistencias, numero_actividades, latitud, longitud y ciudad, descartando las dos que corresponden a los índices del estudiante y del registro.

#Seleccionamos solo las columnas que te interesan
df_sel <- df[, c("institucion","asignatura","calificacion_final", "asistencias", "numero_actividades", "latitud", "longitud", "ciudad")]
#Verificamos el resultado
head(df_sel)
##     institucion asignatura calificacion_final asistencias numero_actividades
## 1 Institución B   Lenguaje                0.2          66                 19
## 2 Institución D   Ciencias                0.3          61                 18
## 3     Instituto   Ciencias                4.5          72                 24
## 4 Institución E   Historia                0.7          65                 23
## 5 Institución B   Ciencias                2.7          52                 17
## 6 Institución C   Lenguaje                2.1          79                 14
##   latitud longitud    ciudad
## 1  5.5353 -73.3678     Tunja
## 2  4.8087 -75.6906   Pereira
## 3  3.4516 -76.5319      Cali
## 4  5.0689 -75.5174 Manizales
## 5  5.5353 -73.3678     Tunja
## 6  4.5339 -75.6811   Armenia

1.5 Utiliza funciones en R como summary() para describir las calificaciones y asistencias.

Al aplicar el comunado summary() sobre las columnas de interés, es posible obtener las siguientes conclusiones: - Las calificaciones oscilan entre 0 y 5 con una media de 2.503 y buena distribución de los datos, dado que el primer y el tercer cuartíl están a una distancia de 1.2 de la mediana. - Las asistencias varían entre 50 y 99, con una media de 74.74 con una leve mayor cercanía al cuartil Q1 que al cuartil Q3. - El número de actividades varía entre 5 y 29, con una media de 17.49 y una leve mayor cercanía al cuartil Q3 que al cuartil Q1.

#Usamos summary() con las columnas seleccionadas
summary(df_sel[, c("calificacion_final", "asistencias", "numero_actividades")])
##  calificacion_final  asistencias    numero_actividades
##  Min.   :0.000      Min.   :50.00   Min.   : 5.00     
##  1st Qu.:1.300      1st Qu.:62.00   1st Qu.:11.00     
##  Median :2.500      Median :74.00   Median :18.00     
##  Mean   :2.503      Mean   :74.74   Mean   :17.49     
##  3rd Qu.:3.700      3rd Qu.:87.00   3rd Qu.:24.00     
##  Max.   :5.000      Max.   :99.00   Max.   :29.00

Ahora creamos un gráfico que relaciona las 3 variables numéricas, de tal manera que se aprecia que no existe una relación lineal entre ellas, apreciandose dispersión entre los valores de cada variable.

# Creamos un pairplot para las tres columnas específicas
pairs(df_sel[, c("calificacion_final", "asistencias", "numero_actividades")],
      main = "Matriz de Dispersión",
      pch = 19,  # Tipo de punto
      col = "blue")  # Color de los puntos

Actividad 2: Visualización de Datos con ggplot2

2.1 Desarrolla gráficos de barras, histogramas, gráficos de dispersión y gráficos de líneas para visualizar tendencias en las calificaciones y asistencias.

En primer lugar hacemos un gráfico de barras con las notas promedio por área del conocimiento. Se puede apreciar que todas las asignaturas tienen en promedio una calificación inferior a 3, en donde el área de Lenguaje obtuvo el promedio más alto.

library(ggplot2)
#Calculamos la calificación promedio por asignatura
df_prom <- df_sel %>%
  group_by(asignatura) %>%
  summarize(calificacion_promedio = mean(calificacion_final, na.rm = TRUE))

# Creamos el gráfico de barras con los valores encima de cada barra
ggplot(df_prom, aes(x = reorder(asignatura, calificacion_promedio), y = calificacion_promedio)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  geom_text(aes(label = round(calificacion_promedio, 2)), vjust = -0.3, size = 4) +  # Añadimos los valores encima de las barras
  labs(title = "Calificación Promedio por Asignatura",
       x = "Asignatura",
       y = "Calificación Promedio") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 14),  # Tamaño de la letra en el eje X
        axis.text.y = element_text(size = 14),  # Tamaño de la letra en el eje Y
        plot.title = element_text(size = 16))  # Tamaño del título

Ahora procedemos con la generación de un diagrama de barras que muestra el promedio general de las asignaturas por institución. Se aprecia que los estudiantes de todas las intituciones tienen un promedio inferior a 3, sin embargo la institución cuyos estudiantes tienen el promedio más alto es “Instituto” con un 2.63.

#Calculamos el calificación promedio por institución
df_prom_institucion <- df_sel %>%
  group_by(institucion) %>%
  summarize(calificacion_promedio = mean(calificacion_final, na.rm = TRUE))

#Creamos el gráfico de barras
ggplot(df_prom_institucion, aes(x = reorder(institucion, calificacion_promedio), y = calificacion_promedio)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  geom_text(aes(label = round(calificacion_promedio, 2)), vjust = -0.3, size = 4) +  # Añadimos los valores encima de las barras
  labs(title = "Calificación Promedio por Institución",
       x = "Institución",
       y = "Calificación Promedio") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 14),  # Tamaño de la letra en el eje X
        axis.text.y = element_text(size = 14),  # Tamaño de la letra en el eje Y
        plot.title = element_text(size = 16))  # Tamaño del título

Ahora procedemos a visualizar el promedio de asistencias por curso. Se aprecia que la asignatura que más asistencias tiene es Historia con 75.94, mientras que la asignatura que tiene menos asistencias es Arte con 73.52.

# Calculamos el promedio de asistencias por asignatura
df_prom_asistencias <- df_sel %>%
  group_by(asignatura) %>%
  summarize(promedio_asistencias = mean(asistencias, na.rm = TRUE))

# Creamos el gráfico de barras
ggplot(df_prom_asistencias, aes(x = reorder(asignatura, promedio_asistencias), y = promedio_asistencias)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  geom_text(aes(label = round(promedio_asistencias, 2)), vjust = -0.3, size = 4) +  # Añadimos los valores encima de las barras
  labs(title = "Promedio de Asistencias por Asignatura",
       x = "Asignatura",
       y = "Promedio de Asistencias") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 14),  # Tamaño de la letra en el eje X
        axis.text.y = element_text(size = 14),  # Tamaño de la letra en el eje Y
        plot.title = element_text(size = 16))  # Tamaño del título

Ahora procedemos a obtener una gráfica que muestra el promedio de asistencias por institución, observando en este caso que la institución F es la que contó con el mayor promedio de asistencias con 76.64, mientras que la institución E es la que contó con el menor promedio de asistencias con 73.82.

# Calculamos el promedio de asistencias por institución
df_prom_asistencias_institucion <- df_sel %>%
  group_by(institucion) %>%
  summarize(promedio_asistencias = mean(asistencias, na.rm = TRUE))

# Creamos el gráfico de barras
ggplot(df_prom_asistencias_institucion, aes(x = reorder(institucion, promedio_asistencias), y = promedio_asistencias)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  geom_text(aes(label = round(promedio_asistencias, 2)), vjust = -0.3, size = 4) +  # Añadimos los valores encima de las barras
  labs(title = "Promedio de Asistencias por Institución",
       x = "Institución",
       y = "Promedio de Asistencias") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 14),  # Tamaño de la letra en el eje X
        axis.text.y = element_text(size = 14),  # Tamaño de la letra en el eje Y
        plot.title = element_text(size = 16))  # Tamaño del título

Creamos ahora un histograma para el atributo calificacion_final. La distribución de los datos en calificacion_final presenta una forma relativamente uniforme, con una ligera concentración en el rango intermedio. En este sentido, ña curva de densidad indica que la mayoría de las calificaciones están distribuidas de manera equilibrada, sin grandes picos en los extremos, aunque existe cierta variabilidad hacia las calificaciones más bajas o altas.

ggplot(df_sel, aes(x = calificacion_final)) +
  geom_histogram(binwidth = 0.5, fill = "skyblue", color = "black", alpha = 0.7, aes(y = ..density..)) +  # Histograma con densidad
  geom_density(color = "red", size = 1) +  # Añadir la curva de densidad
  labs(title = "Histograma de Calificación Final con Curva de Distribución",
       x = "Calificación Final",
       y = "Densidad") +
  theme_minimal()

Ahora creamos el histograma para el atributo asistencias. La distribución de las asistencias es bastante dispersa, con una concentración ligera en los valores intermedios (60-80 asistencias). Aunque la mayoría de los estudiantes tienen un número de asistencias en el rango medio, también existen algunas observaciones con valores más altos o bajos, pero con menor frecuencia.

ggplot(df_sel, aes(x = asistencias)) +
  geom_histogram(binwidth = 0.5, fill = "skyblue", color = "black", alpha = 0.7, aes(y = ..density..)) +  # Histograma con densidad
  geom_density(color = "red", size = 1) +  # Añadir la curva de densidad
  labs(title = "Histograma de Asistencias con Curva de Distribución",
       x = "Asistencias",
       y = "Densidad") +
  theme_minimal()

Ahora procedemos con la creación de un diagrama de dispersión entre las calificaciones finales de Matemáticas y las asistencias en esta asignatura, obteniendo como se mencionó previamente que no existe una relación lineal.

#Filtramos los datos para seleccionar solo las filas correspondientes a 'Matemáticas' y calificaciones mayores a 4
df_matematicas <- df_sel %>%
  filter(asignatura == "Matemáticas")

#Creamos el diagrama de dispersión entre calificación final y asistencias para Matemáticas (solo notas > 4)
ggplot(df_matematicas, aes(x = calificacion_final, y = asistencias)) +
  geom_point(color = "skyblue") +  # Crear los puntos del gráfico
  labs(title = "Diagrama de Dispersión: Calificaciones vs Asistencias en Matemáticas (Notas > 4)",
       x = "Calificación Final",
       y = "Asistencias") +
  theme_minimal()

Ahora obtenemos un diagrama de dispersión entre el promedio de la calificación final de Matemáticas en cada institución y el promedio de calificación final de Lenguaje en estas mismas instituciones. Se aprecia que no existe un buen ajuste para el modelo de regresión lineal, dado que el R2 es de 0.002.

library(tidyr)
#Filtramos los datos para seleccionar solo las asignaturas 'Matemáticas' y 'Lenguaje'
df_matematicas_lenguaje_institucion <- df_sel %>%
  filter(asignatura %in% c("Matemáticas", "Lenguaje"))

#Calculamos el promedio de calificación por institución y asignatura
df_prom_institucion <- df_matematicas_lenguaje_institucion %>%
  group_by(institucion, asignatura) %>%
  summarize(calificacion_promedio = mean(calificacion_final, na.rm = TRUE))

#Reorganizamos los datos para que tengamos una columna por cada asignatura
df_prom_institucion_wide <- df_prom_institucion %>%
  pivot_wider(names_from = asignatura, values_from = calificacion_promedio)

#Ajustamos el modelo de regresión lineal
modelo_regresion <- lm(`Lenguaje` ~ `Matemáticas`, data = df_prom_institucion_wide)

#Extramos el valor de R²
r2_value <- summary(modelo_regresion)$r.squared

#Creamos el diagrama de dispersión con la línea de regresión y el valor de R²
ggplot(df_prom_institucion_wide, aes(x = `Matemáticas`, y = `Lenguaje`, color = institucion)) +
  geom_point() +  # Crear los puntos del gráfico
  geom_smooth(method = "lm", se = FALSE, color = "red") +  # Añadir la línea de regresión en rojo
  geom_text(aes(x = max(`Matemáticas`), y = max(`Lenguaje`), label = paste("R² =", round(r2_value, 3))),
            color = "black", size = 5, hjust = 1) +  # Mostrar R² sobre la línea
  labs(title = "Diagrama de Dispersión: Promedio de Calificaciones de Matemáticas vs Lenguaje por Institución",
       x = "Promedio de Calificación Matemáticas",
       y = "Promedio de Calificación Lenguaje") +
  theme_minimal() +
  scale_color_manual(values = RColorBrewer::brewer.pal(length(unique(df_prom_institucion_wide$institucion)), "Set3"))

Repetimos el mismo proceso para las áreas de Matemáticas y Ciencias, obteniendo el mismo ajuste que con el área de Lenguaje.

#Filtramos los datos para seleccionar solo las asignaturas 'Matemáticas' y 'Ciencias'
df_matematicas_ciencias_institucion <- df_sel %>%
  filter(asignatura %in% c("Matemáticas", "Ciencias"))

#Calculamos el promedio de calificación por institución y asignatura
df_prom_institucion <- df_matematicas_ciencias_institucion %>%
  group_by(institucion, asignatura) %>%
  summarize(calificacion_promedio = mean(calificacion_final, na.rm = TRUE))

#Reorganizamos los datos para que tengamos una columna por cada asignatura
df_prom_institucion_wide <- df_prom_institucion %>%
  pivot_wider(names_from = asignatura, values_from = calificacion_promedio)

#Ajustamos el modelo de regresión lineal
modelo_regresion <- lm(`Ciencias` ~ `Matemáticas`, data = df_prom_institucion_wide)

#Extraemos el valor de R²
r2_value <- summary(modelo_regresion)$r.squared

#Creamos el diagrama de dispersión con la línea de regresión y el valor de R²
ggplot(df_prom_institucion_wide, aes(x = `Matemáticas`, y = `Ciencias`, color = institucion)) +
  geom_point() +  # Crear los puntos del gráfico
  geom_smooth(method = "lm", se = FALSE, color = "red") +  # Añadir la línea de regresión en rojo
  geom_text(aes(x = max(`Matemáticas`), y = max(`Ciencias`), label = paste("R² =", round(r2_value, 3))),
            color = "black", size = 5, hjust = 1) +  # Mostrar R² sobre la línea
  labs(title = "Diagrama de Dispersión: Promedio de Calificaciones de Matemáticas vs Ciencias por Institución",
       x = "Promedio de Calificación Matemáticas",
       y = "Promedio de Calificación Ciencias") +
  theme_minimal() +
  scale_color_manual(values = RColorBrewer::brewer.pal(length(unique(df_prom_institucion_wide$institucion)), "Set3"))

2.2 Crea gráficos de radar, nubes de palabras, gráficos de waffle y boxplots para analizar patrones específicos por asignatura e institución.

En primer lugar creamos un diagrama de radar para comparar el promedio obtenido en las asignaturas de Matemáticas, Lenguaje y Ciencias en las institucionas A, B y C.

En el gráfico de radar, la Institución A (representada por la línea roja) muestra el mejor rendimiento en todas las asignaturas, con los valores más altos en Ciencias, Matemáticas y Lenguaje. La Institución C (línea azul) tiene el rendimiento más bajo en Matemáticas y Lenguaje. La Institución B (línea verde) tiene un rendimiento intermedio, superando a la Institución C pero no alcanzando los niveles de la Institución A.

# Cargar librerías necesarias
library(fmsb)
library(dplyr)

#Filtramos los datos de tu dataframe para las asignaturas 'Matemáticas', 'Ciencias' y 'Lenguaje'
df_instituciones <- df_sel %>%
  filter(institucion %in% c("Institución A", "Institución B", "Institución C"), asignatura %in% c("Matemáticas", "Ciencias", "Lenguaje"))

#Calculamos el promedio de calificación por institución y asignatura
df_prom_institucion <- df_instituciones %>%
  group_by(institucion, asignatura) %>%
  summarize(calificacion_promedio = mean(calificacion_final, na.rm = TRUE), .groups = "drop")  # Agregamos .groups = "drop"

#Reorganizamos los datos para que tengamos una columna por cada asignatura
df_prom_institucion_wide <- df_prom_institucion %>%
  pivot_wider(names_from = asignatura, values_from = calificacion_promedio)

#Asegurarnos de que solo las calificaciones son numéricas (eliminamos la columna 'institucion')
df_prom_institucion_wide_numeric <- df_prom_institucion_wide %>%
  select(-institucion)  # Excluimos la columna 'institucion' para el gráfico de radar

#Normalizamos los valores de 0 a 10 (si es necesario)
max_value <- max(df_prom_institucion_wide_numeric, na.rm = TRUE)
df_prom_institucion_wide_numeric <- df_prom_institucion_wide_numeric / max_value * 10  # Normalizar

#Añadimos las filas necesarias para el gráfico (máximo y mínimo)
df_radar <- rbind(rep(10, ncol(df_prom_institucion_wide_numeric)),  # valor máximo
                  rep(0, ncol(df_prom_institucion_wide_numeric)),   # valor mínimo
                  df_prom_institucion_wide_numeric)  # valores de las instituciones

#Asignamos los nombres de las columnas para las asignaturas
colnames(df_radar) <- colnames(df_prom_institucion_wide_numeric)

#Asignamos los nombres de las filas para las instituciones
rownames(df_radar) <- c("Max", "Min", as.character(df_prom_institucion_wide$institucion))

# Creamos el gráfico de radar con ajustes en la escala
radarchart(df_radar, axistype = 1,
           pcol = rainbow(nrow(df_radar)-2),  # Colores para cada institución
           pfcol = rainbow(nrow(df_radar)-2, alpha = 0.3),  # Colores con transparencia
           plwd = 4,  # Grosor de las líneas
           cglcol = "black", cglwd = 1.5,  # Línea de telaraña más destacada
           axislabcol = "black", caxislabels = seq(0, 10, 2),  # Aseguramos un rango de 0 a 10
           vlcex = 0.8, title = "Promedio de Calificaciones por Institución (Matemáticas, Lenguaje y Ciencias)",
           cglty = 1
)

#Agregamos la leyenda para las instituciones
legend("topright", legend = as.character(df_prom_institucion_wide$institucion),
       fill = rainbow(nrow(df_radar)-2), border = "black", bty = "n", cex = 1, pt.cex = 2, ncol = 1)

Ahora procedemos a crear un diagrama de nube de palabras con el contenido de la columna asignatura. En el gráfico de nube de palabras, se puede observar que las asignaturas con mayor frecuencia en el dataset son Ciencias y Matemáticas, que se muestran en mayor tamaño, indicando que son las más mencionadas. Las asignaturas como Historia, Arte y Lenguaje aparecen en menor tamaño, lo que sugiere que tienen una menor frecuencia en los datos.

# Cargar las librerías necesarias
library(wordcloud)
library(dplyr)

# Extraer las asignaturas y contar la frecuencia de cada una
asignaturas_freq <- df_sel %>%
  count(asignatura) %>%
  arrange(desc(n))

# Crear el diagrama de nube de palabras
wordcloud(words = asignaturas_freq$asignatura,
          freq = asignaturas_freq$n,
          min.freq = 1,
          scale = c(3,0.5),
          colors = brewer.pal(8, "Dark2"))

Ahora procedemos a hacer la nube de palabras con la columna ciudad. La nube de palabras muestra que Bogotá y Tunja concentran la mayor cantidad de estudiantes, mientras que Ibagué y Cali tienen menor representación. Manizales, Armenia y Pereira se ubican en un nivel intermedio, reflejando una distribución desigual del número de estudiantes por ciudad.

#Contamos la frecuencia de cada ciudad
ciudades_freq <- df_sel %>%
  count(ciudad) %>%
  arrange(desc(n))

#Creamos el diagrama de nube de palabras por ciudad
wordcloud(words = ciudades_freq$ciudad,
          freq = ciudades_freq$n,
          min.freq = 1,
          scale = c(3, 0.5),
          colors = brewer.pal(8, "Dark2"))

Ahora procedemos con la creación de un diagrama de wafle con la relación de estudiantes de las diferentes instituciones. El diagrama de waffle muestra la distribución de estudiantes por institución. De este modo, las instituciones con más estudiantes ocupan más espacio en la cuadrícula, destacándose Institución A y Institución B. La Institución C tiene menos representación.

#Cargamos las librerías necesarias
library(ggplot2)
library(dplyr)
library(tidyr)

#Contamos el número de estudiantes por institución
df_estudiantes_por_institucion <- df_sel %>%
  count(institucion)  # Cuenta el número de estudiantes por institución

#Generamos un dataframe para el gráfico de waffle
total_students <- sum(df_estudiantes_por_institucion$n)  # Total de estudiantes

#Repetimos los valores para crear las filas del gráfico de waffle
df_waffle_data <- df_estudiantes_por_institucion %>%
  uncount(weights = n) %>%
  mutate(id = row_number(),
         x = (id - 1) %% 10 + 1,  # Definimos la posición en el gráfico (10 columnas)
         y = 10 - (id - 1) %/% 10)  # Definimos la posición en las filas

#Creamos el gráfico de waffle con ggplot2
ggplot(df_waffle_data, aes(x = x, y = y, fill = institucion)) +
  geom_tile(color = "white") +  # Usamos geom_tile para dibujar los cuadros
  scale_fill_manual(values = brewer.pal(n = nrow(df_estudiantes_por_institucion), name = "Set3")) +  # Colores
  theme_void() +  # Removemos los ejes
  labs(title = "Número de Estudiantes por Institución") +
  theme(legend.position = "bottom")  # Coloca la leyenda en la parte inferior

Ahora realizamos un diagrama de boxplot que muestre la distribución de las notas finales por asignatura. Se puede apreciar que las calificaciones finales están distribuidas de manera similar entre las diferentes asignaturas, con los valores centrales (mediana) bastante alineados. Sin embargo, algunas asignaturas como Historia y Arte presentan una mayor dispersión, lo que sugiere una variabilidad mayor en las calificaciones, mientras que otras, como Matemáticas y Lenguaje, tienen una distribución más concentrada alrededor de la mediana.

#Cargamos la librería necesaria
library(ggplot2)

#Creamos el boxplot de calificaciones por asignatura
ggplot(df_sel, aes(x = asignatura, y = calificacion_final)) +
  geom_boxplot(fill = "skyblue", color = "black") +  # Boxplot con color de relleno
  labs(title = "Boxplot de Calificaciones por Asignatura",
       x = "Asignatura",
       y = "Calificación Final") +
  theme_minimal()  # Estilo minimalista

Ahora creamos un diagrama similar pero esta vez con la distribución de las asistencias por asignatura. El boxplot muestra que las asistencias son bastante consistentes entre las diferentes asignaturas, con un rango de valores similar para todas ellas. La mayoría de las asignaturas se distribuyen entre 70 y 90 asistencias, con una mediana cerca de 80. Se puede concluir que las asistencias no varían excesivamente dentro de cada asignatura.

#Creamos el boxplot de asistencias por asignatura
ggplot(df_sel, aes(x = asignatura, y = asistencias)) +
  geom_boxplot(fill = "lightgreen", color = "black") +  #Boxplot con color de relleno
  labs(title = "Boxplot de Asistencias por Asignatura",
       x = "Asignatura",
       y = "Asistencias") +
  theme_minimal()

2.3 Desarrolla mapas con las coordenadas de las instituciones educativas.

install.packages("leaflet")     # Solo si no lo tienes
## package 'leaflet' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\GABRI\AppData\Local\Temp\RtmpUxbMYX\downloaded_packages
library(leaflet)

Creamos ahora el mapa con la librería leaflet, en el cual se muestra las ciudades en las que se encuentran las instituciones del dataset.

library(dplyr)
#Obtenemos una fila única por institución
instituciones_unicas <- df %>%
  distinct(institucion, latitud, longitud, ciudad)

#Creamos el mapa
mapa<-leaflet(data = instituciones_unicas) %>%
  addTiles() %>%
  addMarkers(~longitud, ~latitud, popup = ~paste("<strong>", institucion, "</strong><br/>Ciudad: ", ciudad)) %>%
  addProviderTiles(providers$CartoDB.Positron)

mapa

2.4 Analiza patrones geográficos de desempeño académico.

Ahora agregamos en cada marcador del mapa el promedio en la columna de calificacion_final, obteniendo en este caso que la ciudad de Bogotá es la que tiene el promedio más alto con 2.59, mientras que la ciudad con el promedio más bajo es Armenia con 2.27.

library(dplyr)
library(leaflet)

# Calculamos promedio por institución
promedios <- df %>%
  group_by(institucion) %>%
  summarize(
    calificacion_promedio = round(mean(calificacion_final, na.rm = TRUE), 2),
    .groups = 'drop'
  )

# Unimos con coordenadas únicas
instituciones_mapa <- df %>%
  distinct(institucion, latitud, longitud, ciudad) %>%
  left_join(promedios, by = "institucion")

# 3. Crear el mapa con el promedio en el popup
mapa <- leaflet(data = instituciones_mapa) %>%
  addTiles() %>%
  addMarkers(
    ~longitud, ~latitud,
    popup = ~paste0(
      "<strong>", institucion, "</strong><br/>",
      "Ciudad: ", ciudad, "<br/>",
      "Promedio: ", calificacion_promedio
    )
  ) %>%
  addProviderTiles(providers$CartoDB.Positron)

#Mostramos mapa
mapa

3. Dashboard Interactivo con Shiny

Mediante Shiny se desarrolló un dashboard interactivo el cual se publicó en los repositorios de Shiny en el siguiente enlace:

Dashboard Interactivo

Note that the echo = FALSE parameter was added to the code chunk to prevent printing of the R code that generated the plot.