Set axis domain dynamically based on minimum and maximum dataset value for a d3.js bubble chart

46 Views Asked by At

I am trying to assign a dynamic domain values for d3.js bubble chart. I am creating bubbles for total amount for an error per client. When I use a hardcoded range, I see the bubbles for each client. I have a working jsfiddle here.

.x(d3.scaleLinear().domain([0, 30]))
.y(d3.scaleLinear().domain([0, 30]))
.r(d3.scaleLinear().domain([0, 250]))

But, when I change these to minimum and maximum that i calculate using d3.extent then it doesn't work.

var minMax = d3.extent(data, function(d) {
    if (d.status == "settlement error") {
      return d.amount;
    }
  });

The problem is that the minimum and maximum is calculated for an error type but not for an aggregated amount.

Here is my simplified data:

id,client,date,amount,status
10003,chase,2023-02-01,8,settlement error
10007,bofa,2022-11-22,2,settlement error
10008,citi,2022-11-22,8,settlement error
10009,chase,2023-02-01,4,settlement error

This should translate into:

chase - error count = 2, total amount = 12
bofa - error count = 1, total amount = 2
citi - error count = 1, total amount = 8

But, if you see the data, the minimum amount is 2, and maximum amount is 8. That is what my minMax value is set to [2,8]. Instead I want [2,12] for better plotting. Even better if I can get some padding like [1,15].

Edit After @pernifloss gave a solution which somehow didn't solve my issue, I tried to take a different route where I added custom_elastic where I am trying to get the min and max from my dimension but var max = clientErrorChart.dimension().top[1][0].value; errors:

function custom_elastic(clientErrorChart) {
    var max = 300;//clientErrorChart.dimension().top[1][0].value;
    var min = 10;//clientErrorChart.dimension().top(10)[9].value;
    clientErrorChart.x(d3.scaleLinear().domain([min, max]))
}

Then, I added this to my chart's preRender and preRedraw event.

clientErrorChart
    .height(300)
    .dimension(clientDim)
    .group(errorsByClient)
    .transitionDuration(1500)
    .colors(colorScale)
    //.x(d3.scaleLinear().domain([0, 3000]))
    .y(d3.scaleLinear().domain([0, 3000]))
    .r(d3.scaleLinear().domain([0, 250]))
    .keyAccessor(function (p) {
        return p.value.payment_sum;
    })
    .valueAccessor(function (p) {
        return p.value.count;
    })
    .radiusValueAccessor(function (p) {
        return p.value.error_count;
    })
    .transitionDuration(1500)
    .elasticY(true)
    .yAxisPadding(1)
    .xAxisPadding(1)
    .label(function (p) {
        return p.key;
    })
    .renderLabel(true)
    .renderTitle(true)
    .title(p => [
        p.key,
        `Total Error: ${formatNumber(p.value.error_count)}`,
        `Total Payment: ${formatNumber(p.value.payment_sum)}`,
        `Total Volume: ${formatNumber(p.value.count)}`          
    ].join('\n')); 

clientErrorChart.on('preRender', custom_elastic)
    .on('preRedraw', custom_elastic);
2

There are 2 best solutions below

2
pernifloss On

minMax is just an array of 2 values, [2,8], if you want to remove to min and add max, you can simply do

minMax = [ minMax[0] - 2 , minMax[1] + 7 ];

Or generate it anyway you want

6
pernifloss On

Here is a reduce to aggregate and sum your data

var sumAmountGroupByClient = data.reduce((a, b) => {
  const found = a.find(d => d.client === b.client);
  if (found) {
    return a.map(v => v.client === b.client ? {
      client: v.client,
      sum: v.sum + b.amount
    } : v);
  }
  return [...a, {
    client: b.client,
    sum: b.amount
  }]
}, []);
var minMax = d3.extent(sumAmountGroupByClient.map(d => d.sum));

data = [{
    "id": "    10003",
    "client": "chase",
    "date": "2023-02-01",
    "amount": 8,
    "status": "settlement error",
    "count": null,
    "dd": "2023-01-31T23:00:00.000Z"
  },
  {
    "id": "    10007",
    "client": "bofa",
    "date": "2022-11-22",
    "amount": 2,
    "status": "settlement error",
    "count": null,
    "dd": "2022-11-21T23:00:00.000Z"
  },
  {
    "id": "    10008",
    "client": "citi",
    "date": "2022-11-22",
    "amount": 8,
    "status": "settlement error",
    "count": null,
    "dd": "2022-11-21T23:00:00.000Z"
  },
  {
    "id": "    10009",
    "client": "chase",
    "date": "2023-02-01",
    "amount": 4,
    "status": "settlement error",
    "count": null,
    "dd": "2023-01-31T23:00:00.000Z"
  }
]


var sumAmountGroupByClient = data.reduce((a, b) => {
  const found = a.find(d => d.client === b.client);
  if (found) {
    return a.map(v => v.client === b.client ? {
      client: v.client,
      sum: v.sum + b.amount
    } : v);
  }
  return [...a, {
    client: b.client,
    sum: b.amount
  }]
}, []);
var minMax = d3.extent(sumAmountGroupByClient.map(d => d.sum));
<script src="https://d3js.org/d3.v7.min.js" charset="utf-8"></script>