Community编程与开发github.com

pwquint24/org-babel-polyglot

构建多语言数据分析流水线、跨代码块变量传递、使用org-sbe进行表格公式运算以及在Org Babel中进行远程代码块调用的指南。

org-babel-polyglot 是什么?

此技能专为Org Babel用户设计,用于构建强大的多语言互操作数据分析工作流。它详细介绍了如何在一个Org文档中无缝集成SQL、Python、R、Shell、Gnuplot或Matplotlib等多种编程语言,通过`:var`头参数实现数据在语言之间的通用传递机制。该技能涵盖了表格预处理(如`:colnames`、`:rownames`、`:hlines`)、表格切片索引、使用`#+CALL:`和行内`call_`进行程序化块调用、利用`org-sbe`宏在Org表格公式中调用任意Babel块构建响应式电子表格、结果后处理(`:post`)以及通过Library of Babel共享全局函数库。它还指出了常见的多语言陷阱(如列名不匹配、表格水平线导致崩溃、未命名块引用错误等)并提供了最佳实践解决方案。此技能适用于Claude Code、Cursor、Codex、Emacs等支持Org Babel标准的环境,帮助用户构建从数据查询、清洗、建模到可视化的完整跨语言分析流水线。

兼容平台~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 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

使用 Org Babel 的多语言数据流水线与互操作性

Org Babel 最强大的功能之一是语言无关的互操作性:数据可以在同一文档中的不同编程语言、Org 表格和公式之间无缝流动。

单个笔记簿可以通过 SQL 查询数据,在 Python 中进行转换和清洗,在 R 中执行统计建模,在 Shell 中运行系统命令,并在 Gnuplot 或 Matplotlib 中可视化结果——而 Org Mode 则充当通用数据总线。


1. 通用数据传递机制::var

:var 头参数在所有支持的语言中具有相同的语法:

:var VARIABLE_NAME=ASSIGNMENT
:var VARIABLE_NAME=FILE:REFERENCE

1.1 :var 支持的赋值类型

输入源语法示例备注
字面量:var n=42 text="sample"数字、布尔值和字符串。
命名 Org 表格:var data=raw_measurements将二维表格作为嵌套列表/数据框传递。
命名纯列表: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 时:显式地将第一行视为列名/表头。
    • 当为 no 时:将第一行视为普通数据。
  • :rownames yes | no(默认值:no):
    • 当为 yes 时:在处理前剥离第一列,并在返回时将其恢复为行名。
  • :hlines yes | no(默认值:no):
    • 当为 no(推荐)时:在将数据传递给解释器之前剥离所有水平线(|-...)。这可以防止类型错误(例如 None 或 'hline' 字符串导致数值例程崩溃)。
    • 当为 yes 时:保留水平线作为 'hline 符号。

2.2 在 :var 中切片和索引表格

您可以在 :var 声明中使用基于0的索引符号 [rows, columns] 直接对行和列进行切片:

#+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] :所有行的第二列。
    • 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)  # 每分钟事件数
      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: <name>[<inside_eval_headers>](<arguments>) <end_result_headers>

# 行内形式:
... call_<name>[<inside_eval_headers>](<arguments>)[<end_result_headers>] ...
  • <name>:目标代码块的 #+NAME:。
  • <arguments>:函数式参数,例如 (n=5, metric="accuracy")。
  • [<inside_eval_headers>]:在执行期间应用(例如 [:results output])。
  • [<end_result_headers>]:应用于格式化返回的结果(例如 :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 "block-name" (param1 value1) (param2 value2))
;; 简写:
(sbe "block-name" (param1 value1))

示例:由 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 头参数将代码块的输出在插入文档之前通过一个格式化代码块进行链式处理。主代码块的输出被绑定到 *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. Babel 库:共享全局函数库

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。
表格水平线(`-`)导致崩溃Python/R 收到 'hline' 符号,而期望的是数字
: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.

相关技能