Sorting Ordinal Values on Parallel Coordinates axis

325 views
Skip to first unread message

Rachel Rinaldi

unread,
Aug 24, 2015, 1:57:59 PM8/24/15
to d3-js
I have implemented a parallel coordinates D3 visualization with ordinal and linear scales.  I reused a function that determines whether a value is quantitative or ordinal and then builds the axis.  I know on the ordinal scales, the values on the axis are printed as they are encountered in the JSON. Is there any way in D3 to explicitly state the order of the ordinal values on my "Product" (on a particular) axis?  I'm not sure where that logic would fit into the below - a separate "else if" possibly?

dimensions.forEach(function(d){
var vals = data.map(function(p) {return p[d];});
if (vals.every(quant_p)){
y[d] = d3.scale.linear()
.domain(d3.extent(vals.map(function(p){ return +p})))
.range([h,0]);}
else{
y[d] = d3.scale.ordinal()
.domain(vals.filter(function(v,i) {return vals.indexOf(v) == i;}))
.rangePoints([h,0],1);}
})

 

Kai Chang

unread,
Aug 24, 2015, 4:59:28 PM8/24/15
to d3...@googlegroups.com
Sort the list of values passed into the ordinal scale's domain().

Also check out these bl.ocks for examples of sorted ordinal dimensions with parallel coordinates. These contain a pattern for explicitly setting the scale per dimension. The last 3 also let you set the axis per-dimension, in case you need to do special formatting.

http://bl.ocks.org/mbostock/3709000
http://bl.ocks.org/syntagmatic/8ff691209324f1814257
http://bl.ocks.org/syntagmatic/94be812f8b410ae29ee2
http://bl.ocks.org/syntagmatic/0d1635533f6fb5ac4da3

--
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+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rachel Rinaldi

unread,
Aug 25, 2015, 11:21:40 AM8/25/15
to d3-js, kai.s...@gmail.com
Appreciate the examples!  I guess I'm at a loss when it comes to defining the ordinal values for one of my axis.  Specifically, I have a 'Products' axis and I'm not sure how to explicitly store the ordinal values for that like the examples do once I'm in my dimensions.ForEach function (if it's even supposed to happen in there).

Kai Chang

unread,
Aug 25, 2015, 6:34:41 PM8/25/15
to Rachel Rinaldi, d3-js
The key part is the domain of the ordinal scale, which is in the "else" branch of inside that loop you pasted. If the Products axis is the only ordinal dimension, you can explicitly set the order there.

Simple example:

y[d] = d3.scale.ordinal()
  .domain(["Oranges", "Peaches", "Grapes"])

Rachel Rinaldi

unread,
Aug 25, 2015, 7:41:36 PM8/25/15
to d3-js, rachel....@gmail.com, kai.s...@gmail.com
Thanks so much for the reply!  I was on the path you stated and I tried multiple if/else logic blocks there but I'm not sure how to explicitly state which ordinal axis I need (I have more than 1).  I figure it's something like "if (<something> = "Products") { code you pasted below }" but not sure what I should be referring to in the data map.  I'm running out of things to console.log haha

Rachel Rinaldi

unread,
Aug 26, 2015, 9:14:11 AM8/26/15
to d3-js, rachel....@gmail.com, kai.s...@gmail.com
Just to show what I've tried...

dimensions.forEach(function(d){
var vals = data.map(function(p) {return p[d];});

if (vals.every(quant_p)){
y[d] = d3.scale.linear()
.domain(d3.extent(vals.map(function(p){ return +p})))
.range([h,0]);}
else if (d="Product"){

y[d] = d3.scale.ordinal()
.domain(["Apple","Orange","Grape"])
.rangePoints([h,0],1);}

else{

y[d] = d3.scale.ordinal()
.domain(vals.filter(function(v,i) {return vals.indexOf(v) == i;}))
.rangePoints([h,0],1);}
})

I then get an error when it tries to draw the lines:

"Uncaught TypeError: y[p] is not a function"

Which has this code:
ctx.lineTo(x(p),y[p](d[p]));

Rachel Rinaldi

unread,
Aug 26, 2015, 2:49:30 PM8/26/15
to d3-js, kai.s...@gmail.com
Ah, the double =.  Posted my solution that works below:

//Create a scale for each (Linear vs Ordinal)
dimensions.forEach(function(d){
var vals = data.map(function(p) {return p[d];});

if (vals.every(quant_p)){
y[d] = d3.scale.linear()
.domain(d3.extent(vals.map(function(p){ return +p})))
.range([h,0]);}


else if (d=="Product"){

y[d] = d3.scale.ordinal()
.domain(["Apple","Orange","Grape"])
.rangePoints([h,0],1);}

else {
y[d] = d3.scale.ordinal()
.domain(vals.filter(function(v,i) {return vals.indexOf(v) == i;}))
.rangePoints([h,0],1);}
})

Kai Chang

unread,
Aug 26, 2015, 5:25:08 PM8/26/15
to d3...@googlegroups.com
If you're able to post your work publicly, I would love to see what you came up with!

Rachel Rinaldi

unread,
Aug 27, 2015, 3:35:34 PM8/27/15
to d3-js, kai.s...@gmail.com
I'll definitely see what I'm allowed to share and hopefully post an entire working example.  Working on a different issue now, would like a tool-tip or text box to show what is being select by the brush.  Have this working for the linear axis but the ordinal axis are just returning the data points and not the value selected.  I've looked at a couple resources online and can't seem to call my scale since it is different per the dimension value.  Any idea how I would call the scales above with the code I added below? 

 // store brush data points
var brushArray = extents[0];

// if brush exists, store their min and max values
if (brushArray != null){
var minBrush= brushArray['0'];
var maxBrush=brushArray['1'];
}

// if min and max exist, print their values along with the brush
if (minBrush != null && maxBrush != null ){
console.log("min:" + minBrush + "max: " + maxBrush);
Reply all
Reply to author
Forward
0 new messages