Marimo Notebook 2026: Reactive Python Thay Thế Jupyter Cho Data Science

Marimo 2026 — notebook Python reactive thay thế Jupyter. Hướng dẫn cài đặt, dùng widget mo.ui, cache tính toán, migrate từ .ipynb và deploy thành app web, kèm code chạy được.

Nói thật, sau gần một thập kỷ "vật lộn" với Jupyter, mình tưởng mình đã chấp nhận sống chung với mấy cái cell chạy lung tung và cái notebook đôi khi "ma ám" rồi. Vậy mà Marimo xuất hiện — một notebook Python reactive đang nổi lên như lựa chọn thay thế hiện đại cho Jupyter trong năm 2026 — và bỗng dưng workflow data science của mình thay đổi hẳn.

Khác với Jupyter (nơi bạn phải nhớ chạy lại từng cell sau khi sửa code), Marimo tự động cập nhật toàn bộ dataflow ngay khi bất kỳ biến nào thay đổi. Notebook lưu dưới dạng file .py thuần. Dễ review trên Git. Dễ deploy như app. Và quan trọng nhất: không bao giờ bị "hidden state" làm hỏng kết quả phân tích nữa.

Trong hướng dẫn này, mình sẽ giúp bạn cài đặt Marimo 0.16+, hiểu mô hình reactive, dùng widget tương tác cho data science, và migrate notebook Jupyter cũ sang Marimo. Toàn bộ ví dụ code đều chạy được với pandas 2.3, Polars 1.20 và Python 3.13 trên macOS, Linux, lẫn Windows. Vậy thì, cùng đi vào việc thôi.

Marimo là gì? Vì sao thay thế Jupyter trong 2026

Marimo là notebook Python mã nguồn mở do Akshay Agrawal phát triển (release 1.0 đầu 2025). Nó giải quyết ba vấn đề kinh điển của Jupyter mà cộng đồng data science đã than phiền hơn một thập kỷ — và đúng kiểu "than rồi để đó", chẳng ai làm gì cho đến khi Marimo xuất hiện:

  • Hidden state: trong Jupyter, bạn có thể xóa một cell định nghĩa biến, nhưng biến đó vẫn còn nguyên trong kernel. Kết quả? Notebook chạy lại từ đầu thì lỗi tan tành.
  • Out-of-order execution: cell có thể chạy theo bất kỳ thứ tự nào người dùng click, dẫn tới kết quả không tái tạo được — ác mộng cho ai làm research nghiêm túc.
  • Khó review trên Git: file .ipynb là JSON kèm output base64, diff đọc xong chỉ muốn nghỉ làm.

Marimo giải quyết bằng cách phân tích AST của tất cả cell, dựng directed acyclic graph (DAG) phụ thuộc giữa các biến, và mỗi khi bạn sửa cell nào, mọi cell phụ thuộc downstream sẽ tự chạy lại. Notebook lưu dưới dạng .py thuần — chạy được như script, import được như module. Đơn giản đến mức nghi ngờ vì sao mãi đến giờ mới có người làm.

So sánh nhanh Marimo vs Jupyter

Tiêu chíMarimo 0.16Jupyter Lab 4.x
Mô hình thực thiReactive (DAG tự động)Imperative (thủ công)
Định dạng file.py (Python thuần).ipynb (JSON)
Hidden stateKhông thể xảy raPhổ biến
Deploy thành appCó sẵn (marimo run)Cần Voilà / Streamlit
Widget tương tácBuilt-in mo.uiipywidgets riêng
Git diffĐọc được dễ dàngCần nbdime
Hot reload moduleCần %autoreload

Cài đặt Marimo và tạo notebook đầu tiên

Marimo yêu cầu Python 3.10+, nhưng mình khuyên bạn dùng Python 3.13 để tận dụng GIL-free mode khi chạy code đa luồng (đặc biệt với pipeline ETL nặng). Cài đặt qua pip — hoặc uv nếu bạn đã chuyển sang công cụ này (mình đã chuyển và không nhìn lại):

# Bằng pip
pip install "marimo[recommended]" pandas polars matplotlib

# Hoặc nhanh hơn 10x với uv
uv pip install "marimo[recommended]" pandas polars matplotlib

# Khởi động notebook mới
marimo edit hello.py

Lệnh marimo edit sẽ mở trình soạn thảo trên trình duyệt tại http://127.0.0.1:2718. Bạn không cần khởi động kernel riêng — Marimo dùng kernel Python ngay khi chạy. Đỡ được một bước phiền phức.

Cell reactive đầu tiên

Tạo ba cell sau và để ý xem chuyện gì xảy ra khi bạn kéo slider n:

# Cell 1
import marimo as mo
import numpy as np

# Cell 2
n = mo.ui.slider(start=10, stop=10000, step=10, value=1000, label="Số mẫu")
n

# Cell 3
data = np.random.normal(0, 1, n.value)
mean, std = data.mean(), data.std()
mo.md(f"**Trung bình**: {mean:.4f} · **Độ lệch chuẩn**: {std:.4f}")

Khi kéo slider, cell 3 tự chạy lại ngay — không cần gọi display(), không cần %matplotlib inline, không cần callback gì hết. Lần đầu nhìn thấy, mình thật sự ngồi nghịch slider khoảng 5 phút chỉ vì nó… mượt.

Widget tương tác cho data exploration

Module mo.ui cung cấp 30+ widget native: slider, dropdown, multiselect, table, file uploader, code editor… Dưới đây là pattern mình hay dùng nhất — cho phép đổi cột phân tích chỉ bằng dropdown, cực hữu ích khi explore dataset mới:

import marimo as mo
import pandas as pd
import altair as alt

df = pd.read_csv("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/penguins.csv")
df = df.dropna()

cot_x = mo.ui.dropdown(options=df.select_dtypes("number").columns.tolist(),
                        value="bill_length_mm", label="Trục X")
cot_y = mo.ui.dropdown(options=df.select_dtypes("number").columns.tolist(),
                        value="flipper_length_mm", label="Trục Y")
mau = mo.ui.dropdown(options=["species", "island", "sex"], value="species", label="Phân nhóm")

mo.hstack([cot_x, cot_y, mau])
chart = (
    alt.Chart(df)
    .mark_circle(size=80, opacity=0.7)
    .encode(x=cot_x.value, y=cot_y.value, color=mau.value, tooltip=list(df.columns))
    .properties(width=560, height=380)
    .interactive()
)
chart

Mỗi lần bạn đổi dropdown, biểu đồ Altair cập nhật mà không cần reload trang. Logic phụ thuộc do Marimo tự suy ra từ tham chiếu biến cot_x.value trong cell vẽ — nghĩa là bạn không phải viết một dòng "callback" nào.

Hiển thị DataFrame thông minh với mo.ui.table

Khác bảng tĩnh bên Jupyter, mo.ui.table hỗ trợ filter, sort, search, và (đây mới là phần hay) trả về dòng được chọn để dùng trong cell khác:

bang = mo.ui.table(df, selection="multi", page_size=15, label="Chọn vài dòng để phân tích sâu")
bang
# Cell tự cập nhật khi user check vào checkbox của bảng
chon = bang.value
mo.md(f"Đã chọn **{len(chon)}** dòng. Khối lượng trung bình: "
      f"**{chon['body_mass_g'].mean():.0f} g**" if len(chon) else "Chưa chọn dòng nào.")

Pattern data science với Marimo

1. Caching kết quả tính toán nặng

Marimo có decorator mo.cache dùng on-disk persistent cache, hashing cả nội dung hàm lẫn argument. Cực kỳ hữu ích khi train model, query database, hay đọc Parquet hàng GB:

import marimo as mo
import polars as pl

@mo.cache
def tai_va_lam_sach(duong_dan: str) -> pl.DataFrame:
    df = pl.read_parquet(duong_dan)
    return df.drop_nulls().with_columns(
        pl.col("price").cast(pl.Float64),
        pl.col("created_at").str.to_datetime()
    )

df = tai_va_lam_sach("s3://bucket/orders_2026.parquet")
df.shape

Lần chạy sau, nếu argument không đổi và body hàm cũng không đổi, Marimo trả kết quả từ cache trong vài mili giây — thay vì đọc lại Parquet mất vài phút. Một lần restart máy lúc demo trước khách, mình đã cảm ơn cái cache này hơi nhiều.

2. Chạy SQL trực tiếp trên DataFrame

Từ phiên bản 0.10, Marimo tích hợp DuckDB cho SQL cell. Bạn viết SQL ngay trong notebook, kết quả là Polars DataFrame:

# Cell SQL — Marimo tự nhận diện
SELECT
    species,
    COUNT(*) AS so_luong,
    AVG(body_mass_g) AS khoi_luong_tb,
    AVG(flipper_length_mm) AS sai_canh_tb
FROM df
GROUP BY species
ORDER BY khoi_luong_tb DESC

Và đây là phần hay: SQL cell tự cập nhật khi DataFrame nguồn (df) thay đổi, vì Marimo hiểu phụ thuộc. Mình hay dùng pattern này khi viết báo cáo nội bộ — dùng SQL khi muốn aggregate, dùng Polars khi cần biến đổi phức tạp.

3. Hot reload module Python ngoài

Khi bạn refactor code sang file .py riêng (best practice cho project lớn — đừng nhồi mọi thứ vào notebook), Marimo theo dõi file đó và tự reload mỗi khi bạn save. Bật trong settings:

# marimo.toml ở thư mục project
[runtime]
auto_instantiate = true
on_cell_change = "autorun"
watcher_on_save = "autorun"

Migrate từ Jupyter sang Marimo

Marimo có lệnh CLI để chuyển .ipynb sang .py tự động:

marimo convert ban_phan_tich.ipynb -o ban_phan_tich.py
marimo edit ban_phan_tich.py

Tuy nhiên, do Marimo cấm tái định nghĩa biến ở nhiều cell (để giữ DAG hợp lệ), bạn sẽ gặp lỗi khi notebook cũ có pattern dạng:

# Cell 1: df = pd.read_csv(...)
# Cell 2: df = df[df["col"] > 0]   # ← Marimo báo lỗi: df đã được định nghĩa
# Cell 3: df = df.reset_index()    # ← Lỗi tương tự

Cách sửa? Gộp các bước transform vào một cell, hoặc đặt tên biến khác (df_loc, df_clean). Đây là pattern mình khuyên dùng:

# Một cell duy nhất cho pipeline xử lý
df_clean = (
    pd.read_csv("orders.csv")
      .query("price > 0 and quantity > 0")
      .assign(total=lambda d: d["price"] * d["quantity"])
      .reset_index(drop=True)
)
df_clean.head()

Cách tiếp cận này (method chaining) thực ra là idiom hiện đại cho pandas/Polars, nên migrate sang Marimo cũng là cơ hội tốt để cải thiện chất lượng code. Một mũi tên trúng hai con chim — không phải tự nhiên mà developers gọi đây là "happy migration".

Deploy notebook thành app web

Đây mới là tính năng "killer feature" của Marimo, theo ý kiến cá nhân của mình. Cùng một file .py có thể chạy ở 3 chế độ:

# Chế độ edit (như Jupyter)
marimo edit dashboard.py

# Chế độ app — read-only, dành cho stakeholder
marimo run dashboard.py --host 0.0.0.0 --port 8080

# Chế độ script — chạy đầu cuối, in kết quả
python dashboard.py

Chế độ marimo run ẩn tất cả cell code, chỉ hiển thị widget và output. Có thể đóng gói Docker dễ như sau:

FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt dashboard.py ./
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 8080
CMD ["marimo", "run", "dashboard.py", "--host", "0.0.0.0", "--port", "8080"]

Marimo Cloud (ra mắt Q2/2026) cho phép share notebook qua URL công khai mà không cần Docker, miễn phí cho project nhỏ. Mình thấy đây là cách lý tưởng để gửi prototype cho team product mà không phải giảng giải "bạn cài Python rồi chạy lệnh này" nữa.

Best practices cho data science với Marimo

  1. Một biến chỉ định nghĩa ở một cell. Đặt tên rõ theo từng bước: df_raw, df_clean, df_features, X_train, y_train. Hơi dài nhưng cực kỳ đỡ đầu khi nhìn lại notebook sau vài tuần.
  2. Đặt import ở cell trên cùng. Marimo gợi ý điều này; thậm chí có lệnh "Sort cells topologically" để tự sắp xếp.
  3. Dùng mo.stop() để skip cell nặng khi điều kiện chưa đủ — ví dụ chưa load file: mo.stop(df is None, "Vui lòng upload file CSV").
  4. Refactor code dài thành function trong file utils.py. Notebook chỉ giữ logic explore và visualization. Marimo hot-reload tự động — bạn sẽ yêu pattern này.
  5. Bật type hint. Marimo tích hợp Ruff và Pyright trong editor, nhắc lỗi type ngay khi gõ.
  6. Test notebook như script: viết if __name__ == "__main__": ở cuối, chạy python notebook.py trong CI để bắt regression sớm.

Khi nào KHÔNG nên dùng Marimo

Honestly, Marimo không phải lúc nào cũng tốt hơn Jupyter. Cân nhắc giữ Jupyter nếu:

  • Team đã có pipeline CI/CD dựa trên papermill hoặc nbconvert (việc rip toàn bộ ra để chuyển sang Marimo có thể không đáng).
  • Cần tutorial dạng có lời giải tuần tự (như khoá học) — mô hình imperative trực quan hơn cho người mới học Python.
  • Phụ thuộc vào kernel ngoài Python (R, Julia) — Marimo chỉ hỗ trợ Python, dứt khoát.
  • Cần feature pin: notebook GPU trên Colab, Kaggle Kernels, Databricks — các nền tảng này chưa hỗ trợ Marimo native (tính đến tháng 5/2026).

FAQ về Marimo

Marimo có miễn phí không?

Có. Marimo là mã nguồn mở (Apache 2.0), miễn phí cho cả mục đích thương mại. Marimo Cloud có gói free cho cá nhân; các gói team/enterprise tính phí theo số seat.

Marimo dùng được với GPU không?

Có. Marimo chỉ là layer notebook — nó dùng kernel Python local của bạn, nên mọi thư viện GPU (CUDA, RAPIDS, PyTorch, JAX) chạy bình thường. Điều cần lưu ý là cell reactive sẽ tự rerun khi bạn sửa input, nên hãy cẩn thận với cell training mất nhiều giờ. Dùng @mo.cache hoặc mo.stop() để tránh chạy lại không cần thiết — bài học mình rút ra sau lần training BERT mất 4 tiếng vì gõ nhầm một biến.

Có thể dùng Marimo trong VS Code không?

Có. Vì Marimo lưu file .py thuần, bạn mở trong VS Code như script bình thường. Extension chính thức "Marimo" (do Astral phát hành tháng 3/2026) cung cấp preview reactive ngay trong VS Code, không cần mở browser. Mình đã chuyển hẳn sang dùng kiểu này — tiện hơn nhiều khi cần xem code lẫn notebook cùng lúc.

Marimo có chậm hơn Jupyter không?

Không đáng kể. Phân tích DAG mất < 5ms cho notebook có 100 cell. Khi bạn sửa một cell, Marimo chỉ rerun các cell phụ thuộc downstream — thực tế thường nhanh hơn cách "Restart and Run All" của Jupyter. Với cell rất nặng, dùng @mo.cache hoặc đặt on_cell_change = "lazy" để chỉ rerun khi click nút Run.

Có thể chia sẻ Marimo notebook qua nbviewer hay GitHub không?

GitHub render file .py có syntax highlight bình thường (đẹp hơn JSON của .ipynb nhiều). Để render reactive đầy đủ, dùng marimo export html-wasm notebook.py — xuất HTML chạy hoàn toàn trong trình duyệt qua Pyodide, không cần server. File này upload lên GitHub Pages hoặc S3 là chia sẻ được liền.

Tổng kết

Marimo 2026 không chỉ là "Jupyter phiên bản đẹp hơn". Nó định nghĩa lại notebook Python theo hướng reactive, phù hợp với workflow data science hiện đại: explore nhanh, deploy thẳng thành app, version control trên Git như code thật. Với chỉ một lệnh pip install marimo, bạn đã có công cụ thay thế Jupyter cho khoảng 80% use case data science thường gặp.

Lời khuyên cá nhân? Nếu bạn đang bắt đầu project mới trong 2026, hãy thử Marimo trước. Nếu đang có codebase Jupyter lớn, đừng vội — migrate dần, bắt đầu từ dashboard và notebook explore (ít state phức tạp), giữ Jupyter cho tutorial và notebook giảng dạy. Cứ thử trong một tuần, mình tin bạn sẽ không quay lại.

Về Tác Giả Editorial Team

Our team of expert writers and editors.