Skip to content

feat(postprocessing): teach pivot() to compute percent-of-row/col/total (#42809) - #42976

Open
Abdulrehman-PIAIC80387 wants to merge 3 commits into
apache:masterfrom
Abdulrehman-PIAIC80387:fix/pivot-pandas-show-values-as-42809
Open

feat(postprocessing): teach pivot() to compute percent-of-row/col/total (#42809)#42976
Abdulrehman-PIAIC80387 wants to merge 3 commits into
apache:masterfrom
Abdulrehman-PIAIC80387:fix/pivot-pandas-show-values-as-42809

Conversation

@Abdulrehman-PIAIC80387

Copy link
Copy Markdown
Contributor

SUMMARY

First half of #42809. Extends superset/utils/pandas_postprocessing/pivot.py with an optional show_values_as argument that computes percent-of-row, percent-of-column, or percent-of-grand-total after pivoting, mirroring the client-side fractionOf semantic in react-pivottable/utilities.ts:739 (hardened by @rusackas in #42810).

WHY THIS IS HALF THE FIX

#42809 has two halves:

  1. Server-side capabilitypivot() needs to know how to compute percentages. This PR.
  2. Frontend wiring — the pivot chart's buildQuery.ts needs to include pivot postprocessing (with show_values_as) in the query so CSV/XLSX exports pick it up. Follow-up PR — needs a design decision on whether to emit the operation always (client would need to tolerate pre-pivoted DataFrames) or only for export flows (result_type in {CSV, XLSX}).

@rusackas / @sadpandajoe — happy to fold Path B into this PR if you have a preferred design, or land this first and open the follow-up. Let me know which shape you want.

FIX

  • Adds show_values_as param on pivot(), accepting percent_row, percent_col, percent_total, or None / "actual" (no-op).
  • Post-pivot, when the mode is set, each cell is divided by the appropriate rollup total.
  • On a multi-metric pivot (MultiIndex columns) the totals are computed within each metric group — never across metrics — so metric A's percentages are never contaminated by metric B's values (matches the client's metricAxis handling).
  • Default is None — no behavior change for existing callers of pivot postprocessing (echarts Timeseries, BigNumber, MixedTimeseries).

EDGE CASES (mirror #42810's client-side guards)

Guard Behavior
NaN/NULL numerator Stays NaN (renders blank) — a genuine SQL NULL does not become 0.0%
Zero row/column denominator Cells for that row/column become NaN, not Infinity
Zero grand total All cells become NaN
Invalid show_values_as value Raises InvalidPostProcessingError (no silent no-op)

BEHAVIOR MATRIX

Mode Effect
None / "actual" pivoted DataFrame unchanged (no-op) — default
"percent_row" each cell = cell / row-total
"percent_col" each cell = cell / column-total
"percent_total" each cell = cell / grand-total

TESTING INSTRUCTIONS

pytest tests/unit_tests/pandas_postprocessing/test_pivot.py -v -k show_values_as

Eight new tests: actual-is-noop, percent_row, percent_col, percent_total, NaN-numerator-preserved, zero-grand-total-produces-NaN, multi-metric-keeps-metrics-separate, invalid-mode-raises.

ADDITIONAL INFORMATION

Extends ``superset/utils/pandas_postprocessing/pivot.py`` with an
optional ``show_values_as`` argument that expresses each metric cell
as a fraction of the row / column / grand total after pivoting,
mirroring the pivot chart's client-side ``fractionOf`` semantic in
``plugin-chart-pivot-table/src/react-pivottable/utilities.ts:739`` so
server-side rendering paths (CSV / XLSX exports, scheduled reports)
can eventually reproduce the browser output.

Values:

- ``percent_row``:   cell / row-total     (denominator sums across columns)
- ``percent_col``:   cell / column-total  (denominator sums across rows)
- ``percent_total``: cell / grand-total   (denominator sums the DataFrame)
- ``None`` / ``"actual"``: no-op (default) — no behavior change for existing
  callers of ``pivot`` postprocessing (echarts Timeseries, BigNumber, etc.)

Edge cases mirror the client-side apache#42810 guards:

- NaN/NULL numerator stays NaN — a genuine SQL NULL renders blank rather
  than a measured "0.0%".
- Zero or NaN denominator produces NaN cells rather than Infinity from
  division-by-zero.
- On a multi-metric pivot (MultiIndex columns) the totals are computed
  *within each metric group* so metric A's percentages are never
  contaminated by metric B's values.

This is the server-side foundation for apache#42809. The pivot chart's
frontend ``buildQuery.ts`` still needs to add ``pivot`` postprocessing
with ``show_values_as`` so exports pick it up — that is a separate PR
because it requires a design decision on whether to emit the operation
for all query paths or only for export-format queries (client tolerance
for a pre-pivoted DataFrame).

Refs apache#42809
@dosubot dosubot Bot added the viz:charts:pivot Related to the Pivot Table charts label Aug 10, 2026
@bito-code-review

bito-code-review Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #f9d2c3

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: ce172a1..ce172a1
    • superset/utils/pandas_postprocessing/pivot.py
    • tests/unit_tests/pandas_postprocessing/test_pivot.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/utils/pandas_postprocessing/pivot.py Outdated
Comment thread superset/utils/pandas_postprocessing/pivot.py Outdated
Comment thread tests/unit_tests/pandas_postprocessing/test_pivot.py
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 15.90909% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.34%. Comparing base (3539c41) to head (848eb4e).
⚠️ Report is 52 commits behind head on master.

Files with missing lines Patch % Lines
superset/utils/pandas_postprocessing/pivot.py 15.90% 35 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42976      +/-   ##
==========================================
- Coverage   66.41%   66.34%   -0.08%     
==========================================
  Files        2858     2861       +3     
  Lines      161446   161770     +324     
  Branches    37190    37266      +76     
==========================================
+ Hits       107222   107320      +98     
- Misses      52187    52401     +214     
- Partials     2037     2049      +12     
Flag Coverage Δ
hive 38.14% <13.63%> (-0.07%) ⬇️
mysql 57.65% <15.90%> (-0.12%) ⬇️
postgres 57.71% <15.90%> (-0.12%) ⬇️
presto 40.10% <13.63%> (-0.08%) ⬇️
python 59.09% <15.90%> (-0.12%) ⬇️
sqlite 57.32% <15.90%> (-0.11%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds server-side support for pivot-table “Show values as” percentage modes in the pandas postprocessing pivot() operator, so non-browser rendering paths (CSV/XLSX exports, scheduled reports) can match the pivot table’s client-side fraction display semantics.

Changes:

  • Extend pivot() with an optional show_values_as argument to compute percent-of-row/column/grand-total after pivoting, including multi-metric isolation.
  • Add unit tests covering the new percentage modes and key edge cases (NaN numerator preservation, zero grand total, invalid mode).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
superset/utils/pandas_postprocessing/pivot.py Implements show_values_as percent-of-row/col/total post-pivot transforms and validates mode values.
tests/unit_tests/pandas_postprocessing/test_pivot.py Adds regression tests for show_values_as behaviors and edge cases.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread superset/utils/pandas_postprocessing/pivot.py Outdated
Comment thread tests/unit_tests/pandas_postprocessing/test_pivot.py
Per review on apache#42976:

1. **marginal_distributions guard** — combining ``show_values_as`` with
   ``marginal_distributions`` would include the ``All`` margin row/col
   in the row/col/grand-total denominators, producing wrong percentages.
   Raise ``InvalidPostProcessingError`` explicitly rather than silently
   returning wrong numbers. Combining the two needs a first-class design
   (probably compute on the non-margin subset then re-insert the
   margins), out of scope here.

2. **Flat-multi-metric percent_total** — a multi-metric pivot with no
   ``columns`` groupby produces a flat column index where each column
   IS a metric. Verified with pandas 2.3.3 that the previous code summed
   across metrics for the grand total (metric a's magnitude leaking
   into metric b's percentages). Now iterate each column as its own
   single-metric block, matching the MultiIndex per-metric semantics.

3. **NaN numerator test was broken** — the previous fixture used
   ``operator="sum"`` on a value that included ``NaN``; ``pandas``
   ``.sum(skipna=True)`` on a single-element ``[NaN]`` group returns
   ``0.0``, not ``NaN``, so the test never actually exercised
   ``_div_preserving_nan``'s NaN-preservation path. Rewrote the fixture
   to use a missing (row, col) combination, which ``pivot_table`` fills
   with a genuine ``NaN`` cell — verified empirically.

4. **Falsy-string validation** — the previous
   ``if show_values_as and show_values_as != "actual"`` guard skipped
   validation for empty strings, silently no-oping bad input. Now
   an explicit sentinel check ``if show_values_as not in (None, "",
   "actual")`` rejects unknown modes uniformly and lets both ``None``
   and ``""`` route to the no-op path.

5. **New zero-row/col-denominator tests** — pin the guard against
   division-by-zero producing ``Infinity`` in row/column modes (already
   handled for grand-total mode). Explicit tests for both axes.

6. **Pandas-3 compat (found while fixing)** — ``df.groupby(level=0,
   axis=1)`` is deprecated in pandas 2.3 (FutureWarning) and removed in
   pandas 3.x. Refactored to iterate ``columns.get_level_values(0)``
   explicitly with ``df.xs``, avoiding the deprecated call and keeping
   the fix forward-compatible.

All six fix paths verified against pandas 2.3.3 with a standalone repro
script before writing tests, mirroring the process discipline learned
from apache#42793.
@netlify

netlify Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 80585a9
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a7ad0ee0d561400083539b6
😎 Deploy Preview https://deploy-preview-42976--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

…pivot_table

CI caught a real bug in the marginal_distributions test — with
``margins=True`` and no ``margins_name`` value, pandas raises
``ValueError: margins_name argument must be a string`` before our
InvalidPostProcessingError guard at the bottom of ``pivot()`` ever
fires. Test failed with the pandas error instead of our intended one.

Hoisting the ``show_values_as`` validation (unknown-mode +
margins-combination) to the *top* of ``pivot()`` — right after the
existing index/aggregates validation and before ``pivot_table`` runs —
means:

1. The test now sees ``InvalidPostProcessingError`` as intended,
   regardless of whether ``marginal_distribution_name`` is set.
2. Users misconfiguring the argument get an immediate, actionable
   error instead of paying for a full pivot they were about to reject.
3. The narrower typing (``percent_mode: Optional[str]``) satisfies
   mypy without ``cast``/``type: ignore`` at the apply-site.

No behavior change for valid configurations.
@bito-code-review

bito-code-review Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #a17e2f

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: ce172a1..848eb4e
    • superset/utils/pandas_postprocessing/pivot.py
    • tests/unit_tests/pandas_postprocessing/test_pivot.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas 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.

Let's land this first and do the frontend wiring as a follow-up, keeps review manageable. Nice work chasing down the codeant and copilot threads, looks like the NaN-numerator test and the zero row/col denominator cases are both fixed in 80585a9. LGTM, approving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L viz:charts:pivot Related to the Pivot Table charts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants