Skip to content

Identical axis labels removed in heatmap (need new category mode or axis type) #1516

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

Closed
lvdmaaten opened this issue Mar 24, 2017 · 18 comments
Closed
Labels
feature something new

Comments

@lvdmaaten
Copy link

Consider the following plot (as JSON):

{'layout': {u'margin': {u'r': 60, u'b': 60, u't': 60, u'l': 60}}, 'data': [{u'zmax': 1.0054633722786, u'colorscale': u'Viridis', u'zmin': -1.4410649905944, u'y': [u'a', u'b', u'a'], u'x': [u'd', u'd'], u'z': [[-0.50443826056116, -0.55385051210768], [-0.52712315391279, 1.0054633722786], [-1.4410649905944, 0.50761805140936]], u'type': u'heatmap'}]}

Note how there are identical axis labels in the JSON on the x and y-axis. This leads to an incorrect rendering:
screen shot 2017-03-24 at 4 09 04 pm

It appears the identical labels get removed automatically.

Additional context here and here.

@etpinard
Copy link
Contributor

Thanks for bringing this up!

I agree that the expected behavior for heatmap traces would have two columns with label 'd'. Unfortunately, this somewhat contradicts our category mapping where each unique category gets assigned one numeric coordinate. For example:

Plotly.plot('graph', [{
  x: ['a', 'b', 'a'],
  y: [1, 2, 3]
}])

generates

image

So I'm thinking maybe we could add a new attribute (e.g. categorymode: 'unique' | 'index') or a new axis type (e.g. xaxis.type: 'category-by-index' or something less verbose 😄 ) to make the distinction between the desired heatmap behavior and the current (scatter) behavior.


In the meantime, you can make those categories unique by inputting x: ['d', 'd ']:

image

@lvdmaaten
Copy link
Author

Ah, interesting work-around. I didn't realize it would remove the whitespaces when rendering. Thanks!

@cpsievert
Copy link

cpsievert commented Mar 24, 2017

Another workaround would be to use ticktext/tickvals:

http://codepen.io/cpsievert/pen/bqKJoe

@alexcjohnson
Copy link
Collaborator

I didn't realize it would remove the whitespaces when rendering.

I don't think it does remove them... look closely at the positioning, the second d is a little left of center. So I guess you could use ' d ' to make it symmetric (though adding spaces will probably muck up hover text), or use a zero-width space, but @cpsievert has a much better solution.

Unless this problem generalizes substantially to the point that ticktext/tickvals becomes unworkable, I wouldn't be in favor of making a new axis mode or type for it. Too many strange follow-on effects if data<->position isn't a 1:1 mapping. Like what does it mean if you try to position something else on the axis at 'd'? Or if you have a second heatmap, or some scatter data, that you want to plot on top of this? What position value do we report for click or hover events?

@etpinard etpinard changed the title Identical axis labels removed in heatmap Identical axis labels removed in heatmap (need new category mode) May 17, 2017
@etpinard etpinard changed the title Identical axis labels removed in heatmap (need new category mode) Identical axis labels removed in heatmap (need new category mode or axis type) May 17, 2017
@etpinard etpinard added the feature something new label May 17, 2017
@etpinard
Copy link
Contributor

To solve this issue, we could:

  • add an axis type value e.g. xaxis.type: 'category-nonunique', or
  • add an axis boolean attribute e.g. xaxis.uniquecategories: true || false
  • add a more general enumerated axis attribute e.g. xaxis.categorymode with possible values: 'unique' (the default) and 'duplicate' and potentially others down the road.

I can't think of another possible categorymode value, so I'd vote for adding a uniquecategories boolean. Thoughts?

@alexcjohnson
Copy link
Collaborator

I still think it’s a bad idea to have an axis that doesn’t map data one-to-one to position. What if you want to tile several heatmaps? What if you want a scatter or bar trace on the same axis? What if you bind a click or hover handler, how will you know on which copy of the item the event really happened? All of these situations would be easy to manage with ticktext / tickvals but would be ambiguous, and probably in some cases impossible to get right, with a many-to-one mapping.

Perhaps we could consider a mode within the heatmap trace (and potentially in other trace types too) that automatically maps x and y string arrays to tickvals and ticktext on the appropriate axis (with an optional x0 and dx etc so tickvals need not be [0, 1, 2...])? That seems like it would yield the same visual result without the ambiguities.

@jdepons
Copy link

jdepons commented Jun 2, 2017

I am having the same issue with a bar chart and see this as a major oversight. I just want to print a simple bar chart for a group of clinical measurements. Some animals were measured multiple times and some were not so traces will not work correctly. I should be able to display multiple values with the same x axis label without having 10 white spaces surrounding the 10th measurement for a specific label.

As an example my x axis may look like the following Rat, Rat, Rat, Rat, Rat, Chicken, Squirrel, Chicken.

Any ideas other than the white space fix above are appreciated.

@zbjornson
Copy link

@jdepons the method suggested by @cpsievert in #1516 (comment) works better than the white space hack, although it's a little bit tedious.

@SaucePan1
Copy link

Is there any official solution to this? Im generating the x_labels automatically in-app and the whitespace solution is definetely not an option!

Any recomendations?

@Dantalion
Copy link

This is actually severe. The priority to fix this should be highest.
It would be best if we had a data attribute to set x and y values to unique or not.
The functionality without that is highly impacted and holds the potential back at the moment.

@alexcjohnson
Copy link
Collaborator

@SaucePan1 @Dantalion have you tried the tickvals/ticktext suggestion @cpsievert mentioned and demonstrated in http://codepen.io/cpsievert/pen/bqKJoe ? I feel pretty strongly that we cannot make an axis type where data do not map 1:1 to position, it just causes unending headaches when you try to do anything beyond a single trace.

One current limitation I see of this approach is that we always show every tick when using tickvals/ticktext. That's a more general problem that has its own issue #1812

@RBVI
Copy link

RBVI commented Jan 3, 2019

Just to add another voice to this. I've got a heatmap where I'm showing the results of differential expression experiment with 6 clusters. I'm showing the top 10 and bottom 10 genes for each cluster, so the Y: genes, X: clusters. In many cases, the genes overlap. I'll certainly try the ticktext approach, but I might have 100's of genes, so the ability to automatically choose the tick interval is extremely nice...

@LeUser111
Copy link

Hey guys!

A little late to the party - however we've just run into this exact issue with the stacked-bar-chart. We are trying to display the properties of tools that are used in a process. Of course tools can be used multiple times.

Thanks to the solution of @cpsievert it works like a charm! (Thank you very much!).

Here's a simple example where I just mixed the example code for stacked-bar-charts with the solution proposed by @cpsievert :

var trace1 = { 
  y: [20, 14, 23],
  name: 'Primary Processing',
  type: 'bar'
};

var trace2 = { 
  y: [12, 18, 29],
  name: 'Secondary Processing',
  type: 'bar'
};

var data = [trace1, trace2];
var layout = {barmode: 'stack', "xaxis": {
    "ticktext": ["tool1", "tool2", "tool1"], "tickvals": [0, 1, 2]
  }};

Plotly.newPlot("graph", data, layout);

The reason why I'm posting this:

Would you kindly consider adding this information to the official documentation? (e.g. here)

As this issue shows it's not an uncommon requirement and since there is a practical solution for this it would be great if it was described close to the charts themselves - maybe as an additional example (e.g. "bars with multiple occurrences of x-values").

(Sorry if it's already there and I just missed it!)

@RBVI
Copy link

RBVI commented Jan 3, 2020

Help! With the latest plotly.js (1.51.3) the tickvals/ticktext solution no longer works. If you look at http://codepen.io/cpsievert/pen/bqKJoe you can see that only one category is shown. This is broken some important functionality for our pipeline.

@zbjornson
Copy link

zbjornson commented Jan 3, 2020

@RBVI your tickvals and x/y values need to match up:

var dat = [{
  "x": [0,1],
  "y": [2,3],
  "z": [[1,2], [3,4]],
  "type": "heatmap"
}];

Plotly.plot(
  "graph", dat,
  {
    "xaxis": {
      "ticktext": ["a", "a"], "tickvals": [0, 1]
    },
    "yaxis": {
      "ticktext": ["b", "c"], "tickvals": [2, 3]
    }
  }
);

(don't want to sign up for codepen to save that.)

The original version of this solution posted in #1516 (comment) matches by ticktext. I'm not sure why that worked or if it actually worked reliably.

@jackparmer
Copy link
Contributor

This issue has been tagged with NEEDS SPON$OR

A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.

Sponsorship range: $10k-$15k

What Sponsorship includes:

  • Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
  • Tests for this feature
  • Long-term support (continued support of this feature in the latest version of Plotly.js)
  • Documentation at plotly.com/javascript
  • Possibility of integrating this feature with Plotly Graphing Libraries (Python, R, F#, Julia, MATLAB, etc)
  • Possibility of integrating this feature with Dash
  • Feature announcement on community.plotly.com with shout out to Sponsor (or can remain anonymous)
  • Gratification of advancing the world's most downloaded, interactive scientific graphing libraries (>50M downloads across supported languages)

Please include the link to this issue when contacting us to discuss.

@thammegowda
Copy link

thammegowda commented Nov 30, 2021

If we are going to use a spacer workaround, using zero-width-space[1] would save some space on the screen

function pad_unique(arr, pad='\u200b'){
  let mem = new Set()
  for (let idx = 0; idx < arr.length; idx++) {
    let val = arr[idx]
    while (mem.has(val)) {
      val += pad
    }
    mem.add(val)
    arr[idx] = val
  }
  return arr
}
// call this on x and y arrays to make them unique 
pad_unique(x)
pad_unique(y)

demo https://codepen.io/thammegowda/pen/jOGEqrG


-[1] https://unicode-table.com/en/200B/

@gvwilson
Copy link
Contributor

Hi - this issue has been sitting for a while, so as part of our effort to tidy up our public repositories I'm going to close it. If it's still a concern, we'd be grateful if you could open a new issue (with a short reproducible example if appropriate) so that we can add it to our stack. Cheers - @gvwilson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new
Projects
None yet
Development

No branches or pull requests