Creación de Cuadernos Computacionales con Org Babel
Org Babel convierte Emacs Org Mode en un entorno interactivo de cuadernos computacionales multilenguaje. Al igual que Jupyter o Mathematica, Org Babel entrelaza código ejecutable, resultados de ejecución en vivo, figuras, tablas y prosa narrativa. A diferencia de los cuadernos basados en JSON, los cuadernos Org son texto plano, legibles por humanos, controlables de forma nativa con Git y admiten la combinación de múltiples lenguajes de programación dentro de un solo documento.
1. Arquitectura Central y Separación de Preocupaciones
Cada cuaderno de Org Babel opera en cinco preocupaciones distintas gobernadas por argumentos de cabecera. Confundirlas es la principal fuente de errores en los cuadernos Org:
| Preocupación | Argumentos de Cabecera Clave | Qué Gobierna |
|---|---|---|
| Ejecución | :session, :eval, :dir, :var, :cache | Si el bloque se ejecuta, en qué proceso, dónde y con qué entradas. |
| Resultados | :results (value vs output, formato, tipo, manejo) | Qué se captura de la ejecución y cómo se renderiza en el búfer. |
| Exportación | :exports (code, results, both, none) | Qué aparece en los informes exportados (HTML, PDF, Markdown). |
| Tangling | :tangle, :shebang, :noweb | Qué se extrae a archivos de código fuente independientes en disco. |
| Composición | :var, #+CALL:, <<noweb>>, org-sbe | Cómo los bloques alimentan datos o código entre sí. |
2. Estructura del Cuaderno y Configuración del Preámbulo
Un archivo de cuaderno Org (.org) comienza con metadatos del documento y configuraciones predeterminadas de todo el búfer, seguido de encabezados jerárquicos que contienen texto narrativo, fórmulas matemáticas y bloques de código.
2.1 Plantilla de Preámbulo de Cuaderno Estándar
#+TITLE: Análisis Computacional del Experimento X
#+AUTHOR: Jane Doe
#+DATE: [2025-05-18 Sun]
#+OPTIONS: toc:2 num:t ^:{}
#+STARTUP: overview hideblocks inlineimages
# Valores predeterminados globales de Babel para todos los bloques en este búfer:
#+PROPERTY: header-args :eval yes :exports both :results drawer
# Valores predeterminados específicos del lenguaje:
#+PROPERTY: header-args:python :session *py-analysis* :results value :exports both
#+PROPERTY: header-args:R :session *R-analysis* :results value :exports both
#+PROPERTY: header-args:sh :session none :results output :exports both
Configuraciones Clave Dentro del Búfer:
#+STARTUP: inlineimages: Muestra automáticamente las imágenes/gráficos generados en línea cuando se insertan los resultados o cuando se abre el búfer.#+STARTUP: hideblocks: Comienza con los bloques de código plegados, mostrando solo la narrativa y los resultados para facilitar la lectura.#+STARTUP: nohideblocks: Muestra todos los bloques de código expandidos.#+PROPERTY: header-args ...: Valores predeterminados de todo el búfer. Se pueden añadir conheader-args+.
2.2 Habilitación de Lenguajes en la Configuración de Emacs
Org solo habilita la evaluación de Emacs Lisp de forma predeterminada. Todos los demás lenguajes deben cargarse explícitamente en su configuración de Emacs:
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(python . t)
(R . t)
(shell . t)
(sqlite . t)
(gnuplot . t)
(C . t)))
Para evitar los diálogos de confirmación de ejecución interactiva en cuadernos de confianza:
;; nil permite que los bloques se ejecuten sin avisos de confirmación
(setq org-confirm-babel-evaluate nil)
3. Anatomía de un Bloque de Código
#+NAME: <nombre_del_bloque>
#+HEADER: <argumentos_de_cabecera_adicionales>
#+BEGIN_SRC <lenguaje> <interruptores> <argumentos_de_cabecera>
<cuerpo>
#+END_SRC
#+NAME: <nombre>: Identificador único opcional. Obligatorio si el bloque se va a referenciar como una función, pasar como entrada a otro bloque mediante:varo llamar a través de#+CALL:.<lenguaje>: Identificador reconocido por Babel (python,R,sh,emacs-lisp,sqlite,sql,julia,calc,gnuplot, etc.).<interruptores>: Interruptores opcionales (por ejemplo,-npara numeración de líneas durante la exportación,-rpara eliminar etiquetas).<argumentos_de_cabecera>: Pares clave-valor que controlan la ejecución, los resultados y la exportación.#+HEADER:: Líneas secundarias para especificar limpiamente listas largas de argumentos de cabecera.
Bloques de Código en Línea
Para incrustar valores calculados dinámicamente dentro de un párrafo:
La precisión final calculada fue src_python[:session *py* :results value]{f"{acc * 100:.2f}%"}.
4. Referencia de Argumentos de Cabecera
4.1 Gestión de Sesiones: Exploración Interactiva vs. Reproducibilidad Hermética
:session none(Predeterminado): Evalúa el bloque en un proceso de intérprete externo único. Hermético, limpio y evita la contaminación del estado, pero reinicializa las importaciones y el entorno en cada ejecución.:session <nombre>: Evalúa en un búfer REPL inferior persistente de Emacs (por ejemplo,*py-analysis*).- Las variables, funciones y módulos importados persisten en múltiples bloques que comparten el mismo nombre de sesión.
- Esencial para conjuntos de datos pesados, pesos de modelos grandes y flujos de trabajo exploratorios.
- Mejor práctica: Cree prototipos con
:session <nombre>; para la verificación final, reinicie la sesión o pruebe las secciones críticas con:session nonepara garantizar la reproducibilidad.
4.2 Directorios de Trabajo y Entornos Remotos
:dir /ruta/al/proyecto: Ejecuta el bloque dentro del directorio especificado.:mkdirp yes: Crea el directorio de destino si no existe.- Ejecución Remota (TRAMP):
:dir /ssh:usuario@cluster-gpu:/workspace/dataejecuta el bloque de forma remota en un servidor mientras renderiza la salida dentro de su búfer local de Emacs.
4.3 Almacenamiento en Caché de Cálculos Costosos
:cache yes: Calcula un hash SHA1 del texto del bloque, las entradas y los parámetros:var.- Si el hash coincide con el almacenado en la línea
#+RESULTS[hash]:, se omite la evaluación y se conservan los resultados almacenados en caché. - Utilícelo solo para cálculos deterministas y puros sin efectos secundarios externos.
5. Manejo de Resultados: La Distinción Crítica
El argumento :results toma hasta cuatro componentes ortogonales:
:results [colección] [tipo] [formato] [manejo]
5.1 Colección: Funcional (value) vs. Scripting (output)
Comprender esta diferencia elimina el 90 % de la confusión con Babel:
| Modo de Colección | Qué se Captura | Comportamiento en Python | Comportamiento en Shell |
|---|---|---|---|
:results value (Predeterminado para Python, R, elisp) | Valor de retorno de la última expresión o return explícito. | Debe usar una declaración return <val> explícita. Todo el resultado de print() se descarta. | Devuelve el código de salida o el valor de cadena. |
:results output (Predeterminado para shell, bash) | Flujo de salida estándar emitido durante la ejecución. | Captura todas las declaraciones print(). Cualquier return se ignora. | Captura toda la salida estándar del comando (echo, ls, etc.). |
Ejemplo Funcional en Python (:results value):
#+BEGIN_SRC python :results value
data = [1, 2, 3, 4]
# En modo funcional, se REQUIERE un return explícito en Python:
return sum(data)
#+END_SRC
#+RESULTS:
: 10
Ejemplo de Scripting en Python (:results output):
#+BEGIN_SRC python :results output
for i in range(3):
print(f"Época {i}: pérdida={0.5/(i+1):.4f}")
#+END_SRC
#+RESULTS:
: Época 0: pérdida=0.5000
: Época 1: pérdida=0.2500
: Época 2: pérdida=0.1667
5.2 Tipos de Resultados
table/vector: Da formato a las listas/arrays devueltos como una tabla de Org Mode.scalar/verbatim: Da formato a la salida como texto entrecomillado sin formato de tabla.file: Dirige la salida a un archivo en disco (especificado con:file <nombre_de_archivo>) e inserta un enlace de archivo Org en los resultados.
5.3 Formatos de Resultados
drawer(Recomendado): Envuelve los resultados en delimitadores:RESULTS:…:END:. Mantiene los documentos ordenados, especialmente para salidas de varias líneas.raw: Inserta la salida directamente como texto Org sin escapar (útil para generar encabezados o tablas dinámicos).code: Envuelve la salida en#+BEGIN_SRC <lang>…#+END_SRC.link/graphics: Trata el resultado como una ruta a una imagen/archivo y genera[[file:ruta]].
5.4 Manejo
replace(Predeterminado): Reemplaza el bloque#+RESULTS:anterior.append: Agrega nuevos resultados debajo de los resultados existentes.prepend: Inserta nuevos resultados encima de los resultados existentes.silent: Muestra los resultados en el área de eco/minibúfer de Emacs sin escribirlos en el búfer Org.
6. Visualizaciones y Figuras en Cuadernos
Para generar gráficos y figuras que se rendericen directamente en el cuaderno:
6.1 Gráfico de Matplotlib en Python
#+NAME: grafico-distribucion-normal
#+BEGIN_SRC python :session *py-vis* :results graphics file :file figures/normal_dist.png :exports both
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(6, 3.5))
x = np.linspace(-3, 3, 200)
plt.plot(x, np.exp(-x**2 / 2) / np.sqrt(2 * np.pi), color="#2b5c8f", lw=2)
plt.title("Distribución Normal Estándar")
plt.xlabel("z")
plt.ylabel("Densidad")
plt.tight_layout()
plt.savefig("figures/normal_dist.png", dpi=150)
plt.close()
#+END_SRC
#+RESULTS: grafico-distribucion-normal
[[file:figures/normal_dist.png]]
6.2 Gráfico ggplot2 en R
#+NAME: grafico-dispersion-r
#+BEGIN_SRC R :session *r-vis* :results graphics file :file figures/r_scatter.png :exports both
library(ggplot2)
p <- ggplot(mtcars, aes(x=wt, y=mpg, color=factor(cyl))) +
geom_point(size=3) +
theme_minimal() +
labs(title="Eficiencia de Combustible vs. Peso", x="Peso (1000 lbs)", y="MPG")
ggsave("figures/r_scatter.png", plot=p, width=6, height=4, dpi=150)
#+END_SRC
#+RESULTS: grafico-dispersion-r
[[file:figures/r_scatter.png]]
Consejo: Ejecute M-x org-toggle-inline-images (C-c C-x C-v) en Emacs para alternar la visualización de los gráficos generados directamente dentro del búfer.
7. Ejecución Interactiva y Flujo de Trabajo de Edición
Combinaciones de Teclas Esenciales
| Combinación de Teclas | Comando | Descripción |
|---|---|---|
C-c C-c | org-babel-execute-src-block | Ejecuta el bloque bajo el punto e inserta/actualiza los resultados. |
C-c ' | org-edit-special | Abre el cuerpo del bloque en un búfer dedicado de modo principal (LSP completo, resaltado de sintaxis, autocompletado). Presione C-c ' de nuevo para guardar y volver. |
C-c C-v b | org-babel-execute-buffer | Ejecuta todos los bloques de código en el cuaderno secuencialmente (como "Ejecutar todas las celdas"). |
C-c C-v s | org-babel-execute-subtree | Ejecuta todos los bloques de código bajo el encabezado actual. |
C-c C-o | org-open-at-point | En un enlace #+RESULTS:, abre la imagen o el archivo generado en un visor o búfer externo. |
M-<down> | org-babel-pop-to-session | Cambia el enfoque directamente al búfer REPL interactivo para la sesión de este bloque. |
C-c C-v v | org-babel-expand-src-block | Previsualiza el bloque completamente expandido (con todas las expansiones :var y noweb resueltas). |
8. Exportación y Publicación de Informes
Un cuaderno se puede convertir en documentos HTML, PDF o Markdown independientes.
8.1 Control del Contenido de Exportación con :exports
:exports code: Exporta solo el bloque de código.:exports results: Exporta solo la salida/figura (oculta el código, ideal para informes ejecutivos).:exports both: Exporta tanto el código como la salida (ideal para documentación técnica).:exports none: Oculta tanto el código como los resultados del documento exportado (se ejecuta durante la evaluación si es necesario).
8.2 Control de la Evaluación de Exportación con :eval
:eval never-export: Evita volver a ejecutar bloques durante la exportación, utilizando los#+RESULTS:existentes en el búfer. Acelera la exportación y evita cambios no deterministas.:eval query-export: Pregunta antes de ejecutar durante la exportación.
8.3 Comandos de Exportación
C-c C-e h h(org-html-export-to-html): Genera un informe HTML.C-c C-e h o(org-html-export-to-html-and-open): Genera y abre HTML en su navegador.C-c C-e l p(org-latex-export-to-pdf): Genera un informe PDF con formato tipográfico a través de LaTeX.
9. Errores Comunes y Lista de Verificación de Solución de Problemas
| Síntoma / Error | Causa | Solución |
|---|---|---|
| El bloque de Python no devuelve resultados | Se usó :results value con print(...) en lugar de return ... | Reemplace print(...) con return ..., o cambie la cabecera a :results output. |
No org-babel-execute function for <lang> | El lenguaje no está registrado en org-babel-load-languages | Agregue (<lang> . t) a org-babel-load-languages en su configuración de Emacs y evalúelo. |
| Se produce un enlace de imagen pero la imagen no se muestra | Las imágenes en línea están desactivadas en Org Mode | Ejecute M-x org-toggle-inline-images (C-c C-x C-v) o agregue #+STARTUP: inlineimages. |
Las salidas de la sesión contienen indicaciones repetidas (>>>) | Texto de indicación interactiva persistente en el búfer REPL | Limpie o reinicie el búfer de la sesión (*nombre-de-sesión*). |
| La exportación es lenta o se cuelga | Org está re-ejecutando todos los bloques del cuaderno durante la exportación | Establezca :eval never-export en bloques de larga duración o globalmente mediante #+PROPERTY: header-args :eval never-export. |
| La entrada de tabla produce un error en las reglas horizontales | Las reglas horizontales (` | -`) se tratan como filas sin procesar |
10. Ejemplo de Cuaderno Anotado Completo
#+TITLE: Análisis de Frecuencia de Llamaradas Solares
#+AUTHOR: Dr. Alex Rivera
#+DATE: [2025-05-18 Sun]
#+OPTIONS: toc:nil num:nil
#+STARTUP: inlineimages nohideblocks
#+PROPERTY: header-args :session *solar* :results drawer :exports both
* Introducción
Analizamos las frecuencias de llamaradas observacionales registradas durante una ventana de observación de 6 meses.
* Observaciones en Bruto
Aquí está la tabla de medición de referencia:
#+NAME: observaciones_en_bruto
| Mes | Clase C | Clase M | Clase X |
|------+---------+---------+---------|
| Ene | 12 | 3 | 0 |
| Feb | 19 | 5 | 1 |
| Mar | 15 | 2 | 0 |
| Abr | 27 | 8 | 2 |
| May | 31 | 11 | 3 |
| Jun | 22 | 7 | 1 |
* Procesamiento de Datos y Estadísticas Resumidas
Calculamos la actividad solar total usando Python:
#+NAME: calcular_totales
#+BEGIN_SRC python :var obs=observaciones_en_bruto :colnames yes :results value table
eventos_totales = []
for fila in obs:
mes = fila[0]
total = sum(fila[1:])
eventos_totales.append([mes, total])
return [["Mes", "Eventos Totales"]] + eventos_totales
#+END_SRC
#+RESULTS: calcular_totales
:RESULTS:
| Mes | Eventos Totales |
|-----+-----------------|
| Ene | 15 |
| Feb | 25 |
| Mar | 17 |
| Abr | 37 |
| May | 45 |
| Jun | 30 |
:END:
* Visualización
generamos un gráfico de barras de eventos totales por mes.
#+NAME: grafico-barras
#+BEGIN_SRC python :session *solar* :results graphics file :file figures/eventos_solares.png :exports both
import matplotlib.pyplot as plt
meses = ["Ene", "Feb", "Mar", "Abr", "May", "Jun"]
totales = [15, 25, 17, 37, 45, 30]
plt.figure(figsize=(8, 4))
plt.bar(meses, totales, color="#d62728")
plt.title("Eventos Solares Totales por Mes")
plt.xlabel("Mes")
plt.ylabel("Número de Eventos")
plt.grid(axis="y", linestyle="--", alpha=0.7)
plt.tight_layout()
plt.savefig("figures/eventos_solares.png", dpi=150)
plt.close()
#+END_SRC
#+RESULTS: grafico-barras
[[file:figures/eventos_solares.png]]
* Resumen
Se observó un pico de actividad en mayo con 45 eventos totales, principalmente impulsado por llamaradas de clase C y M.
Créditos: Este documento es un resumen traducido de la guía original en inglés, manteniendo la estructura y los detalles técnicos clave de Org Babel para su uso en español.