syntagmatic / parallel-coordinates

A d3-based parallel coordinates plot in canvas. This library is no longer actively developed.
http://syntagmatic.github.com/parallel-coordinates/
Other
510 stars 211 forks source link

Proper use of new dimension objects? #283

Closed jmgelman closed 8 years ago

jmgelman commented 8 years ago

In the older versions of this library, I would pass dimensions into the initial configuration:

axesIds = {"0":"Cost", "1":"Risk", "2":"Failure"} 
parcoords = d3.parcoords({
        dimensions: axesIds
    })("#plot")

This worked fine. But since this is now deprecated, I'm trying to update my code to work with the new dimension objects that were added this past Fall.

I've tried...

var new_dimensions = {};
for (var key in axesIds) {
    if (axesIds.hasOwnProperty(key)) {
        new_dimensions[key] = {"title": axesIds[key]}
        }
}

This gives me new_dimensions = {"0": {"title": "Cost"}, "1": {"title: "Risk"}, "2": {"title": "Failure"}}, which appears to follow the new format described in the documentation.

I then try to set these using parcoords.dimensions(new_dimensions);. But this does not work. Oddly enough, my SlickGrid containing the data works with this, but not the parallel coordinate plot itself.

I end up getting the error TypeError: undefined is not an object (evaluating '__.dimensions[d].yscale.domain'). And again, this did not occur under the old library...so not sure what might be happening here.

Any thoughts? Am I missing something? Thanks for any input!

BroHammie commented 8 years ago

Could you post a subset of your data? I ultimately want to know if you are passing csv or an array of arrays. Also, in your dimension config for older versions of the library I think that doesn't do anything as you are passing an object not an array and we will call detectDimensions and overwrite that. I was looking at ab3a6634d74dfa574936cc5cd0cd3ab2dbb6b21c when I came to that conclusion.

jmgelman commented 8 years ago

Here's example data for two of the variables:

var data = [[29.590009,60.177936],[29.673377,57.251534],[29.388802,64.022217],[30.528795,53.93295],[29.779732,56.872213],[29.057494,72.864019],[31.351729,52.475999],[30.470619,54.071297],[29.49173,63.068659],[30.05493,55.35044],[29.1982,68.566168],[30.288034,54.353399],[30.881802,53.224194],[31.078451,52.731874],[29.877289,56.338552],[29.29382,66.610002],[30.656034,53.545763],[30.97185,53.026824],[30.176387,54.757653],[28.999523,83.060264],[29.926222,55.763012],[32.032619,62.508834],[31.484606,63.856069],[31.506438,63.798567],[28.193158,71.57869],[29.811863,66.242523],[30.011395,65.695077],[27.815274,83.72378],[32.641585,61.591817],[28.297111,69.58995],[28.523924,68.952202],[29.592817,66.398465],[29.973849,66.165299],[29.047955,67.064318],[28.82124,68.47718],[31.629077,63.537776],[30.801231,64.757945],[31.121343,64.636903],[30.286868,64.886637],[28.086475,72.745577],[27.969515,74.876335],[31.954519,63.156435],[32.117239,62.059468],[32.268857,62.461819],[30.242973,65.373388],[28.084188,71.565593],[30.962595,64.14243],[32.435487,61.904271],[27.799854,86.117622],[30.791509,65.121476],[29.592817,66.398465],[27.992391,77.118263],[32.641585,61.591817],[28.297111,69.58995],[28.523924,68.952202],[27.891228,81.934874],[29.986234,65.63374],[28.883943,66.993853],[32.095141,63.187948],[31.740994,64.064571],[31.874726,63.837244],[31.981758,63.472208],[32.39788,62.151026],[32.389828,62.205169],[30.990827,64.386861],[32.649208,61.556714],[31.29748,64.022757],[28.909519,68.370442],[30.364243,65.340371],[29.305499,66.238719],[27.936935,74.907428],[28.18561,72.212663],[28.077677,74.230763],[28.297111,69.58995],[28.523924,68.952202],[28.896213,68.584615],[29.047955,67.064318],[29.973849,66.165299],[27.871139,79.327463],[30.21196,65.759577],[29.252705,66.897265],[27.757438,84.938348],[32.167237,62.869006],[31.132374,64.268011],[32.442618,61.897362],[30.78311,64.777677],[31.775686,63.198377]] Under the old version of the library, I would instantiate things like this:

parcoords = d3.parcoords({
    dimensionTitles: axesIds
})("#plot")
    .height($('.ui-layout-center').height())
    .margin({
        top: 10,
        left: 0,
        right: 0,
        bottom: 20
    })
    .mode('queue')
    .data(data)
    .render()
    .shadows()
    .reorderable()
    .brushMode("1D-axes");

Where axesIds might be something like axesIds = {"0":"Cost", "1":"Risk"}.

With the new version, if I remove dimensionTitles from the config, and instead run parcoords.detectDimensions(); after initialization, the polylines do appear (but the dimension titles do not...the keys of the axesIds object appear as the dimension titles, not the values).

If I try to set the dimension titles using parcoords.dimensions(axesIds), the polylines do not appear.

BroHammie commented 8 years ago

Looks like your dimension syntax was a bit off. Try parcoords.dimensions({"0": {title: "Cost"}, "1": {title: "Risk"}}) to set the dimension properties for the data.

jmgelman commented 8 years ago

Thanks. Unfortunately, I tried that and I still get the same error: TypeError: undefined is not a constructor (evaluating '__.dimensions[p.key].yscale(d[p.key])').

Here's how I set the dimensions:

var dimensions = {"0": {title: 'Cost'}, "1": {title: 'Risk'}};
parcoords.dimensions(dimensions);

And just for confirmation, when I type console.log(JSON.stringify(parcoords.dimensions())), here's what I get back:

{
"0":{"title":"Cost","orient":"left","ticks":5,"innerTickSize":6,"outerTickSize":0,"tickPadding":3,"type":"number","index":0},
"1":{"title":"Risk","orient":"left","ticks":5,"innerTickSize":6,"outerTickSize":0,"tickPadding":3,"type":"number","index":1}
}"

Do I need to have a yscale property?

BroHammie commented 8 years ago

I wonder if its the order of operations you are doing on parcoords. Take a look at: http://bl.ocks.org/mcwillso/0f4b52de0d68f122c325

jmgelman commented 8 years ago

Yep, it seems like the order made a difference. Here's what I tried that resolved the error I was getting:

// Convert old axesIds into new dimensions object of objects
var axesIds = {"0":"Cost", "1":"Risk", "2":"Failure"} 
var new_dimensions = {};
    for (var key in axesIds) {
        if (axesIds.hasOwnProperty(key)) {
            new_dimensions[key] = {"title": axesIds[key]}
        }
    }

// Instantiate the library
    parcoords = d3.parcoords()("#plot");

// Configure attributes
    parcoords
        .height($('.ui-layout-center').height())
        .margin({
          top: 10,
          left: 0,
          right: 0,
          bottom: 20
        })
        .alpha(0.4)
        .dimensions(new_dimensions)
        .data(data)
        .hideAxis(["id"])
        .mode('queue')
        .render()
        .shadows()
        .reorderable()

After doing this, I no longer got the TypeError: undefined is not an object (evaluating '__.dimensions[d].yscale.domain') anymore.

It was a little odd that everything worked fine under the old version. But perhaps I was doing things in the wrong order in the old version, and it just happened to work by chance.

Thanks so much for the help!!

FredericS2018 commented 7 years ago

Apologies for commenting to a closed thread, but I'm having the same issue and have checked my parcoords order of operations & my dimensions object of objects, and cannot figure it out.

var dimensions = {
    "bookingDate": {type: "Date"}, 
    "checkinDate" : {type: "Date"}, 
    "Length of Stay": {type: "number"}, 
    "totalRate": {type: "number"}, 
    "avgNightlyRate": {type: "number"}};
    parcoords
        .alpha(0.4)
        .dimensions(dimensions)
        .data(sales)
        // progressive rendering
        .mode("queue")
        .render()
        .shadows()
        .createAxes()
        .composite("darker")
        .reorderable()
        .brushMode("1D-axes");  // enable brushing

console.log(dimensions);

gives me Object { unit: Object, bookingDate: Object, checkinDate: Object, Length of Stay: Object, totalRate: Object, avgNightlyRate: Object }

which looks correct as far as I can tell