Communityコーディング&開発github.com

pwquint24/org-babel-polyglot

複数言語によるデータ分析パイプライン、ブロック間変数受け渡し、org-sbeによるテーブル数式、リモートブロック呼び出しをOrg Babelで実現するためのガイド。

org-babel-polyglot とは?

このスキルは、Emacs Org Babelの多言語相互運用機能を最大限に活用するための包括的なガイドです。SQL、Python、R、Shell、Gnuplotなど、異なるプログラミング言語間でデータをシームレスに受け渡し、同一ドキュメント内で統合的なデータ分析パイプラインを構築する方法を解説します。特に、`:var`ヘッダー引数による変数受け渡し、`:colnames`や`:hlines`などのテーブル前処理オプション、`#+CALL:`によるブロックのプログラム的呼び出し、`org-sbe`マクロを使ったテーブル数式内でのBabelブロック呼び出し、`:post`ヘッダーによる結果の後処理、そしてLibrary of Babelによる関数ライブラリの共有といった高度な機能をカバーしています。このスキルは、Emacs Org-modeのユーザーであれば誰でも利用可能で、Orgファイル上で再現可能なデータ分析レポートを作成するための強力な基盤を提供します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/pwquint24/org-babel-agent-skills/tree/HEAD/skills/org-babel-polyglot

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Org Babelによる多言語データパイプラインと相互運用性

Org Babelの最も強力な機能の一つは、言語に依存しない相互運用性です。データは、同一ドキュメント内で、異なるプログラミング言語、Orgテーブル、数式の間をシームレスに流れることができます。

単一のノートブックで、SQLによるデータクエリ、Pythonによる変換とクリーニング、Rによる統計モデリング、Shellによるシステムコマンドの実行、GnuplotやMatplotlibによる可視化を、Org Modeをユニバーサルデータバスとして実現できます。


1. ユニバーサルデータ受け渡しメカニズム :var

:var ヘッダー引数は、サポートされているすべての言語で同一の構文を持ちます。

:var 変数名=代入
:var 変数名=ファイル:参照先

1.1 :var でサポートされる代入の種類

入力ソース構文例備考
リテラル値:var n=42 text="sample"数値、真偽値、文字列。
名前付きOrgテーブル:var data=raw_measurements2次元テーブルをネストされたリスト/データフレームとして渡します。
名前付きプレーンリスト:var items=todo_listトップレベルのリストアイテムを配列/ベクターとして渡します。
引数なしのコードブロック:var stats=compute_stats()compute_stats を実行し、その戻り値を渡します。
引数付きのコードブロック:var res=calc(rate=0.05, n=12)指定されたパラメータでターゲットブロックを評価します。
ブロック内容 / リテラル:var template=config_template[]サンプルまたはコードブロックの本文をそのまま渡します。
Emacs Lisp評価:var file=(buffer-file-name)( [ ' ` で始まる値はElispとして評価されます。
外部Orgファイル:var ext=other.org:data_table外部ファイル内の参照を解決します。

2. Orgテーブルの受け渡しとフォーマット

Orgテーブルは自動的にネイティブな構造(Pythonのリストのリスト、Rのデータフレームなど)に変換されます。ヘッダー引数でテーブルの前処理を制御できます。

2.1 テーブル前処理用引数

  • :colnames yes | no | nil (デフォルト: nil):
    • nil の場合: テーブルの1行目に水平線がある場合、Babelはヘッダーを削除し、行を変換し、返されるテーブルに再適用します。
    • yes の場合: 1行目を明示的に列名/ヘッダーとして扱います。
    • no の場合: 1行目を通常のデータとして扱います。
  • :rownames yes | no (デフォルト: no):
    • yes の場合: 処理前に最初の列を削除し、戻り値で行名として復元します。
  • :hlines yes | no (デフォルト: no):
    • no (推奨) の場合: データをインタープリタに渡す前に、すべての水平線(|-...)を削除します。これにより、型エラー(数値ルーチンでの None や 'hline' 文字列のクラッシュなど)を防ぎます。
    • yes の場合: 水平線を 'hline シンボルとしてそのまま残します。

2.2 :var でのテーブルのスライスとインデックス指定

0ベースのインデックス記法 [行, 列] を使用して、:var 宣言内で直接行と列をスライスできます。

#+NAME: full_dataset
| ID | Age | Score |
|----+-----+-------|
|  1 |  25 |  88.5 |
|  2 |  30 |  92.0 |
|  3 |  22 |  79.0 |
|  4 |  35 |  95.5 |

# 'Score'列のみを渡す場合 (列インデックス2、行1から最後まで):
#+BEGIN_SRC python :var scores=full_dataset[1:, 2] :results value
  return sum(scores) / len(scores)
#+END_SRC

#+RESULTS:
: 88.75
  • インデックス構文:
    • data[0] : 最初の行。
    • data[,1] : 全行の2列目。
    • data[1:3, :] : 行1から3まで、全列(包括)。
    • data[-1] : 最後の行。
    • data[*] または data[0:-1] : 全行。
  • 重要: インデックス指定は、:colnames や :hlines による削除の前に行われます。

3. 多言語パイプラインパターン: SQL -> Python -> R -> グラフィックス

以下は、完全な多言語データワークフローの例です。

#+TITLE: 多言語パイプライン

* ステップ 1: データベースクエリ (SQLite)
生のイベントカウントを抽出:

#+NAME: raw_events
#+BEGIN_SRC sqlite :db telemetry.db :results table :colnames yes
  SELECT device_id, event_type, count(*) as count
  FROM events
  WHERE event_date >= '2025-01-01'
  GROUP BY device_id, event_type;
#+END_SRC

#+RESULTS: raw_events
| device_id | event_type | count |
|-----------+------------+-------|
| dev-01    | heartbeat  |  1420 |
| dev-01    | alert      |    12 |
| dev-02    | heartbeat  |  1380 |
| dev-02    | alert      |    45 |

* ステップ 2: データクリーニングと正規化 (Python)
SQLiteテーブルをPythonで処理:

#+NAME: clean_telemetry
#+BEGIN_SRC python :var events=raw_events :colnames yes :results value table
  cleaned = []
  for row in events:
      dev_id, ev_type, count = row[0], row[1], int(row[2])
      rate = round(count / 1440.0, 4)  # 1分あたりのイベント数
      cleaned.append([dev_id, ev_type, count, rate])
  return [["Device", "Event", "Count", "RatePerMin"]] + cleaned
#+END_SRC

#+RESULTS: clean_telemetry
| Device | Event     | Count | RatePerMin |
|--------+-----------+-------+------------|
| dev-01 | heartbeat |  1420 |     0.9861 |
| dev-01 | alert     |    12 |     0.0083 |
| dev-02 | heartbeat |  1380 |     0.9583 |
| dev-02 | alert     |    45 |     0.0313 |

* ステップ 3: 統計モデリング (R)
Pythonでクリーニングされた出力をRで処理:

#+NAME: r_stat_summary
#+BEGIN_SRC R :var df=clean_telemetry :colnames yes :results value table
  summary_df <- aggregate(RatePerMin ~ Event, data=df, FUN=mean)
  colnames(summary_df) <- c("Event", "AvgRatePerMin")
  summary_df
#+END_SRC

#+RESULTS: r_stat_summary
| Event     | AvgRatePerMin |
|-----------+---------------|
| alert     |        0.0198 |
| heartbeat |        0.9722 |

4. ブロックのプログラム的呼び出し: #+CALL: とインライン call_

名前付きブロックを再利用可能な関数として扱い、ブロックを書き直すことなく呼び出すことができます。

4.1 構文

# ブロック形式:
#+CALL: <名前>[<内部評価ヘッダー>](<引数>) <結果ヘッダー>

# インライン形式:
... call_<名前>[<内部評価ヘッダー>](<引数>)[<結果ヘッダー>] ...
  • <名前>: ターゲットブロックの #+NAME:。
  • <引数>: 関数形式のパラメータ。例: (n=5, metric="accuracy")。
  • [<内部評価ヘッダー>]: 実行時に適用されます(例: [:results output])。
  • [<結果ヘッダー>]: 返された結果のフォーマットに適用されます(例: :results raw)。

4.2 例

#+NAME: power_calc
#+BEGIN_SRC python :var base=2 exp=3 :results value
  return base ** exp
#+END_SRC

# カスタム引数での呼び出し:
#+CALL: power_calc(base=3, exp=4)

#+RESULTS:
: 81

# インライン呼び出し:
総容量は call_power_calc(base=2, exp=8) 単位です。

5. リアクティブスプレッドシート: テーブル数式でのBabel (org-sbe)

Org Modeのテーブルには、組み込みのスプレッドシート計算機能 (#+TBLFM:) があります。org-sbe マクロを使用すると、任意のテーブルセル数式から、任意の言語で書かれたBabelブロックを呼び出すことができます。

(org-sbe "ブロック名" (パラメータ1 値1) (パラメータ2 値2))
;; 短縮形:
(sbe "ブロック名" (パラメータ1 値1))

例: PythonとRの数式で動作するテーブル

#+NAME: py_tax_rate
#+BEGIN_SRC python :var country="US" :results value
  rates = {"US": 0.08, "UK": 0.20, "DE": 0.19}
  return rates.get(country, 0.0)
#+END_SRC

#+NAME: invoice_table
| Item        | Country | Base Price | Tax Rate | Total Price |
|-------------+---------+------------+----------+-------------|
| Server Node | US      |       1200 |     0.08 |      1296.0 |
| Storage SAN | UK      |       3000 |      0.2 |      3600.0 |
| Gateway     | DE      |        800 |     0.19 |       952.0 |
#+TBLFM: $4='(org-sbe "py_tax_rate" (country $"$2")) :: $5=$3 * (1 + $4)
  • テーブルで、#+TBLFM 行にカーソルを合わせ C-u C-c C-c と入力すると、すべての数式が再計算され、各行に対してPythonブロックが動的に評価されます。
  • $"$2" は、2列目の値を文字列リテラルとして渡します。

6. :post による結果の後処理

:post ヘッダー引数は、ブロックの出力をドキュメントに挿入する前に、2番目のフォーマットブロックにチェーンします。プライマリブロックの出力は *this* にバインドされます。

# カスタムLaTeXエクスポート属性を追加する後処理ブロック:
#+NAME: wrap_latex_figure
#+BEGIN_SRC emacs-lisp :var data="" caption="Figure"
  (format "#+CAPTION: %s\n#+ATTR_LATEX: :width 0.8\\textwidth\n%s" caption data)
#+END_SRC

# :post を使用したプライマリプロットブロック:
#+BEGIN_SRC python :results file link :file figures/out.pdf :post wrap_latex_figure(caption="Model Accuracy", data=*this*)
  import matplotlib.pyplot as plt
  plt.plot([1, 2, 3], [4, 5, 6])
  plt.savefig("figures/out.pdf")
  plt.close()
  return "figures/out.pdf"
#+END_SRC

7. Library of Babel: 共有グローバル関数ライブラリ

Library of Babel を使用すると、コードを複製することなく、複数の異なる .org ファイル間で名前付きブロックをインポートして共有できます。

  1. 名前付きブロックを含む共有ライブラリファイル(例: ~/org/shared_babel.org)を作成します。
  2. Emacsセッションでライブラリを取り込みます:
    (org-babel-lob-ingest "~/org/shared_babel.org")
    
    またはキーボードショートカット: C-c C-v i (org-babel-lob-ingest)。
  3. 任意のバッファで、#+CALL: block_name(args) を使用してそのファイルのブロックを呼び出せます。

8. 多言語環境での一般的な落とし穴とベストプラクティス

落とし穴結果対策
列ヘッダーの不一致ヘッダー文字列が0行目のデータとして読み取られるため、数値計算が失敗するテーブル入力を消費するブロックでは、常に :colnames yes を指定する。
テーブルの水平線 (`-`) によるクラッシュ数値が期待される場所に 'hline' シンボルが渡され、Python/Rがクラッシュする
:var での名前なしブロックReference 'xyz' not found in buffer エラー入力ブロックまたはテーブルの直前に、対応する #+NAME: xyz キーワードがあることを確認する。
org-sbe でのテーブルセル変数の引用引用符なしのテキストセルは、未定義のLispシンボルとして読み取られる列参照を文字列で囲む: (country $"$2")。
ヘッダーを使用した場合のインデックス順序data[0] がインデックス時にヘッダー行を返すテーブルのスライスは :colnames による削除の前に行われることに注意。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

関連スキル