Skip to content

Add native zero-inclusive symlog axis scale#109

Merged
FarhanAliRaza merged 7 commits into
mainfrom
codex/add-zero-inclusive-long-tail-axis-scale
Jul 22, 2026
Merged

Add native zero-inclusive symlog axis scale#109
FarhanAliRaza merged 7 commits into
mainfrom
codex/add-zero-inclusive-long-tail-axis-scale

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Motivation

  • Long-tailed numeric measures commonly include valid zeros which a log axis cannot represent while a linear axis compresses the tail, so a zero-inclusive long-tail scale (symmetric log / symlog) is needed.
  • The goal is to provide a native axis type that preserves original data-space values for ticks, hover, selection, pan/zoom, density rendering, and payloads without leaking display transforms into application logic.

Description

  • Add symlog as an accepted axis type_ and a constant parameter to x_axis()/y_axis() and Figure.set_axis() to configure the linear region width around zero, including validation of a positive constant. (python/xy/components.py, python/xy/_figure.py)
  • Serialize scale: "symlog" and constant into axis payloads and ensure Figure._axis_spec emits scale/constant while preserving existing semantics for linear/log. (python/xy/_figure.py)
  • Implement symmetric-log transforms in the static PNG/SVG pipeline via _Scale and tick generation that produce ticks in original data units (including zero). (python/xy/_svg.py)
  • Wire symlog through the browser-side code: add symlog branch to axis mapping, implement JS coordinate transform and inverse, propagate a per-axis constant uniform into GLSL, and update shader map/calls so WebGL rendering, density, hit/pick, and interaction layers operate using the symlog transform consistently. (python/xy/static/index.js, python/xy/static/standalone.js)
  • Add documentation for the zero-inclusive long-tail scale and an integration test exercising API, payloads, static transforms, and ticks. (docs/core-concepts/axes-and-scales.md, tests/test_symlog_axis.py)

Testing

  • Ran focused and integrated Python tests: PYTHONPATH=python python -m pytest tests/test_symlog_axis.py -q (8 passed) and PYTHONPATH=python python -m pytest tests/test_figure.py tests/test_svg_export.py tests/test_png_export.py -q (212 passed, 1 skipped), and PYTHONPATH=python python -m pytest tests/test_components.py -q (76 passed).
  • Static JavaScript validation succeeded with node --check python/xy/static/index.js and node --check python/xy/static/standalone.js and git diff --check reported no issues.
  • Browser conformance / screenshot smoke could not be run here because Playwright’s Chromium executable is not installed in this environment (so end-to-end screenshot verification was not executed).

Closes #83

@codspeed-hq

codspeed-hq Bot commented Jul 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 97 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing codex/add-zero-inclusive-long-tail-axis-scale (44cd759) with main (2b5d43d)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

@FarhanAliRaza

Copy link
Copy Markdown
Contributor Author
image without vs

with
image

@FarhanAliRaza FarhanAliRaza added valid Issue is validated. This pr actually fixes the issue. Needs Code Review. and removed codex labels Jul 20, 2026

@Alek99 Alek99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review findings:

  • [P1] js/src/40_gl.js:76-78: symlog is applied after decoding offset-encoded f32 coordinates. With x=[0, 1, 1e12] and constant=1, 0 and 1 collapse to the same decoded value even though symlog should visibly separate them. Encode geometry in scale coordinates or retain sufficient precision before the transform.
  • [P1] js/src/40_gl.js:348: density aggregation remains uniform in raw data space. Clusters at x=1 and x=1000 can land in the same raw grid cell even though they are far apart in symlog display space. Bin/decimate in scale coordinates.
  • [P2] python/xy/_svg.py:2260: static heatmap/density grids are stretched only between transformed endpoints, so internal cells are not symlog-transformed. PNG follows the same pattern.
  • [P2] python/xy/_figure.py:993: scale="symlog" changes renderer semantics but the protocol remains 3; an older client will silently treat it as linear. Bump the protocol and regenerate bundles.

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a native symmetric-logarithmic axis for zero-inclusive, long-tailed data. The main changes are:

  • Public Python API support for symlog scales and configurable constants.
  • Original-unit transforms, ticks, interactions, and static SVG/PNG rendering.
  • WebGL support across marks, picking, density grids, and heatmaps.
  • Scale-aware geometry encoding, decimation, and density aggregation.
  • Updated protocol, documentation, examples, and integration tests.

Confidence Score: 5/5

This looks safe to merge.

No blocking issues found in the changed code.

T-Rex T-Rex Logs

What T-Rex did

  • Executed the symlog-focused Pytest run in the project working directory and finished with exit code 0.
  • Attached a complete Pytest log capturing the full output to the symlog-focused-pytest.log artifact for review.
  • The command, working directory, and exit status are documented in the run evidence to enable reproduction.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
python/xy/_figure.py Adds symlog validation, coordinate conversion, and axis payload serialization.
python/xy/_payload.py Makes geometry encoding, decimation, and density aggregation aware of nonlinear axis coordinates.
python/xy/_svg.py Adds symlog transforms, ticks, and nonlinear heatmap warping to static rendering.
js/src/40_gl.ts Adds per-axis symlog transforms and constants throughout the WebGL shaders.
js/src/50_chartview.ts Wires symlog coordinates, ticks, uniforms, and grid ranges into browser rendering.
tests/test_symlog_axis.py Covers API validation, payloads, transforms, ticks, precision, and static rendering.

Reviews (5): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Alek99 and others added 4 commits July 21, 2026 22:39
…ive-long-tail-axis-scale

# Conflicts:
#	examples/demo-advance.ipynb
#	js/src/40_gl.ts
#	js/src/50_chartview.ts
#	python/xy/static/index.js
#	python/xy/static/standalone.js
…e v5

Review fixes for the native symlog axis (#83):

- Pin the f32 encode offset to 0.0 on log-family axes. A midpoint offset
  makes the f32 error absolute (~span/1e7), collapsing exactly the
  near-zero values symlog exists to separate (x=0 and x=1 encoded to the
  same word on a 1e12 domain); offset 0 keeps the error relative, which
  the transform maps to a sub-pixel coordinate error at every magnitude.
- Aggregate in scale coordinates on nonlinear axes: density grids and M4
  buckets bin transformed values so every cell covers a uniform strip of
  screen, and the drill handoff's local density matches. The raw-space
  tile pyramid cannot compose such grids, so those traces always take
  the exact scan.
- Static exports: density images are scale-coordinate-uniform (linear
  stretch between transformed endpoints is exact); data-uniform heatmap
  grids resample per pixel through the inverse transform in SVG/PNG, and
  the WebGL heatmap shader inverts per fragment.
- Bump the renderer protocol to 5 so a cached pre-symlog client refuses
  the spec loudly instead of silently rendering symlog as linear.
- Numeric format grammar accepts literal affixes ("$,.0f" -> "$14,741"),
  the form issue #83's API example and the docs already use.
- Resolve the pyplot shim's ty diagnostics at the type boundary instead
  of suppressing them.

Specs updated in lockstep (dossier 16/28, wire-protocol, lod
architecture, styling, chart grammar); adds the symlog demo notebook.
Both branches had bumped the wire protocol 4->5 independently (main: streaming
append contract; this branch: symlog scale). Resolved by keeping main's v5 and
re-bumping the symlog/scale-space additions to v6 in config.py, 00_header.ts,
and spec/design/wire-protocol.md, then regenerating the minified static
bundles with node js/build.mjs.
…ive-long-tail-axis-scale

# Conflicts:
#	python/xy/static/index.js
#	python/xy/static/standalone.js
@FarhanAliRaza
FarhanAliRaza merged commit 9947177 into main Jul 22, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

valid Issue is validated. This pr actually fixes the issue. Needs Code Review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a native zero-inclusive long-tail axis scale

2 participants