plotly / plotly.js

Open-source JavaScript charting library behind Plotly and Dash
https://plotly.com/javascript/
MIT License
17.14k stars 1.87k forks source link

add native range charts #7288

Open ndrezn opened 6 days ago

ndrezn commented 6 days ago

MinutePhysics called us out (by name!) for not having native support for range charts: https://www.youtube.com/watch?v=5zBg9pH_6bE

However, to be clear, it is possible to build this chart type in Plotly using the base attribute for bar charts and doing a bit of data preparation.

Here's a live demo of a custom range chart and our built-ins: https://codepen.io/ndrezn/pen/XJrWboB

Code snippet ```js /** * Creates a range bar chart using Plotly.js. * * @param {Object} data - The dataset, with keys as categories (e.g., cities) and values as arrays of measurements. * @param {Array} labels - The labels for each measurement (e.g., temperature metrics). * @param {string} containerId - The ID of the HTML container to render the chart. * @param {string} title - The title of the chart. */ function rangeChart(data, labels, containerId, title) { const categories = Object.keys(data); // Extract categories (e.g., cities) // Generate bar traces for each label const barTraces = labels.map((label, index) => { const bases = categories.map((category) => index === 0 ? data[category][index] : data[category][index - 1] ); // base, i.e. the previous value const heights = categories.map( (category) => data[category][index] - bases[categories.indexOf(category)] ); // Height relative to the base, i.e. the current value return { x: categories, y: heights, // Bar heights base: bases, // Starting points for each bar name: label, type: "bar" }; }); // Layout for the chart const layout = { title: title, barmode: "overlay", // Overlapping bars to show ranges xaxis: { title: "Category" }, yaxis: { title: "Value" } }; // Render the chart Plotly.newPlot(containerId, barTraces, layout); } const ranges = { Ontario: [-9, 3, 8, 13, 27], England: [3, 8, 12, 16, 24], Kentucky: [-3, 8, 14, 20, 30] }; const labels = [ "Winter mean low", "Annual mean low", "Annual mean", "Annual mean high", "Summer mean high" ]; rangeChart( ranges, labels, "rangePlot", "Temperature Ranges in Three Londons (range)" ); ```

Image

That being said, having this chart-type built in would be great. The mental gymnastics are a bit tricky to get a true range chart working correctly.

Here are the built-in ways to achieve a similar chart:

Code snippet ```js // Data for the three Londons const ranges = { Ontario: [-9, 3, 8, 13, 27], England: [3, 8, 12, 16, 24], Kentucky: [-3, 8, 14, 20, 30] }; const labels = [ "Winter mean low", "Annual mean low", "Annual mean", "Annual mean high", "Summer mean high" ]; const cities = Object.keys(ranges); // Scatter plot traces const scatterTraces = labels.map((label, index) => ({ x: cities, y: cities.map((city) => ranges[city][index]), mode: "markers", name: label, type: "scatter" })); const scatterLayout = { title: "Temperature Ranges in Three Londons (scatter)", xaxis: { title: "City" }, yaxis: { title: "Temperature" } }; Plotly.newPlot("scatterPlot", scatterTraces, scatterLayout); // Bar plot traces const barTraces = labels.map((label, index) => ({ x: cities, y: cities.map((city) => ranges[city][index]), name: label, type: "bar" })); const barLayout = { title: "Temperature Ranges in Three Londons (overlay)", barmode: "overlay", xaxis: { title: "City" }, yaxis: { title: "Temperature" } }; Plotly.newPlot("barPlot", barTraces, barLayout); // Bar plot traces const stackedBarTraces = labels.map((label, index) => ({ x: cities, y: cities.map((city) => ranges[city][index]), name: label, type: "bar" })); const stackedBarLayout = { title: "Temperature Ranges in Three Londons (stacked)", barmode: "stack", xaxis: { title: "City" }, yaxis: { title: "Temperature" } }; Plotly.newPlot("stackedBarPlot", stackedBarTraces, stackedBarLayout); ```

However, these aren't quite right.

Image

emilykl commented 6 days ago

Probably wouldn't be too hard to add this as a Plotly Express chart, px.range or similar.

ndrezn commented 6 days ago

Yeah that's a good idea! It doesn't need to live upstream as a native chart in Plotly.js.

That being said it could be implemented not as a new chart type but as an option on Bar, e.g. end parameter or mode or something...