Marimo 完全ガイド 2026:Pythonリアクティブノートブックでデータ分析の再現性を高める

Marimoは依存セルを自動再実行するリアクティブPythonノートブック。Jupyterの隠れた状態問題を解消し、.py形式でGit差分・テスト・WASM配布が容易。インストールから本番運用まで実装例付きで解説します。

Marimo 完全ガイド 2026:Pythonノートブック入門

最終更新: 2026年6月17日

Marimoは、セルを変更すると依存する他のセルが自動的に再実行されるリアクティブなPythonノートブックです。Jupyterのように「セルを上から順に手で実行する」必要がなく、ノートブックは純粋な.pyファイルとして保存されるため、Gitでの差分管理・テスト・FastAPIなど既存アプリへの組み込みが容易です。本ガイドではMarimo 0.13系を前提に、インストールからリアクティブモデルの仕組み、pandas/Polarsとの統合、Jupyterからの移行、Marimo CloudやWebAssemblyでの公開までを実装コード付きで解説します。

  • Marimoはセル間の依存関係をDAGとして解析し、変更したセルに依存するセルだけを自動再実行するため、Jupyter特有の「実行順依存による状態汚染」が原理的に発生しません。
  • ノートブックは.py形式で保存され、PEP 723のインラインスクリプトメタデータとuvに対応しているため、依存パッケージごとシングルファイルで共有できます。
  • mo.ui.slidermo.ui.dataframeなどのUI要素はリアクティブグラフに直接組み込まれ、Streamlitに似たアプリを追加コードなしで作成可能です。
  • marimo export html-wasmでPyodideベースの静的なWebAssemblyアプリとして書き出せるため、サーバ不要で配布できます。
  • FastAPIや既存のCLIスクリプトと共存しやすく、marimo.Appを使えばノートブックをASGIエンドポイントとしてマウントすることもできます。

Marimoとは?リアクティブノートブックの基本

Marimoは、Pythonコードのセル間依存関係を静的解析して有向非巡回グラフ(DAG)を作成し、あるセルの出力が変わると依存セルだけを自動的に再評価する次世代ノートブックです。私はFastAPIで非同期APIを書く傍ら、分析タスクではずっとJupyterを使ってきましたが、「同じノートブックを再起動すると違う結果が出る」「Slackで送られてきた.ipynbのセルを上から実行するとNameErrorになる」といった問題に毎週遭遇していました。Marimoはこれらの根本原因である「ノートブックの隠れた状態(hidden state)」をなくすために設計されています。

具体的には、Marimoは以下の制約と機能をひとつのパッケージにまとめています。

  • 各変数はノートブック内で1セルにつき1回しか定義できない。これは制約に見えますが、リファクタしてセルを分割する強制力として働きます。
  • セルの実行順はトポロジカルソートに従う。上から下に並んでいる必要はなく、UI上は好きな順番に配置できます。
  • ノートブックは.pyとして保存される。Pythonの関数とデコレータの並びとして書き出されるため、blackruffがそのまま使えます。
  • CLIとして実行可能python my_notebook.pyで普通のスクリプトとして走ります。

「ノートブックの形をした再現性のあるアプリ」、それがMarimoの本質です。公式ドキュメントでは設計思想として「Notebooks as code, notebooks as apps, notebooks as code」と表現されています。

インストールと最初のノートブックを起動する

MarimoはPyPI公式パッケージとして配布されており、Python 3.9以上で動作します。私のチームではuvを標準にしているので、本記事ではuvpipの両方のコマンドを示します。

# uv を使う場合(推奨)
uv tool install marimo

# pip を使う場合
pip install --upgrade marimo

# 動作確認
marimo --version
# marimo 0.13.x

インストール後、対話的なチュートリアルが付属しているので、初回はこれを起動するのが手っ取り早いです。

# 内蔵チュートリアルを開く
marimo tutorial intro

# 新しい空のノートブックを編集モードで開く
marimo edit hello.py

# 既存ノートブックを「アプリ」として配信(読み取り専用、UIだけ操作可能)
marimo run hello.py --port 8080

marimo editを実行するとブラウザが自動的に開きます。最初のセルに以下を貼り付けてみてください。リアクティブモデルの実感をつかむのに最適です。

import marimo as mo
import polars as pl

# セル1: データ生成
df = pl.DataFrame({
    "month": ["1月", "2月", "3月", "4月", "5月"],
    "revenue": [120, 145, 180, 210, 240],
})

# セル2: UIスライダー
multiplier = mo.ui.slider(1, 5, value=1, label="売上倍率")
multiplier

# セル3: 派生データフレーム(multiplierを変えると自動更新される)
scaled = df.with_columns(
    (pl.col("revenue") * multiplier.value).alias("scaled_revenue")
)
scaled

セル2のスライダーを動かすと、セル3が瞬時に再計算されます。Jupyterで同じことをやろうとするとipywidgetsとコールバック関数を組まないといけませんが、Marimoは変数の参照関係だけで自動的に依存を解決します。

リアクティブ実行モデル:再現性を保証する仕組み

リアクティブモデルが何を保証するのかを正確に理解しておくと、後のデバッグが格段に楽になります。Marimoはノートブックを読み込んだ瞬間に、すべてのセルをPythonのastモジュールで解析し、各セルがどの変数を定義する(define)か、どの変数を参照する(reference)かをグラフ化します。実行時はこのグラフを使って「依存セルの集合」を計算し、必要な最小範囲だけを再実行します。

セルの並び順は意味を持たない

これがJupyterユーザーにとって最大の発想転換です。Marimoではセルを画面の上下どこに置いても、論理的な実行順序はDAGによって決まります。私が初めて触ったとき「import文を一番下に置いてもエラーにならない」と気づいて衝撃を受けました。逆に言うと、セルを上から順に読むという慣習は捨てる必要があります

「定義の重複」は静的エラーになる

同じ変数名を2つのセルで代入すると、Marimoはノートブックを開いた瞬間にエラーを表示します。例えばdf = ...がセルAとセルBの両方にあると、どちらが正なのかDAGが組めないためです。これは厳しい制約に見えますが、df_raw / df_clean / df_features といったように変数名で意味を分けるスタイルを強制するので、後から読み返したときの可読性が劇的に上がります。

遅延実行モード(lazy execution)

重いETLや学習を行うセルはconfigure → 「遅延実行モード」に切り替えると、依存変化時にも自動再実行せず「再実行可能」のマーカーだけが表示されます。クリックで明示的に走らせる運用にできるので、巨大データのリアクティブが煩わしいケースに対応できます。バックエンド開発者として「副作用が大きい処理は明示的に」という原則をそのまま適用できる設計です。

Marimo vs Jupyter:機能比較表で違いを把握する

「結局Jupyterと何が違うのか」をチームに説明する場面が多いので、よく聞かれる観点を1枚にまとめました。どちらが優れているかは用途によりますが、長期メンテする分析資産はMarimo、一回限りの探索メモはJupyterと使い分けるのが私の感覚です。

観点MarimoJupyter (Lab/Notebook)
実行モデルリアクティブ(DAG自動再実行)命令型(手動でセル実行)
ファイル形式純粋な.pyJSONベースの.ipynb
Git差分そのままレビュー可nbdime等が事実上必須
UI ウィジェットmo.ui.*が標準で組込みipywidgetsを別途構築
アプリ配信marimo runで即配信Voila等が必要
WASM配布export html-wasmで対応JupyterLiteで対応(別ツール)
スクリプト実行python file.pyで動作変換が必要(nbconvert
テスト容易性関数を直接インポート可能セルの抽出が必要
セルの重複変数禁止(静的に検出)許容(最後の代入が勝つ)
学習コスト中(リアクティブ思考が必要)低(標準的)

特に注目してほしいのがGit差分の行です。JSONの.ipynbはメタデータと実行カウントが変わるたびに差分が爆発し、コードレビューが現実的に困難になります。MarimoはPythonのソースコードそのものなので、GitHubのPRビューでそのまま読めるという利点があります。

pandas・PolarsとMarimo UIを統合する

Marimoの強みのひとつが、データフレームライブラリとの統合の深さです。pandasとPolarsの両方に対し、mo.ui.dataframemo.ui.data_explorerという専用ウィジェットが用意されており、フィルタ・ソート・サマリ計算がGUIから行えます。バックエンドAPIに渡す前の前処理を「コードで書きつつ、結果はGUIで確認」できる体験は、想像以上に開発スピードを上げてくれます。

例えば、Polarsの基礎を踏まえて以下のようなインタラクティブEDAが3セルで書けます。

import marimo as mo
import polars as pl

# CSVを遅延読み込み
df = pl.scan_csv("sales.csv").collect()

# データフレームトランスフォーマー:GUIで集計・フィルタ
transformer = mo.ui.dataframe(df)
transformer

# 変換結果に対してチャートを描画
import altair as alt
chart = alt.Chart(transformer.value.to_pandas()).mark_bar().encode(
    x="category:N",
    y="sum(amount):Q",
)
chart

mo.ui.dataframeのGUI操作はすべて裏側でコードに記録され、「現在の変換状態を生成するPythonスニペット」をエクスポートできます。EDAで試行錯誤して納得した変換を、そのままパイプラインに移植できるわけです。私はこれをきっかけに「分析用ノートブックと本番ETLの乖離」というよくある問題を、かなり緩和できました。

pandas 3.0のCopy-on-WriteもMarimoのリアクティブモデルと相性がよく、データフレームの破壊的更新による「隠れた状態」を排除できます。両者を組み合わせると、ノートブックが「ピュアな計算グラフ」に近づきます。

インタラクティブUI要素でデータ探索を加速する

Marimoのもうひとつの目玉が、Streamlitに匹敵するUIライブラリが組込みになっている点です。mo.ui名前空間にスライダー、ドロップダウン、フォーム、チャットUI、ファイルアップローダーなどが用意されており、すべてリアクティブグラフに自動接続されます。「Pythonでアプリを書く」のではなく、「ノートブックを書いていたらアプリになっていた」という感覚です。

import marimo as mo

# セル1: 入力フォーム
form = mo.ui.batch(
    mo.md("""
    **モデルパラメータ**

    学習率: {lr}
    エポック: {epochs}
    オプティマイザ: {optim}
    """),
    {
        "lr": mo.ui.slider(0.0001, 0.1, value=0.01, step=0.0001),
        "epochs": mo.ui.number(start=1, stop=100, value=10),
        "optim": mo.ui.dropdown(["adam", "sgd", "adamw"], value="adam"),
    },
).form(submit_button_label="学習開始")

form

# セル2: 送信されたときだけ実行
if form.value is not None:
    config = form.value
    mo.md(f"学習を開始します: lr={config['lr']}, epochs={config['epochs']}")
    # ここでscikit-learnなどを呼ぶ

mo.ui.batch.form()の組み合わせで「送信ボタンが押されるまで再計算しない」というStreamlit的な挙動も簡単に実現できます。バックエンド開発者の視点では「イベント駆動とリアクティブをノートブック上で同居させられる」のが嬉しいポイントです。

WASMエクスポートとMarimo Cloudで公開する

作ったノートブックを社内外に共有する手段として、Marimoは3つの選択肢を提供します。それぞれに向き不向きがあるので、要件に応じて選んでください。

  1. WebAssemblyへの静的エクスポート: marimo export html-wasm notebook.py -o site/を実行すると、Pyodideで動作する単一HTMLファイル群が生成されます。GitHub PagesやCloudflare Pagesに置くだけで動的サーバなしのインタラクティブダッシュボードが作れます。
  2. セルフホスト(marimo run: 自前のサーバやKubernetesにmarimo run notebook.py --host 0.0.0.0 --port 8080でデプロイ。公式デプロイガイドにDockerfileの例が載っています。
  3. Marimo Cloud(マネージド): GitHubリポジトリと連携してプッシュごとに自動デプロイされるホスティング。チームでの共有や認証付き配信に向きます。

WASMエクスポートは特に強力です。「Pythonがブラウザだけで動く」ため、社内のセキュリティポリシー上サーバ立てが難しい環境でもインタラクティブな分析アプリを配布できます。ただし、Pyodideで利用できないネイティブ依存ライブラリ(例: 一部のC拡張)は使えないので、事前に互換性を確認してください。

JupyterノートブックからMarimoへ移行する手順

既存のJupyter資産がある場合、いきなり全部移行する必要はありません。私のチームでは「長期メンテするものから移す」という方針で、3ヶ月かけて段階的に置き換えました。実際の手順は次のとおりです。

  1. marimo convert legacy.ipynb -o legacy.py.pyに変換します。出力はMarimo形式のPythonファイルです。
  2. marimo edit legacy.pyで開くと、リアクティブ違反(変数の重複定義など)が赤くハイライトされます。
  3. 典型的な修正パターンは「同じ変数名の再代入を別名にする」「ループ内のdf = df.dropna()を関数化する」の2つです。
  4. セルの並び順は維持されますが、Marimoでは並び順に意味がないため、レビュー時に「論理的に近いセルを近くに置く」リファクタを行います。
  5. テストを書ける部分はtests/test_pipeline.pyとして切り出します。Marimoノートブックはfrom legacy import compute_featuresのように普通の関数としてインポート可能です。

移行時に最もハマるのは「セルをまたいで状態を変更するパターン」です。Jupyterではdf.dropna(inplace=True)を別セルで何度も呼んで状態を更新する書き方がよく見られますが、Marimoではこれが許されません。代わりに各ステップで新しい変数を作るスタイル(パイプ・メソッドチェーンと相性が良い)に書き直します。

FastAPIプロジェクトでMarimoを使うパターン

FastAPIで本番APIを書いている人間として、Marimoは「分析用」だけでなく「API開発のサンドボックス」としても優秀です。リクエスト・レスポンスのスキーマをpydanticで定義し、ノートブック内でMockサーバを叩きながらデータ加工ロジックを練り、納得したらそのままFastAPIのエンドポイントに移植できます。FastAPIでのMLモデル本番デプロイの前段として使うとワークフローが綺麗にまとまります。

import marimo as mo
import httpx
import pydantic

# pydanticスキーマ
class Prediction(pydantic.BaseModel):
    label: str
    score: float

# UIで対象URLを切替(dev / staging / prod)
endpoint = mo.ui.dropdown(
    {
        "dev": "http://localhost:8000/predict",
        "staging": "https://staging.example.com/predict",
    },
    value="dev",
)
endpoint

# リクエスト送信
async def call(url: str, payload: dict) -> Prediction:
    async with httpx.AsyncClient(timeout=10.0) as client:
        resp = await client.post(url, json=payload)
        resp.raise_for_status()
        return Prediction.model_validate(resp.json())

result = mo.runtime.context.execution_context.run(
    call(endpoint.value, {"text": "サンプル"})
)
result

Marimoはasync関数を直接トップレベルでawaitできる仕組みを持っているため、FastAPIで書いているのと同じ非同期コードをそのままノートブックに持ち込めます。ここは長年Jupyterでのasyncio.run()地獄に苦しんできた身として、特に強調したい点です。

よくある落とし穴とトラブルシューティング

ここまで紹介したリアクティブモデルは強力ですが、移行初期に必ず遭遇する地雷がいくつかあります。私が実際に踏み抜いたものを挙げておきます。

セルが「無限ループ」しているように見える

UI要素を更新するセルが、そのUI要素自身の値を参照するとループになるように見えます。MarimoはDAG構造をチェックするので実際には循環を検出してエラーにしますが、「なぜ更新されないのか」と混乱します。UI要素は読み取り専用に扱い、別のセルで派生値を作るのが定石です。

巨大なオブジェクトが毎回再計算される

1GBのDataFrameを返すセルが上流で軽微に変わるたびに再実行されるとパフォーマンスが破綻します。先述の@mo.cacheに加え、設定で「遅延実行モード」を有効にし、重い処理は明示的にトリガーする運用に切り替えましょう。

環境再現性のためにuvと組み合わせる

Marimoはノートブック先頭にPEP 723形式のメタデータを書けます。uv run notebook.py必要なパッケージを自動的にインストールしてから実行してくれるため、requirements.txtを別に管理する必要がありません。これは分析資産の長期メンテで大きなメリットです。

# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "marimo>=0.13",
#   "polars>=1.10",
#   "altair>=5.4",
# ]
# ///
import marimo as mo
import polars as pl
import altair as alt
# ... 以下、通常のMarimoノートブック

このメタデータ付きファイルは、メールやSlackで1ファイル送るだけで再現可能なので、私のチームではJupyter時代のconda env exportから完全に乗り換えました。

よくある質問

Marimoは無料で使えますか?

はい、MarimoのコアエディタとランタイムはApache 2.0ライセンスのオープンソースとしてGitHubで公開されています。商用利用も無料で、ローカル・自前サーバでの利用に追加費用はかかりません。Marimo Cloud(マネージド配信サービス)のみ有料プランがあります。

既存のJupyterノートブックをMarimoで開けますか?

直接ipynbを開くことはできませんが、marimo convert notebook.ipynb -o notebook.pyでMarimo形式のPythonファイルに変換できます。変換後、変数重複などのリアクティブ違反が検出された箇所をエディタが赤くハイライトしてくれるため、修正箇所が一目で分かります。

MarimoはStreamlitやDashの代わりになりますか?

シンプルなダッシュボードや社内向けデータアプリならMarimoだけで完結します。mo.uiに主要なウィジェットが揃っており、marimo runで即配信できるためです。一方、認証やマルチページの大規模Webアプリはまだ専用フレームワークの方が成熟しています。

Marimoノートブックでテストを書けますか?

はい。Marimoノートブックは純粋な.pyファイルなので、内部で定義した関数をfrom notebook import my_funcのようにインポートしてpytestで検証できます。リアクティブセルそのものを直接テストする仕組みはまだ限定的ですが、ロジックを関数に切り出していれば通常のユニットテストが可能です。

Marimoは大規模データを扱えますか?

扱えます。リアクティブ実行が負担になる場合は「遅延実行モード」に切り替え、重いセルだけ手動トリガーにします。さらにPolarsの遅延API(scan_*)やDuckDBと組み合わせれば、メモリに乗らないサイズのデータでも分析できます。

Tomás Oliveira
著者について Tomás Oliveira

Python backend developer who came to data work via FastAPI. Bridges the messy world between APIs and pipelines.