Native Polars computation engine for Graphic Walker — translates GW workflow payloads directly to Polars operations
Project description
polars-gw
Native Polars computation engine for Graphic Walker.
Translates Graphic Walker IDataQueryPayload workflow steps directly into Polars operations — no DuckDB, no SQL intermediate.
Installation
pip install polars-gw # core translator only
pip install 'polars-gw[viz]' # core + built-in walk() UI
The core install only pulls in polars. The [viz] extra adds FastAPI,
uvicorn and pydantic so you can launch a local Graphic Walker against a
DataFrame in one line.
Interactive UI
import polars as pl
from gw_polars import walk
df = pl.read_parquet("sales.parquet")
handle = walk(df) # opens http://127.0.0.1:<free-port> in your browser
# ...
handle.stop()
walk() starts a FastAPI server in a background daemon thread, serves
the Graphic Walker UI bundled inside the wheel (no CDN, no network
required), and wires its computation callback to execute_workflow.
Export / Import chart specs
Charts you build in the UI can be saved and restored across sessions:
# Export the current charts to a JSON file
handle.export("my_charts.json")
# Or just grab the spec as a Python list
spec = handle.export()
Next time, pass the file back to walk() to restore exactly where you left off:
handle = walk(df, spec_file="my_charts.json")
The spec file is a plain JSON array of Graphic Walker IChart objects — safe
to version-control or share with collaborators.
Logs
By default walk() prints one line per compute call so you can see
what GW is asking the backend for:
13:42:11 INFO gw_polars.viz: Graphic Walker running on http://127.0.0.1:54221 — 12 000 000 rows x 19 cols, max_rows=1000000
13:42:18 INFO gw_polars.viz: compute: 2 step(s) → 847 row(s) in 142.3 ms
13:42:24 INFO gw_polars.viz: compute: 1 step(s) → 1000000 row(s) in 1284.7 ms [CAPPED]
[CAPPED] means the result hit max_rows and was truncated — Graphic
Walker shows its "Data Limit Reached" toast in the UI when this
happens. Default cap is 1 000 000 rows; tune it with max_rows= or
disable with max_rows=None.
Quiet things down with log_level="warning", or get more detail with
log_level="debug":
walk(df, log_level="warning") # only warnings and errors
walk(df, max_rows=None) # no row cap (use with care on big data)
If you've already configured Python logging yourself (logging.basicConfig,
a framework, pytest's caplog, etc.), walk() won't overwrite it —
it only attaches a console handler when nothing else owns logging.
Usage
import polars as pl
from gw_polars import execute_workflow, get_fields
df = pl.read_csv("data.csv")
# Get field definitions for Graphic Walker
fields = get_fields(df)
# [{"fid": "city", "name": "city", "semanticType": "nominal", "analyticType": "dimension"}, ...]
# Execute a Graphic Walker computation payload
payload = {
"workflow": [
{"type": "filter", "filters": [
{"fid": "age", "rule": {"type": "range", "value": [18, 65]}}
]},
{"type": "view", "query": [
{"op": "aggregate", "groupBy": ["city"], "measures": [
{"field": "salary", "agg": "mean", "asFieldKey": "avg_salary"}
]}
]},
{"type": "sort", "sort": "descending", "by": ["avg_salary"]}
],
"limit": 100
}
results = execute_workflow(df, payload)
# Returns list[dict] — ready to return as IRow[] to Graphic Walker
Supported Operations
Workflow Steps
| Step Type | Description |
|---|---|
filter |
Range, temporal range, one of, not in, regexp |
view/aggregate |
Group by + aggregation (sum, count, mean, median, min, max, variance, stdev, distinctCount) |
view/fold |
Unpivot (wide to long) |
view/bin |
Numeric binning |
view/raw |
Column selection |
sort |
Ascending/descending sort |
transform |
Computed fields (bin, log/log2/log10, binCount, dateTimeDrill, dateTimeFeature, one, expr) |
Field Inference
get_fields() maps Polars dtypes to Graphic Walker field types:
| Polars Type | Semantic Type | Analytic Type |
|---|---|---|
| Float*, Decimal | quantitative | measure |
| Int*, UInt* | quantitative | dimension |
| Date, Datetime, Time, Duration | temporal | dimension |
| Utf8, Categorical, Boolean, etc. | nominal | dimension |
How It Differs from PyGWalker
PyGWalker (and panel-graphic-walker) always route through DuckDB — even for Polars DataFrames:
DataFrame → DuckDB → SQL → execute → dicts
polars-gw translates directly to Polars operations:
DataFrame → Polars expressions → execute → dicts
No DuckDB dependency. No SQL intermediate. Just Polars.
polars-gw vs PyGWalker — which should you use?
Different tools, different jobs. polars-gw is a focused compute engine
- a standalone browser UI. PyGWalker is a broader product with richer UX surface area.
Use polars-gw when
- You're already in the Polars ecosystem (e.g. LazyFrames, streaming, or tools like Flowfile) and don't want a DuckDB round-trip in the middle.
- You need type fidelity for
Categorical,Decimal,Duration,List,Struct— they pass through as-is instead of degrading through SQL. - You want a lean install (~40 MB core, no DuckDB).
- You're building your own frontend and just need the translator
(
execute_workflow+get_fields). - You want debuggable query plans (Polars expressions, not generated SQL).
- You need a standalone browser UI (
walk(df)) without notebook dependencies.
Use PyGWalker when
- You want the Jupyter inline widget — PyGWalker renders the UI
inside a notebook cell via anywidget.
gw_polars.walk()pops a browser tab. - You need first-party framework integrations (Streamlit, Gradio, Dash). PyGWalker has these; polars-gw does not.
- You rely on chart persistence — saving/loading chart specs,
exporting HTML/PNG,
vis_specround-tripping. - You want heterogeneous input support (pandas + Polars + parquet + SQL tables under one DuckDB layer).
- Your data is too large to collect into memory and you need DuckDB's battle-tested out-of-core execution.
- You're using Kanaries cloud features (sharing, cloud chat, etc.).
At a glance
| Concern | polars-gw | PyGWalker |
|---|---|---|
| Backend | Polars expressions | DuckDB SQL |
| Install weight | Lean (polars only) | DuckDB + heavier deps |
| Jupyter inline widget | ❌ (browser tab) | ✅ |
| Streamlit / Gradio / Dash | ❌ | ✅ |
| Standalone browser UI | ✅ (walk()) |
✅ |
| Chart save/load/export | ❌ (defers to GW client) | ✅ |
| LazyFrame native | ✅ | via DuckDB |
| Polars Categorical/Decimal/Duration fidelity | ✅ | lossy through SQL |
| New GW payload ops | Requires translator update | Often works via SQL |
| Heterogeneous inputs (pandas/parquet/SQL) | Polars-only | ✅ |
Short version: if you're all-in on Polars and want the fast, native path,
use polars-gw. If you want inline-notebook, Streamlit, or chart
persistence out of the box, use PyGWalker.
Development
Python
uv sync --extra viz # runtime + viz + dev deps
uv run pytest # 60+ tests
uv run ruff check .
Bundling the viz assets
The walk() UI ships a pre-built JS/CSS bundle under
gw_polars/viz_assets/ (committed to the repo) so end users don't need
Node to pip install polars-gw[viz].
Maintainers rebuild when bumping Graphic Walker:
cd js
npm install
npm run build # one-shot production build
Or iterate with watch mode (rebuilds JS + CSS on save, source maps on):
npm run dev # in one shell
uv run python example/walk_demo.py # in another — refresh browser to pick up changes
Bundle layout:
graphic-walker.js— Graphic Walker + React 19.2.0, minified IIFE (~4.4 MB)graphic-walker.css— Tailwind-compiled stylesheet (~57 KB)versions.json— pinned npm versions + build mode + timestamp
See js/README.md for details (including why React is pinned to
exactly 19.2.0).
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polars_gw-0.1.0.tar.gz.
File metadata
- Download URL: polars_gw-0.1.0.tar.gz
- Upload date:
- Size: 1.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4c943bca47c9ea454b3cbf46b3f4355bea840c1b20189b04095a6cf6ade17d5
|
|
| MD5 |
b5b2e6ff1a3ff6d649d9be6e71d96e19
|
|
| BLAKE2b-256 |
decd2d345c1979df3e0957ebd1c4432400f0afa1d0817480521d00ebfd93c44c
|
Provenance
The following attestation bundles were made for polars_gw-0.1.0.tar.gz:
Publisher:
publish.yml on Edwardvaneechoud/polars-gw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_gw-0.1.0.tar.gz -
Subject digest:
e4c943bca47c9ea454b3cbf46b3f4355bea840c1b20189b04095a6cf6ade17d5 - Sigstore transparency entry: 1304734272
- Sigstore integration time:
-
Permalink:
Edwardvaneechoud/polars-gw@9b6d9f2e8cd6e5c672b87b96d1c7ee2264affcf8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Edwardvaneechoud
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9b6d9f2e8cd6e5c672b87b96d1c7ee2264affcf8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file polars_gw-0.1.0-py3-none-any.whl.
File metadata
- Download URL: polars_gw-0.1.0-py3-none-any.whl
- Upload date:
- Size: 1.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09b0887a858007c8c3b1658be7b8037b171b0bf8238509677eb0a1feffd75e97
|
|
| MD5 |
e29b5caa5e247aee182bc8e94b938044
|
|
| BLAKE2b-256 |
b299b645a77d2755ec871d58dc1c941d73ab183e4e0c9bdbff3c9ca56ce516b1
|
Provenance
The following attestation bundles were made for polars_gw-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Edwardvaneechoud/polars-gw
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_gw-0.1.0-py3-none-any.whl -
Subject digest:
09b0887a858007c8c3b1658be7b8037b171b0bf8238509677eb0a1feffd75e97 - Sigstore transparency entry: 1304734356
- Sigstore integration time:
-
Permalink:
Edwardvaneechoud/polars-gw@9b6d9f2e8cd6e5c672b87b96d1c7ee2264affcf8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Edwardvaneechoud
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9b6d9f2e8cd6e5c672b87b96d1c7ee2264affcf8 -
Trigger Event:
release
-
Statement type: