Este documento explica línea por línea cómo funciona el ejercicio de gráficas de gastos de carro, perfecto para quienes están aprendiendo R-exams con Python.
Un ejercicio de selección múltiple que:
Question
========
¿Qué hace? Define que esto es una pregunta de R-exams.
```{r configuracion_inicial, echo=FALSE, message=FALSE, warning=FALSE}
# Cargar librerías necesarias
library(exams) # Para crear exámenes
library(reticulate) # Para usar Python desde R
library(knitr) # Para procesar documentos
# Configurar Python
use_python('/usr/bin/python3')
# Configurar opciones para evitar notación científica
options(scipen = 999) # Evita 1e+05, muestra 100000
options(digits = 10) # Más decimales
Sys.setlocale(category='LC_NUMERIC', locale='C') # Formato numérico estándar
¿Qué hace cada línea?
library(exams)
: Carga el paquete para crear
exámeneslibrary(reticulate)
: Permite usar Python dentro de
Ruse_python()
: Le dice a R qué versión de Python
usaroptions(scipen = 999)
: Evita números como 1.23e+05, los
muestra como 123000Sys.setlocale()
: Configura el formato de números (punto
decimal, no coma)# Detectar formato de salida para gráficas
typ <- match_exams_device()
es_pdf <- (typ == "pdf")
# Detectar formatos que requieren archivos PNG
formatos_png <- c("pdf", "pandoc", "docx", "odt")
usar_png <- (typ %in% formatos_png)
# Detectar formatos HTML/Moodle que usan plt.show()
formatos_moodle <- c("exams2moodle", "exams2qti12", "exams2qti21", "exams2openolat")
es_html_moodle <- (match_exams_call() %in% formatos_moodle) || (typ == "html")
¿Qué hace?
match_exams_device()
: Detecta si estamos generando
HTML, PDF, etc.usar_png
: Variable que dice si necesitamos generar
archivos PNGes_html_moodle
: Variable que dice si es para
web/Moodle¿Por qué es importante?
```{r generar_datos_aleatorios, echo=FALSE}
# Generar datos aleatorios para 4 semanas
set.seed(sample(1:1000, 1)) # Semilla aleatoria para datos únicos
# Crear datos de gastos por semana (en pesos colombianos)
datos <- list()
datos$gastos_semanas <- list()
for(i in 1:4) {
datos$gastos_semanas[[i]] <- list(
gasolina = sample(20000:50000, 1), # Entre $20,000 y $50,000
parqueadero = sample(15000:30000, 1), # Entre $15,000 y $30,000
peajes = sample(10000:25000, 1) # Entre $10,000 y $25,000
)
}
¿Qué hace cada línea?
set.seed(sample(1:1000, 1))
: Genera una semilla
aleatoria diferente cada vezdatos <- list()
: Crea una lista vacía para guardar
todos los datosfor(i in 1:4)
: Repite 4 veces (una por cada
semana)sample(20000:50000, 1)
: Escoge un número aleatorio
entre 20,000 y 50,000¿Por qué estos rangos?
# Calcular totales por categoría
datos$totales_categoria <- list(
gasolina = sum(sapply(datos$gastos_semanas, function(x) x$gasolina)),
parqueadero = sum(sapply(datos$gastos_semanas, function(x) x$parqueadero)),
peajes = sum(sapply(datos$gastos_semanas, function(x) x$peajes))
)
# Calcular totales por semana
datos$totales_semana <- sapply(datos$gastos_semanas, function(semana) {
semana$gasolina + semana$parqueadero + semana$peajes
})
¿Qué hace?
sapply()
: Aplica una función a cada elemento de la
listasum()
: Suma todos los valoresfunction(x) x$gasolina
: Función que extrae solo la
gasolina de cada semanaEjemplo de resultado:
```{python generar_graficas_archivos, echo=FALSE, results='hide'}
import matplotlib.pyplot as plt
import numpy as np
import os
# Configurar matplotlib para generar archivos
plt.ioff() # Modo no interactivo (no muestra ventanas)
plt.rcParams['font.size'] = 10 # Tamaño de fuente base
¿Qué hace cada línea?
import matplotlib.pyplot as plt
: Importa la librería
para hacer gráficasimport numpy as np
: Importa librería para cálculos
matemáticosplt.ioff()
: Desactiva el modo interactivo (no abre
ventanas)plt.rcParams['font.size'] = 10
: Establece el tamaño de
letra por defecto# Obtener datos de R
gastos_semanas = r.datos['gastos_semanas']
totales_semana = r.datos['totales_semana']
porc_gasolina = r.porc_gasolina
porc_parqueadero = r.porc_parqueadero
porc_peajes = r.porc_peajes
porc_semanas = r.porc_semanas
¿Qué hace?
r.datos
: Accede a la variable datos
que
creamos en Rr.porc_gasolina
: Accede al porcentaje de gasolina
calculado en R¿Cómo funciona?
reticulate
permite que Python lea variables de R usando
r.nombre_variable
# Preparar datos
categorias = ['Gasolina', 'Parqueadero', 'Peajes']
semanas = ['Semana 1', 'Semana 2', 'Semana 3', 'Semana 4']
gasolina_por_semana = [gastos_semanas[i]['gasolina'] for i in range(4)]
parqueadero_por_semana = [gastos_semanas[i]['parqueadero'] for i in range(4)]
peajes_por_semana = [gastos_semanas[i]['peajes'] for i in range(4)]
¿Qué hace?
[gastos_semanas[i]['gasolina'] for i in range(4)]
: List
comprehension que extrae la gasolina de cada semanaResultado ejemplo: -
gasolina_por_semana = [44000, 26000, 41000, 28000]
# Paletas de colores profesionales
colores_categorias = ['#2E5984', '#5B9BD5', '#A5A5A5'] # Azul oscuro, azul claro, gris
colores_semanas = ['#1F4E79', '#2E75B6', '#5B9BD5', '#8DB4E2'] # Gradiente azul
¿Por qué estos colores?
# GRÁFICA A: Circular por categoría
fig_a, ax_a = plt.subplots(figsize=(7, 6))
porcentajes_cat = [porc_gasolina, porc_parqueadero, porc_peajes]
wedges, texts, autotexts = ax_a.pie(porcentajes_cat, labels=categorias, autopct='%1.1f%%',
colors=colores_categorias, startangle=90,
explode=(0.05, 0.05, 0.05), shadow=True,
textprops={'fontsize': 10, 'fontweight': 'bold'})
¿Qué hace cada parámetro?
figsize=(7, 6)
: Tamaño de la figura en pulgadas (ancho
x alto)autopct='%1.1f%%'
: Formato de porcentajes (1
decimal)startangle=90
: Empieza el gráfico desde arriba (90
grados)explode=(0.05, 0.05, 0.05)
: Separa ligeramente cada
sectorshadow=True
: Agrega sombra para efecto 3Dtextprops
: Propiedades del texto (tamaño y peso)¿Por qué esta gráfica NO es la respuesta correcta?
for autotext in autotexts:
autotext.set_color('white') # Texto blanco
autotext.set_fontweight('bold') # Texto en negrita
autotext.set_fontsize(11) # Tamaño de fuente
¿Qué hace?
autotexts
: Lista con los textos de porcentajesset_color('white')
: Cambia el color a blanco para mejor
contrasteset_fontweight('bold')
: Hace el texto más grueso y
visibleax_a.set_title('Gráfica circular por categoría', fontsize=12, fontweight='bold', pad=20)
plt.tight_layout()
plt.savefig('grafica_a.png', dpi=150, bbox_inches='tight', facecolor='white', edgecolor='none')
plt.savefig('grafica_a.pdf', dpi=150, bbox_inches='tight', facecolor='white', edgecolor='none')
plt.close()
¿Qué hace cada parámetro?
set_title()
: Agrega título a la gráficapad=20
: Espacio entre título y gráficatight_layout()
: Ajusta automáticamente los
márgenesdpi=150
: Resolución (puntos por pulgada) - calidad de
impresiónbbox_inches='tight'
: Recorta espacios en blancofacecolor='white'
: Fondo blancoedgecolor='none'
: Sin bordeplt.close()
: Cierra la figura para liberar memoria¿Por qué guardar PNG y PDF?
# GRÁFICA B: Barras apiladas por semana
fig_b, ax_b = plt.subplots(figsize=(9, 6))
x = np.arange(len(semanas)) # Posiciones [0, 1, 2, 3]
width = 0.4 # Ancho de las barras
p1 = ax_b.bar(x, gasolina_por_semana, width, label='Gasolina', color=colores_categorias[0])
p2 = ax_b.bar(x, parqueadero_por_semana, width, bottom=gasolina_por_semana,
label='Parqueadero', color=colores_categorias[1])
p3 = ax_b.bar(x, peajes_por_semana, width,
bottom=np.array(gasolina_por_semana) + np.array(parqueadero_por_semana),
label='Peajes', color=colores_categorias[2])
¿Qué hace cada línea?
np.arange(len(semanas))
: Crea [0, 1, 2, 3] para
posiciones en Xwidth = 0.4
: Barras ocupan 40% del espacio
disponiblebottom=gasolina_por_semana
: La segunda barra empieza
donde termina la primeranp.array()
: Convierte listas a arrays para poder
sumarlos¿Por qué ES la respuesta correcta?
max_total = max(totales_semana)
for i, total in enumerate(totales_semana):
ax_b.text(i, total + max_total * 0.02, f'${total:,}', ha='center', va='bottom',
fontweight='bold', fontsize=10, color='#333333')
¿Qué hace?
max(totales_semana)
: Encuentra el total más altoenumerate()
: Da tanto el índice (i) como el valor
(total)total + max_total * 0.02
: Posición Y = total + 2% del
máximo (para separación)f'${total:,}'
: Formato con símbolo $ y separadores de
milesha='center'
: Alineación horizontal centradava='bottom'
: Alineación vertical desde abajoax_b.set_xlabel('Semanas', fontsize=11, fontweight='bold')
ax_b.set_ylabel('Gastos (pesos)', fontsize=11, fontweight='bold')
ax_b.set_xticks(x)
ax_b.set_xticklabels(semanas, fontsize=10)
ax_b.legend(loc='upper left', frameon=True, fancybox=True, shadow=True, fontsize=10)
¿Qué hace cada línea?
set_xlabel()
: Etiqueta del eje Xset_xticks(x)
: Posiciones donde van las etiquetas [0,
1, 2, 3]set_xticklabels(semanas)
: Textos de las etiquetas
[‘Semana 1’, ‘Semana 2’, …]legend()
: Leyenda con estilo profesionalloc='upper left'
: Posición de la leyendafancybox=True
: Esquinas redondeadasshadow=True
: Sombra en la leyendaax_b.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
ax_b.tick_params(axis='y', labelsize=9)
ax_b.grid(True, alpha=0.3, linestyle='--', linewidth=0.5)
ax_b.set_axisbelow(True)
¿Qué hace?
FuncFormatter()
: Función personalizada para formatear
númeroslambda x, p: f'${x:,.0f}'
: Función que agrega $ y
separadores de milestick_params()
: Cambia el tamaño de las etiquetas del
ejegrid()
: Agrega líneas de cuadrículaalpha=0.3
: Transparencia de la cuadrícula (30%
visible)linestyle='--'
: Líneas punteadasset_axisbelow(True)
: Cuadrícula detrás de las
barras- {width=70%}
- {width=80%}
- {width=70%}
- {width=90%}
¿Por qué esta sintaxis?

: Sintaxis Markdown estándar para
imágenes{width=70%}
: Atributo Pandoc para controlar el
tamañoAnswerlist
----------
* Gráfica circular por categoría
* Gráfica de barras apiladas por semana
* Gráfica circular por semana
* Gráfica de barras agrupadas por categoría
¿Qué hace?
Answerlist
: Palabra clave de R-exams para las
opciones----------
: Separador requerido*
: Cada opción empieza con asteriscoMeta-information
================
extype: schoice
exsolution: 0100
exname: gastos_carro_graficas_comparacion
¿Qué significa?
extype: schoice
: Single choice (selección única)exsolution: 0100
: La respuesta correcta es la segunda
(B)exname
: Nombre interno del ejercicio¡Felicidades! Ahora entiendes cómo crear ejercicios profesionales con R-exams y Python. 🎉