Community코딩 & 개발github.com

pwquint24/org-babel-polyglot

Org Babel을 활용한 다중 언어 데이터 분석 파이프라인 구축, 블록 간 변수 전달, org-sbe 테이블 수식, 원격 블록 호출 가이드

org-babel-polyglot란 무엇인가요?

Org Babel의 가장 강력한 기능 중 하나는 언어에 구애받지 않는 상호 운용성입니다. 하나의 Org 문서 내에서 SQL, Python, R, Shell, Gnuplot 등 여러 프로그래밍 언어 간에 데이터를 원활하게 전달하고 처리할 수 있습니다. `:var` 헤더 인자를 통해 Org 테이블, 코드 블록 결과, 리터럴 값 등을 직접 변수로 전달할 수 있으며, `:colnames`, `:hlines` 등의 옵션으로 테이블 전처리를 세밀하게 제어할 수 있습니다. 또한 `#+CALL:` 및 인라인 `call_` 구문을 사용해 재사용 가능한 함수처럼 블록을 호출하고, `org-sbe`를 통해 테이블 수식 내에서 어떤 언어의 블록이든 계산에 활용할 수 있습니다. `:post`를 통한 결과 후처리와 Library of Babel을 통한 전역 함수 공유까지 지원하여, 복잡한 다국어 데이터 파이프라인을 단일 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 VARIABLE_NAME=ASSIGNMENT
:var VARIABLE_NAME=FILE:REFERENCE

1.1 :var가 지원하는 할당 유형

입력 소스구문 예시참고 사항
리터럴 값:var n=42 text="sample"숫자, 불리언, 문자열
명명된 Org 테이블:var data=raw_measurements2D 테이블을 중첩 리스트/데이터 프레임으로 전달
명명된 일반 목록: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. 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를 지정합니다.
테이블 가로줄 (`-`)로 인한 충돌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.

관련 스킬