mcpField
M
marimo-notebooks

marimo-notebooks

github.com/pymc-labsデータ・データベース1940 回インストール38 週間前に更新
リポジトリ

marimo-notebooks について

marimo の Python ノートブックを開発する人向けのガイドです。リアクティブなセル、インタラクティブ UI、キャッシュに加え、コード表示や変数管理、編集前後のチェック方法を説明します。

主な機能

  1. 01依存関係に応じたセルの自動再実行
  2. 02Python ファイルでのノートブック定義
  3. 03インタラクティブな入力とデータコンポーネント
  4. 04レイアウト・状態・出力の制御
  5. 05セッションおよび永続キャッシュ
  6. 06ノートブックの検査と編集手順

活用シーン

  1. 01リアクティブなデータ分析ノートブックの構築
  2. 02インタラクティブな操作付きチュートリアルの作成
  3. 03表形式データの探索と選択
  4. 04計算コストの高い処理のキャッシュ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pymc-labs/agent-skills --skill marimo-notebooks

インストール後、Claude Code で/marimo-notebooksを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

CLAUDE.md
assets/
data_analysis_template.py
minimal_template.py
references/
advanced.md
caching.md
ui_components.md
wigglystuff.md
scripts/
convert_notebook.py

詳細

Marimo ノートブック

Marimo ノートブックは、純粋な .py ファイルとして保存されるリアクティブな Python ノートブックです。依存関係が変わると、セルが有向非巡回グラフ(DAG)に基づいて自動実行されます。

基本概念

リアクティビティモデル
  • marimo は静的解析を使用し、変数の参照と定義から依存関係グラフを構築します
  • セルが実行されると、そのセルで定義された変数を参照するすべてのセルが自動的に再実行されます
  • 実行順序は視覚的なセルの順序ではなく、依存関係グラフに従います
  • 各グローバル変数は、必ず1つのセルだけで定義する必要があります
  • marimo はオブジェクトの変更(list.append() など)を追跡しません。オブジェクトを作成したセル内で変更するか、新しい変数を作成してください
変数名の競合を避ける

各グローバル変数は、必ず1つのセルだけで定義する必要があります。方法は2つあります。

1. コードを関数で囲む(再利用可能なパターンでは推奨):

@app.cell
def _(data):
    def compute_mean_with_new_col(df):
        temp = df.copy()
        temp["new_col"] = temp["x"] * 2
        return temp.mean()

    return (compute_mean_with_new_col(data),)

2. 意味が明確で一意な変数名を使う:

@app.cell
def _(model1_data):
    model1_transformed = model1_data.copy()
    model1_transformed["new_col"] = model1_transformed["x"] * 2
    return (model1_transformed,)

一意な変数名を生成するために、アンダースコアの接頭辞を使ってはいけません。例外はありません。

ノートブックの構造

import marimo

__generated_with = "0.10.0"
app = marimo.App(width="medium")

@app.cell
def _():
    import marimo as mo
    return (mo,)

@app.cell
def _(mo):
    mo.md("# Hello")
    return

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

主なルール:

  • 各セルは @app.cell でデコレートされた関数です
  • タプルを返すことで変数を共有します:return (var1, var2,)
  • セルは変数をパラメーターとして受け取ります:def _(mo, df):
  • 実行順序は位置ではなく、依存関係グラフに従います
  • CellTour のターゲットに使えるよう、セルには内容を表す名前を付けます:def model_specification():

CLI コマンド

# Create & Edit
marimo new                           # Create new notebook
marimo edit notebook.py              # Open editor
marimo edit notebook.py --watch      # Live reload on file changes

# Run as App
marimo run notebook.py               # Run as app (code hidden by default)
marimo run notebook.py --include-code  # Show code in app view

# Convert
marimo convert notebook.ipynb -o notebook.py  # Jupyter to marimo

# Export
marimo export html notebook.py -o out.html    # Static HTML
marimo export ipynb notebook.py -o out.ipynb  # To Jupyter

# Validate
marimo check notebook.py             # Lint and validate
marimo check notebook.py --fix       # Auto-fix issues

実行モードでのコード表示

チュートリアルでは重要です:デフォルトでは、marimo run はコードを非表示にします。表示するには mo.show_code() を使ってください。

mo.show_code() - セルごとの表示

重要:mo.show_code() は戻り値の文の中ではなく、単独の行に文として呼び出してください。

@app.cell
def model_definition(mo, pm, X, y):
    with pm.Model() as model:
        alpha = pm.Normal("alpha", mu=0, sigma=10)
        beta = pm.Normal("beta", mu=0, sigma=10)
        mu = alpha + beta * X
        pm.Normal("y", mu=mu, sigma=1, observed=y)

    # Show this cell's code alongside its output
    mo.show_code(model, position="above")
    return (model,)

誤った例と正しい例:

# WRONG - do not put mo.show_code() in return statement
return mo.show_code(result)

# RIGHT - call as statement, then return separately  
mo.show_code(result, position="above")
return (result,)
  • position="above" はコードを先に、その後に出力を表示します(チュートリアルに最適です)
  • position="below" は出力を先に、その後にコードを表示します(デフォルト)

mo.md() を使った Markdown

@app.cell
def _(mo):
    mo.md(r"""
    # Title

    Interpolate Python: {slider}

    **LaTeX**: $f(x) = e^x$

    $$\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}$$

    **Icons**: ::lucide:rocket:: or ::mdi:home::
    """)
    return
  • LaTeX には raw 文字列(r"""...""")を使います
  • UI 要素を埋め込みます:f"Value: {slider}"
  • 複雑なオブジェクトには、次を使います:f"Plot: {mo.as_html(fig)}"

UI コンポーネント(mo.ui.*)

基本的な入力
slider = mo.ui.slider(0, 100, value=50, label="Value")
number = mo.ui.number(0, 100, value=50)
text = mo.ui.text(value="", placeholder="Enter text")
checkbox = mo.ui.checkbox(value=False, label="Enable")
dropdown = mo.ui.dropdown(["a", "b", "c"], value="a")
radio = mo.ui.radio(["option1", "option2"], value="option1")
multiselect = mo.ui.multiselect(["a", "b", "c"])
ボタン
button = mo.ui.button(label="Click")
run_button = mo.ui.run_button(label="Run")  # For triggering computation
データコンポーネント
table = mo.ui.table(df)            # Interactive table with selection
dataframe = mo.ui.dataframe(df)    # Editable dataframe
data_explorer = mo.ui.data_explorer(df)  # No-code exploration
UI 要素のグループ化
# Forms (require submit button)
form = mo.ui.text().form()

# Batch in markdown
form = mo.md("""
**Name**: {name}
**Age**: {age}
""").batch(
    name=mo.ui.text(),
    age=mo.ui.number(0, 120)
).form()

すべての内容については、references/ui_components.md を参照してください。

レイアウト関数

# Stacking
mo.hstack([el1, el2, el3], justify="center", gap=2)
mo.vstack([el1, el2, el3], align="start", gap=1)

# Containers
mo.accordion({"Section 1": content1, "Section 2": content2})
mo.tabs({"Tab 1": content1, "Tab 2": content2})
mo.callout(content, kind="info")  # info, warn, success, danger, neutral
mo.sidebar([nav_content])

# Display
mo.tree({"a": {"b": 1}})    # Tree view
mo.stat(value="42", label="Users", caption="+5%")
mo.lazy(expensive_component)  # Defer until visible

出力関数

mo.output.replace(new_content)  # Replace cell output
mo.output.append(additional)    # Append to output
mo.output.clear()               # Clear output

with mo.redirect_stdout():
    print("This goes to cell output")

ステータスインジケーター

# Progress bar
for item in mo.status.progress_bar(items, title="Processing"):
    process(item)

# Spinner
with mo.status.spinner(title="Loading..."):
    load_data()

制御フロー

# Stop execution conditionally
mo.stop(condition, mo.md("*Message when stopped*"))

# Example: require button click
run_button = mo.ui.run_button()
mo.stop(not run_button.value, mo.md("Click Run to execute"))
expensive_computation()

キャッシュ

# In-memory cache (session only)
@mo.cache
def expensive_function(x, y):
    return compute(x, y)

# Persistent cache (survives restarts)
@mo.persistent_cache(name="embeddings")
def compute_embeddings(text):
    return model.encode(text)

モデル出力のキャッシュパターンについては、references/caching.md を参照してください。

状態管理

警告:mo.state() の使用は必要最小限にしてください。99%を超えるケースでは必要ありません。

@app.cell
def _(mo):
    get_count, set_count = mo.state(0)
    return get_count, set_count

@app.cell
def _(mo, get_count, set_count):
    mo.ui.button(
        label=f"Count: {get_count()}",
        on_click=lambda _: set_count(lambda n: n + 1)
    )
    return

履歴の保持、双方向の UI 同期、または循環の導入が必要な場合にのみ使用してください。

インタラクティブなプロット

# Altair with selection
chart = alt.Chart(df).mark_point().encode(x="x", y="y")
selection = mo.ui.altair_chart(chart, chart_selection="point")
selected_data = selection.value  # DataFrame of selected points

# Plotly
fig = px.scatter(df, x="x", y="y")
interactive = mo.ui.plotly(fig)
interactive.value  # Selected points

# Matplotlib interactive
fig, ax = plt.subplots()
ax.plot(x, y)
mo.mpl.interactive(fig)

対応対象:Matplotlib、Seaborn、Plotly、Altair、Bokeh、HoloViews、hvPlot

Wigglystuff ウィジェット

from wigglystuff import Slider2D, Paint, SortableList, Matrix, CellTour
import marimo as mo

slider2d = mo.ui.anywidget(Slider2D())
slider2d.x, slider2d.y

paint = mo.ui.anywidget(Paint(width=400, height=300))
paint.to_pil()

tour = mo.ui.anywidget(CellTour(
    steps=[
        {"cell_name": "intro", "title": "Welcome", "description": "..."},
        {"cell_name": "model", "title": "Model", "description": "..."},
    ],
    auto_start=False
))

すべてのウィジェットについては、references/wigglystuff.md を参照してください。

ベストプラクティス

  1. 再利用可能なコードは関数で囲む - 中間変数をローカルに保てます
  2. 意味が明確で一意な変数名を使う - 例:model1_sigma、model2_sigma
  3. セルをまたいで変更しない - 同じセル内で変更するか、新しい変数を作成してください
  4. 冪等なセルを書く - 同じ入力から同じ出力が得られるようにします
  5. mo.stop() を使う - 高コストの処理を条件やボタンの背後に置きます
  6. 遅延読み込みを使う - タブやアコーディオン内の高コストなコンポーネントには mo.lazy() を使います
  7. 高コストの処理をキャッシュする - セッションには @mo.cache、ディスクには @mo.persistent_cache を使います

重要:編集前のチェックリスト

marimo ノートブックを編集する前に、必ず次の作業を行ってください:

  1. 現在のファイルの状態を確認する — marimo のエディターによってファイルが変更されている場合があります
  2. marimo check notebook.py を実行する — 編集の前後で有効な状態であることを確認します

編集の完了後:

  1. print( を grep で検索する — すべての print 文を marimo の出力(mo.md()、mo.stat()、mo.callout())に置き換えます
  2. marimo check notebook.py を実行する — エラーが追加されていないことを確認します

よくある注意点

出力と表示
  • セル内で print() を絶対に使用しないでください:実行モードでは print 文は表示されません。必ず次を使用してください:

    • mo.md(f"**Label:** {value}") — 書式付きテキスト
    • mo.stat(value=f"{x}", label="Label") — メトリックカード
    • mo.callout(content, kind="info") — コールアウトボックス
    • データフレーム/オブジェクトを直接返す — 自動的に表示されます
  • mo.show_code() は return 内ではなく、文として呼び出す必要があります

  • 出力を置き換えずに削除しないでください:print 文を削除する場合は、同等の marimo 出力に置き換えてください

リアクティビティと変数
  • ループ内のクロージャ:lambda v: ... i ではなく、デフォルト引数 lambda v, i=i: ... を使用してください
  • on_change ハンドラー:要素がグローバル変数にバインドされている場合にのみ機能します
  • 動的 UI 要素:mo.ui.array()、mo.ui.dictionary()、または mo.ui.batch() でラップする必要があります
  • 型アノテーション:引用符で囲まない場合は参照として登録されます:x: "SomeType"
ライブラリ
  • matplotlib の切り抜き:出力前に plt.tight_layout() を呼び出してください
  • dotenv:dotenv.load_dotenv(dotenv.find_dotenv(usecwd=True)) を使用してください

参考資料

コメント