Marimo — реактивні ноутбуки в Python: повний посібник на 2026

Marimo — це реактивний ноутбук для Python із детермінованим DAG виконання. Розбираю встановлення, SQL-комірки з DuckDB, WASM-експорт і міграцію з Jupyter.

Marimo Python: реактивні ноутбуки 2026

Оновлено: 12 серпня 2026

Marimo — реактивний ноутбук для Python, у якому будь-яка зміна в комірці автоматично переобчислює всі залежні від неї комірки, усуває приховане ядро-стан Jupyter і зберігається як звичайний файл .py. А отже, він версіонується в Git, запускається як скрипт і публікується як вебзастосунок. У цьому посібнику я покажу, як встановити marimo у 2026 році, побудувати відтворюваний ноутбук, використовувати SQL-комірки з DuckDB, розгорнути його у WASM та мігрувати з Jupyter, з робочим кодом, який виконується без адаптації.

  • Marimo виконує комірки за орієнтованим ациклічним графом залежностей (DAG): зміна змінної запускає всіх споживачів у топологічному порядку.
  • Ноутбуки: читабельні .py-файли, тож формат усуває конфлікти злиття, характерні для .ipynb.
  • Мінімальне встановлення: pip install marimo. Для SQL-комірок і DataFrame-переглядача використовуйте pip install "marimo[recommended]".
  • Команда marimo run notebook.py віддає ноутбук як інтерактивний застосунок із прихованим Python-кодом.
  • Публікація без сервера через marimo export html-wasm запускає Python у браузері через Pyodide.
  • У 2026 році marimo отримав плагін для PyCharm, режим marimo pair для агентів ШІ й інтеграції з Quarto та MDX.

Що таке marimo notebook у Python?

Marimo: ноутбук із відкритим кодом для Python, який замінює імперативну модель Jupyter на детермінований реактивний рантайм. Кожна комірка декларує змінні, які виробляє та споживає, рантайм будує граф залежностей і переобчислює лише те, що потрібно, коли щось змінюється. Простіше кажучи, ноутбук ніколи не залежить від порядку, у якому ви клацали комірки. А це усуває цілий клас багів «комірка не в порядку», що переслідують аналітиків із Jupyter понад десять років. Формально це модель потокового програмування (dataflow), знайома з Observable, Pluto.jl та Excel, але вперше повноцінно реалізована для звичного Python-стеку з pandas, NumPy та scikit-learn.

Проєкт запустила marimo Inc. у 2024 році. Справжня тяга з'явилася у 2025–2026, коли команди даних почали використовувати його не лише для розвідувального аналізу, а й для внутрішніх дашбордів та відтворюваних пайплайнів. Я застосовую marimo у двох різних сценаріях. По-перше, як ноутбук для розвідки пайплайнів у Airflow (реактивність не дає мені запустити дорогий groupby над df, якого вже фактично немає в пам'яті). По-друге, як шар UI, де ML-інженери ітеративно пишуть DuckDB-запити без HTML. Ключова перевага в тому, що той самий .py-файл слугує трьом ролям: редагований ноутбук, запланований скрипт і опублікований вебзастосунок, без переписування.

Як встановити marimo у 2026 році

Щоб встановити marimo, виконайте pip install marimo у середовищі з Python 3.9 або новішим. Якщо ви користуєтесь uv (а я рекомендую саме його у 2026 році, бо він розв'язує залежності за мілісекунди), команда буде uv add marimo у проєкті, ініціалізованому через uv init. Базова версія містить реактивний редактор, експорт у HTML та інтеграції з matplotlib, plotly і altair. Для SQL-комірок, автодоповнень ШІ та інтерактивного DataFrame-переглядача варто встановити розширений пакунок.

# Мінімальне встановлення
pip install marimo

# Рекомендоване встановлення (SQL, ШІ, DataFrame viewer)
pip install "marimo[recommended]"

# Тільки SQL-розширення (DuckDB + драйвери)
pip install "marimo[sql]"

# Через uv (у 10-100 разів швидше за pip)
uv add "marimo[recommended]"

# Запуск інтерактивного туторіалу
marimo tutorial intro

Після встановлення команда marimo edit запускає локальний сервер (за замовчуванням http://127.0.0.1:2718), де ви створюєте й редагуєте ноутбуки. Щоб створити конкретний файл із терміналу, використайте marimo edit analiz_prodazhiv.py. Якщо файлу немає, marimo згенерує коректний .py-скелет. Підтвердьте версію командою marimo --version. Серія 0.10.x актуальна на середину 2026 року і отримує релізи практично щотижня в офіційному репозиторії на GitHub.

Реактивність: як marimo усуває приховане ядро-стан

Найчастіше на код-рев'ю мене питають: «Навіщо реактивність, якщо в Jupyter є Run All?». Коротка відповідь: Run All лікує симптом, а не причину. У Jupyter ядро тримає глобальний мутовний простір імен, і ніщо не заважає визначити df у комірці 3, перевизначити його в комірці 7 і знову звернутися до старого df в комірці 5. Результат повністю залежить від порядку виконання, а файл .ipynb цей порядок не зберігає. Він зберігає лише той output, що був на екрані під час збереження. Це відома проблема, і академічне дослідження мільйона Jupyter-ноутбуків із GitHub показало, що близько 36% публічних ноутбуків або не запускаються заново, або дають інший результат.

Marimo робить три речі, щоб знищити цей клас багів. По-перше, забороняє перевизначення змінних між комірками: якщо дві комірки декларують df, ноутбук не запускається, а редактор показує конфлікт. По-друге, парсить AST кожної комірки, щоб визначити, які змінні читаються й записуються, і будує граф залежностей ще до першого запуску. По-третє, коли ви виконуєте комірку, всі її нащадки автоматично позначаються як stale й переобчислюються (або чекають вашого підтвердження, якщо ви вимкнули автоматичне виконання для дорогих обчислень).

import marimo as mo
import pandas as pd

# Комірка 1: завантаження сирих даних
df_raw = pd.read_parquet("s3://bucket/podii.parquet")

# Комірка 2: фільтрація
# Якщо ви зміните фільтр, Комірка 3 переобчислиться сама.
df_filtered = df_raw[df_raw["event"] == "purchase"]

# Комірка 3: агрегація (залежить від df_filtered)
revenue_per_day = (
    df_filtered
    .groupby(pd.Grouper(key="ts", freq="1D"))["amount"]
    .sum()
    .rename("revenue_uah")
)

На практиці це означає, що коли я змінюю фільтр у комірці 2, агрегація в комірці 3 та будь-який downstream-графік автоматично переобчислюються й лишаються узгодженими з новим станом. Чесно кажучи, я не раз рятувала так нічний on-call. Раніше помилковий дашборд у Jupyter міг годинами дурити команду, поки хтось не помічав, що джерело перевизначили трьома комірками вище. Якщо ви багато працюєте з groupby для часових рядів, доречним наступним кроком буде прогнозування часових рядів у Python з ARIMA і SARIMA. Модель реактивних обчислень чудово підходить для ітеративного підбору моделі.

Marimo проти Jupyter: у чому різниця на практиці?

Порівняння marimo vs Jupyter є найпоширенішим пошуковим запитом навколо теми, і відповідь коротка. Jupyter, універсальний REPL із UI, а marimo, детермінований дата-застосунок у форматі ноутбука. Обидва інструменти живі й потрібні у 2026 році. Jupyter лишається неперевершеним для швидких експериментів у сирому REPL-стилі, тоді як marimo виграє, коли ноутбук має жити довго, потрапляти в Git і бути опублікованим. Таблиця нижче узагальнює десять вимірів, які реально впливають на щоденну роботу.

Вимірmarimo 0.10Jupyter Lab 4
Формат зберіганняЧистий .py, читається git-diffJSON .ipynb з output-ами
Модель виконанняРеактивний DAGІмперативне ядро
Прихований станНеможливий (заборонене перевизначення)Типова причина багів
Merge conflictsСтандартні для Python-кодуЧасті через JSON-output
Розгортання як застосунокВбудоване: marimo runПотрібні Voila, Panel або Streamlit
SQL-коміркиНативні, повертають DataFrameЧерез jupysql або магії
Виконання як скриптpython notebook.py, такЧерез nbconvert або papermill
Публікація у WASMmarimo export html-wasmJupyterLite (окремий стек)
Автовідновлення після крахуТак, кеш комірокНі
AI-агенти для редагуванняmarimo pair та MCP-серверJupyter AI, Copilot

Коли обирати що? З мого досвіду, я лишаю Jupyter для сирої розвідки. Тобто перших 15 хвилин занурення в незнайомий датасет, коли я поки що не знаю, чи буде цей ноутбук колись перезапущено. Щойно з'являється рішення «тут буде щось важливіше за одноразовий скетч», переношу все в marimo. Це також однозначний вибір для дашбордів для нетехнічних стейкхолдерів: marimo run --headless notebook.py віддає застосунок за одну команду, без окремого фронтенд-проєкту.

SQL-комірки з DuckDB та нативними з'єднаннями

SQL-комірки: це, мабуть, найкорисніша фіча marimo для дата-інженерів. Ви пишете чистий SQL, а marimo автоматично парсить його, знаходить таблиці, від яких він залежить (наприклад, попередній pandas-DataFrame), виконує запит через DuckDB та повертає результат як новий DataFrame, який автоматично стає реактивним. Це той самий рушій, з яким я працюю в повному посібнику з DuckDB для аналітики, тому у marimo він виглядає природно.

import marimo as mo
import pandas as pd

# Комірка 1: pandas DataFrame як джерело
orders = pd.read_parquet("s3://data/orders.parquet")

# Комірка 2: SQL-комірка (створюється через "SQL" у меню)
# marimo автоматично реєструє orders як таблицю в DuckDB
top_customers = mo.sql(
    f"""
    SELECT
        customer_id,
        COUNT(*)             AS orders_count,
        SUM(total_amount)    AS total_spent_uah,
        AVG(total_amount)    AS avg_order_uah
    FROM orders
    WHERE order_date >= '2026-01-01'
    GROUP BY customer_id
    ORDER BY total_spent_uah DESC
    LIMIT 100
    """
)

# Комірка 3: top_customers доступний як звичайний DataFrame
top_customers.head()

Що робить це особливо потужним, так це реактивність між SQL і Python. Якщо ви зміните фільтр дати в SQL-комірці, DataFrame top_customers переобчислиться, будь-який pandas-код, який його читає, теж, і плюс до цього перебудуються графіки та зведені таблиці нижче. Ви фактично отримуєте dbt-подібний DAG всередині одного файлу. Для великих датасетів (десятки гігабайт) marimo також підтримує з'єднання з DuckDB через нативні підключення до Postgres, MotherDuck, S3 та Iceberg. Таблиці Iceberg reactivity-friendly, бо DuckDB читає лише потрібні розділи.

Реактивні UI-елементи без колбеків

UI-елементи marimo, це, ймовірно, найбільша зміна для тих, хто раніше боровся з ipywidgets і callback-функціями. У Jupyter, щоб зробити слайдер, який перебудовує графік, треба реєструвати обробники подій, керувати станом і синхронізувати виджет із фігурою вручну. У marimo це один рядок, і жодних колбеків, бо реактивний рантайм робить це за вас.

import marimo as mo
import numpy as np
import matplotlib.pyplot as plt

# Комірка 1: слайдер для згладжування
window = mo.ui.slider(1, 60, value=7, step=1, label="Вікно ковзного середнього (днів)")
window  # Відображення виджета

# Комірка 2: реактивний графік
# window.value автоматично оновлюється, і Комірка 2 переобчислюється
fig, ax = plt.subplots(figsize=(10, 4))
series = revenue_per_day.rolling(window.value).mean()
ax.plot(series.index, series.values)
ax.set_title(f"Виторг: ковзне середнє за {window.value} дн.")
ax.grid(True, alpha=0.3)
fig

marimo надає повний набір: mo.ui.slider, mo.ui.dropdown, mo.ui.multiselect, mo.ui.date_picker, mo.ui.table (сортована й фільтрована), mo.ui.file, mo.ui.form та композитний mo.ui.array/dictionary для батарей контролів. Усі вони автоматично поводяться як реактивні джерела: значення читається як .value, і будь-яка комірка, що на нього посилається, переобчислюється при зміні. Для дашбордів із десятками контролів це на порядки менше коду, ніж еквівалент на Streamlit чи Panel. І на відміну від Streamlit, стан ноутбука не скидається при кожній взаємодії.

Розгортання: вебзастосунок, скрипт і WASM у браузері

Один із аргументів на користь marimo полягає в тому, що той самий .py-файл служить трьом контекстам виконання без переписування: інтерактивний ноутбук, headless-скрипт і опублікований застосунок. Це усуває найдорожчий крок традиційного пайплайну «розвідка → продакшн», коли аналітик передає .ipynb інженеру, а інженер переписує все у Python-скрипт або FastAPI-сервіс.

# 1) Ноутбук з редактором
marimo edit analiz.py

# 2) Виконання як скрипт (без UI, з CLI-аргументами)
python analiz.py --input data.parquet --output results.csv

# 3) Веб-застосунок (код Python прихований від користувача)
marimo run analiz.py --port 8080 --headless

# 4) Статичний HTML із виконуваним Python у браузері (Pyodide/WASM)
marimo export html-wasm analiz.py --output dist/ --mode edit

# 5) Експорт у звичайний HTML (снапшот без інтерактиву)
marimo export html analiz.py --output report.html

WASM-експорт заслуговує на окрему увагу. Він компілює ноутбук у самодостатній HTML-файл, який ви можете задеплоїти на GitHub Pages, Netlify або S3, і Python виконується прямо в браузері через Pyodide. Це означає нуль інфраструктури: жодного контейнера, VM чи FastAPI-сервера. Для внутрішніх звітів, розділених з клієнтами, або освітніх матеріалів це справжня зміна правил. Обмеження такі: розмір бандлу (кожна залежність підтягується у браузер) і сумісність пакунків (не все pure-Python). Якщо вам потрібен продакшн ML-сервіс із HTTP-API, це все одно робота для розгортання ML-моделей на FastAPI, але для інтерактивних дашбордів WASM-режим є оптимальним у 2026.

Як конвертувати Jupyter-ноутбуки в marimo

Щоб конвертувати наявний Jupyter-ноутбук у marimo, використайте вбудовану команду marimo convert notebook.ipynb -o notebook.py. Конвертер зберігає комірки, залишає markdown-осередки у вигляді викликів mo.md(...) і, головне, перейменовує змінні при конфліктах, щоб задовольнити правило унікальності. Це реалістичний перший крок, але не завжди останній. Логіку, побудовану на послідовних перевизначеннях того самого df, доведеться переписати вручну.

# Пакетна конвертація всіх .ipynb у теці
find . -name "*.ipynb" -not -path "*/.ipynb_checkpoints/*" | \
  xargs -I {} marimo convert {} -o {}.py

# Або через Python API для складніших сценаріїв
python -c "
import marimo._convert.converters as conv
from pathlib import Path
for nb in Path('notebooks').rglob('*.ipynb'):
    py = nb.with_suffix('.py')
    py.write_text(conv.convert_from_ipynb(nb.read_text()))
"

З мого досвіду, автоматична конвертація дає близько 70% готового ноутбука. Решта: рефакторинг циклічних мутацій у чисті стадії. Хороший патерн: розбити пайплайн підготовки даних на іменовані DataFrames (df_raw, df_typed, df_deduped, df_features) або обгорнути кроки у функції з чітким контрактом входу-виходу. Останнє добре узгоджується з тим, як я рекомендую будувати ML-пайплайни у сучасних ML-пайплайнах з scikit-learn 1.8.

Як версіонувати marimo-ноутбуки в Git

Одна з причин, чому команди даних тікають з Jupyter, це біль з версіонуванням. Файл .ipynb: це JSON, у якому вбудовані output-и, base64-зображення та випадкові execution_count. Будь-який git-diff перетворюється на нечитабельну стіну змін. Marimo вирішує це радикально: ноутбук, це просто Python-модуль, який виконується через __generated_with-хук. Diff, blame та rebase працюють точно так, як для будь-якого .py-файлу.

# Приклад diff при зміні порогу фільтра
$ git diff notebooks/analiz_prodazhiv.py
- df_filtered = df_raw[df_raw["amount"] > 100]
+ df_filtered = df_raw[df_raw["amount"] > 250]

# Виконати ноутбук у CI, щоб перевірити, що він досі валідний
$ python notebooks/analiz_prodazhiv.py --data test_fixtures/orders.parquet
$ marimo check notebooks/analiz_prodazhiv.py  # статичний аналіз DAG

Для команд, яким потрібні лінтинг і форматування, marimo сумісний з ruff, black та mypy «з коробки», бо файл є звичайним Python. У pre-commit-хуку я запускаю ruff check і marimo check. Останній перевіряє граф залежностей і виявляє мертві комірки або циклічні залежності до того, як вони потраплять у review. Це і є те, чого не могло дати покоління .ipynb: ноутбук як повноцінний Python-код, гідний тих самих інженерних практик, що й решта кодової бази.

Часті питання

Чи marimo безкоштовний і з відкритим кодом?

Так. Marimo випущений під ліцензією Apache 2.0 і повністю безкоштовний для будь-якого використання, включно з комерційним. Компанія marimo Inc. розробляє додатковий продукт marimo Cloud для командної співпраці та розгортання, але core-ноутбук та всі описані у статті можливості (SQL-комірки, WASM-експорт, реактивність) доступні у відкритій версії без обмежень.

Чи можна відкрити .ipynb у marimo без конвертації?

Ні, marimo працює лише з .py-форматом. Але вбудована команда marimo convert notebook.ipynb -o notebook.py робить перетворення за секунди, зберігаючи комірки, markdown і зображення. Зворотної конвертації немає, бо .ipynb не може представити реактивний DAG marimo.

Чи працює marimo з JupyterHub або в Colab?

Marimo працює в JupyterHub через розширення jupyterlab-marimo, яке додає marimo як окремий тип файлу поруч із класичними ноутбуками. У Google Colab прямої інтеграції немає, але ви можете встановити marimo у клітинці Colab і запустити marimo edit --host 0.0.0.0 --port 2718 через ngrok або Colab tunnels.

Наскільки marimo впливає на продуктивність?

Реактивний рантайм додає мінімальний оверхед (близько 1-2 мс на комірку для аналізу AST і оновлення графа) у порівнянні з класичним REPL Jupyter. Для аналітичних робочих процесів, де окремі комірки виконуються від секунд до хвилин, цей оверхед незначний. Крім того, marimo кешує неактивні комірки й не переобчислює їх, якщо їхні входи не змінилися, тож у сумі складні ноутбуки часто працюють швидше, ніж у Jupyter.

Чи можна публікувати marimo-ноутбуки як приватні застосунки з аутентифікацією?

Так. marimo run сумісний із будь-яким reverse proxy (nginx, Traefik, Caddy), тож ви можете додати HTTP Basic Auth, OAuth2-proxy або корпоративний SSO перед ним. Для контейнеризованого розгортання marimo надає офіційний Docker-образ, а тег --headless прибирає CLI-повідомлення для чистих логів у продакшні.

Dr. Elena Vasquez
Про Автора Dr. Elena Vasquez

Data scientist with a PhD in computational statistics. Translates papers into pandas one notebook at a time.