datashader-browser 0.1.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,49 @@
1
+ name: Build browser demo
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches: [main]
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pages
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: mamba-org/setup-micromamba@v2
23
+ with:
24
+ init-shell: none
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+ cache: pip
29
+ - run: pip install -r requirements-build.txt
30
+ - name: Build JupyterLite site
31
+ run: >-
32
+ jupyter lite build
33
+ --XeusAddon.environment_file=environment.yml
34
+ --contents content
35
+ --output-dir dist
36
+ - uses: actions/upload-pages-artifact@v3
37
+ with:
38
+ path: dist
39
+
40
+ deploy:
41
+ environment:
42
+ name: github-pages
43
+ url: ${{ steps.deployment.outputs.page_url }}
44
+ runs-on: ubuntu-latest
45
+ needs: build
46
+ steps:
47
+ - name: Deploy to GitHub Pages
48
+ id: deployment
49
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,48 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Build release distributions
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v6
18
+ with:
19
+ enable-cache: true
20
+ - name: Verify tag matches package version
21
+ run: test "$(uv run python -c 'from datashader_browser import __version__; print(__version__)')" = "${GITHUB_REF_NAME#v}"
22
+ - name: Build wheel and source distribution
23
+ run: uv build --out-dir release-dist
24
+ - name: Validate package metadata
25
+ run: uv run --with twine twine check release-dist/*
26
+ - uses: actions/upload-artifact@v4
27
+ with:
28
+ name: python-distributions
29
+ path: release-dist/
30
+
31
+ publish:
32
+ name: Publish release to PyPI
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ environment:
36
+ name: pypi
37
+ url: https://pypi.org/p/datashader-browser
38
+ permissions:
39
+ id-token: write
40
+ steps:
41
+ - uses: actions/download-artifact@v4
42
+ with:
43
+ name: python-distributions
44
+ path: release-dist/
45
+ - name: Publish package distributions to PyPI
46
+ uses: pypa/gh-action-pypi-publish@release/v1
47
+ with:
48
+ packages-dir: release-dist/
@@ -0,0 +1,16 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v6
13
+ with:
14
+ enable-cache: true
15
+ - run: uv sync --extra test --extra data
16
+ - run: uv run pytest
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .cache/
7
+ dist/
8
+ _output/
9
+ .jupyterlite.doit.db
10
+ .jupyterlite.doit.db.*
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Sumaiya Islam
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.5
2
+ Name: datashader-browser
3
+ Version: 0.1.0a2
4
+ Summary: Bounded-memory Datashader pipelines for Python running in the browser
5
+ Project-URL: Homepage, https://github.com/SuMayaBee/datashader-browser
6
+ Project-URL: Issues, https://github.com/SuMayaBee/datashader-browser/issues
7
+ Project-URL: Repository, https://github.com/SuMayaBee/datashader-browser
8
+ Author: Sumaiya Islam
9
+ License-Expression: BSD-3-Clause
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: datashader<0.20,>=0.19.1
22
+ Requires-Dist: pandas>=2.2
23
+ Provides-Extra: data
24
+ Requires-Dist: pillow>=11; extra == 'data'
25
+ Requires-Dist: pyarrow>=21; extra == 'data'
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest>=8; extra == 'test'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # datashader-browser
31
+
32
+ Run Datashader-powered visualizations entirely in a browser with Python and
33
+ WebAssembly—without assuming that the complete dataset fits in browser memory.
34
+
35
+ This is an early, independent project. It is not yet an official HoloViz
36
+ project and is not affiliated with or endorsed by the Datashader maintainers.
37
+
38
+ [Try the live browser demo](https://sumayabee.github.io/datashader-browser/).
39
+ Open `streaming-points.ipynb` and run all cells.
40
+
41
+ ## What works now
42
+
43
+ The first vertical slice is a bounded-memory point aggregator:
44
+
45
+ ```python
46
+ import datashader as ds
47
+ import datashader.transfer_functions as tf
48
+
49
+ from datashader_browser import StreamingCanvas, iter_csv_chunks, iter_parquet_batches
50
+
51
+ source = iter_csv_chunks(
52
+ "points.csv",
53
+ chunksize=250_000,
54
+ columns=["longitude", "latitude", "value"],
55
+ )
56
+
57
+ canvas = StreamingCanvas(
58
+ plot_width=1000,
59
+ plot_height=600,
60
+ x_range=(-180, 180),
61
+ y_range=(-90, 90),
62
+ )
63
+ aggregate = canvas.points(source, "longitude", "latitude", agg=ds.mean("value"))
64
+ image = tf.shade(aggregate)
65
+
66
+ # A local Parquet file can yield projected Arrow record batches directly.
67
+ parquet_source = iter_parquet_batches(
68
+ "points.parquet", columns=["longitude", "latitude"], batch_size=250_000
69
+ )
70
+ aggregate = canvas.points(parquet_source, "longitude", "latitude", agg=ds.count())
71
+ ```
72
+
73
+ Only one input chunk and Datashader's fixed-size aggregate buffers are live at
74
+ once. The implementation calls Datashader's own compiler, so point binning and
75
+ reductions match native Datashader instead of being reimplemented.
76
+
77
+ Currently verified reductions are `count`, `sum`, `mean`, `min`, and `max`.
78
+ Explicit `x_range` and `y_range` values are required to preserve a true
79
+ single-pass pipeline.
80
+
81
+ ## NYC Taxi real-data verification
82
+
83
+ The streaming accumulator successfully rendered all **11,842,094 pickup
84
+ locations** in HoloViz's transformed
85
+ [January 2015 NYC Taxi dataset](https://examples.holoviz.org/gallery/nyc_taxi/nyc_taxi.html).
86
+ The 385 MB Parquet file was decoded as 48 Arrow/Pandas batches of at most
87
+ 250,000 rows; the complete table was never materialized as one DataFrame.
88
+
89
+ ![Datashader rendering of 11.8 million January 2015 NYC taxi pickups](docs/assets/nyc-taxi-pickups.png)
90
+
91
+ One warm-cache Linux run produced the 1200-by-700 image in 3.24 seconds with
92
+ 514 MiB peak process RSS. Runtime and memory will vary by machine. Reproduce it
93
+ with:
94
+
95
+ ```bash
96
+ uv sync --extra data
97
+ uv run python examples/nyc_taxi.py --download
98
+ ```
99
+
100
+ This benchmark validates `StreamingCanvas` against a real large dataset on
101
+ native Python. The browser build now includes an experimental PyArrow Parquet
102
+ reader: open `nyc-taxi-browser.ipynb`, place the source file in the browser
103
+ filesystem, and it streams projected Arrow record batches through the same
104
+ accumulator. Full 385 MB end-to-end browser timing and memory results are still
105
+ an explicit validation task, not a claim made by this repository.
106
+
107
+ ## Browser runtime
108
+
109
+ The initial deployment target is JupyterLite with the Xeus-Python WebAssembly
110
+ kernel. Its environment is described in [`environment.yml`](environment.yml)
111
+ and uses the experimental Numba and llvmlite packages from emscripten-forge.
112
+ The kernel executes in a Web Worker, keeping aggregation off the browser UI
113
+ thread. The Parquet-capable build is currently about 256 MB because PyArrow
114
+ brings compiled Arrow and compression dependencies; this is an engineering
115
+ baseline, not the final delivery architecture. The first visit therefore needs
116
+ a substantial download, while subsequent use benefits from the browser cache.
117
+
118
+ Browser verification: `streaming-parquet.ipynb` has been run end-to-end in the
119
+ Xeus-Python WebAssembly kernel. It created a 57.2 MB, two-million-row Parquet
120
+ file in the browser filesystem, streamed it through 20 Arrow batches, checked
121
+ the final row count, and rendered the shaded aggregate with no browser console
122
+ errors. The full 385 MB NYC Taxi browser benchmark remains a separate task.
123
+
124
+ ```bash
125
+ uv sync --extra test
126
+ uv run pytest
127
+
128
+ # Build the browser site (requires the build dependencies below).
129
+ uv pip install -r requirements-build.txt
130
+ uv run jupyter lite build \
131
+ --XeusAddon.environment_file=environment.yml \
132
+ --contents content \
133
+ --output-dir dist
134
+ ```
135
+
136
+ ## Large-data strategy
137
+
138
+ “In the browser” does not mean that an unlimited dataset can be copied into a
139
+ Pandas DataFrame. The project uses three execution levels:
140
+
141
+ 1. **Stream:** scan CSV or local Parquet/Arrow batches through a bounded-memory Datashader
142
+ accumulator. Total row count affects time, not peak aggregate memory.
143
+ 2. **Prune:** use Parquet row groups, spatial partitions, and DuckDB-Wasm to
144
+ read only the viewport and required columns.
145
+ 3. **Tile:** for billion-row interactive datasets, fetch spatial partitions or
146
+ precomputed multiresolution aggregates instead of rescanning the source on
147
+ every zoom.
148
+
149
+ See [Architecture](docs/architecture.md), [Research](docs/research.md), and the
150
+ [roadmap](ROADMAP.md) for the engineering decisions and current limitations.
151
+
152
+ ## Status
153
+
154
+ This repository is an alpha-stage research implementation. The WebAssembly
155
+ Numba/llvmlite stack works in current demonstrations but its patches have not
156
+ yet been fully upstreamed. Production use should wait for reproducible browser
157
+ builds, compatibility CI, and measured browser memory limits.
158
+
159
+ ## License
160
+
161
+ BSD-3-Clause. Datashader is a separate BSD-3-Clause project owned by its
162
+ respective copyright holders and contributors.
@@ -0,0 +1,133 @@
1
+ # datashader-browser
2
+
3
+ Run Datashader-powered visualizations entirely in a browser with Python and
4
+ WebAssembly—without assuming that the complete dataset fits in browser memory.
5
+
6
+ This is an early, independent project. It is not yet an official HoloViz
7
+ project and is not affiliated with or endorsed by the Datashader maintainers.
8
+
9
+ [Try the live browser demo](https://sumayabee.github.io/datashader-browser/).
10
+ Open `streaming-points.ipynb` and run all cells.
11
+
12
+ ## What works now
13
+
14
+ The first vertical slice is a bounded-memory point aggregator:
15
+
16
+ ```python
17
+ import datashader as ds
18
+ import datashader.transfer_functions as tf
19
+
20
+ from datashader_browser import StreamingCanvas, iter_csv_chunks, iter_parquet_batches
21
+
22
+ source = iter_csv_chunks(
23
+ "points.csv",
24
+ chunksize=250_000,
25
+ columns=["longitude", "latitude", "value"],
26
+ )
27
+
28
+ canvas = StreamingCanvas(
29
+ plot_width=1000,
30
+ plot_height=600,
31
+ x_range=(-180, 180),
32
+ y_range=(-90, 90),
33
+ )
34
+ aggregate = canvas.points(source, "longitude", "latitude", agg=ds.mean("value"))
35
+ image = tf.shade(aggregate)
36
+
37
+ # A local Parquet file can yield projected Arrow record batches directly.
38
+ parquet_source = iter_parquet_batches(
39
+ "points.parquet", columns=["longitude", "latitude"], batch_size=250_000
40
+ )
41
+ aggregate = canvas.points(parquet_source, "longitude", "latitude", agg=ds.count())
42
+ ```
43
+
44
+ Only one input chunk and Datashader's fixed-size aggregate buffers are live at
45
+ once. The implementation calls Datashader's own compiler, so point binning and
46
+ reductions match native Datashader instead of being reimplemented.
47
+
48
+ Currently verified reductions are `count`, `sum`, `mean`, `min`, and `max`.
49
+ Explicit `x_range` and `y_range` values are required to preserve a true
50
+ single-pass pipeline.
51
+
52
+ ## NYC Taxi real-data verification
53
+
54
+ The streaming accumulator successfully rendered all **11,842,094 pickup
55
+ locations** in HoloViz's transformed
56
+ [January 2015 NYC Taxi dataset](https://examples.holoviz.org/gallery/nyc_taxi/nyc_taxi.html).
57
+ The 385 MB Parquet file was decoded as 48 Arrow/Pandas batches of at most
58
+ 250,000 rows; the complete table was never materialized as one DataFrame.
59
+
60
+ ![Datashader rendering of 11.8 million January 2015 NYC taxi pickups](docs/assets/nyc-taxi-pickups.png)
61
+
62
+ One warm-cache Linux run produced the 1200-by-700 image in 3.24 seconds with
63
+ 514 MiB peak process RSS. Runtime and memory will vary by machine. Reproduce it
64
+ with:
65
+
66
+ ```bash
67
+ uv sync --extra data
68
+ uv run python examples/nyc_taxi.py --download
69
+ ```
70
+
71
+ This benchmark validates `StreamingCanvas` against a real large dataset on
72
+ native Python. The browser build now includes an experimental PyArrow Parquet
73
+ reader: open `nyc-taxi-browser.ipynb`, place the source file in the browser
74
+ filesystem, and it streams projected Arrow record batches through the same
75
+ accumulator. Full 385 MB end-to-end browser timing and memory results are still
76
+ an explicit validation task, not a claim made by this repository.
77
+
78
+ ## Browser runtime
79
+
80
+ The initial deployment target is JupyterLite with the Xeus-Python WebAssembly
81
+ kernel. Its environment is described in [`environment.yml`](environment.yml)
82
+ and uses the experimental Numba and llvmlite packages from emscripten-forge.
83
+ The kernel executes in a Web Worker, keeping aggregation off the browser UI
84
+ thread. The Parquet-capable build is currently about 256 MB because PyArrow
85
+ brings compiled Arrow and compression dependencies; this is an engineering
86
+ baseline, not the final delivery architecture. The first visit therefore needs
87
+ a substantial download, while subsequent use benefits from the browser cache.
88
+
89
+ Browser verification: `streaming-parquet.ipynb` has been run end-to-end in the
90
+ Xeus-Python WebAssembly kernel. It created a 57.2 MB, two-million-row Parquet
91
+ file in the browser filesystem, streamed it through 20 Arrow batches, checked
92
+ the final row count, and rendered the shaded aggregate with no browser console
93
+ errors. The full 385 MB NYC Taxi browser benchmark remains a separate task.
94
+
95
+ ```bash
96
+ uv sync --extra test
97
+ uv run pytest
98
+
99
+ # Build the browser site (requires the build dependencies below).
100
+ uv pip install -r requirements-build.txt
101
+ uv run jupyter lite build \
102
+ --XeusAddon.environment_file=environment.yml \
103
+ --contents content \
104
+ --output-dir dist
105
+ ```
106
+
107
+ ## Large-data strategy
108
+
109
+ “In the browser” does not mean that an unlimited dataset can be copied into a
110
+ Pandas DataFrame. The project uses three execution levels:
111
+
112
+ 1. **Stream:** scan CSV or local Parquet/Arrow batches through a bounded-memory Datashader
113
+ accumulator. Total row count affects time, not peak aggregate memory.
114
+ 2. **Prune:** use Parquet row groups, spatial partitions, and DuckDB-Wasm to
115
+ read only the viewport and required columns.
116
+ 3. **Tile:** for billion-row interactive datasets, fetch spatial partitions or
117
+ precomputed multiresolution aggregates instead of rescanning the source on
118
+ every zoom.
119
+
120
+ See [Architecture](docs/architecture.md), [Research](docs/research.md), and the
121
+ [roadmap](ROADMAP.md) for the engineering decisions and current limitations.
122
+
123
+ ## Status
124
+
125
+ This repository is an alpha-stage research implementation. The WebAssembly
126
+ Numba/llvmlite stack works in current demonstrations but its patches have not
127
+ yet been fully upstreamed. Production use should wait for reproducible browser
128
+ builds, compatibility CI, and measured browser memory limits.
129
+
130
+ ## License
131
+
132
+ BSD-3-Clause. Datashader is a separate BSD-3-Clause project owned by its
133
+ respective copyright holders and contributors.
@@ -0,0 +1,35 @@
1
+ # Roadmap
2
+
3
+ ## 0.1: bounded-memory correctness
4
+
5
+ - Point glyphs over iterables of Pandas chunks.
6
+ - Count, sum, mean, min, and max parity with Datashader 0.19.1.
7
+ - Progress and cancellation hooks.
8
+ - Xeus-Python/JupyterLite environment using emscripten-forge Numba.
9
+ - Browser build and deployment CI.
10
+
11
+ ## 0.2: browser data plane
12
+
13
+ - Local Parquet record-batch source via PyArrow in the Xeus-Python worker.
14
+ - DuckDB-Wasm worker for local and remote Parquet.
15
+ - Column projection and viewport predicates.
16
+ - Transferable buffers between the data worker and Python worker.
17
+ - Generation IDs so obsolete pan/zoom jobs are cancelled or ignored.
18
+
19
+ ## 0.3: indexed exploration
20
+
21
+ - Spatially partitioned Parquet manifest format.
22
+ - Partition pruning by canvas bounds.
23
+ - Cache recent partitions in OPFS.
24
+ - Progressive low-resolution then full-resolution rendering.
25
+
26
+ ## Later
27
+
28
+ - Lines with one-row partition overlap.
29
+ - Categorical aggregation with explicit category budgets.
30
+ - Panel/Bokeh integration outside the JupyterLab shell.
31
+ - Optional GPU aggregation experiments after CPU parity and memory behavior are
32
+ characterized.
33
+
34
+ Polygons, trimesh, quadmesh, Dask, CUDA, and arbitrary row-index reductions are
35
+ outside the initial scope.
@@ -0,0 +1,10 @@
1
+ # Datashader Browser Lab
2
+
3
+ Start with `streaming-parquet.ipynb` to create, stream, and render a Parquet
4
+ dataset entirely in this browser tab. `streaming-points.ipynb` is the smaller
5
+ in-memory-generated example. `nyc-taxi-browser.ipynb` contains the full
6
+ 11.8-million-row HoloViz NYC Taxi workflow and instructions for supplying its
7
+ 385 MB source file.
8
+
9
+ The Xeus-Python kernel and all Python packages are WebAssembly assets served by
10
+ the static site; no Python server is involved.
@@ -0,0 +1,119 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# NYC Taxi: 11.8 million points in browser Python\n",
8
+ "\n",
9
+ "This notebook renders all January 2015 NYC Taxi pickup locations from the 385 MB HoloViz Parquet dataset. Upload or mount `nyc_taxi_wide.parq` in JupyterLite, or enable the download cell below. Parquet decoding and Datashader aggregation are bounded by the selected batch size; the complete table is never constructed as one DataFrame."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": null,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "from pathlib import Path\n",
19
+ "from urllib.request import urlopen\n",
20
+ "\n",
21
+ "import pyarrow.parquet as pq\n",
22
+ "import datashader.transfer_functions as tf\n",
23
+ "\n",
24
+ "from datashader_browser import StreamingCanvas, iter_parquet_batches\n",
25
+ "\n",
26
+ "DATA_URL = 'https://datasets.holoviz.org/nyc_taxi/v1/nyc_taxi_wide.parq'\n",
27
+ "DATA_PATH = Path('nyc_taxi_wide.parq')\n",
28
+ "X_RANGE = (-8_256_000, -8_209_000)\n",
29
+ "Y_RANGE = (4_964_000, 4_990_000)"
30
+ ]
31
+ },
32
+ {
33
+ "cell_type": "markdown",
34
+ "metadata": {},
35
+ "source": [
36
+ "Set `DOWNLOAD = True` to fetch the public dataset. Mounting or uploading it through JupyterLite avoids repeating this 385 MB download on subsequent sessions."
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "execution_count": null,
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "DOWNLOAD = False\n",
46
+ "\n",
47
+ "if DOWNLOAD and not DATA_PATH.exists():\n",
48
+ " with urlopen(DATA_URL) as response, DATA_PATH.open('wb') as output:\n",
49
+ " while block := response.read(1024 * 1024):\n",
50
+ " output.write(block)\n",
51
+ "\n",
52
+ "if not DATA_PATH.exists():\n",
53
+ " raise FileNotFoundError(\n",
54
+ " f'{DATA_PATH} is missing. Upload/mount it in JupyterLite or set DOWNLOAD=True.'\n",
55
+ " )\n",
56
+ "\n",
57
+ "parquet = pq.ParquetFile(DATA_PATH)\n",
58
+ "print(f'{parquet.metadata.num_rows:,} rows; {DATA_PATH.stat().st_size / 1_000_000:.1f} MB')"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": null,
64
+ "metadata": {},
65
+ "outputs": [],
66
+ "source": [
67
+ "def report(progress):\n",
68
+ " if progress.chunks % 10 == 0 or progress.rows == parquet.metadata.num_rows:\n",
69
+ " print(f'aggregated {progress.rows:,}/{parquet.metadata.num_rows:,} rows')\n",
70
+ "\n",
71
+ "canvas = StreamingCanvas(\n",
72
+ " plot_width=1_200,\n",
73
+ " plot_height=700,\n",
74
+ " x_range=X_RANGE,\n",
75
+ " y_range=Y_RANGE,\n",
76
+ ")\n",
77
+ "aggregate = canvas.points(\n",
78
+ " iter_parquet_batches(\n",
79
+ " DATA_PATH,\n",
80
+ " batch_size=250_000,\n",
81
+ " columns=['pickup_x', 'pickup_y'],\n",
82
+ " ),\n",
83
+ " 'pickup_x',\n",
84
+ " 'pickup_y',\n",
85
+ " on_progress=report,\n",
86
+ ")\n",
87
+ "aggregate.attrs"
88
+ ]
89
+ },
90
+ {
91
+ "cell_type": "code",
92
+ "execution_count": null,
93
+ "metadata": {},
94
+ "outputs": [],
95
+ "source": [
96
+ "assert aggregate.attrs['source_rows'] == parquet.metadata.num_rows\n",
97
+ "image = tf.shade(\n",
98
+ " aggregate,\n",
99
+ " cmap=['#111827', '#1d4ed8', '#06b6d4', '#facc15', '#fff7ed'],\n",
100
+ " how='eq_hist',\n",
101
+ ")\n",
102
+ "tf.set_background(image, '#050814').to_pil()"
103
+ ]
104
+ }
105
+ ],
106
+ "metadata": {
107
+ "kernelspec": {
108
+ "display_name": "Python (XPython)",
109
+ "language": "python",
110
+ "name": "xpython"
111
+ },
112
+ "language_info": {
113
+ "name": "python",
114
+ "version": "3.13"
115
+ }
116
+ },
117
+ "nbformat": 4,
118
+ "nbformat_minor": 5
119
+ }