dc.js Rowchart grouped

235 views
Skip to first unread message

sevil yılmaz

unread,
Dec 22, 2020, 6:03:28 AM12/22/20
to dc-js user group
Hello, I want to make the grouped row chart as in the screenshot below. Is it possible?

Help please:)



Gordon Woodhull

unread,
Dec 22, 2020, 12:30:57 PM12/22/20
to dc.js user group
Hi, grouping and stacking are not supported directly by the dc.js row chart.

If you are okay with
1. ordering by group name instead of from high to low
2. clicking filtering by both variables instead of just the group

you can hack this by creating a dimension key that includes both your group and color variables.

I'll call them "cat" and "col".

// random data
const categories = ['A', 'B', 'C', 'D'],
colors = ['Red', 'Black'];
const rndCat = d3.randomInt(0, 4),
  rndCol = d3.randomInt(0, 2);
const data = d3.range(200).map(() => ({
  cat: categories[rndCat()],
  col: colors[rndCol()]
}));

const cf = crossfilter(data),
  catDim = cf.dimension(d => [d.cat, d.col]),
  catGroup = catDim.group();

Then set the ordering to use cat and col:

  .ordering(({key: [cat, col]}) => cat + (col==='Red' ? 0 : 1))

use the color part of the key for coloring:

  .colorAccessor(({key: [cat, col]}) => col)
  .colors(d3.scaleOrdinal().domain(colors).range(colors));

and tweak the positions each time the chart is drawn:

chart.on('pretransition', chart => {
  d3.selectAll('g.row rect')
     .attr('y', ({key: [cat, col]}) => col === 'Black' ? -4 : 0);
})

Screenshot and fiddle link below.

I can imagine ways to get past the first caveat, although it will make things more complicated (since it has to sort based on more information than is in the group bin).

I can't think of a good way to click to select the whole group, though.


Complete example:


--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/adf39f30-16ad-4cf4-9229-a9c9e39462een%40googlegroups.com.

sevil yılmaz

unread,
Dec 23, 2020, 3:20:13 AM12/23/20
to dc-js-us...@googlegroups.com
Thank you so much for your help Gordon :)

Gordon Woodhull <gor...@woodhull.com>, 22 Ara 2020 Sal, 20:30 tarihinde şunu yazdı:

sevil yılmaz

unread,
Dec 29, 2020, 1:14:15 AM12/29/20
to dc-js user group
Hi Gordon,
Could I ask you a question. I'm sorry to keep you busy :)

I am asked to make a grouped rowbar. I tried to try it with dc.js, but it didn't work. I found a grouped example with D3, but I want it to work with a crossfilter and be filtered. Could you help me with this? I'm attach a screenshot.


https://jsfiddle.net/developersevil/5f1jmok6/31/

the code you sent me with dc.js in this link,but I couldn't do this with object data and crossfilter


Best Regards,

23 Aralık 2020 Çarşamba tarihinde saat 11:20:13 UTC+3 itibarıyla sevil yılmaz şunları yazdı:
rowchart.png

Gordon Woodhull

unread,
Dec 29, 2020, 7:02:54 AM12/29/20
to dc.js user group
Hi Sevil,

You included links to the grouped bar demo that I sent earlier, as well as a D3 demo which appears to be generic and not your data.

Could you explain what problem you had with "object data and crossfilter", and include a sample of the data you had trouble using with dc.js?

Currently I have no idea what the problem is.

Thanks,
Gordon


sevil yılmaz

unread,
Dec 29, 2020, 3:39:56 PM12/29/20
to dc-js user group
Hi Gordon,
I have to bring the total of other fields by grouping by product.
For example, product will be grouped, grouping will be made by taking the sum of the quantity, total, total2 areas.

When I filter the type from the selection menu, the products of that type will come and the total quantity, total and total 2 prices of those products will be grouped.

I  attach a sample screenshot.You can see the example I tried to do on the link.

I hope I made myself clear.
Thanks,
Cheers

https://jsfiddle.net/developersevil/damzb6gv/90/


29 Aralık 2020 Salı tarihinde saat 15:02:54 UTC+3 itibarıyla gor...@woodhull.com şunları yazdı:
rowbar.jpg

Gordon Woodhull

unread,
Dec 30, 2020, 8:09:49 AM12/30/20
to dc.js user group
Hi Sevil,

Okay, here is a version that groups by product and has individual bars for quantity, total, total2.



It's a bit more complicated because we need to reduce each field:

  var dmsGroup = dms2.group().reduce(
    function(p, v) { // add
      p.quantity += v.quantity;
      p.total += v.total;
      p.total2 += v.total2;
      return p;
    },
    function(p, v) { // remove
      p.quantity -= v.quantity;
      p.total -= v.total;
      p.total2 -= v.total2;
      return p;    
    },
    function() { // init
      return {quantity: 0, total: 0, total2: 0}
    });

and then flatten into a group with multikeys:

 function flatten_group(group, fields) {
   return {
     all: () => group.all().flatMap(
     ({key, value}) => fields.map(
       field => ({key: [key, field], value: value[field]})))
   }
 }

Y positions have also gotten more complicated with three bars per group:

mychart.on('pretransition', chart => {
  chart.selectAll('g.row rect')
     .attr('y', ({key: [cat, col]}) => {
       switch(col) {
       case 'quantity': return +3;
       case 'total': return 0;
       case 'total2': return -3;
       }
     })
  chart.selectAll('g.row text')
     .attr('y', ({key: [cat, col]}) => {
       switch(col) {
       case 'quantity': return 15;
       case 'total': return 12;
       case 'total2': return 9;
       }
     })
})

This also handles text positions, which my last example did not.

As a bonus, I've also added selection by group: basically we need all bars within a group to act as one, and apply a filter on just the first field of the key:

mychart.addFilterHandler((filters, filter) => {
  Array.prototype.push.apply(filters, fields.map(field => [filter[0], field]))
  return filters;
})
mychart.removeFilterHandler((filters, filter) => {
  return filters.filter(([cat, col]) => cat !== filter[0]);
})
var _filterHandler = mychart.filterHandler();
mychart.filterHandler((dimension, filters) => {
  var keys = {};
  filters.forEach(([cat, col]) => keys[cat] = true);
  _filterHandler(dimension, Object.keys(keys));
})

It's not possible to select individual bars in this case, since bars represent columns and not rows, so this is the only thing that makes sense.

Also note that we need two product dimension (dms and dms2) in order for the select menu and chart to filter each other. Not particularly interesting behavior, but consistent.

Cheers,
Gordon



sevil yılmaz

unread,
Dec 31, 2020, 12:07:39 PM12/31/20
to dc-js-us...@googlegroups.com
Hi Gordon,

Thank you very much for your help. This is exactly what I was looking for :))
I have one more question to ask. “How could I sort the total values from maximum to minimum (ascending / descending)?”

Happy new year. I hope, this year will bring  happiness and health to your life.

 Happy Christmast.
Sevil

Gordon Woodhull <gor...@woodhull.com>, 30 Ara 2020 Çar, 16:09 tarihinde şunu yazdı:

Gordon Woodhull

unread,
Jan 1, 2021, 12:07:23 PM1/1/21
to dc.js user group
Yes; it gets more complicated because you have to remember the total for each group, using an object for the value instead of a number:

 function flatten_group(group, fields) {
   return {
     all: () => group.all().flatMap(
     ({key, value}) => {
       const tot = value.quantity + value.total + value.total2;
       return fields.map(
         field => ({key: [key, field], value: {
         
         x: value[field],
         xtot: tot
         }}))})
   }
 }

then extract the `x` value:

  .valueAccessor(({value: {x}}) => x)

and finally use the group total as well as subgroup number for the ordering:

  .ordering(({key: [cat, col], value: {xtot}}) => -xtot*100 + suborder[col])


All of this is a pretty convoluted way to add a feature to dc.js that would make more sense as part of the library.


On Dec 31, 2020, at 12:07 PM, sevil yılmaz <programc....@gmail.com> wrote:

Hi Gordon,

Thank you very much for your help. This is exactly what I was looking for :))
I have one more question to ask. “How could I sort the total values from maximum to minimum (ascending / descending)?”

Happy new year. I hope, this year will bring  happiness and health to your life.

 Happy Christmast.
Sevil

Gordon Woodhull <gor...@woodhull.com>, 30 Ara 2020 Çar, 16:09 tarihinde şunu yazdı:
Hi Sevil,

Okay, here is a version that groups by product and has individual bars for quantity, total, total2.

<PastedGraphic-2.png>

sevil yılmaz

unread,
Jan 1, 2021, 12:15:36 PM1/1/21
to dc-js user group
Thank you very much . 

1 Ocak 2021 Cuma tarihinde saat 20:07:23 UTC+3 itibarıyla gor...@woodhull.com şunları yazdı:
Reply all
Reply to author
Forward
0 new messages