Skip to content

fix(Radar): Radar chart normalisation#33559

Merged
msyavuz merged 33 commits intoapache:masterfrom
amaannawab923:feat/radar-chart-normalisation
May 26, 2025
Merged

fix(Radar): Radar chart normalisation#33559
msyavuz merged 33 commits intoapache:masterfrom
amaannawab923:feat/radar-chart-normalisation

Conversation

@amaannawab923
Copy link
Contributor

@amaannawab923 amaannawab923 commented May 22, 2025

🔧 Summary

This PR introduces a fix for for the Radar Chart in Superset, addressing a critical issue where large discrepancies in metric magnitudes made the chart visually misleading and unusable for comparison.

🐛 The Issue

When plotting multiple metrics with vastly different scales (e.g., 208000, 2590, 9200), the Radar Chart currently uses a faulty calculation mechanism which renders just a Polygon because each value occupies the max on its axis. This causes small values to appear indistinguishable from large ones—rendering the chart ineffective for visual comparison.

🎥 Before (no normalization):

Screenshot 2025-05-26 at 11 08 55 PM

As it can be seen in above screenshot 797.73 & 2.43k appear visually equivalent, even though they arent & the end user is not able to differentiate between them through the radar chart

🧠 Root Cause

Radar Charts plot all metric values using a common 0-to-max scale. This approach ignores the fact that some metrics may span much smaller ranges. As a result:

A metric like 2.5k appears to stretch just as far as 208k

There’s no visual cue for the viewer to recognize magnitude differences

✅ The Fix

This PR introduces:

A new normalisation function in Radar chart where each metric’s values are scaled between 0 and 1
This results in equal visual weight per metric, regardless of absolute value
Safe denormalization logic for tooltips and labels
Tooltips and labels show original values, not normalized ones
Denormalization map tracks each series’ normalized-to-original value mapping

🧪 How It Works

The Normalize function activates per-series normalization during data transformation.
Original values are stored in a SeriesNormalizedMap structure.
Custom formatters use this map to denormalize values when displaying them in tooltips or labels.

🖼️ UX Enhancements

Tooltip and label values now reflect true, raw numbers even when normalized values are plotted.

Improves clarity when comparing metrics with different scales.

🎥 After (with normalization):

Screenshot 2025-05-26 at 11 07 12 PM

Now 797.73 appears visually appropriate in comparison to 2.43k

Metrics now appear proportionally within their own scale, improving accuracy and readability.

🧰 Developer Notes

Introduced new tooltip formatter and label formatter handling
Implemented safe getDenormalisedSeriesValue() accessor

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

TESTING INSTRUCTIONS

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@korbit-ai
Copy link

korbit-ai bot commented May 22, 2025

Based on your review schedule, I'll hold off on reviewing this PR until it's marked as ready for review. If you'd like me to take a look now, comment /korbit-review.

Your admin can change your review schedule in the Korbit Console

@amaannawab923 amaannawab923 changed the title Feat/radar chart normalisation feat(Radar): Radar chart normalisation May 22, 2025
@github-actions
Copy link
Contributor

@geido Processing your ephemeral environment request here. Action: up. More information on how to use or configure ephemeral environments

@github-actions
Copy link
Contributor

@geido Ephemeral environment spinning up at http://34.213.64.49:8080. Credentials are 'admin'/'admin'. Please allow several minutes for bootstrapping and startup.

@amaannawab923 amaannawab923 marked this pull request as ready for review May 22, 2025 10:53
@dosubot dosubot bot added the viz:charts:radar Related to the Radar chart label May 22, 2025
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const numberFormatter = getNumberFormatter(numberFormat);
const denormalizedSeriesValues: SeriesNormalizedMap = {};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We use an Object to store the denormalised values belonging to each series because after normalising we need to pass the denormalised values to the label formatter & tooltip formatter to keep the chart consistent & preserve original values

Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

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

Review by Korbit AI

Korbit automatically attempts to detect when you fix issues in new commits.
Category Issue Status
Readability Unnecessary Boolean constructor ▹ view ✅ Fix detected
Performance Inefficient Value Lookup Pattern ▹ view ✅ Fix detected
Readability Inconsistent Series Name Fallback ▹ view ✅ Fix detected
Readability Complex Value Formatting Logic ▹ view ✅ Fix detected
Design Unsafe Tooltip Formatter Typing ▹ view ✅ Fix detected
Functionality Normalization Zero Division Edge Case ▹ view 🧠 Incorrect
Files scanned
File Path Reviewed
superset-frontend/plugins/plugin-chart-echarts/src/Radar/types.ts
superset-frontend/plugins/plugin-chart-echarts/src/Radar/controlPanel.tsx
superset-frontend/plugins/plugin-chart-echarts/src/Radar/transformProps.ts

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

Comment on lines 67 to 72
const { name = '', value } = params;
const formattedValue = numberFormatter(value as number);
const formattedValue = numberFormatter(
isNormalised
? (getDenormalisedSeriesValue(name, String(value)) as number)
: (value as number),
);

This comment was marked as resolved.

Comment on lines 133 to 142
if (
Object.prototype.hasOwnProperty.call(
denormalizedSeriesValues,
seriesName,
) &&
Object.prototype.hasOwnProperty.call(
denormalizedSeriesValues[seriesName],
normalisedValue,
)
) {

This comment was marked as resolved.

Comment on lines 254 to 261
const normalizeArray = (arr: number[], decimals = 10, seriesName: string) => {
const max = Math.max(...arr);
return arr.map(value => {
const normalisedValue = Number((value / max).toFixed(decimals));
denormalizedSeriesValues[seriesName][String(normalisedValue)] = value;
return normalisedValue;
});
};

This comment was marked as resolved.

},
];

const NormalisedTooltipFormaterr = function (params: any) {

This comment was marked as resolved.


// Fallback: return the normalised value itself as a number
return Number(normalisedValue);
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The getDenormalisedSeriesValue is getter function for safely getting the denormalised value , if we dont find the value we fallback to normalised value, this shouldnt happen but we do this to prevent crashes & unexpected errors which can crash the user experience

@github-actions
Copy link
Contributor

@Antonio-RiveroMartnez Ephemeral environment spinning up at http://54.214.198.178:8080. Credentials are 'admin'/'admin'. Please allow several minutes for bootstrapping and startup.

@amaannawab923 amaannawab923 changed the title feat(Radar): Radar chart normalisation fix(Radar): Radar chart normalisation May 26, 2025
@github-actions
Copy link
Contributor

@kgabryje Processing your ephemeral environment request here. Action: up. More information on how to use or configure ephemeral environments

@github-actions
Copy link
Contributor

@kgabryje Ephemeral environment spinning up at http://54.202.38.228:8080. Credentials are 'admin'/'admin'. Please allow several minutes for bootstrapping and startup.

@kgabryje
Copy link
Member

I didn't follow the discussion too closely here, but shouldn't we keep the control for changing the scales? Also, we should make sure that the existing charts are not affected, and the best way to do it would be to either set the control to false by default, or if we need it to be enabled by default, then we should create a migration for existing Radar charts.
Also, if there's a consensus about changing the name from "Normalise" to something else, could you change the variable and functions names in the code as well?

@amaannawab923
Copy link
Contributor Author

I didn't follow the discussion too closely here, but shouldn't we keep the control for changing the scales? Also, we should make sure that the existing charts are not affected, and the best way to do it would be to either set the control to false by default, or if we need it to be enabled by default, then we should create a migration for existing Radar charts. Also, if there's a consensus about changing the name from "Normalise" to something else, could you change the variable and functions names in the code as well?

Hi @kgabryje , as per discussion with @betodealmeida @geido , Since the radar chart was broken , we have agreed not to keep backwards compatibility & no toggle or control will be available hence the pr got changed from feature to fix since this is a fix for radar chart

Also for the normalisation naming , it was a visiblity issue , that whether if we show the end user normalisation or not.... But on an implementation level , we are normalising the variables on scale of 0 to 1 with respect to max metric value hence renaming wont be required
Thanks for the comment , i will update these details as well in pr description

@kgabryje
Copy link
Member

Thanks for explanation @amaannawab923. Approving but it would be great if @betodealmeida could give it a final pass

Copy link
Member

@msyavuz msyavuz left a comment

Choose a reason for hiding this comment

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

Thanks for this, maybe we should have more fitting examples for different charts. Some example data do not fit the chart use cases at all.

@amaannawab923
Copy link
Contributor Author

Please give it a final pass @betodealmeida , Thanks

@github-actions
Copy link
Contributor

@msyavuz Processing your ephemeral environment request here. Action: up. More information on how to use or configure ephemeral environments

@github-actions
Copy link
Contributor

@msyavuz Ephemeral environment spinning up at http://34.219.31.206:8080. Credentials are 'admin'/'admin'. Please allow several minutes for bootstrapping and startup.

@msyavuz msyavuz merged commit bdfb698 into apache:master May 26, 2025
57 of 58 checks passed
LevisNgigi pushed a commit to LevisNgigi/superset that referenced this pull request Jun 18, 2025
Co-authored-by: Amaan Nawab <nelsondrew07@gmail.com>
alexandrusoare pushed a commit to alexandrusoare/superset that referenced this pull request Jun 19, 2025
Co-authored-by: Amaan Nawab <nelsondrew07@gmail.com>
(cherry picked from commit bdfb698)
@github-actions github-actions bot added 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 6.0.0 First shipped in 6.0.0 labels Dec 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels plugins size/L viz:charts:radar Related to the Radar chart 🚢 6.0.0 First shipped in 6.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.