-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parallel Categories trace type for multi dimensional categorical data #1
Changes from 3 commits
1a8a0f0
406cd24
ee7fd17
3e43ef3
0c9c75f
7b75100
d2c5ae8
a34dafa
71e212b
6797a83
2c14168
c8e3cc9
6a5c20e
22346d0
3680084
2d07f4d
7f90fc1
5e60062
66c90fa
69f6922
f2aa9b9
4eb5317
a897388
98d76ee
66f21fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* Copyright 2012-2017, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
module.exports = require('../src/traces/parcats'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
/** | ||
* Copyright 2012-2017, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var extendFlat = require('../../lib/extend').extendFlat; | ||
var colorAttributes = require('../../components/colorscale/attributes'); | ||
|
||
var scatterAttrs = require('../scatter/attributes'); | ||
var scatterMarkerAttrs = scatterAttrs.marker; | ||
var colorbarAttrs = require('../../components/colorbar/attributes'); | ||
|
||
var marker = extendFlat({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems more like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. I think I started out calling it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 7b75100 |
||
editType: 'calc' | ||
}, colorAttributes('marker', {editType: 'calc'}), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we at some point want to support selections similar to how parcoords works, perhaps we want Viridis here too? How well does this work with continuous color values, does it attempt to sort in color order or does it just keep distinct values together? Also I don't see a mock with a colorbar and in your brushing example it looks like you just use two distinct colors - does it support providing an array of color strings or just numeric values? Actually I'm not sure if Anyway if you're just using color as a marker of selected/deselected it seems a bit hacky to have to create a colorscale with the two colors you want. Perhaps instead (or in addition) we should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A path (thick line) is created for each unique combination of the dimension categories and raw color (the value as specified in The display color of the path is computed based on the path's unique raw color value. If no colorscale is present, then the raw color is the display color (So yes, you can pass an array of color strings to So continuous colorscales are fine as long as the numeric color values are somewhat discrete. If there are 1000 unique values in the color array, then the diagram will sort of degenerate into a bunch of tiny paths. In my own analysis work I've either used a categorical colormap with an integer color array (this is the brushing scenario), or used a continuous colormap and manually discretized the color values. In both cases I would generally stay under 10 discrete colors. The selection metaphor here is interesting. So far I'm just emitting the |
||
{ | ||
showscale: scatterMarkerAttrs.showscale, | ||
colorbar: colorbarAttrs, | ||
shape: { | ||
valType: 'enumerated', | ||
values: ['straight', 'curved'], | ||
dflt: 'curved', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps to match scatter we should have Also, I wonder if the default should be The other visual changes you made vs the existing implementations, which seem to come from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'linear' + 'hspline' with 'linear' as default sounds good to me! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 7b75100 |
||
role: 'info', | ||
editType: 'plot', | ||
description: 'Sets the shape of the paths'}, | ||
}); | ||
|
||
module.exports = { | ||
domain: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... and correespondingly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in d2c5ae8 |
||
x: { | ||
valType: 'info_array', | ||
role: 'info', | ||
items: [ | ||
{valType: 'number', min: 0, max: 1, editType: 'calc'}, | ||
{valType: 'number', min: 0, max: 1, editType: 'calc'} | ||
], | ||
dflt: [0, 1], | ||
editType: 'calc', | ||
description: [ | ||
'Sets the horizontal domain of this `parcats` trace', | ||
'(in plot fraction).' | ||
].join(' ') | ||
}, | ||
y: { | ||
valType: 'info_array', | ||
role: 'info', | ||
items: [ | ||
{valType: 'number', min: 0, max: 1, editType: 'calc'}, | ||
{valType: 'number', min: 0, max: 1, editType: 'calc'} | ||
], | ||
dflt: [0, 1], | ||
editType: 'calc', | ||
description: [ | ||
'Sets the vertical domain of this `parcats` trace', | ||
'(in plot fraction).' | ||
].join(' ') | ||
}, | ||
editType: 'calc' | ||
}, | ||
|
||
tooltip: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be handled by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. I'll work through the comparison with Sankey, but I'm thinking the flaglist would be a combination of 'count' and/or 'probability', or 'none' to disable it all together. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 3680084
Having the |
||
valType: 'boolean', | ||
dflt: true, | ||
role: 'info', | ||
editType: 'plot', | ||
description: 'Shows a tooltip when hover mode is `category` or `color`.' | ||
}, | ||
|
||
hovermode: { | ||
valType: 'enumerated', | ||
values: ['none', 'category', 'color'], | ||
dflt: 'category', | ||
role: 'info', | ||
editType: 'plot', | ||
description: 'Sets the hover mode of the parcats diagram' | ||
}, | ||
bundlecolors: { | ||
valType: 'boolean', | ||
dflt: true, | ||
role: 'info', | ||
editType: 'plot', | ||
description: 'Sort paths so that like colors are bundled together' | ||
}, | ||
sortpaths: { | ||
valType: 'enumerated', | ||
values: ['forward', 'backward'], | ||
dflt: 'forward', | ||
role: 'info', | ||
editType: 'plot', | ||
description: [ | ||
'If `forward` then sort paths based on dimensions from left to right.', | ||
'If `backward` sort based on dimensions from right to left.' | ||
].join(' ') | ||
}, | ||
// labelfont: fontAttrs({ | ||
// editType: 'calc', | ||
// description: 'Sets the font for the `dimension` labels.' | ||
// }), | ||
// | ||
// catfont: fontAttrs({ | ||
// editType: 'calc', | ||
// description: 'Sets the font for the `category` labels.' | ||
// }), | ||
|
||
dimensions: { | ||
_isLinkedToArray: 'dimension', | ||
label: { | ||
valType: 'string', | ||
role: 'info', | ||
editType: 'calc', | ||
description: 'The shown name of the dimension.' | ||
}, | ||
catDisplayInds: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (here and below: lowercase please) I'm thinking I guess There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, this is a nice parallel with Cartesian axes. To summarize:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
valType: 'data_array', | ||
role: 'info', | ||
editType: 'calc', | ||
dflt: [], | ||
description: [ | ||
'' | ||
].join(' ') | ||
}, | ||
catValues: { | ||
valType: 'data_array', | ||
role: 'info', | ||
editType: 'calc', | ||
dflt: [], | ||
description: [ | ||
'' | ||
].join(' ') | ||
}, | ||
catLabels: { | ||
valType: 'data_array', | ||
role: 'info', | ||
editType: 'calc', | ||
dflt: [], | ||
description: [ | ||
'' | ||
].join(' ') | ||
}, | ||
values: { | ||
valType: 'data_array', | ||
role: 'info', | ||
dflt: [], | ||
editType: 'calc', | ||
description: [ | ||
'Dimension values. `values[n]` represents the category value of the `n`th point in the dataset,', | ||
'therefore the `values` vector for all dimensions must be the same (longer vectors', | ||
'will be truncated). Each value must an element of `catValues`.' | ||
].join(' ') | ||
}, | ||
displayInd: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again I'd spell out There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. Do you prefer all lowercase for the internal JavaScript object properties as well? Right now all of these are in camelCase, but I can change that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just use lowercase for attribute names in the figure JSON (data and layout). In the code, camelCase is preferred. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Still need to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
valType: 'integer', | ||
role: 'info', | ||
editType: 'calc', | ||
description: [ | ||
'The display index of dimension, from left to right, zero indexed, defaults to dimension' + | ||
'index.' | ||
].join(' ') | ||
}, | ||
editType: 'calc', | ||
description: 'The dimensions (variables) of the parallel categories diagram.' | ||
}, | ||
|
||
marker: marker, | ||
counts: { | ||
valType: 'number', | ||
min: 0, | ||
dflt: 1, | ||
arrayOk: true, | ||
role: 'info', | ||
editType: 'calc', | ||
description: [ | ||
'The number of observations represented by each state. Defaults to 1 so that each state represents ' + | ||
'one observation' | ||
] | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* Copyright 2012-2017, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
var getModuleCalcData = require('../../plots/get_data').getModuleCalcData; | ||
var parcatsPlot = require('./plot'); | ||
|
||
var PARCATS = 'parcats'; | ||
exports.name = PARCATS; | ||
|
||
exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { | ||
|
||
var cdModuleAndOthers = getModuleCalcData(gd.calcdata, PARCATS); | ||
|
||
if(cdModuleAndOthers.length) { | ||
var calcData = cdModuleAndOthers[0]; | ||
parcatsPlot(gd, calcData, transitionOpts, makeOnCompleteCallback); | ||
} | ||
}; | ||
|
||
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) { | ||
var hadTable = (oldFullLayout._has && oldFullLayout._has('parcats')); | ||
var hasTable = (newFullLayout._has && newFullLayout._has('parcats')); | ||
|
||
if(hadTable && !hasTable) { | ||
oldFullLayout._paperdiv.selectAll('.parcats').remove(); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting... can you say more? Is this just an extension of
loneHover
to support multiple labels or does it add something else? I only see one label at a time when I play with parcats.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At one point I had added a mode that would display a separate tooltip for each color of the category you hover over. It got pretty unwieldy and I reverted back to
loneHover
. I'll remove thiscustomHover
function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unwieldy in the code or on screen? We have some upcoming
sankey
enhancements that may call for a very similar multi-label hover effect, though the details still need to be worked out. But if the code is clean and it was just not looking good in practice, it still may be worth keeping this around.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the screen. I think the implementation worked well enough. I'll look over it again and write a better explanation of what it does 🙂