有没有办法为数据中的唯一组创建不同的D3轴? (d3.js)

发布于 2025-01-29 02:50:15 字数 3809 浏览 1 评论 0原文

我正在尝试为某些数据创建Multiarray 欢乐情节,但我很难制定一个适当的轴。我面临一个问题,即Joy图中的每一列都需要其特定的比例域,因为数据与其他列不同。

我编码的方式是将数据与d3.groups()分组,然后使用此方法构建欢乐图的结构。但是,使用这种方法,我可以创建图,但不能绘制轴。有没有办法实现这一目标?

我的第一个想法是,我可以将数据(将.data())映射到.call()。在.call()调用的函数中,我可以包含一些代码,这些代码在Joy图中为每个数组创建规模和轴。但是,这似乎不起作用,因为.call调用的函数 t可以访问数据。

以下是一些示例数据和生成图的图:

const width = window.innerWidth * 0.9;
const height = window.innerHeight * 0.9;
const margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 20,
};
const groups = 2;
const padding = 10;
const paddingZ = 200;

const gWidth = width - margin.left - margin.right;
const gHeight = height - margin.top - margin.bottom;

const histogramHeight = (padding + gHeight + padding) / groups;
const histogramWidth = (padding + gWidth + padding) / groups;
const histogramZheight = (paddingZ + histogramHeight + paddingZ) / 3;

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

const g = svg
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);

const main = async () => {

  data = [
    {bib: 1, split: 1, course: "A", performance: 2.4},
    {bib: 1, split: 1, course: "B", performance: 7},
    {bib: 1, split: 2, course: "A", performance: 2.7},
    {bib: 1, split: 2, course: "B", performance: 8},
    {bib: 2, split: 1, course: "A", performance: 2.9},
    {bib: 2, split: 1, course: "B", performance: 5.0},
    {bib: 2, split: 2, course: "A", performance: 9},
    {bib: 2, split: 2, course: "B", performance: 6},
    {bib: 2, split: 2, course: "A", performance: 3},
    {bib: 1, split: 2, course: "A", performance: 3},
    {bib: 1, split: 2, course: "B", performance: 1.2},
    {bib: 2, split: 1, course: "B", performance: 3},

  ]


  const dataGrouped = d3.groups(
    data,
    (d) => d.day,
    (d) => d.split,
    (d) => d.course
  );

  const cellX = g
    .selectAll(".cell")
    .data(dataGrouped)
    .join("g")
    .attr("class", "cellX")
    .attr("transform", (d) => console.log(d))
    .attr(
      "transform",
      (d, i) => `translate(0, ${i * (padding + histogramHeight + padding)})`
    );

  const cellY = cellX
    .selectAll(".cellX")
    .data((d) => d[1])
    .join("g")
    .attr("class", "cellXY")
    .attr(
      "transform",
      (d, i) => `translate(${i * (padding + histogramWidth + padding)},0)` //${ i * (padding + histogramWidth + padding)},0)`
    );

  const cellZ = cellY
    .selectAll(".cellXY")
    .data((d) => d[1])
    .join("g")
    .attr("class", "cellZ")
    .attr(
      "transform",
      (d, i) => `translate(0, ${i <= 2 ? i * paddingZ : i * (paddingZ)})` //(0, ${i * (paddingZ + histogramZheight + paddingZ)})`
    );

  const path = cellZ
    .selectAll(".lo")
    .data((d) => d)
    .join("path")
    .attr("d", (d) => {
      const xScale = d3
        .scaleLinear()
        .domain([-3, 40])
        .range([0, histogramWidth]);

      console.log(xScale.domain());

      const bingenerator = d3
        .bin()
        .domain(xScale.domain())
        .value((d) => d.performance);

      const binnedData = bingenerator(d);

      const topHistogramYscale = d3
        .scaleLinear()
        .domain(d3.extent(binnedData, (d) => d.length))
        .range([histogramZheight, 0]);

      const xAxis = d3.axisBottom().scale(xScale);

      const topHistogramLineGenerator = d3
        .area()
        .x((d) => xScale(d.x0))
        .y0(histogramZheight)
        .y1((d) => topHistogramYscale(d.length))
        .curve(d3.curveBasis);

      return topHistogramLineGenerator(binnedData);
    })


}

main();

I am trying to create multiarray joy plot for some data, but I struggle to make a proper axis. I am facing the problem that each column in the joy plot needs its specific scale domain because the data is different from the other columns.

enter image description here

The way I have coded the plot is to group the data with d3.groups() and then build the structure of the joy plot with this approach. However, using this approach, I can create the plot but not draw the axis. Is there a way to accomplish this?

My first thought was that I could pass the data (mapped to .data()) to .call(). In the function that .call() calls, I could include some code that creates the scale and axis for each array in the joy plot. However, this doesnt seem to work because the function that .call calls doesnt have access to the data.

Here are some example data and the plot that generates the plot:

const width = window.innerWidth * 0.9;
const height = window.innerHeight * 0.9;
const margin = {
  top: 20,
  right: 20,
  bottom: 30,
  left: 20,
};
const groups = 2;
const padding = 10;
const paddingZ = 200;

const gWidth = width - margin.left - margin.right;
const gHeight = height - margin.top - margin.bottom;

const histogramHeight = (padding + gHeight + padding) / groups;
const histogramWidth = (padding + gWidth + padding) / groups;
const histogramZheight = (paddingZ + histogramHeight + paddingZ) / 3;

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

const g = svg
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);

const main = async () => {

  data = [
    {bib: 1, split: 1, course: "A", performance: 2.4},
    {bib: 1, split: 1, course: "B", performance: 7},
    {bib: 1, split: 2, course: "A", performance: 2.7},
    {bib: 1, split: 2, course: "B", performance: 8},
    {bib: 2, split: 1, course: "A", performance: 2.9},
    {bib: 2, split: 1, course: "B", performance: 5.0},
    {bib: 2, split: 2, course: "A", performance: 9},
    {bib: 2, split: 2, course: "B", performance: 6},
    {bib: 2, split: 2, course: "A", performance: 3},
    {bib: 1, split: 2, course: "A", performance: 3},
    {bib: 1, split: 2, course: "B", performance: 1.2},
    {bib: 2, split: 1, course: "B", performance: 3},

  ]


  const dataGrouped = d3.groups(
    data,
    (d) => d.day,
    (d) => d.split,
    (d) => d.course
  );

  const cellX = g
    .selectAll(".cell")
    .data(dataGrouped)
    .join("g")
    .attr("class", "cellX")
    .attr("transform", (d) => console.log(d))
    .attr(
      "transform",
      (d, i) => `translate(0, ${i * (padding + histogramHeight + padding)})`
    );

  const cellY = cellX
    .selectAll(".cellX")
    .data((d) => d[1])
    .join("g")
    .attr("class", "cellXY")
    .attr(
      "transform",
      (d, i) => `translate(${i * (padding + histogramWidth + padding)},0)` //${ i * (padding + histogramWidth + padding)},0)`
    );

  const cellZ = cellY
    .selectAll(".cellXY")
    .data((d) => d[1])
    .join("g")
    .attr("class", "cellZ")
    .attr(
      "transform",
      (d, i) => `translate(0, ${i <= 2 ? i * paddingZ : i * (paddingZ)})` //(0, ${i * (paddingZ + histogramZheight + paddingZ)})`
    );

  const path = cellZ
    .selectAll(".lo")
    .data((d) => d)
    .join("path")
    .attr("d", (d) => {
      const xScale = d3
        .scaleLinear()
        .domain([-3, 40])
        .range([0, histogramWidth]);

      console.log(xScale.domain());

      const bingenerator = d3
        .bin()
        .domain(xScale.domain())
        .value((d) => d.performance);

      const binnedData = bingenerator(d);

      const topHistogramYscale = d3
        .scaleLinear()
        .domain(d3.extent(binnedData, (d) => d.length))
        .range([histogramZheight, 0]);

      const xAxis = d3.axisBottom().scale(xScale);

      const topHistogramLineGenerator = d3
        .area()
        .x((d) => xScale(d.x0))
        .y0(histogramZheight)
        .y1((d) => topHistogramYscale(d.length))
        .curve(d3.curveBasis);

      return topHistogramLineGenerator(binnedData);
    })


}

main();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文