Skip to content
Published on

2026年の新しい Python ノートブックスタック — Marimo・Quarto と Jupyter 以後のデータワークフロー(Observable・Pluto・Polars・DuckDB まで)

Authors

プロローグ — Jupyter の隠れた状態問題

2026 年でも毎週のようにどこかで同じ場面が繰り返される。データサイエンティストが同僚にノートブックを渡し、同僚が「Run All」を押すと、5 つ目のセルで爆発する。変数が定義されていないと言う。送った人の画面ではちゃんと動いている。

原因はほぼ常に同じ — **隠れた状態(hidden state)**だ。セルを上から下に一度も回さないまま、途中のセルだけ何度も実行していると、ノートブックのテキストとカーネルのメモリ状態がずれていく。送り主のカーネルには df が生きているが、ノートブックファイルからは df を作るセルが消えている。

これは Jupyter のバグではない。設計判断だ。Jupyter はセルを任意の順序で実行できることを資産と見なした — それが REPL の本質だから。しかしその自由は協業・再現性・CI に入ると負債になる。

「ノートブックは 1 人なら天国、2 人以上では地獄」

2024 年から 2026 年にかけて、この負債を新しい設計で返そうとする動きが本格化した。Marimo はセル間のデータフローグラフを静的解析し、依存関係に従って自動再実行する — セル順序ではなく、データ依存性こそが真実だ。Quarto はノートブックを入力として受け取り、HTML・PDF・ウェブサイト・論文補助として出版する多言語・多フォーマットエンジン。Observable Framework は JS ネイティブノートブックを静的サイトとしてビルドする。それらの下にあるコンピュート層も Pandas から Polars・DuckDB へ移っている。

この記事ではそのスタックを深く見る — Jupyter が何を間違え、Marimo がどう解決し、Quarto でどう出版し、いつまだ Jupyter を使うべきか。


1 章 · Jupyter の 4 つの負債

Jupyter(正確には IPython カーネル + ipynb JSON + Notebook/Lab UI の組み合わせ)は 2014 年以来のデータサイエンス標準環境だった。やったことは多い — 出力をコードの隣に置き、インライン可視化・Markdown・LaTeX を組み合わせて「実行可能な文書」を作った。しかし 12 年が経って 4 つの負債が積み上がった。

負債 1 — 隠れた状態

セルを In[14]、In[3]、In[27] の順で実行しても、ノートブックファイルにその順序は残らない。誰かが「Run All」を押したとき同じ結果が出る保証はない。真実はノートブックファイルではなくメモリに住むカーネルにあり、そのカーネルは終了した瞬間に消える。

負債 2 — diff 不可能(.ipynb は JSON)

.ipynb はコード・Markdown・画像・出力・実行カウンターを 1 つの JSON ファイルに詰める。PR レビュアーが GitHub で diff を開くと、base64 エンコードされた PNG が一行で延々と続く。nbstripoutjupytext のような回避策が必要なこと自体がシグナル — フォーマットが git と本質的に合わない。

負債 3 — 再現不能

上の 2 つを合わせると再現不能になる。「このノートブックを回した」が「この環境、この順序、このデータで回した」を意味しない。論文補助ノートブックを 6 か月後に開けば依存関係が壊れ、出力はコードとずれている。

負債 4 — UI の一方向性

Jupyter は「上から下へ」の線形モデルを前提とする。データ依存性を見せるグラフビューがない — このセルの出力がどこへ流れるか、どのセルがどのセルを呼ぶかを知るには、人間が頭で追跡するしかない。セルが 50 を超えたら事実上不可能だ。

これら 4 つの負債は互いを強化する。隠れた状態があるから再現できず、再現できないから diff を見ても意味がなく、diff が無意味だから協業もできない。


2 章 · Marimo — ノートブックを再設計する

Marimo は 2023 年後半に登場し、2026 年中頃時点で 0.10.x シリーズに到達したオープンソース Python ノートブックだ。出発点は単純 — 「ノートブックを最初から再設計したら何が変わるか」

3 つの核となる設計判断:

判断 1 — .py ファイルとして保存

Marimo ノートブックは .ipynb ではなく 純粋な Python ファイルだ。各セルはデコレータが付いた関数。

import marimo as mo

app = mo.App()


@app.cell
def __():
    import polars as pl
    df = pl.read_csv("sales.csv")
    return (df,)


@app.cell
def __(df):
    summary = df.group_by("region").agg(pl.col("revenue").sum())
    return (summary,)


@app.cell
def __(summary):
    summary
    return ()


if __name__ == "__main__":
    app.run()

ここで 2 つのことが起きる。第一に、このファイルは そのまま Python スクリプトとしても動くpython notebook.py だけで終わり。第二に、git diff に意味がある。変数名を変えれば diff に変数名の変更が見える。base64 エンコードされた PNG はどこにもない。

判断 2 — データフローグラフ、セル順序ではなく

Marimo はノートブックを静的解析して、各セルがどの変数を定義し、どの変数を参照するかを見つける。これが 有向非巡回グラフ(DAG) になる。セルを実行すると、Marimo はそのセルに 依存する 全セルを自動的に再実行する — Excel スプレッドシートと同じモデルだ。

結果: 隠れた状態は 不可能になる。変数を再代入すれば、それを使う全セルが即時更新される。セルを削除すれば、その変数を使うセルは即「未定義」エラーを出す。「Run All」が常に動くノートブックファイルと、メモリ内のカーネル状態が一致する。

判断 3 — ノートブック as アプリモード

Marimo は同じファイルを 2 つのモードで起動できる:

  • 編集モード — セルを書いて実行する通常のノートブック。
  • 実行モードmarimo run notebook.py で起動すると、セルが隠れて UI コンポーネント(スライダー・ドロップダウン・テーブル)だけが見える インタラクティブアプリ になる。

UI コンポーネントは mo.ui モジュールで定義する。

@app.cell
def __(mo):
    region = mo.ui.dropdown(
        options=["US", "EU", "APAC"],
        value="US",
        label="Region",
    )
    region
    return (region,)


@app.cell
def __(df, region):
    filtered = df.filter(pl.col("region") == region.value)
    filtered
    return (filtered,)

ドロップダウンの値が変わると、それを使うセルが自動的に再実行される。Streamlit/Gradio なしでノートブック自体がアプリになる。

Marimo がうまく解く他の問題

  • WASM デプロイ — Pyodide を通じてノートブックをただの静的 HTML として export できる。サーバー不要。ブラウザで直接触れる。
  • AI アシスト — セルごとの AI 補完(モデル非依存)が内蔵。
  • 型チェック.py ファイルだから mypy/pyright がそのまま動く。
  • テスト — ノートブックセルを pytest に import してユニットテストできる。
  • 純粋関数セル — 各セルが明示的な入出力を持つ関数だから、ユニットテストが自然。

3 章 · Jupyter vs Marimo — 同じ作業、異なるコード

同じワークフロー(データロード → フィルタ → 可視化)を 2 つのノートブックでどう表現するか。

Jupyter のセル

# Cell 1
import pandas as pd
df = pd.read_csv("sales.csv")
df.head()

# Cell 2
df_us = df[df["region"] == "US"]
df_us.shape

# Cell 3
import matplotlib.pyplot as plt
df_us.groupby("month")["revenue"].sum().plot(kind="bar")
plt.show()

# Cell 4 — 30 分後、df の定義を忘れて書き直す
df = pd.read_csv("sales_v2.csv")  # 以前の df_us はまだ古いデータを指している!

Cell 4 を実行してチャート(Cell 3)を見直すと — df_us が古い df をキャプチャしているからチャートは更新されない。メモリ状態とコード状態がずれた。隠れた状態。

Marimo のセル

@app.cell
def __():
    import polars as pl
    df = pl.read_csv("sales.csv")
    df
    return (df, pl)


@app.cell
def __(df, pl):
    df_us = df.filter(pl.col("region") == "US")
    df_us
    return (df_us,)


@app.cell
def __(df_us):
    chart = df_us.group_by("month").agg(pl.col("revenue").sum())
    chart
    return (chart,)

ここで最初のセルの dfsales_v2.csv に変えると — Marimo は df に依存する 2 つ目、それに依存する 3 つ目のセルを 自動的に再実行する。チャートは即時更新。メモリ状態とコード状態が本質的に同期する。

さらに、Marimo は 同じ変数を 2 つのセルで再定義することを禁じる。「その変数は別のセルで定義済み」というエラーを出し、どちらか 1 つを選ばせる。最初はもどかしいが、隠れた状態が永遠に消える


4 章 · Quarto — ノートブックを出版可能な文書に

Marimo が「ノートブックの再設計」なら、Quarto は「ノートブックの出版」だ。Quarto は RStudio の RMarkdown の後継として 2022 年に 1.0 を打ち、2026 年時点で 1.5+ に到達している。多言語(Python・R・Julia・Observable JS)・多フォーマット(HTML・PDF・EPUB・Word・ウェブサイト・本)を 1 つのツールで扱う。

Quarto 文書の形

Quarto 文書は .qmd 拡張子を使い、YAML フロントマター + Markdown + コードフェンスで構成される。

---
title: "2025 Sales Analysis"
author: "Data Team"
date: "2026-05-14"
format:
  html:
    code-fold: true
    toc: true
  pdf:
    documentclass: article
execute:
  echo: true
  warning: false
---

## データロード

今四半期の売上を分析する。

\`\`\`{python}
import polars as pl
df = pl.read_csv("sales.csv")
df.head()
\`\`\`

## 地域別合計

\`\`\`{python}
df.group_by("region").agg(pl.col("revenue").sum())
\`\`\`

(上の例では MDX パーサーが混乱しないようにバックティックフェンスをエスケープしている。実際の .qmd では普通にバックティック 3 つを使う。)

このファイル 1 つを quarto render report.qmd --to html でビルドするとインタラクティブ HTML が、--to pdf でビルドすると LaTeX 品質の PDF が出る。コードが実行され、出力が文書に埋まる。

Quarto が対応する出力フォーマット

  • HTML — 検索・テーマ・インタラクティブウィジェット・Mermaid 図・MathJax。
  • PDF(LaTeX) — 学術論文水準の組版。book クラスで本も作れる。
  • ウェブサイト — 複数の .qmd を束ねて静的サイトをビルド(Jekyll/Hugo と同じモデル)。
  • — 多章 PDF/HTML 本。実際 R for Data Science は Quarto でビルドされている。
  • Reveal.js スライド — コード実行結果をスライドに入れる。
  • Word・EPUB — 出版社ワークフロー向け。

多言語 — Python・R・Julia・Observable JS を 1 ファイルで

Quarto はコードフェンスの言語識別子を見て適切なカーネルを起動する。python フェンスは Jupyter カーネル、r フェンスは knitr、julia フェンスは IJulia、{ojs} フェンスは Observable JS ランタイムで実行される。同じ文書内で Python でデータを加工し、R で統計モデルを回し、Observable JS でインタラクティブチャートを描くことが可能だ。

Quarto + Marimo、Quarto + Jupyter

Quarto は入力として .qmd 以外にも Jupyter ノートブック(.ipynb)Marimo ノートブック を受け取る。つまり Quarto はノートブックフォーマット非依存。「Marimo で作業し Quarto で出版する」ワークフローが自然だ。

学術論文と補助資料

Quarto は Journal of Statistical Software のような学術誌の公式テンプレートがある。APA・IEEE・Nature・Elsevier スタイルをフロントマター 1 行で切り替える。引用は BibTeX や Zotero から直接引き、CSL スタイルでフォーマットする。論文本文とその論文の分析コードが同じファイルに入る — 再現性の決定版


5 章 · Observable Framework — JS ネイティブノートブックの進化

Observable は 2018 年頃 Mike Bostock(d3.js の作者)が作った JS ネイティブノートブックだ。セルが反応的に結ばれ(Marimo のインスピレーション元)、可視化が一級市民。しかしホスティングが Observable.com に縛られる難点があった。

2024 年発表の Observable Framework はこのモデルを 静的サイトビルダー に持ってきた。.md ファイルに JS セルを書き、observable build を叩くと静的 HTML/CSS/JS が出る。ホスティングは Vercel・Netlify・GitHub Pages どこでも OK。

Observable Framework が得意な領域:

  • データダッシュボード — 社内分析サイト、KPI トラッキング、運用ダッシュボード。
  • インタラクティブデータストーリーテリング — d3 可視化、ユーザー入力に反応するグラフ、データジャーナリズム。
  • ビルド時データローダー — Python・R・Shell スクリプトでビルド時にデータを更新し、結果を静的アセットとして固める。

Marimo が「Python ユーザーがインタラクティブを欲しがるとき」の答えなら、Observable Framework は「JS と可視化中心のデータ出版」の答え。両者は競合というより別市場だ。


6 章 · Pluto.jl — Julia の反応型ノートブック、そして元祖

Pluto.jl は Julia 用の反応型ノートブックで、Marimo の直接的なインスピレーション元だ。2020 年に初めて登場し、「セル間データフローグラフ → 自動再実行」モデルをノートブック世界に最初に導入した。

Pluto が提示した核となる原則:

  • ノートブックファイルが真実の単一の源 — メモリ内カーネルではなく、ファイルが状態を定義する。
  • セル順序は意味を持たない — ファイルのどこにあっても依存性グラフで実行順序が決まる。
  • 同じ変数を 2 つのセルで再定義できない — グラフが曖昧になるから。
  • .jl ファイルとして保存 — そのまま Julia スクリプトとしても動く。

Marimo がこのモデルを Python の世界に持ってきた一方、Pluto は Julia データサイエンスコミュニティ(SciML・Plots.jl・DataFrames.jl)とより深く結合していった。Julia を使うなら Pluto が事実上のデフォルト選択。


7 章 · 新しいコンピュート層 — Polars・DuckDB

変わったのはノートブック UX だけではない。その下でデータを実際に転がすコンピュート層も変わった。

Polars — Rust ベースの DataFrame

Polars は Rust で書かれた DataFrame ライブラリで、Apache Arrow をネイティブメモリフォーマットとして使う。Pandas 比:

  • 速い — マルチスレッドがデフォルト、列指向メモリレイアウト、クエリ最適化。
  • lazy evaluation — クエリを構築し .collect() で一度に実行(Spark・SQL と同じモデル)。
  • API が一貫している — Pandas に積み上がった非一貫性(apply vs applymap、index 地獄など)がない。
  • メモリが小さい — Arrow zero-copy で複数のツールが同じデータを共有。

重いノートブックで Polars に移ると 5〜30 倍速くなることが普通にある。

DuckDB — インプロセス OLAP DB

DuckDB は SQLite の OLAP(分析)版だ。組み込み、列指向、シングルノード分析に最適化。ファイル・Parquet・CSV・JSON・Arrow を直接クエリする。

ノートブックで DuckDB が輝く瞬間:

import duckdb
duckdb.sql("""
    SELECT region, SUM(revenue)
    FROM 'sales/*.parquet'
    WHERE date >= '2026-01-01'
    GROUP BY region
""").to_df()

Spark クラスタなしでノートブックから 100GB の Parquet を直接クエリ。Polars と DuckDB は Arrow を通じて zero-copy でデータをやり取りするから、同じパイプラインで両方を混ぜてもコストはない。

このコンピュート層が Marimo・Quarto の下に敷かれると、単一ノートブックが扱えるデータ規模が一桁 GB から三桁 GB に上がる。


8 章 · 比較マトリックス

項目JupyterMarimoQuartoObservable FrameworkPluto.jl
言語Python(多カーネル)Python多言語JS 中心Julia
ファイル形式.ipynb(JSON).py.qmd.md.jl
反応型実行なしあり(DAG)なし(出版ツール)ありあり(元祖)
Git diff悪い良い良い良い良い
ノートブック as アプリ別途(Voila・Panel)内蔵別途静的サイトビルド部分的
静的 exportHTML(再実行なし)HTML/WASMHTML・PDF・サイト・本静的サイトHTML
学習曲線低い低〜中中(JS 要)低い
エコシステム規模巨大成長中大きい可視化中心中(Julia)
代表的用途EDA・教育・研究EDA・社内ツール・アプリレポート・論文・本・ブログダッシュボード・データジャーナリズムJulia 科学計算
ライセンスBSD-3Apache-2.0MITISCMIT

9 章 · 実際のワークフローシナリオ

シナリオ A · データサイエンティストの日常 EDA

  • 以前: Jupyter Lab + Pandas。セルを再実行して隠れた状態にハマる。結果は同僚に PNG スクリーンショットで送る。
  • 以後: Marimo + Polars + DuckDB。ノートブックが .py だから PR で上げる。同僚が marimo run で同じ結果を即再現。セルにスライダーを差し込んで「地域選択」UI を作れば、非データ系の同僚もそれを直接触る。

シナリオ B · 四半期レポート → 社内役員

  • 以前: Jupyter で分析 → PowerPoint にキャプチャ。次の四半期にデータが更新されれば、またキャプチャをやり直す。
  • 以後: Quarto ウェブサイト。四半期ごとにデータだけ更新して quarto render。結果は社内イントラネットに静的 HTML としてデプロイ。検索・テーマ・印刷用 PDF まで 1 つのビルドから出る。

シナリオ C · 学術論文 + 補助資料

  • 以前: LaTeX で論文、別途 .ipynb で分析コード。6 か月後にレビュアーコメントが来てもどのノートブックからどの図が出たか見つけられない。
  • 以後: Quarto + Marimo。論文本文と分析コードが同じ .qmd ファイルに。quarto render --to pdf で LaTeX 出力。補助資料は同じファイルを --to html でビルド。再現可能な論文。

シナリオ D · 社内データツール

  • 以前: Streamlit/Gradio で書いた別アプリ。データサイエンティストがノートブックで作業し、エンジニアがそれをアプリに移植 — 二度書き。
  • 以後: Marimo ノートブック as アプリ。ノートブックに mo.ui コンポーネントを差し込んで marimo run で起動。エンジニアリングハンドオフなし。同じファイルがノートブックでありアプリ。

シナリオ E · データジャーナリズム・インタラクティブストーリー

  • Observable Framework。d3.js 可視化、ユーザー入力に反応するチャート、ビルド時データローダー(Python でデータ加工 → 静的 JSON に固める)→ 静的サイト。

シナリオ F · Julia 科学計算

  • Pluto.jl。SciML・DifferentialEquations.jl のような Julia エコシステムが深く、反応型モデルがシミュレーション・パラメータスイープによく合う。

10 章 · いつまだ Jupyter を使うべきか

Marimo・Quarto が強力でも、Jupyter のほうが良い場所はまだある。

まだ Jupyter

  • 多言語カーネル依存が強いとき — Spark・PyTorch Distributed・Wolfram Language など特殊カーネルが必要な環境。Jupyter のカーネルプロトコルが標準だ。
  • JupyterHub・Binder のインフラがすでにあるとき — 大学・研究所が運用しているシステムを一気にひっくり返すのは難しい。
  • 使い捨ての速いスクラッチ — 「5 行の実験」1 つならセットアップオーバーヘッドが小さい Jupyter が速い。
  • 教育 — 学生にセル順序・In[N]/Out[N] のメンタルモデルを教えるのが講義の目的の場合。(ただし 2026 年には Marimo で教える講義も増えている。)
  • VS Code・Cursor の統合ノートブック UI.ipynb をそのまま開くワークフローが IDE に深く埋まっている。

Marimo に移るシグナル

  • ノートブックを協業資産として使っている(PR・レビュー)。
  • 「Run All」が壊れることが頻繁にある。
  • 社内ツールとしてノートブックをデプロイしたい。
  • 非データ系の同僚がノートブックを触ることがある。

Quarto に移るシグナル

  • 分析結果を定期的に外部に出版する(ブログ・レポート・論文)。
  • 同じコンテンツを複数フォーマット(HTML・PDF)で出す必要がある。
  • 本・多章文書を作る。
  • 多言語(Python・R・JS)分析を 1 文書にまとめたい。

11 章 · 移行 — どう移すか

Jupyter → Marimo

Marimo は marimo convert notebook.ipynb で自動変換を提供する。一度できれいに移るわけではない — 同じ変数を複数セルで再定義するなど Jupyter で合法だったパターンが Marimo ではエラー。最初の変換後 30 分〜1 時間ほど手を入れる覚悟が必要だ。

推奨: 新規ノートブックから Marimo で書き、既存の .ipynb はそのままにする。一気に全部移さないこと。

Jupyter → Quarto

Quarto は .ipynb をそのまま入力として受け取る — 変換は事実上不要。quarto render notebook.ipynb だけで済む。出力フォーマットが増えるのが核となる価値。

Marimo + Quarto の組み合わせ

最強の組み合わせ。Marimo で執筆・インタラクティブ探索 → 結果を Quarto で出版。両方とも .py.ipynb を入出力で扱うから、パイプラインがきれい。


12 章 · アンチパターン

この新しいスタックでも避けるべき罠がある。

アンチパターン 1 — Marimo を単なる Jupyter 代替として使う

Marimo の本当の価値は反応型・.py 保存・ノートブック as アプリ。それを使わずに単にセル単位でコードを書くツールとして使うと、学習曲線だけ払って得がない。UI コンポーネントを差し込み、反応型依存性を活用すること。

アンチパターン 2 — Quarto 文書に巨大な分析をそのまま書く

Quarto のビルド時間が爆発する。重いデータ加工は別スクリプトに分け、Quarto には結果データ(Parquet/CSV)だけロードさせる。または freeze: true でキャッシュ。

アンチパターン 3 — ノートブックを永遠にノートブックのままにする

Marimo・Quarto が良いからといって全コードをノートブックに置くのは罠だ。安定したデータパイプラインは普通の .py モジュールに移し、ノートブックは探索・文書・インターフェースの役割に限定する。ノートブックは終着点ではなくインターフェースだ。

アンチパターン 4 — Polars/DuckDB を試さず Pandas に固執する

データが GB 級を超えると Pandas はメモリ・速度の両面で崩れる。Polars への移行コストは 1 週間以下。その 1 週間で次の 1 年の待ち時間を半分にできる。

アンチパターン 5 — Marimo の変数再定義制約を回避しようとする

最初もどかしいからといって globals() ハックで回避するな。その制約こそが隠れた状態を防ぐ中核メカニズムだ。もどかしければ変数名を新しくつけ、セルを統合する。


エピローグ — ノートブックの次の 10 年

Jupyter は 2014〜2024 年のデータサイエンスワークフローの標準だった。2026 年にその標準が壊れているのではない — 標準が分化している。

  • 探索・社内ツール・アプリ → Marimo(または Julia なら Pluto)。
  • 出版・論文・本・多フォーマット → Quarto。
  • JS ネイティブ可視化・静的サイト → Observable Framework。
  • 使い捨ての速いスクラッチ・多言語カーネル → まだ Jupyter。

そして全員の下に Polars・DuckDB・Arrow が新しいコンピュート層として敷かれる。

核心は ノートブックがもう「コードを一度回すツール」ではない という点だ。ノートブックは 再現可能な協業資産(Marimo の .py)、出版可能な文書(Quarto)、インタラクティブアプリ(Marimo run mode)、データストーリー(Observable)— 4 つのインターフェースを 1 ファイルが全て提供できる時代になった。

ノートブックは終着点ではなくインターフェースだ。良いインターフェースはあらゆる読者に違う形で開かれる — データサイエンティストにはコード、同僚にはアプリ、役員には PDF、インターネットには静的サイト。

チェックリスト — 新しいノートブックワークフローに移るか決める

  • ノートブックを PR でレビューすることがあるか?(あれば Marimo へ。)
  • 「Run All」が壊れたことがあるか?(あれば Marimo へ。)
  • 結果を外部に出版するか?(すれば Quarto を追加。)
  • 非データ系の同僚が結果を直接触ることを望むか?(望むなら Marimo ノートブック as アプリ。)
  • データが数 GB を超えるか?(超えるなら Polars・DuckDB に移る。)
  • Julia エコシステムを使うか?(使うなら Pluto.jl。)
  • JS・可視化中心か?(中心なら Observable Framework。)

アンチパターン要約

  • Marimo をただの Jupyter 代替として使う(反応型・アプリモードを使わなければ得なし)。
  • Quarto に重い加工を詰める(別スクリプトに分離)。
  • ノートブックを永遠にノートブックのままにする(安定化したらモジュールへ)。
  • Polars/DuckDB を試さず Pandas に固執(GB 級以上で崩れる)。
  • Marimo の再定義制約を回避(隠れた状態が戻ってくる)。

次回予告

  • データパイプラインの新標準 — Dagster・Prefect・Airflow を 2026 年の視点で比較。ノートブックが探索なら、パイプラインは生産だ。
  • Polars 深堀り — Pandas から移るときに知るべき表現式モデル・lazy 評価・Streaming engine
  • DuckDB でシングルノード 100GB Parquet をクエリ — Spark なしの分析ワークフロー

参考 / References