biocore / emperor

Emperor a tool for the analysis and visualization of large microbial ecology datasets
http://biocore.github.io/emperor/
Other
52 stars 50 forks source link

Inconsistent sorting for mixed-type (numeric + non-numeric) fields in the color legend #761

Closed fedarko closed 4 years ago

fedarko commented 4 years ago

When a field contains mixed numeric and non-numeric values, the non-numeric values are correctly assigned colors from the "start" of the color palette: but they aren't placed in the legend before the numeric values. This results in the legend looking off (the red and blue colors are the first two in the Classic QIIME Colors map, but they aren't listed in this order in the screenshot below).

image

For reference, I'm attaching the sample metadata I used to generate the above visualization (it's just the Q2 moving pictures data), and the QZV file for the visualization (renamed to use .zip so that GitHub doesn't explode):

sample_metadata_badscaling_test.txt

unweighted_unifrac_emperor.zip

fedarko commented 4 years ago

OK, I think I've isolated the source of the problem. It traces back to DecompositionView.setCategory(), which adds items to a dataView in what seems like an arbitrary order:

https://github.com/biocore/emperor/blob/023b6ecb761c31cd7f60a2e38e418d71199eb4e1/emperor/support_files/js/view.js#L769-L794

Flipping things around, so that we sort the attributes' keys first, seems to fix the problem:

DecompositionView.prototype.setCategory = function(attributes,
                                                   setPlottableAttributes,
                                                   category) {
  var scope = this, dataView = [], plottables;

  var fieldValues = util.naturalSort(_.keys(attributes));

  _.each(fieldValues, function(fieldVal, index) {
    /*
     *
     * WARNING: This is mixing attributes of the view with the model ...
     * it's a bit of a gray area though.
     *
     **/
    plottables = scope.decomp.getPlottablesByMetadataCategoryValue(category,
                                                                   fieldVal);
    if (setPlottableAttributes !== null) {
      setPlottableAttributes(scope, attributes[fieldVal], plottables);
    }

    dataView.push({id: index, category: fieldVal, value: attributes[fieldVal],
                   plottables: plottables});
  });
  this.needsUpdate = true;

  return dataView;
};

This results in the legend elements being sorted as expected --

image

@ElDeveloper, I can try to file a PR on this next week (or integrate it into #763), just let me know what you'd prefer. Thanks!

ElDeveloper commented 4 years ago

Thanks for tracking this down @fedarko! I think it would be best to keep this in a separate PR. Changes look good :+1: