Rédaction de Cahiers Computationnels avec Org Babel
Org Babel transforme Emacs Org Mode en un environnement de cahier computationnel interactif et multi-langage. Comme Jupyter ou Mathematica, Org Babel entremêle du code exécutable, des résultats d'exécution en direct, des figures, des tableaux et du texte narratif. Contrairement aux cahiers basés sur JSON, les cahiers Org sont en texte brut, lisibles par l'homme, nativement versionnables avec Git et supportent le mélange de plusieurs langages de programmation au sein d'un même document.
1. Architecture Fondamentale & Séparation des Préoccupations
Chaque cahier Org Babel fonctionne à travers cinq préoccupations distinctes régies par des arguments d'en-tête. Les confondre est la principale source de bogues dans les cahiers Org :
| Préoccupation | Arguments d'En-tête Clés | Ce qu'elle Gère |
|---|---|---|
| Exécution | :session, :eval, :dir, :var, :cache | Le bloc s'exécute-t-il, dans quel processus, où et avec quelles entrées ? |
| Résultats | :results (value vs output, format, type, traitement) | Qu'est-ce qui est capturé lors de l'exécution et comment est-ce rendu dans le tampon ? |
| Exportation | :exports (code, results, both, none) | Qu'est-ce qui apparaît dans les rapports exportés (HTML, PDF, Markdown) ? |
| Tangle | :tangle, :shebang, :noweb | Qu'est-ce qui est extrait vers des fichiers source de code autonomes sur le disque ? |
| Composition | :var, #+CALL:, <<noweb>>, org-sbe | Comment les blocs s'alimentent-ils mutuellement en données ou en code ? |
2. Structure du Cahier & Configuration du Préambule
Un fichier de cahier Org (.org) commence par des métadonnées de document et des paramètres par défaut à l'échelle du tampon, suivis de titres hiérarchiques contenant du texte narratif, des formules mathématiques et des blocs de code.
2.1 Modèle de Préambule Standard du Cahier
#+TITLE: Analyse Computationnelle de l'Expérience X
#+AUTHOR: Jane Doe
#+DATE: [2025-05-18 Dim]
#+OPTIONS: toc:2 num:t ^:{}
#+STARTUP: overview hideblocks inlineimages
# Valeurs par défaut globales de Babel pour tous les blocs du tampon :
#+PROPERTY: header-args :eval yes :exports both :results drawer
# Valeurs par défaut spécifiques au langage :
#+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
Paramètres Clés dans le Tampon :
#+STARTUP: inlineimages: Affiche automatiquement les graphiques/images générés en ligne lorsque les résultats sont insérés ou lorsque le tampon est ouvert.#+STARTUP: hideblocks: Démarre avec les blocs de code repliés, n'affichant que le texte narratif et les résultats pour une meilleure lisibilité.#+STARTUP: nohideblocks: Affiche tous les blocs de code développés.#+PROPERTY: header-args ...: Valeurs par défaut à l'échelle du tampon. Peut être complété avecheader-args+.
2.2 Activation des Langages dans la Configuration Emacs
Org n'active que l'évaluation Emacs Lisp par défaut. Tous les autres langages doivent être explicitement chargés dans votre configuration 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)))
Pour éviter les dialogues de confirmation d'exécution interactive pour les cahiers de confiance :
;; nil permet aux blocs de s'exécuter sans invites de confirmation
(setq org-confirm-babel-evaluate nil)
3. Anatomie d'un Bloc de Code
#+NAME: <nom_du_bloc>
#+HEADER: <arguments_d_en-tête_supplémentaires>
#+BEGIN_SRC <langage> <switches> <arguments_d_en-tête>
<corps>
#+END_SRC
#+NAME: <nom>: Identifiant unique optionnel. Obligatoire si le bloc sera référencé comme une fonction, passé en entrée d'un autre bloc via:var, ou appelé via#+CALL:.<langage>: Identifiant reconnu par Babel (python,R,sh,emacs-lisp,sqlite,sql,julia,calc,gnuplot, etc.).<switches>: Switches optionnels (par exemple-npour la numérotation des lignes lors de l'exportation,-rpour supprimer les étiquettes).<arguments_d_en-tête>: Paires clé-valeur contrôlant l'exécution, les résultats et l'exportation.#+HEADER:: Lignes secondaires pour spécifier proprement de longues listes d'arguments d'en-tête.
Blocs de Code en Ligne
Pour intégrer des valeurs calculées dynamiquement dans un paragraphe :
La précision finale calculée était src_python[:session *py* :results value]{f"{acc * 100:.2f}%"}.
4. Référence des Arguments d'En-tête
4.1 Gestion des Sessions : Exploration Interactive vs Reproductibilité Hermétique
:session none(Par défaut) : Évalue le bloc dans un processus interpréteur externe ponctuel. Hermétique, propre et évite la pollution de l'état, mais réinitialise les imports et l'environnement à chaque exécution.:session <nom>: Évalue dans un tampon REPL inférieur Emacs persistant (par exemple*py-analysis*).- Les variables, fonctions et modules importés persistent entre les blocs partageant le même nom de session.
- Essentiel pour les ensembles de données volumineux, les poids de modèles importants et les flux de travail exploratoires.
- Meilleure Pratique : Prototyper avec
:session <nom>; pour la vérification finale, redémarrer la session ou tester les sections critiques avec:session nonepour garantir la reproductibilité.
4.2 Répertoires de Travail & Environnements Distants
:dir /chemin/vers/projet: Exécute le bloc dans le répertoire spécifié.:mkdirp yes: Crée le répertoire cible s'il n'existe pas déjà.- Exécution Distante (TRAMP) :
:dir /ssh:utilisateur@cluster-gpu:/workspace/dataexécute le bloc à distance sur un serveur tout en rendant la sortie à l'intérieur de votre tampon Emacs local.
4.3 Mise en Cache des Calculs Coûteux
:cache yes: Calcule un hachage SHA1 du texte du bloc, des entrées et des paramètres:var.- Si le hachage correspond à celui stocké sur la ligne
#+RESULTS[hash]:, l'évaluation est ignorée et les résultats mis en cache sont conservés. - À utiliser uniquement pour des calculs déterministes et purs sans effets secondaires externes.
5. Gestion des Résultats : La Distinction Critique
L'argument :results prend jusqu'à quatre composantes orthogonales :
:results [collection] [type] [format] [traitement]
5.1 Collection : Fonctionnelle (value) vs Script (output)
Comprendre cette différence élimine 90 % de la confusion avec Babel :
| Mode de Collection | Ce qui est Capturé | Comportement Python | Comportement Shell |
|---|---|---|---|
:results value (Par défaut pour Python, R, elisp) | Valeur de retour de la dernière expression ou return explicite. | Doit utiliser une instruction return <val> explicite. Toute sortie print() est ignorée. | Retourne le code de sortie ou la valeur de chaîne. |
:results output (Par défaut pour shell, bash) | Flux stdout émis pendant l'exécution. | Capture toutes les instructions print(). Tout return est ignoré. | Capture tout stdout de commande (echo, ls, etc.). |
Exemple Python Fonctionnel (:results value) :
#+BEGIN_SRC python :results value
data = [1, 2, 3, 4]
# En mode fonctionnel, un return explicite est REQUIS en Python :
return sum(data)
#+END_SRC
#+RESULTS:
: 10
Exemple Python Script (:results output) :
#+BEGIN_SRC python :results output
for i in range(3):
print(f"Époque {i} : perte={0.5/(i+1):.4f}")
#+END_SRC
#+RESULTS:
: Époque 0 : perte=0.5000
: Époque 1 : perte=0.2500
: Époque 2 : perte=0.1667
5.2 Types de Résultats
table/vector: Formate les listes/tableaux retournés en tant que tableau Org Mode.scalar/verbatim: Formate la sortie en texte brut entre guillemets sans formatage de tableau.file: Dirige la sortie vers un fichier sur le disque (spécifié avec:file <nomfichier>) et insère un lien de fichier Org dans les résultats.
5.3 Formats de Résultats
drawer(Recommandé) : Encadre les résultats avec les délimiteurs:RESULTS:…:END:. Garde les documents ordonnés, en particulier pour les sorties multi-lignes.raw: Insère la sortie directement comme texte Org non échappé (utile pour générer des titres ou tableaux dynamiques).code: Encadre la sortie dans#+BEGIN_SRC <lang>…#+END_SRC.link/graphics: Traite le résultat comme un chemin vers une image/un fichier et génère[[file:chemin]].
5.4 Traitement
replace(Par défaut) : Remplace le bloc#+RESULTS:précédent.append: Ajoute les nouveaux résultats en dessous des résultats existants.prepend: Insère les nouveaux résultats au-dessus des résultats existants.silent: Affiche les résultats dans la zone d'écho Emacs / le minibuffer sans les écrire dans le tampon Org.
6. Visualisations & Figures dans les Cahiers
Pour générer des graphiques et des figures qui se rendent directement dans le cahier :
6.1 Graphique Python Matplotlib
#+NAME: graphique-distribution-normale
#+BEGIN_SRC python :session *py-vis* :results graphics file :file figures/dist_normale.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("Distribution Normale Standard")
plt.xlabel("z")
plt.ylabel("Densité")
plt.tight_layout()
plt.savefig("figures/dist_normale.png", dpi=150)
plt.close()
#+END_SRC
#+RESULTS: graphique-distribution-normale
[[file:figures/dist_normale.png]]
6.2 Graphique R ggplot2
#+NAME: r-diagramme-dispersion
#+BEGIN_SRC R :session *r-vis* :results graphics file :file figures/r_dispersion.png :exports both
library(ggplot2)
p <- ggplot(mtcars, aes(x=wt, y=mpg, color=factor(cyl))) +
geom_point(size=3) +
theme_minimal() +
labs(title="Efficacité vs Poids", x="Poids (1000 lbs)", y="MPG")
ggsave("figures/r_dispersion.png", plot=p, width=6, height=4, dpi=150)
#+END_SRC
#+RESULTS: r-diagramme-dispersion
[[file:figures/r_dispersion.png]]
Astuce : Exécutez M-x org-toggle-inline-images (C-c C-x C-v) dans Emacs pour activer/désactiver l'affichage des graphiques générés directement dans le tampon.
7. Exécution Interactive & Flux de Travail d'Édition
Raccourcis Clavier Essentiels
| Raccourci | Commande | Description |
|---|---|---|
C-c C-c | org-babel-execute-src-block | Exécute le bloc sous le curseur et insère/met à jour les résultats. |
C-c ' | org-edit-special | Ouvre le corps du bloc dans un tampon dédié au mode majeur (LSP complet, coloration syntaxique, autocomplétion). Appuyez à nouveau sur C-c ' pour enregistrer et revenir. |
C-c C-v b | org-babel-execute-buffer | Exécute tous les blocs de code du cahier séquentiellement (comme "Exécuter toutes les cellules"). |
C-c C-v s | org-babel-execute-subtree | Exécute tous les blocs de code sous le titre actuel. |
C-c C-o | org-open-at-point | Sur un lien #+RESULTS:, ouvre l'image ou le fichier généré dans un visualiseur externe ou un tampon. |
M-<down> | org-babel-pop-to-session | Bascule le focus directement vers le tampon REPL interactif pour la session de ce bloc. |
C-c C-v v | org-babel-expand-src-block | Prévisualise le bloc entièrement développé (avec toutes les expansions :var et noweb résolues). |
8. Exportation & Publication de Rapports
Un cahier peut être tissé en documents HTML, PDF ou Markdown autonomes.
8.1 Contrôle du Contenu Exporté avec :exports
:exports code: Exporte uniquement le bloc de code.:exports results: Exporte uniquement la sortie/la figure (masque le code, idéal pour les rapports exécutifs).:exports both: Exporte à la fois le code et la sortie (idéal pour la documentation technique).:exports none: Masque à la fois le code et les résultats du document exporté (s'exécute pendant l'évaluation si nécessaire).
8.2 Contrôle de l'Évaluation lors de l'Exportation avec :eval
:eval never-export: Empêche la ré-exécution des blocs pendant l'exportation, en utilisant les#+RESULTS:existants dans le tampon. Accélère l'exportation et empêche les changements non déterministes.:eval query-export: Demande confirmation avant d'exécuter pendant l'exportation.
8.3 Commandes d'Exportation
C-c C-e h h(org-html-export-to-html) : Génère un rapport HTML.C-c C-e h o(org-html-export-to-html-and-open) : Génère et ouvre le HTML dans votre navigateur.C-c C-e l p(org-latex-export-to-pdf) : Génère un rapport PDF composé via LaTeX.
9. Pièges Courants & Liste de Dépannage
| Symptôme / Erreur | Cause | Solution |
|---|---|---|
| Un bloc Python ne retourne aucun résultat | Utilisation de :results value avec print(...) au lieu de return ... | Remplacez print(...) par return ..., ou changez l'en-tête en :results output. |
No org-babel-execute function for <lang> | La langue n'est pas enregistrée dans org-babel-load-languages | Ajoutez (<lang> . t) à org-babel-load-languages dans votre config Emacs et évaluez-le. |
| Le lien d'image est produit mais l'image ne s'affiche pas | Les images en ligne sont désactivées dans Org Mode | Exécutez M-x org-toggle-inline-images (C-c C-x C-v) ou ajoutez #+STARTUP: inlineimages. |
Les sorties de session contiennent des invites répétées (>>>) | Texte d'invite interactif persistant dans le tampon REPL | Nettoyez ou redémarrez le tampon de session (*nom-session*). |
| L'exportation est lente ou se bloque | Org réexécute tous les blocs du cahier pendant l'exportation | Définissez :eval never-export sur les blocs de longue durée ou globalement via #+PROPERTY: header-args :eval never-export. |
| L'entrée de tableau produit une erreur sur les règles horizontales | Les règles horizontales (` | -`) sont traitées comme des lignes brutes |
10. Exemple de Cahier Annoté Complet
#+TITLE: Analyse de la Fréquence des Éruptions Solaires
#+AUTHOR: Dr. Alex Rivera
#+DATE: [2025-05-18 Dim]
#+OPTIONS: toc:nil num:nil
#+STARTUP: inlineimages nohideblocks
#+PROPERTY: header-args :session *solar* :results drawer :exports both
* Introduction
Nous analysons les fréquences d'éruptions observées enregistrées sur une fenêtre d'observation de 6 mois.
* Observations Brutes
Voici le tableau de mesure de base :
#+NAME: observations_brutes
| Mois | Classe C | Classe M | Classe X |
|------+----------+----------+----------|
| Jan | 12 | 3 | 0 |
| Fév | 19 | 5 | 1 |
| Mar | 15 | 2 | 0 |
| Avr | 27 | 8 | 2 |
| Mai | 31 | 11 | 3 |
| Jui | 22 | 7 | 1 |
* Traitement des Données & Statistiques Récapitulatives
Nous calculons l'activité solaire totale à l'aide de Python :
#+NAME: calcul_totaux
#+BEGIN_SRC python :var obs=observations_brutes :colnames yes :results value table
total_events = []
for row in obs:
month = row[0]
total = sum(row[1:])
total_events.append([month, total])
return [["Mois", "Total Événements"]] + total_events
#+END_SRC
#+RESULTS: calcul_totaux
:RESULTS:
| Mois | Total Événements |
|------+------------------|
| Jan | 15 |
| Fév | 25 |
| Mar | 17 |
| Avr | 37 |
| Mai | 45 |
| Jui | 30 |
:END:
* Visualisation
Nous traçons l'activité au fil du temps.
#+NAME: graphique_activite_solaire
#+BEGIN_SRC python :session *solar* :results graphics file :file figures/activite_solaire.png :exports both
import matplotlib.pyplot as plt
months = ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jui']
totals = [15, 25, 17, 37, 45, 30]
plt.bar(months, totals, color='#d62728')
plt.title("Activité Solaire par Mois")
plt.xlabel("Mois")
plt.ylabel("Nombre Total d'Événements")
plt.savefig("figures/activite_solaire.png", dpi=150)
plt.close()
#+END_SRC
#+RESULTS: graphique_activite_solaire
[[file:figures/activite_solaire.png]]
* Conclusion
L'activité solaire a atteint un pic en mai avec 45 événements totaux, suggérant une corrélation avec le pic du cycle solaire actuel.