Combining various datasets with identical X values in a D3 bar graph

I'm currently working on creating a grouped bar chart to display performance test results using D3 for the first time.

The X axis should represent parallelism, indicating the number of threads used, while the Y axis will show the duration in milliseconds. If there are multiple records with the same parallelism value, they should be displayed together, ideally sorted by duration although sorting is not critical.

I found inspiration in this code snippet:

After spending over an hour tweaking the code, I am still struggling to get the grouping right. The issue seems to lie in how I define the fx and x functions, and in how I assign

.attr("x", d => marginLeft + fx(d.duration))
. Despite trying numerous variations, I haven't been successful yet.

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);

  // Sorting by duration but grouping by parallelism attempt
  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 bars
  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", ([parallelism]) => `translate(${x(parallelism)},0)`)
    .selectAll()
    .data(([, d]) => d)
    .join("rect")
      .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));

  svg.append("g")
      .attr("transform", `translate(${marginLeft},${height - marginBottom})`)
      .call(d3.axisBottom(x));

  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>

Answer №1

The example plot you are following seems overly complex to me. It does not adhere to the standard d3 graph structure, with excessive manipulation of margins and data that could benefit from cleaning up.

That being said, I have made significant modifications to your code. Here is an overview of the changes:

<!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 },
      ];

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

      // Container creation with axis margins
      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); 
      const durationDomain = d3.extent(runData, d => d.duration); 
      const innerDomain = d3.range(0, d3.max(groupedData, d => d[1].length)); 
      const outerDomain = Array.from(groupedData.keys()).sort(); 

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

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

      // Color assignment for bars by index
      const color = d3
        .scaleLinear()
        .domain(innerDomain)
        .range(['orange', 'steelblue', 'brown']);

      // Y-scale for duration
      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)`)
        .selectAll()
        .data(([, d]) => d)
        .join('rect')
        .attr('x', (d,i) => x(i))
        .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>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can the front design of Material-UI's Card header be customized?

Currently, I am facing an issue with the Material-UI card header as the background color is affecting the readability of the default font. My aim is to use the typography prop h4 for the header, but I am struggling to achieve this. https://i.stack.imgur.c ...

What could be the reason for the Checkbox's value not showing up after making changes?

In my React and Material UI project, I am facing an issue where I want to check all checkboxes in a list by simply checking one checkbox in a parent component. Despite passing down the correct value of the parent checkbox through props, the visual changes ...

Tips on concealing all classes except one through touch swiping

If you have a website with a single large article divided into multiple sections categorized as Title, Book1, Book2, & Book3, and you want to implement a swipe functionality where only one section is displayed at a time, you may encounter some issues. ...

AngularJS Toggle Directive tutorial: Building a toggle directive in Angular

I'm attempting to achieve a similar effect as demonstrated in this Stack Overflow post, but within the context of AngularJS. The goal is to trigger a 180-degree rotation animation on a button when it's clicked – counterclockwise if active and c ...

The initial setTimeout function functions correctly, however the subsequent ones do not operate as expected

I have developed the following code: bot.on('message', message=> { if(message.content === "come here") { message.channel.send('hey'); setTimeout(() => { message.channel.send('i am here' ...

When should ng-repeat be utilized: only when the object type is an array?

I have a detailed object structure below: $scope.document = { "GENERAL_FIELDS": { "Source_Type": "custom", "Annotations": [ "216/content/Factiva_CM_001/Proteins", "216/content/Factiva_CM_001/Fact" ], "Content": [ " ...

Converting PHP date format to JavaScript date format

I'm struggling to solve this problem $datetime = new Date().toLocaleString(); // returns current date in format 10/21/2021, 14:29:43 How can I generate the desired date format using JavaScript? The output should look like this: 2021-10-21 16:30:01 ...

Unable to change the color of the RaisedButton component in Material-UI

Struggling to alter the color of the material-ui RaisedButton with inline style customization using backgroundColor: '#fb933c', yet it continues to display the default color. ...

What is the process for importing a jquery plugin like turnjs into a React component?

After searching through countless posts on stackoverflow, it seems like there is no solution to my problem yet. So... I would like to integrate the following: into my react COMPONENT. -I attempted using the script tag in the html file, but react does no ...

What are some ways to direct users from one page to another without relying on server-side programming?

Is there a way to create a redirect page using jQuery or JavaScript? What is the process of writing client-side scripting code to redirect the browser from one page (page1) to another page (page n)? ...

Using JavaScript: How to utilize Array.reduce within a function that also accepts arguments

let foo = 0; let bar = 0; const arr1 = [1, 2, 3, 4, 5]; const arr2 = [6, 7, 8, 9, 10]; function calculateSum(arr) { return arr.reduce((accum, val) => accum + val, 0); } foo = calculateSum(arr1); // Expect foo to equal 15 bar = calculateSum(arr2); ...

Four unique chip/tag colors, personalized to suit your style

Currently, I have integrated two arrays into my autocomplete menu where the chip/tag color is either primary or secondary based on the array the selected component belongs to. I aim to include all four arrays in the menu (top10Songs, top10Artists, top10Fi ...

What is the process for customizing the heading titles on various pages within the Next.js application directory?

Within the app directory of Next.js 13, I have a default root layout setup: import "./globals.css"; export default function RootLayout({ children }) { return ( <html lang="en"> <head> <title>Create ...

Updating the iFrame source using jQuery depending on the selection from a dropdown menu

I want to create a dynamic photosphere display within a div, where the source is determined by a selection from a drop-down menu. The select menu will provide options for different rooms that the user can view, and the div will contain an iframe to showca ...

JQuery Ajax encounters a 500 error message due to an internal server issue

I'm currently using the jQuery library to send an ajax request to a PHP file. Initially, everything was working perfectly fine with a relative path like this: url:"fetch_term_grades.php", However, when I modified the path to be more specific like th ...

What is the process of extracting a utility function from a helper file on a node.js server?

I'm facing a challenge with my node/express server where I need to access a function from a helper file in my app.js. The function in the helper file looks like this: CC.CURRENT.unpack = function(value) { var valuesArray = value.split("~"); ...

Enhancing Web Forms with PHP and AJAX Javascript

Currently, I'm working on implementing a "stream" feature that allows users to input their status. So far, I have successfully set it up, but my goal is to enable users to see their new status without having to refresh the page. I understand that uti ...

Windows location does not change after an XMLHttpRequest is made

Here is my code that uses XMLHttpRequest: function SignUp() { signUpConnection = new XMLHttpRequest(); signUpConnection.onreadystatechange = processRegistration; signUpConnection.open('GET', 'index.php?registrarse=&username= ...

What could be causing my UI Bootstrap datepicker-popup to suddenly stop functioning?

After updating to UI Bootstrap 0.11.0, I encountered an issue where my datepickers were no longer appearing correctly. To demonstrate this problem, I have created a plunker which can be viewed here. Essentially, the code snippet causing the problem is as f ...

What is the best way to use jQuery to find and select an "a" tag that links to a file with a specific

My goal is to select links that have different types of files using jQuery: jQuery('a[href$=".pdf"], a[href$=".doc"], a[href$=".docx"], a[href$=".ppt"], a[href$=".pptx"], a[href$=".xls"], a[href$=".slxs"], a[href$=".epub"], a[href$=".odp"], a[href$=" ...