Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 2k
Axis category ordering - adds feature #189#419
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
94e0133e36d163df9ee94eb499d4c7fb84b02ed85853f12da14908d0b21ebb2File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /** | ||
| * Copyright 2012-2016, 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 layoutAttributes = require('./layout_attributes'); | ||
| module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, coerce) { | ||
| if(containerIn.type !== 'category') return; | ||
| var validCategories = layoutAttributes.categoryorder.values; | ||
| var propercategoryarray = Array.isArray(containerIn.categoryarray) && containerIn.categoryarray.length > 0; | ||
| if(validCategories.indexOf(containerIn.categoryorder) === -1 && propercategoryarray) { | ||
| // when unspecified or invalid, use the default, unless categoryarray implies 'array' | ||
| coerce('categoryorder', 'array'); // promote to 'array' | ||
| } else if(containerIn.categoryorder === 'array' && !propercategoryarray) { | ||
| // when mode is 'array' but no list is given, revert to default | ||
| containerIn.categoryorder = 'trace'; // revert to default | ||
| coerce('categoryorder'); | ||
| } else { | ||
| // otherwise use the supplied mode, or the default one if unsupplied or invalid | ||
| coerce('categoryorder'); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /** | ||
| * Copyright 2012-2016, 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 d3 = require('d3'); | ||
| // flattenUniqueSort :: String -> Function -> [[String]] -> [String] | ||
| function flattenUniqueSort(axisLetter, sortFunction, data) { | ||
| // Bisection based insertion sort of distinct values for logarithmic time complexity. | ||
| // Can't use a hashmap, which is O(1), because ES5 maps coerce keys to strings. If it ever becomes a bottleneck, | ||
| // code can be separated: a hashmap (JS object) based version if all values encountered are strings; and | ||
| // downgrading to this O(log(n)) array on the first encounter of a non-string value. | ||
Contributor 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. Great comment. Thanks! | ||
| var categoryArray = []; | ||
| var traceLines = data.map(function(d) {return d[axisLetter];}); | ||
| var i, j, tracePoints, category, insertionIndex; | ||
| var bisector = d3.bisector(sortFunction).left; | ||
| for(i = 0; i < traceLines.length; i++) { | ||
| tracePoints = traceLines[i]; | ||
| for(j = 0; j < tracePoints.length; j++) { | ||
| category = tracePoints[j]; | ||
| // skip loop: ignore null and undefined categories | ||
| if(category === null || category === undefined) continue; | ||
| insertionIndex = bisector(categoryArray, category); | ||
| // skip loop on already encountered values | ||
| if(insertionIndex < categoryArray.length - 1 && categoryArray[insertionIndex] === category) continue; | ||
| // insert value | ||
| categoryArray.splice(insertionIndex, 0, category); | ||
| } | ||
| } | ||
| return categoryArray; | ||
| } | ||
| /** | ||
| * This pure function returns the ordered categories for specified axisLetter, categoryorder, categoryarray and data. | ||
| * | ||
| * If categoryorder is 'array', the result is a fresh copy of categoryarray, or if unspecified, an empty array. | ||
| * | ||
| * If categoryorder is 'category ascending' or 'category descending', the result is an array of ascending or descending | ||
| * order of the unique categories encountered in the data for specified axisLetter. | ||
| * | ||
| * See cartesian/layout_attributes.js for the definition of categoryorder and categoryarray | ||
| * | ||
| */ | ||
| // orderedCategories :: String -> String -> [String] -> [[String]] -> [String] | ||
| module.exports = function orderedCategories(axisLetter, categoryorder, categoryarray, data) { | ||
| switch(categoryorder) { | ||
| case 'array': return Array.isArray(categoryarray) ? categoryarray.slice() : []; | ||
| case 'category ascending': return flattenUniqueSort(axisLetter, d3.ascending, data); | ||
| case 'category descending': return flattenUniqueSort(axisLetter, d3.descending, data); | ||
| case 'trace': return []; | ||
| default: return []; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "data": [{ | ||
| "x": ["c","a","e","b","d"], | ||
| "y": [15,11,12,13,14]} | ||
| ], | ||
| "layout": { | ||
| "xaxis": { | ||
| "title": "category ascending", | ||
| "type": "category", | ||
| "categoryorder": "category ascending", | ||
| "categoryarray": ["y","b","x","a","d","z","e","c", "q", "k"] | ||
| }} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| { | ||
| "data": [ | ||
| { | ||
| "x": [ | ||
| 1, | ||
| 2, | ||
| null, | ||
| 4, | ||
| 5 | ||
| ], | ||
| "y": [ | ||
| 1, | ||
| 2, | ||
| 3, | ||
| 4, | ||
| 5 | ||
| ], | ||
| "connectgaps": false, | ||
| "uid": "8ac13a" | ||
| } | ||
| ], | ||
| "layout": { | ||
| "title": "categoryarray", | ||
| "xaxis": { | ||
| "type": "category", | ||
| "range": [ | ||
| -0.18336673346693386, | ||
| 3.1833667334669338 | ||
| ], | ||
| "autorange": true, | ||
| "categoryorder": "array", | ||
| "categoryarray": [2,4,5,1] | ||
| }, | ||
| "yaxis": { | ||
| "type": "linear", | ||
| "range": [ | ||
| 0.7070063694267517, | ||
| 5.292993630573249 | ||
| ], | ||
| "autorange": true | ||
| }, | ||
| "height": 450, | ||
| "width": 1000, | ||
| "autosize": true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "data": [{ | ||
| "x": ["c","a","e","b","d"], | ||
| "y": [15,11,12,13,14]} | ||
| ], | ||
| "layout": { | ||
| "title": "categoryarray with truncated tails (y, q, k not plotted)", | ||
| "xaxis": { | ||
| "type": "category", | ||
| "categoryorder": "array", | ||
| "categoryarray": ["y","b","x","a","d","z","e","c", "q", "k"] | ||
| }} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "data": [ | ||
| { | ||
| "x": [ | ||
| 5, | ||
| 1, | ||
| 3, | ||
| 2, | ||
| 4 | ||
| ], | ||
| "y": [ | ||
| 1, | ||
| 2, | ||
| 3, | ||
| 4, | ||
| 5 | ||
| ], | ||
| "connectgaps": false, | ||
| "uid": "8ac13a" | ||
| } | ||
| ], | ||
| "layout": { | ||
| "title": "category descending", | ||
| "xaxis": { | ||
| "type": "category", | ||
| "categoryorder": "category descending", | ||
| "categoryarray": [2,4,5,1] | ||
| }, | ||
| "yaxis": { | ||
| "type": "linear", | ||
| "range": [ | ||
| 0.7070063694267517, | ||
| 5.292993630573249 | ||
| ], | ||
| "autorange": true | ||
| }, | ||
| "height": 450, | ||
| "width": 1000, | ||
| "autosize": true | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "data": [ | ||
| { | ||
| "x": [ | ||
| 5, | ||
| null, | ||
| 3, | ||
| 2, | ||
| 4 | ||
| ], | ||
| "y": [ | ||
| 1, | ||
| 2, | ||
| 3, | ||
| null, | ||
| 5 | ||
| ], | ||
| "connectgaps": false, | ||
| "uid": "8ac13a" | ||
| } | ||
| ], | ||
| "layout": { | ||
| "title": "category descending", | ||
| "xaxis": { | ||
| "type": "category", | ||
| "categoryorder": "category descending", | ||
| "categoryarray": [2,4,5,1] | ||
| }, | ||
| "yaxis": { | ||
| "type": "linear", | ||
| "range": [ | ||
| 0.7070063694267517, | ||
| 5.292993630573249 | ||
| ], | ||
| "autorange": true | ||
| }, | ||
| "height": 450, | ||
| "width": 1000, | ||
| "autosize": true | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
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.
@cldougl@chriddyp does ⏫ look ok to you?
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.
👍 for me
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.
Maybe we should include truncated versions of those values as well e.g.
'cat asc','cat des'?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.
I'd suggest avoiding muti-word strings. Maybe just
ascendinganddescending?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.
for me just
ascending`descendingdoesn't have a clear relation to _alpha_numeric ordering. I definitely understand the aversion to multi-word strings, but I would vote for more description thanascending\descending`plus if
'value ascending'\'value descending'are going to be implemented eventuallyThere 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.
but I gotta say,
catergoryorderdoesn't sound bad at all.Uh oh!
There was an error while loading. Please reload this page.
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.
You can add them as labels to the axes (currently described as
ticktext) or use them as data (currently described asx). Given that theaxis.typeiscategory, one might think thatcategoriesis how you would add data to that axis.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.
and maybe
categoryarrayinstead ofcategorylist@chriddyp we're already using the value
'array'fortickmode.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.
More like: categories are added automatically. Categories have no effect on
ticktext.Uh oh!
There was an error while loading. Please reload this page.
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.
Great point 👍
That's why I'm thinking that
categoryordermight be the winner here.