我正在try 为性能测试结果创建分组条形图.这是我第一次使用D3.

X轴应该显示parallelism,这是使用的线程数和Y图表持续时间(毫秒).如果有多条记录对应于相同的X值(相同的并行度),则它们应该相邻出现,最好是按持续时间排序,但这并不重要.

我的灵感来自以下代码:https://observablehq.com/@d3/grouped-bar-chart/2?intent=fork

我花了一个多小时摆弄它,但我仍然没有正确地进行分组.这个问题当然是由我如何定义fxx函数以及如何分配.attr("x", d => marginLeft+fx(d.duration))引起的.我try 了许多不同的版本,但都没有效果.

const runData = [
      {"duration":74950.52171194553,"parallelism":1},
      {"duration":88687.86499893665,"parallelism":0,"exitCode":0},
      {"duration":60000,"parallelism":1,"exitCode":0},
      {"duration":90000,"parallelism":0,"exitCode":0},
      {"duration":90000,"parallelism":0,"exitCode":0}
  ];

  const width = 700;
  const height = 400;
  const marginTop = 10;
  const marginRight = 10;
  const marginBottom = 20;
  const marginLeft = 40;
  // Create the SVG container.
  const svg = d3.select("#ca")
    .append("svg")
    .attr("width", width)
    .attr("height", height);

  const paralelismSet = new Set(runData.map(x=>x.parallelism));
  const paralelismList = runData.map(x=>x.parallelism);
  paralelismList.sort();
  const durationSet = new Set(runData.map(x=>x.duration));

  const minDuration = Math.min(...durationSet);
  const maxDuration = Math.max(...durationSet);

  // I am trying here to sort by duration but group by parallelism
  const fx = d3.scaleBand()
      .domain(durationSet)
      .rangeRound([marginLeft, width - marginRight])
      .paddingInner(0.1);

  const x = d3.scaleBand()
      .domain(paralelismList)
      .rangeRound([0, fx.bandwidth()])
      .padding(0.05);

  const color = d3.scaleLinear([minDuration, maxDuration], ["green", "red"]);

  // Y encodes the height of the bar.
  const y = d3.scaleLinear()
      .domain([0, maxDuration]).nice()
      .rangeRound([height - marginBottom, marginTop]);

  svg.append("g")
    .selectAll()
    .data(d3.group(runData, d => d.parallelism))
    .join("g")
      .attr("transform", ([paralelism]) => `translate(${x(paralelism)},0)`)
    .selectAll()
    .data(([, d]) => d)
    .join("rect")
      // I tried various combinations of subtracting fx from x and vice versa
      .attr("x", d => marginLeft+fx(d.duration))
      .attr("y", d => y(d.duration))
      .attr("width", x.bandwidth())
      .attr("height", d => y(0) - y(d.duration))
      .attr("fill", d => color(d.duration));

  // The horizontal axis does not work well either, it is too short
  svg.append("g")
      .attr("transform", `translate(${marginLeft},${height - marginBottom})`)
      .call(d3.axisBottom(x));

  // This code works fine
  svg.append("g")
      .attr("transform", `translate(${marginLeft},0)`)
      .call(d3.axisLeft(y).ticks(null, "s"))
      .call(g => g.selectAll(".domain").remove());
<div id="ca">
</div>
<script src="https://d3js.org/d3.v6.min.js"></script>

推荐答案

你以此为例的情节,对我来说,是不必要的混乱.它不遵循通常的d3图表 struct ,边距有太多的改动,数据操作可能需要清理.

也就是说,我严重重构了你的try . 我添加了注释来解释更改:

<!DOCTYPE html>

<html>
  <head>
    <script src="https://d3js.org/d3.v6.min.js"></script>
  </head>

  <body>
    <div id="ca"></div>
    <script>
      const runData = [
        { duration: 74950.52171194553, parallelism: 1 },
        { duration: 88687.86499893665, parallelism: 0, exitCode: 0 },
        { duration: 60000, parallelism: 1, exitCode: 0 },
        { duration: 90000, parallelism: 0, exitCode: 0 },
        { duration: 90000, parallelism: 0, exitCode: 0 },
      ];

      // this is usual d3 plot set
      var margin = {top: 30, right: 30, bottom: 70, left: 60},
          width = 460 - margin.left - margin.right,
          height = 400 - margin.top - margin.bottom;

      // create the container with margins for axix
      // plotting happens in the offset child g
      var svg = d3.select("#ca")
        .append("svg")
          .attr("width", width + margin.left + margin.right)
          .attr("height", height + margin.top + margin.bottom)
        .append("g")
          .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")");

      const groupedData = d3.group(runData, d => d.parallelism); //<-- group the data upfront
      const durationDomain = d3.extent(runData, d => d.duration); //<-- min and max of duration
      const innerDomain = d3.range(0, d3.max(groupedData, d => d[1].length)); //<-- domain inside the group
      const outerDomain = Array.from(groupedData.keys()).sort(); //<-- domain of the groups

      // scale of groups
      const fx = d3
        .scaleBand()
        .domain(outerDomain)
        .rangeRound([0, width])
        .paddingInner(0.1);

      // scale inside group
      const x = d3
        .scaleBand()
        .domain(innerDomain)
        .rangeRound([0, fx.bandwidth()])
        .padding(0.05);

      // color of each bar ordered by index
      const color = d3
        .scaleLinear()
        .domain(innerDomain)
        .range(['orange', 'steelblue', 'brown']);

      // duration y-scale
      const y = d3
        .scaleLinear()
        .domain([0, durationDomain[1]])
        .nice()
        .rangeRound([height, 0]);

      svg
        .append('g')
        .selectAll()
        .data(groupedData)
        .join('g')
        .attr('transform', (d) => `translate(${fx(d[0])},0)`) //<-- move group to position
        .selectAll()
        .data(([, d]) => d)
        .join('rect')
        .attr('x', (d,i) => x(i)) //<-- set position inside group
        .attr('y', (d) => y(d.duration))
        .attr('width', x.bandwidth())
        .attr('height', (d) => y(0) - y(d.duration))
        .attr('fill', (d,i) => color(i));

      svg
        .append('g')
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(fx));

      svg
        .append('g')
        .call(d3.axisLeft(y));

    </script>
  </body>
</html>

Javascript相关问答推荐

RxJS setTimeout操作符等效

*ngFor和@代表输入decorator 和选角闭合

如何才能拥有在jQuery终端中执行命令的链接?

在React中获取数据后,如何避免不必要的组件闪现1秒?

Chart.js V4切换图表中的每个条,同时每个条下有不同的标签.怎么做?

Rehype将hashtag呈现为URL

查找最长的子序列-无法重置数组

如何使用子字符串在数组中搜索重复项

Angular 订阅部分相互依赖并返回数组多个异步Http调用

400 bad request error posting through node-fetch

钛中的onClick事件需要在两次点击之间等待几秒钟

如何在JAVASCRIPT中合并两组对象并返回一些键

使用自动识别发出信号(&Q)

如何找到带有特定文本和测试ID的div?

通过跳过某些元素的对象进行映射

未找到用于 Select 器的元素:in( puppeteer 师错误)

如何设置时间选取器的起始值,仅当它获得焦点时?

Played link-Initialize.js永远显示加载符号

如何创建一个for循环,用于计算仪器刻度长度并将其放入一个HTML表中?

JavaScript -如何跳过某个字符(S)来打乱字符串中的字符