Skip to main content

cellwalk

Reconstruct irregular spreadsheet tables by associating data cells with explicit spatial headers. Retain physical coordinates and merged-cell anchor provenance for data values and every associated header.

import pandas as pd
from cellwalk import CellGrid

raw = pd.DataFrame([[None, 2024, None], [None, "Q1", "Q2"], ["Mexico", 10, 20]], dtype=object)
base = CellGrid.from_dataframe(raw).data(rows=[2], columns=[1, 2])
headers = base.headers_above("year", row=0, span="right").headers_above("quarter", row=1)
result = headers.headers_left("country", column=0).to_frame()
print(result[["country", "year", "quarter", "value"]])

Install

Python 3.11+ and Pandas 2.2+ are required. Install from a checkout until the initial PyPI release has been verified:

python -m pip install .
# After the first verified PyPI release:
python -m pip install cellwalk
# Optional XLSX reader:
python -m pip install "cellwalk[xlsx]"

There is no dependency on R or sibling projects, runtime service, or telemetry.

Coordinates, bounds and navigation

All coordinates are zero-based positions. DataFrame index and column labels are ignored as geometry, so duplicate labels are supported. Cell exposes row, column, value, source_row, and source_column. For a DataFrame cell, the source anchor equals its physical coordinate. Grid operations return new grids and do not modify inputs or previous selections.

API Behavior
CellGrid.from_dataframe(frame) Snapshot cell scalars without whole-array coercion
grid.cell(row, column) Fetch a cell inside current bounds
grid.within(row_start, row_stop, column_start, column_stop) Restrict navigation to a half-open subrectangle
grid.above/below/left/right(row, column, skip_blank=True) Find nearest cell in that direction; return None at the boundary
grid.data(rows=[...], columns=[...], include_blank=False) Select explicit Cartesian-product target positions
grid.headers_above(name, row=..., span="none", missing="raise") Attach a fixed header row
grid.headers_left(name, column=..., span="none", missing="raise") Attach a fixed header column
grid.associate(name, mapping, missing="raise") Map selected data coordinates to arbitrary header coordinates
grid.to_frame() Materialize records with all value and coordinate provenance

Bounds use excluded stops, like Python slicing, and can only narrow an existing region. Navigation and spans cannot cross them. within() clears targets and associations; call it before data(). Data selection follows the supplied row order, then column order, and clears old associations when repeated. Use separate grids for adjacent tables. Empty selections return an empty output with the expected schema.

Structural blanks are scalar missing values or empty/whitespace-only strings. Zero and False are values. Blank data cells are omitted by default; include_blank=True preserves them. skip_blank=False navigation returns the immediate physical neighbor, even if blank.

Explicit header rules

headers_above requires the specified row to be above every target. By default, the header comes from the exact target column. span="right" explicitly fills a blank heading from the nearest nonblank header at its left on that same row, stopping at the region boundary. This supports year spans above quarterly headings without guessing header levels. Add another named association for each level.

headers_left similarly requires a column left of every target. Its opt-in span="down" carries a nonblank heading from above within that column. Default span="none" never infers a span. Missing/blank headers raise GridError; missing="none" retains targets with an unassigned header and null header coordinates.

For nonrectangular layouts, use an explicit mapping:

selected = CellGrid.from_dataframe(raw).data(rows=[2], columns=[1, 2])
mapped = selected.associate("year", {(2, 1): (0, 1), (2, 2): (0, 1)})

Mappings may choose headers in any direction but must refer to selected data cells and headers inside bounds. No result values are hardcoded; the mapping specifies source relationships. See examples/nonrectangular.py.

Output and provenance

to_frame() always includes row, column, source_row, source_column, and value. Each header named year, for example, adds year, year_row, year_column, year_source_row, and year_source_column. The first coordinates identify the associated physical cell; source coordinates identify its value's anchor. Header names colliding with these metadata fields or another generated field raise an error. Object-backed columns avoid rounding large integers or coercing original mixed scalars when another record is missing. Cast to desired analysis dtypes explicitly after reconstruction.

Optional XLSX reading and merged cells

grid = CellGrid.from_xlsx("report.xlsx", sheet="Data", merged="propagate")

sheet is a worksheet name or zero-based index. merged="blank", the default, keeps nonanchor merged children empty. merged="propagate" supplies the anchor value to every child. Both retain each physical coordinate and the original anchor's source coordinates. A subsequently selected propagated header therefore retains its child location and anchor location separately.

The adapter uses openpyxl with data_only=True, keep_vba=False, and keep_links=False. It reads cached formula values; it never evaluates formulas, executes macros or follows workbook links. A formula without a saved cached value appears as missing. Existing Excel numeric precision limits still apply; the library cannot recover precision already lost in the workbook. Workbooks are closed after reading, and source files are never saved or modified.

Use cases, alternatives and limitations

Use cellwalk for layered period/country reports, grouped row headings, sparse header geometry, adjacent tables with explicit regions, and merged headings whose anchor provenance matters. unpivotr inspired its spatial approach. Pandas melt/MultiIndex, Polars unpivot and openpyxl already solve reshaping and cell access. cellwalk combines them into explicit header association and provenance; see RESEARCH.md.

Version 0.1.0 does not infer table boundaries, recognize formatting/OCR/HTML, guess header meaning, or provide a Polars backend. It stores the whole grid in memory, including the worksheet's used dimensions, so heavily formatted large workbooks may require preprocessing. Header spans continue within the selected region until another nonblank header; choose bounds or explicit mappings when blank gaps have a different meaning. Mutable objects inside DataFrame cells remain caller-owned; avoid modifying them after creating a grid.

Validation and benchmark

python -m pip install -e ".[dev]"
python -m pytest --cov=cellwalk --cov-report=term-missing
python -m ruff check .
python -m mypy src
python examples/quickstart.py
python examples/nonrectangular.py
python benchmarks/benchmark.py --rows 2000 --repeat 5

Tests cover all directions, boundaries, multi-level and nonrectangular headers, missing cells, duplicate labels, exact large integer round trips, generated dimensions, and temporary XLSX merged/formula fixtures. The benchmark compares equivalent values and data coordinates with a manual Pandas coordinate recipe, then times repeated runs and separately measures traced Python memory. Raw results and methodology include environment, spread and the cost of additional provenance. No speed advantage is claimed.

See CONTRIBUTING.md. Original code is MIT licensed; upstream inspiration does not imply affiliation. Proposed 0.2.0 work: selection by explicit cell predicates, indexed navigation for large sparse grids and a streaming XLSX region reader.

Metadata

Release files for cellwalk 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cellwalk 0.1.0
File Size Uploaded Provenance
cellwalk-0.1.0.tar.gz 21.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for cellwalk 0.1.0
File Interpreter ABI Platform
cellwalk-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.4 kB

Release files / cellwalk-0.1.0.tar.gz

Download URL cellwalk-0.1.0.tar.gz
Size 21.6 kB
Tags Source
SHA-256 checksum
How to use checksums
5cb95c523c69126f36f4ce506bb04b2ab6c287a07e1e044e1ae577038bd73686
BLAKE2b-256 checksum
How to use checksums
d799a3c40872110673bbbeaeff47a6d2e6cb6529f7668722c48cf2bb4822af2b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Release files / cellwalk-0.1.0-py3-none-any.whl

Download URL cellwalk-0.1.0-py3-none-any.whl
Size 9.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1a867c8c90ebbd1a1a41752b0af43432a8371f7a719d0be9c1a5f66f810ad657
BLAKE2b-256 checksum
How to use checksums
9346b4832048fc27aed21af7ffa36311fb9341fb2ca41029c501aecf6acb9b33
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Security for cellwalk 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for cellwalk-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page