edepaVIII EscueladeVerano Escuela de Matemática Modalidad Presencial

Parte 2. Creando exámenes masivos aleatorios con exams

Contenido del taller
  1. Instalación de librerías
  2. Visualización de ejemplos
  3. Generando ejercicios
    • Paso 1. Estructura básica de un ejercicio aleatorio
    • Paso 2. Plantilla básica para compilar pdflatex
    • Paso 3. Compilación de ejercicios
  4. Generando exámenes
  5. Práctica
  6. Otras tecnologías avanzadas
Crear .rmdGenerar .pdf

En el comprimido proyecto.zip están todos los archivos para un mejor seguimiento durante el taller. Escoge alguna de las siguientes alternativas de trabajo:

  • Descarga y sube el archivo comprimido a un New RStudio Project en Posit Cloud (recomendada para quienes se inician en la programación).
  • Descarga y extrae el archivo comprimido en el directorio proyecto, y trabaja en local desde tu computadora (recomendada para quienes tenga familiaridad con la programación en R).
  • Crea los archivos (en local o en la nube) conforme avanza el taller (recomendada para quienes programan en R y LaTeX).

Si deseas mayor información sobre la librería exams(), consulta aquí.


1. Instalación de librerías

El archivo librerias.R contiene las librerías básicas para configurar nuestros exámenes: exams(), tinytex(). Además, para que la compilación de pdflatex se haga desde nuestra directorio de trabajo, usaremos la librería rstudioapi() para reconocer rutas de directorios en nuestra computadora. Para poder realizar operaciones con archivos en PDF, usaremos la librería pdftools(). Para realizar algunos gráficos en 3D usaremos plot3D.

# 1) Instalar exams
if(!require("exams"))install.packages("exams")

# 2) Para ejecutar pdflatex desde el folder de trabajo
if(!require("rstudioapi"))install.packages("rstudioapi")

# 3) Instalar pdftools
if(!require("pdftools"))install.packages("pdftools")

# 4) Instalar plot3D
if(!require("plot3D"))install.packages("plot3D")

# 5) Instalar tinytex (con soporte para español)
tinytex::install_tinytex()
tinytex::tlmgr_install(c("babel-spanish", "hyphen-spanish"))
tinytex::tlmgr_update()

2. Visualización de ejemplos

Las primeras líneas del archivo examen.R contienen las intrucciones para ejecturar pdflatex desde nuestro directorio de trabajo, así como algunos de los ejemplos que se pueden cargar por defecto en la librería exams().

# 1) Escoger el folder desde donde se ejecutará pdflatex
library(rstudioapi)
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
sep <- ifelse(.Platform$OS.type == "windows", ";", ":")
Sys.setenv(
  TEXINPUTS = paste0(
    getwd(),
    sep,
    Sys.getenv("TEXINPUTS")
  )
)

# 2) Cargar la librería exams
library(exams)

# 3) Ver algunos ejemplos
exams2pdf("Rlogo.Rmd")
exams2pdf("regression.Rmd")

3. Generando ejercicios

Empezaremos viendo la estructura de los archivos para crear ejercicios en nuestros exámenes. Dichos ejercicios se harán usando R Markdown.

  • En el Paso 1 se muestran 4 plantillas básicas, correspondientes con los archivos ejercicio1_con_solucion.Rmd, ejercicio1_sin_solucion.Rmd, ejercicio2_con_solucion.Rmd y ejercicio2_sin_solucion.Rmd.
  • En el Paso 2 se muestra la plantilla en LaTeX plain_personalizado.tex, la cual contiene la estructura visual para nuestros exámenes.
  • En el Paso 3 compilaremos los ejercicios del Paso 1 dentro de un PDF, generándose un examen (aleatorio) para cada estudiante. Se dispondrá de una versión con respuestas (cuyas versiones para cada estudiante están en el directorio examenes_con_solucion) y otra versión sin respuestas (cuyas versiones para cada estudiante están en el directorio examenes_sin_solucion). Cada versión con solución está pareada con su versión sin solución a través de una clave única.

Paso 1

Estructura básica de un ejercicio aleatorio

Contenido del archivo ejercicio1_con_solucion.Rmd.

```{r echo=FALSE, results='hide'}
x <- sample(1:10, 1)
y <- sample(1:10, 1)
res <- x + y
```

Question
========
¿Cuál es la suma de $x = `r x`$ y $y = `r y`$?

Solution
========
La suma correcta es `r res`.

Meta-information
================
extype: num
exsolution: `r res`
extol: 0.01

Contenido del archivo ejercicio1_sin_solucion.Rmd.

```{r echo=FALSE, results='hide'}
x <- sample(1:10, 1)
y <- sample(1:10, 1)
res <- x + y
```

Question
========
¿Cuál es la suma de $x = `r x`$ y $y = `r y`$?

<!-- Solution -->
<!-- ======== -->
<!-- La suma correcta es `r res`. -->

Meta-information
================
extype: num
exsolution: `r res`
extol: 0.01

Contenido del archivo ejercicio2_con_solucion.Rmd.

```{r echo=FALSE, results='hide'}
mu    <- sample(5:15, 1)
sigma <- sample(1:3, 1)
n     <- 1000
x     <- rnorm(n, mean = mu, sd = sigma)
```

Question
========
En el siguiente histograma hemos muestreado `r n` valores de una distribución normal con media `r mu` y desviación estándar `r sigma`, es decir, la función de densidad está dada por

\begin{align*}
f(x) = \frac{1}{\sqrt{2\pi\times `r sigma`^2}}\exp\left(\frac{-(x - `r mu`)^2}{2\pi\times`r sigma`^2}\right),\quad x\in\mathbb{R}.
\end{align*}

¿Cuál es el valor de la media de la distribución?

```{r fig1, echo=FALSE, fig.width=4, fig.asp=1, fig.cap='Esta es una figura.'}
hist(x,
     main     = paste0("Histograma de ", n, " observaciones"),
     xlab     = "Valor de la muestra",
     ylab     = "Frecuencia",
     nclass   = sqrt(n),
     cex.main = 0.5,
     cex.lab  = 0.5,
     cex.axis = 0.5)
abline(v = mu, col = "red", lwd = 2)
```

Solution
========
La media es `r mu`.

Meta-information
================
extype: num
exsolution: `r mu`
extol: 0.01

Contenido del archivo ejercicio2_sin_solucion.Rmd.

```{r echo=FALSE, results='hide'}
mu    <- sample(5:15, 1)
sigma <- sample(1:3, 1)
n     <- 1000
x     <- rnorm(n, mean = mu, sd = sigma)
```

Question
========
En el siguiente histograma hemos muestreado `r n` valores de una distribución normal con media `r mu` y desviación estándar `r sigma`, es decir, la función de densidad está dada por

\begin{align*}
f(x) = \frac{1}{\sqrt{2\pi\times `r sigma`^2}}\exp\left(\frac{-(x - `r mu`)^2}{2\pi\times`r sigma`^2}\right),\quad x\in\mathbb{R}.
\end{align*}

¿Cuál es el valor de la media de la distribución?

```{r fig1, echo=FALSE, fig.width=4, fig.asp=1, fig.cap='Esta es una figura.'}
hist(x,
     main     = paste0("Histograma de ", n, " observaciones"),
     xlab     = "Valor de la muestra",
     ylab     = "Frecuencia",
     nclass   = sqrt(n),     
     cex.main = 0.5,
     cex.lab  = 0.5,
     cex.axis = 0.5)
abline(v = mu, col = "red", lwd = 2)
```

<!-- Solution -->
<!-- ======== -->
<!-- La media es `r mu`. -->

Meta-information
================
extype: num
exsolution: `r mu`
extol: 0.01

Paso 2

Plantilla básica para compilar pdflatex

Contenido del archivo plain_personalizado.tex.

\documentclass[a4paper]{article}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% Paquetes y comandos obligatorios para la comunicación con exams %%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage[utf8]{inputenc}
\usepackage{a4wide,color,Sweave,url,amsmath,booktabs,longtable,eurosym,amssymb}
\newenvironment{question}{\item }{}
\newenvironment{solution}{\textbf{Solución:}\newline}{}
\newenvironment{answerlist}{\renewcommand{\labelenumii}{(\alph{enumii})}\begin{enumerate}}{\end{enumerate}}
\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\providecommand{\pandocbounded}[1]{#1}
\setkeys{Gin}{keepaspectratio}
\makeatletter
\newcommand{\ID}[1]{\def\@ID{#1}}
\ID{00001}
\newcommand{\myID}{\@ID}
\makeatother
\exinput{header}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% Paquetes y definiciones de usuario %%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage[spanish]{babel}
\usepackage{float}
\floatplacement{figure}{H}
\usepackage[tmargin = 1in, bmargin = 1in, lmargin = 1in, rmargin = 1in]{geometry}
\usepackage{graphicx}
\usepackage{multirow}
\usepackage{array}
\usepackage{fontawesome5}
\usepackage{amsmath}
\usepackage{amssymb}
\definecolor{customred}{RGB}{231, 0, 51}
\newcommand{\calendario}{\textcolor{customred}{\faCalendar*}}
\newcommand{\reloj}{\textcolor{customred}{\faClock}}
\newcommand{\tarea}{\textcolor{customred}{\faEdit}}
\newcommand{\usuario}{\textcolor{customred}{\faUserSecret}}
\renewcommand{\figurename}{Figura}
\decimalpoint
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\begin{center}
    \begin{table}[!htp]
    \centering
    \begin{tabular}{>{\centering\arraybackslash}p{0.13\textwidth}  >{\centering\arraybackslash}p{0.65\textwidth}  >{\centering\arraybackslash}p{0.13\textwidth}}
    %\hline
    \multirow{4}{*}[0pt]{\raisebox{0pt}[4\baselineskip][0pt]{%
        \makebox[\linewidth]{\includegraphics[height=4\baselineskip]{imagenes/figura_iz.pdf}}%
    }} 
    & {\bf Universidad} 
    & \multirow{4}{*}[0pt]{\raisebox{0pt}[4\baselineskip][0pt]{%
        \makebox[\linewidth]{\includegraphics[height=4\baselineskip]{imagenes/figura_de.pdf}}%
    }} \\[0pt]
    %\cline{2-2}
    & {\bf Recinto} & \\[0pt]
    %\cline{2-2}
    & Facultad & \\[0pt]
    %\cline{2-2}
    & Departamento & \\[0pt]
    %\hline
    \end{tabular}
    \end{table}
    MATERIA | Semestre \\
    \large{\tarea\,\, \textbf{Tarea}} \LARGE{$|$}
    \large{\calendario\,\, Fecha}
    \LARGE{$|$} \large{\reloj\,\, Hora} \LARGE{$|$} \large{\usuario\,\, \myID}
    \\   
\end{center}
\bigskip

\noindent {\bf Estudiante:} \hrulefill\\
\noindent {\bf Instrucciones:} Aquí van.

\begin{enumerate}
%% \exinput{exercises}
\end{enumerate}
\end{document}

Paso 3

Compilación de ejercicios

Las siguientes líneas del archivo examen.R contienen las intrucciones para crear una versión del examen para cada estudiante, tanto en su formato con solución como en el formato sin solución.

# 4) Generar nuestros propios exámenes
cantidad <- 2
width    <- nchar(as.character(cantidad))
ids      <- paste0("ID-", formatC(1:cantidad, width  = width, flag   = "0"))

#   a) Exámenes con solución
set.seed(2025)
unlink("examenes_con_solucion", recursive = TRUE)
dir.create("examenes_con_solucion")
for (i in 1:cantidad){
  exams2pdf(c("ejercicio1_con_solucion.Rmd", "ejercicio2_con_solucion.Rmd"),
            n        = 1,
            name     = paste0("Examen_con_solucion_", ids[i], "_"),
            header   = list(ID = ids[i]),
            dir      = "examenes_con_solucion",
            template = "plain_personalizado.tex",
            edir     = ".")
}

#   b) Exámenes sin solución
set.seed(2025)
unlink("examenes_sin_solucion", recursive = TRUE)
dir.create("examenes_sin_solucion")
for (i in 1:cantidad){
  exams2pdf(c("ejercicio1_sin_solucion.Rmd", "ejercicio2_sin_solucion.Rmd"),
            n        = 1,
            name     = paste0("Examen_sin_solucion_", ids[i], "_"),
            header   = list(ID = ids[i]),
            dir      = "examenes_sin_solucion",
            template = "plain_personalizado.tex",
            edir     = ".")
}

4. Generando exámenes

Las últimas líneas del archivo examen.R contienen las intrucciones para unir todos los exámenes en un solo, tanto en su formato con solución como en el formato sin solución.

# 5) Cargar la librería pdftools
library(pdftools)

# 6) Lista todos los PDFs de los folders
pdfs_con <- list.files("examenes_con_solucion", full.names = TRUE)
pdfs_sin <- list.files("examenes_sin_solucion", full.names = TRUE)

# 7) Generar único PDF con soluciones y sin soluciones
unlink("final_con_soluciones", recursive = TRUE)
unlink("final_sin_soluciones", recursive = TRUE)
dir.create("final_con_soluciones")
dir.create("final_sin_soluciones")
pdf_combine(input  = pdfs_con, output = "final_con_soluciones/final_con_sol.pdf")
pdf_combine(input  = pdfs_sin, output = "final_sin_soluciones/final_sin_sol.pdf")

5. Práctica


6. Otras tecnologías avanzadas

  • Librerías como exams de R permiten exportar a XML para incrustrar contenido estático autoevaluable en Moodle.
  • Librerías como shiny de R y software como GeoGebra permiten incrustar contenido dinámico autoevaluable en Moodle.

Contacto

Eugenio Guerrero Ruiz
Catedrático Auxiliar

Departamento de Matemáticas
Facultad de Ciencias Naturales
Universidad de Puerto Rico
Recinto de Río Piedras
Oficina C.125
787 764 0000 Ext. 88265

Jairo A. Ayala Godoy
Catedrático Asociado

Instituto de Estadística y Sistemas
Computadorizados de Información
Facultad de Administración de Empresas
Universidad de Puerto Rico
Recinto de Río Piedras
787 764-0000 Ext. 87079