Customization of Parallel Coordinates Plot Chart

154 views
Skip to first unread message

Sumit Bansal

unread,
Aug 4, 2017, 6:08:40 AM8/4/17
to d3-js

I am using d3 chart library for my project Also,i am able to create my PCP successfully with JSON data set. But I am having a requirement for grouping the data set as mentioned below:


In the above screen A to E is one group then some space and F to J is one group.This grouping is always fixed.Also i have mentioned the code which i am using right now:

<!DOCTYPE html>
<html >
<head>
  <meta charset="UTF-8">
  <title>Parallel Coordinates </title>
  

  
      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>
  

  <script>
  var m = [200, 30, 50, 150],
  w = 1500 - m[1] - m[3],
  h = 800 - m[0] - m[2];

var x = d3.scale.ordinal().rangePoints([0, w], 1),
  y = {},
  dragging = {};

var line = d3.svg.line(),
  axis = d3.svg.axis().orient("left"),
  background,
  foreground;
var color = d3.scale.ordinal()
  .domain([1,2,3])
  .range(['red','green','blue']);

//var color = function(d) { return green_to_blue(d['Plant']); };
var svg = d3.select("body").append("svg:svg")

  .attr("width", w + m[1] + m[3])
  .attr("height", h + m[0] + m[2])
  .append("svg:g")
  .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

var data = [{
  "Plant": "American Blueberry",
  "A": 0.2, 
"B": 3, 
"C": 23, 
"D": 432, 
"E": 123, 
"F": 74, 
"G": 1231, 
"H": 14, 
"I": 43, 
"J": 675
}, {
  "Plant": "American Blueberry5",
 "A": 0.2, 
"B": 3, 
"C": 23, 
"D": 432, 
"E": 123, 
"F": 74, 
"G": 1231, 
"H": 14, 
"I": 3, 
"J": 675
}, {
  "Plant": "Blueberry1",
  "A": 0.2, 
"B": 3, 
"C": 23, 
"D": 432, 
"E": 123, 
"F": 74, 
"G": 1231, 
"H": 14, 
"I": 43, 
"J": 675
}, {
  "Plant": "American Blueberry2",
 "A": 0.2, 
"B":100, 
"C": 50, 
"D": 40, 
"E": 90, 
"F": 88, 
"G": 231, 
"H": 14, 
"I": 43, 
"J": 675
}];

// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(data[0]).filter(function(d) {
  if (d === "name") return false;
  if (d === "Plant" || d === "Chemical" || d === "Pathway" || d === "Gene" || d === "Disease") {
    y[d] = d3.scale.ordinal()
      .domain(data.map(function(p) {
        return p[d];
      }))
      .rangePoints([h, 0]);
  } else {
    y[d] = d3.scale.linear()
      .domain(d3.extent(data, function(p) {
        return +p[d];
      }))
      .range([h, 0]);
  }
  return true;
}));

// Add grey background lines for context.
background = svg.append("svg:g")
  .attr("class", "background")
  .selectAll("path")
  .data(data)
  .enter().append("svg:path")
  .attr("d", path);

// Add blue foreground lines for focus.
foreground = svg.append("svg:g")
  .attr("class", "foreground")
  .selectAll("path")
  .data(data)
  .enter().append("svg:path")
  .attr("d", path)
.attr('stroke', function(d) { return color(d.label); });
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
  .data(dimensions)
  .enter().append("svg:g")
  .attr("class", "dimension")
  .attr("transform", function(d) {
    return "translate(" + x(d) + ")";
  })
 .call(d3.behavior.drag()
    .on("dragstart", function(d) {
      dragging[d] = this.__origin__ = x(d);
      background.attr("visibility", "hidden");
    })
    .on("drag", function(d) {
      dragging[d] = Math.min(w, Math.max(0, this.__origin__ += d3.event.dx));
      foreground.attr("d", path);
      dimensions.sort(function(a, b) {
        return position(a) - position(b);
      });
      x.domain(dimensions);
      g.attr("transform", function(d) {
        return "translate(" + position(d) + ")";
      })
    })
    .on("dragend", function(d) {
      delete this.__origin__;
      delete dragging[d];
      transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
      transition(foreground)
        .attr("d", path);
      background
        .attr("d", path)
        .transition()
        .delay(500)
        .duration(0)
        .attr("visibility", null);
    }));

// Add an axis and title.
g.append("svg:g")
  .attr("class", "axis")
  .each(function(d) {
    d3.select(this).call(axis.scale(y[d]));
  })
  .append("svg:text")
  .attr("text-anchor", "middle")
  .attr("y", -9)
  .text(String);

// Add and store a brush for each axis.
g.append("svg:g")
  .attr("class", "brush")
  .each(function(d) {
    d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brush", brush));
  })
  .selectAll("rect")
  .attr("x", -8)
  .attr("width", 16);

function position(d) {
  var v = dragging[d];
  return v == null ? x(d) : v;
}

function transition(g) {
  return g.transition().duration(500);
}

// Returns the path for a given data point.
function path(d) {
  return line(dimensions.map(function(p) {
    return [position(p), y[p](d[p])];
  }));
}

// Handles a brush event, toggling the display of foreground lines.
function brush() {
  var actives = dimensions.filter(function(p) {
      return !y[p].brush.empty();
    }),
    extents = actives.map(function(p) {
      return y[p].brush.extent();
    });
  foreground.style("display", function(d) {
    return actives.every(function(p, i) {
      return extents[i][0] <= d[p] && d[p] <= extents[i][1];
    }) ? null : "none";
  });
}
   </script>

</body>
</html>
Can any one suggest me how we can achieved this?

Japhy Bartlett

unread,
Aug 7, 2017, 2:06:33 PM8/7/17
to d3...@googlegroups.com
In this case imo it would probably be easiest to transform the data before you create the scales.


something like:
```
var data =  [{
  "Plant": "Blueberry1",
  "A": 0.2, 
"B": 3, 
"C": 23, 
"D": 432, 
"E": 123, 
"F": 74, 
"G": 1231, 
"H": 14, 
"I": 43, 
"J": 675
},]


data = data.map( function(d) {

    // combine the keys however you need to

    return { "Plant": d.Plant, "A": d.A+d.B+d.C..., "B": etc}

})


// then the example code
```


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Sumit Bansal

unread,
Aug 8, 2017, 2:36:05 AM8/8/17
to d3...@googlegroups.com, ja...@pearachute.com
Thanks Japhy for your quick response.

In that case then how we will give some space after the one group ie in your case that will be Group A.? Also,i have mentioned screen shot above for your reference.can you please suggest any idea about this?

Thanks & Regards
Sumit Bansal
BI Developer

--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/Yl-oVxx_pIA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to d3-js+unsubscribe@googlegroups.com.

Kai Chang

unread,
Aug 8, 2017, 2:41:20 AM8/8/17
to d3...@googlegroups.com, ja...@pearachute.com
Sumit,

You could use an ordinal scale to specify coordinates for each column to create the gap. For example this scale:

  d3.scaleOrdinal()
    .domain(["a","b","c","d","e","f"])
    .range([0, 20, 40, 120, 140, 160])


Creates a gap of 20 between groups, with a gap of 80 between "c" and "d".

Sumit Bansal

unread,
Aug 9, 2017, 6:25:36 AM8/9/17
to d3...@googlegroups.com, kai.s...@gmail.com, Japhy Bartlett
Thanks Kai for your quick response.But I have some scenario where my JSON structure is coming dynamically as like mentioned below:

{
   "A": 100, 
"B":100, 
"C": 150, 
"G": 291, 
"H": 214, 
"I": 243, 
"J": 675
}

In this JSON here D to E is missing then the function which we are using it will create the gap between H and I as mentioned screen shot below for your further reference.





 var x=d3.scale.ordinal()
    .domain(["A","B","C","D","E","F","G","H","I","J"])
  .range([0, 40, 80,120,160,400,440,480,520,560]),

But can we adjust the gap automatically- input parameter A to E is always fixed ,Output Parameter F to J is always fixed 

But the selection of  input and output parameter are not fixed .user can select may be A to C as input and F to H as output. So i want gap should be between input and output parameter .Is it possible in the PCP chart?

Thanks & Regards
Sumit Bansal
BI Developer

Sumit Bansal

unread,
Aug 11, 2017, 11:15:39 AM8/11/17
to d3...@googlegroups.com, Kai Chang, Japhy Bartlett
Any suggestion on this..please tell me is it possible in PCP chart?

Thanks & Regards
Sumit Bansal
BI Developer

steve rickus

unread,
Aug 11, 2017, 1:55:47 PM8/11/17
to d3-js, kai.s...@gmail.com, ja...@pearachute.com
I don't see why not -- you just need to build two arrays as input to the domain and range methods, something like this...

// get the domain arrays from some UI selection by the user... or whatever
var inp_domain = ["A", "B", "C"];
var out_domain = ["F", "G"];
var out_range = [];

var col_index = 0;
var col_width = 40;
var col_gutter = 80;
for (var x in inp_domain) {
    out_range
.push(col_index);
    col_index
+= col_width;
}
col_index
+= col_gutter;
for (var x in out_domain) {
    out_range
.push(col_index);
    col_index
+= col_width;
}

var x=d3.scale.ordinal()
   
.domain(inp_domain.concat(out_domain))
   
.range(out_range)

Is this what you are trying to do?
--
Steve


On Friday, August 11, 2017 at 11:15:39 AM UTC-4, Sumit Bansal wrote:
Any suggestion on this..please tell me is it possible in PCP chart?

Sumit Bansal

unread,
Nov 12, 2017, 4:24:43 AM11/12/17
to d3...@googlegroups.com, Japhy Bartlett, Kai Chang
Hi Kai/Japhy,

I am trying to integrate d3 pcp with my angular 4 application but getting error can not find module 'dimension'.Could you please suggest me where i am doing wrong.Below is my script which i am using.The same method(dawChart) is working when i am running in the html.

import { Component, OnInit,ViewEncapsulation } from '@angular/core';
import * as d3 from 'd3';

@Component({
moduleId: module.id,
  selector: 'app-pcp',
  template:`<div class="col s12" id='test'></div>`,
  styleUrls: ['./pcpstyle.css'],
  encapsulation: ViewEncapsulation.None
  
})
export class PCPComponent implements OnInit {

  private background:any;
 private foreground:any;
// line:any;

  constructor() { }

  ngOnInit() {
  this.drawChart();
   }

 drawChart() {
  let m = [100, 10, 10, 60],
  w = 1000 - m[1] - m[3],
  h = 300 - m[0] - m[2];

//var x = d3.scalePoint().range([0, w]).padding(.1),
//var x= d3.scaleBand().rangeRound([0, w]),
   let x = d3.scaleBand().rangeRound([0, w]).padding(1),
  y = {},
  dragging = {};

let line = d3.line();
 //  this.axis = d3.axisLeft();
 //alert(line)

let svg = d3.select("#test").append("svg:svg")
  .attr("width", w + m[1] + m[3])
  .attr("height", h + m[0] + m[2])
  .append("svg:g")
  .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

let data = [{
  
  "A": 200, 
"B": 3000, 
"C": 2300, 
"D": 4320, 
"E": 1230, 
"F": 7400, 
"G": 1431, 
"H": 1400, 
"I": 4300, 
"J": 6750
}, {
  
 "A": 1002, 
"B": 2003, 
"C": 2773, 
"D": 3432, 
"E": 1673, 
"F": 740, 
"G": 1231, 
"H": 1900, 
"I": 3000, 
"J": 675
}];

//let dimensions
// Extract the list of dimensions and create a scale for each.
 x.domain(dimensions = d3.keys(data[0]).filter(function(d) {
  if (d === "name") return false;
  if (d === "Plant" || d === "Chemical" || d === "Pathway" || d === "Gene" || d === "Disease") {
    y[d] = d3.scaleOrdinal()
      .domain(data.map(function(p) {
        return p[d];
      }))
      .range([h, 0]);
  } else {
    y[d] = d3.scaleLinear()
   y[d] = d3.scalePoint().domain(data.map(function(p) {
  return p[d];
})).range([h, 0]);
  }
  return true;
}));


// Add grey background lines for context.
this.background = svg.append("svg:g")
  .attr("class", "background")
  .selectAll("path")
  .data(data)
  .enter().append("svg:path")
  .attr("d", path);

// Add blue foreground lines for focus.
this.foreground = svg.append("svg:g")
  .attr("class", "foreground")
  .selectAll("path")
  .data(data)
  .enter().append("svg:path")
  .attr("d", path);

// Add a group element for each dimension.
let g = svg.selectAll(".dimension")
  .data(dimensions)
  .enter().append("svg:g")
  .attr("class", "dimension")
  .attr("transform", function(d) {
    return "translate(" + x(d) + ")";
  })
 
//alert(this.dimensions)
// Add an axis and title.
g.append("svg:g")
  .attr("class", "axis")
  .each(function(d) {
    d3.select(this).call(d3.axisLeft(y[d]));
  })
  .append("svg:text")
  .attr("text-anchor", "middle")
  .attr("y", -50)
  .attr("x",-10)
  .text(String);
  //.text(function(d) { return d; });

function position(d) {
  var v = dragging[d];
  return v == null ? x(d) : v;
}

function transition(g) {
  return g.transition().duration(500);
}

// Returns the path for a given data point.
function path(d) {
      return line(dimensions.map(function(p) {
      return [position(p), y[p](d[p])];
  }));

}

    }


}
Thanks & Regards
Sumit Bansal
BI Developer

Sumit Bansal

unread,
Nov 12, 2017, 12:51:01 PM11/12/17
to d3...@googlegroups.com, Japhy Bartlett, Kai Chang
Anyone suggest me where i am doing mistake to integrate d3 pcp chart with angular 4

Thanks & Regards
Sumit Bansal
BI Developer

Reply all
Reply to author
Forward
0 new messages