You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3.js堆叠柱状图:如何生成适配layout.stack()的数据集?

Hey there! Let's work through this D3.js stack layout challenge together—since you're new to the library, getting data formatted just right for stacked bar charts can feel a bit overwhelming at first, but we'll break it down step by step.

First, let's recap what D3's layout.stack() expects for data structure (since the gist you're referencing uses D3 v3, we'll stick to that syntax):

  • You need an array of series (each series represents one year in your case).
  • Each series should have a values array, where each entry corresponds to an X-axis category (your planet detection methods like RV, transit).
  • Each entry in values needs an x (the detection method) and y (the count of planets found that year with that method). If a method has no planets for a year, set y to 0 so the stack layout can calculate baselines correctly.

Step 1: Transform Raw Data to Aggregated Counts

Assuming your raw data looks like a list of planet entries (each with discovery_method and year), we first need to count how many planets were found per method per year. We'll use D3's nest() function for this grouping:

// Example raw data (adjust to match your actual data structure)
const rawPlanets = [
  { discovery_method: "RV", year: 2000 },
  { discovery_method: "transit", year: 2000 },
  { discovery_method: "RV", year: 2001 },
  { discovery_method: "RV", year: 2002 },
  { discovery_method: "transit", year: 2002 },
  { discovery_method: "imaging", year: 2002 }
];

// Group data by year, then by method, and count entries
const nestedData = d3.nest()
  .key(d => d.year) // First group by year
  .key(d => d.discovery_method) // Then group by detection method
  .rollup(group => group.length) // Count how many planets are in each group
  .entries(rawPlanets);

Step 2: Normalize Data for Stack Layout

Next, we need to make sure every year series includes all detection methods (even those with 0 counts). This ensures the stack layout aligns correctly across the X-axis:

// Extract all unique detection methods (these become your X-axis categories)
const allMethods = [...new Set(rawPlanets.map(d => d.discovery_method))];

// Convert nested data to stack-friendly format
const stackReadyData = nestedData.map(yearGroup => ({
  key: yearGroup.key, // The year (used for color/legend later)
  values: allMethods.map(method => {
    // Find the count for this method in the current year
    const methodEntry = yearGroup.values.find(g => g.key === method);
    return {
      x: method,
      y: methodEntry ? methodEntry.values : 0 // Use 0 if no planets found
    };
  })
}));

Step 3: Apply the Stack Layout

Now you can pass this formatted data to d3.layout.stack()—it will add a y0 property to each entry, which represents the baseline (starting Y position) for that segment of the stacked bar:

// Initialize the stack layout
const stackLayout = d3.layout.stack()
  .values(d => d.values); // Tell D3 where to find the values for each series

// Process the data
const stackedData = stackLayout(stackReadyData);

Quick Example of Drawing the Bars

Once your data is stacked, you can use it to draw the bars (adjust scales and SVG dimensions to fit your project):

// Set up scales
const width = 600;
const height = 400;
const margin = { top: 20, right: 20, bottom: 30, left: 40 };

const xScale = d3.scale.ordinal()
  .domain(allMethods)
  .rangeRoundBands([margin.left, width - margin.right], 0.1);

const maxTotal = d3.max(stackedData, d => d3.max(d.values, v => v.y0 + v.y));
const yScale = d3.scale.linear()
  .domain([0, maxTotal])
  .range([height - margin.bottom, margin.top]);

// Create SVG
const svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);

// Draw each year's stack segment
const yearGroups = svg.selectAll(".year-group")
  .data(stackedData)
  .enter().append("g")
  .attr("class", "year-group")
  .style("fill", (d, i) => d3.schemeCategory10[i]); // Use a color scheme

yearGroups.selectAll("rect")
  .data(d => d.values)
  .enter().append("rect")
  .attr("x", d => xScale(d.x))
  .attr("y", d => yScale(d.y0 + d.y))
  .attr("height", d => yScale(d.y0) - yScale(d.y0 + d.y))
  .attr("width", xScale.rangeBand());

Troubleshooting Tips

  • If your raw data is structured differently (e.g., each entry is a method with year counts), adjust the grouping step to match—just remember the end goal: each year series must have an entry for every detection method.
  • Check your console for errors: if you're seeing missing bars or misaligned stacks, double-check that allMethods includes every method present in your data, and that no year is missing any method entries (even with y=0).

内容的提问来源于stack exchange,提问作者Sharfuddin Shawon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:49:51