Search code examples
javascriptjqueryhtmld3.jsplottable

How to make label in histogram respond to dynamic user input


I have the following working code. It takes the input data and display the histogram based on a threshold.

If you run, the code below, the code is working to the extend that the histogram changes as you drag the sliding bar.

However the label 26 GENES does not change as we drag the threshold from 1.5 to 2. I expect the value change to 30 GENES. These values are encoded in the var input_data especially nof_genes.

How can I make that to take effect?

"use strict";

var histograms,
  thresholds = [];

var input_data = [{
  "threshold": 1.5,
  "histograms": [{
    "sample": "Sample1",
    "nof_genes": 26,
    "values": [{
      "score": 6.7530200000000002,
      "celltype": "Bcells"
    }, {
      "score": 11.432763461538459,
      "celltype": "DendriticCells"
    }, {
      "score": 25.823089615384621,
      "celltype": "Macrophages"
    }, {
      "score": 9.9911211538461551,
      "celltype": "gdTCells"
    }, {
      "score": 7.817228076923076,
      "celltype": "StemCells"
    }, {
      "score": 17.482806923076922,
      "celltype": "StromalCells"
    }, {
      "score": 29.335427692307697,
      "celltype": "Monocytes"
    }, {
      "score": 28.914959615384621,
      "celltype": "Neutrophils"
    }, {
      "score": 13.818888461538467,
      "celltype": "NKCells"
    }, {
      "score": 9.5030688461538464,
      "celltype": "abTcells"
    }]
  }]
}, {
  "threshold": 2,
  "histograms": [{
    "sample": "Sample1",
    "nof_genes": 30,
    "values": [{
      "score": 5.1335499999999996,
      "celltype": "Bcells"
    }, {
      "score": 16.076072499999999,
      "celltype": "DendriticCells"
    }, {
      "score": 46.182032499999998,
      "celltype": "Macrophages"
    }, {
      "score": 6.5895700000000001,
      "celltype": "gdTCells"
    }, {
      "score": 5.3218800000000002,
      "celltype": "StemCells"
    }, {
      "score": 53.643625,
      "celltype": "StromalCells"
    }, {
      "score": 85.1618225,
      "celltype": "Monocytes"
    }, {
      "score": 55.559129999999996,
      "celltype": "Neutrophils"
    }, {
      "score": 7.6717524999999984,
      "celltype": "NKCells"
    }, {
      "score": 6.3277800000000006,
      "celltype": "abTcells"
    }]
  }]
}];

processData(input_data);

function processData(data) {
  histograms = data[0].histograms.map(function(data) {
    return {
      title: data.sample,
      dataset: new Plottable.Dataset(),
      nofgenes:  new Plottable.Dataset(),
      dataByThreshold: {},
                nofGenesByThreshold: {},
                load_nof_genes: function (threshold) {

                    this.nofgenes.data(this.nofGenesByThreshold[threshold]);
                },
      load: function(threshold) {
        this.dataset.data(this.dataByThreshold[threshold]);
      }
    };
  });

  data.forEach(function(data) {
    var threshold = data.threshold;
    thresholds.push(threshold);
    data.histograms.forEach(function(histogram, i) {
      histograms[i].dataByThreshold[threshold] = histogram.values;
       histograms[i].nofGenesByThreshold[threshold] = histogram.nof_genes;
    });
  });



  // Here we generalize the slide bar maximum threshold
  $('#threshold').attr('max', thresholds.length - 1);
  updateDatasets(thresholds[0]);
  updateNofGenes(thresholds[0]);
  buildPlots();
  updateThreshold();



}



$('#threshold').change(updateThreshold);

function updateThreshold() {

  // This is where the user input updating slider
  // takes place and where the QTIP is in action.

  var thresholdIndex = parseInt($('#threshold').val(), 10);
  $("#foldchange_threshold").html(thresholds[thresholdIndex]);
  updateDatasets(thresholds[thresholdIndex]);
   updateNofGenes(thresholds[thresholdIndex]);
  $(".tooltipped .content rect").qtip({
    overwrite: true,
    position: {
      my: "bottom middle",
      at: "top middle"
    },
    style: {
      classes: "qtip-light"
    },
    content: {
      text: function() {
        return $(this).attr("qtip2-title");
      }
    }
  });
}

function updateDatasets(threshold) {
  histograms.forEach(function(histogram) {
    histogram.load(threshold);
  });
}

    function updateNofGenes (threshold) {
        histograms.forEach(function (histogram) {
           
            histogram.load_nof_genes(threshold);
        });
    }


function buildPlots() {
  var $histogramContainer = $('#sample-histograms');

  histograms.forEach(function(histogram, index) {
    var elementId = "sample-histogram-" + index;


    $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
      .css({
        width: '200px',
        height: '200px',
        display: 'inline-block'
      })
      .attr('id', elementId)
      .appendTo($histogramContainer);

    plotSampleHistogram(histogram.title,histogram.nofgenes, histogram.dataset, '#' + elementId);
  });

}

function plotSampleHistogram(title, nof_genes, dataset, targetElement) {
  var xScale = new Plottable.Scales.Category(),
    yScale = new Plottable.Scales.Linear(),
    colorScale = new Plottable.Scales.Color();
var gene_count = nof_genes._data.toString().concat(" genes");

    var xAxis = new Plottable.Axes.Category(xScale, "bottom"),
        yAxis = new Plottable.Axes.Numeric(yScale, "left"),
        titleLabel = new Plottable.Components.TitleLabel(title),
        nofGeneLabel = new Plottable.Components.AxisLabel(gene_count);

    xAxis.tickLabelAngle(-90)
    yScale.domainMin(0);

    var plot = new Plottable.Plots.Bar()
      .addDataset(dataset)
      .x(function(d) { return d.celltype; }, xScale)
      .y(function(d) { return d.score; }, yScale)
      .attr("fill", function(d) { return d.celltype; }, colorScale)
      .attr("qtip2-title", function(d) { return '<div class="bartip">' + d.celltype + " (" + d.score.toFixed(2) + ') </div>'; })
      .addClass("tooltipped");



    new Plottable.Components.Table([
        [null, titleLabel],
        [null, nofGeneLabel],
        [yAxis, plot],
        [null, xAxis]
    ]).renderTo(targetElement);
}

function drawHistogramLegend(targetElement) {
    new Plottable.Components.Legend(colorScale)
        .renderTo(targetElement);
}
<html>

<head>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.css" rel="stylesheet" />
  <link href="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.css" rel="stylesheet" />


</head>

<body>

  <!-- Display the sliding bar -->
  <input id="threshold" type="range" min="0" max="1" step="1" value="0" />
  <br>

  <!-- Show foldchange threshold -->
  <div id="foldchange_threshold" style="display: inline-block; align:center;"></div>



  <!-- Show histograms -->
  <div id="sample-histograms"></div>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.js"></script>




</body>

</html>


Solution

  • Here's the fixed code. What I changed was that histogram object doesn't hold the number of genes as a Dataset, but instead holds the AxisLabel object. With that, updating the label simply means updating the text() of that label.

    The most important pieces are:

    load_nof_genes: function (threshold) {
                            this.axisLabel.text(this.nofGenesByThreshold[threshold] + 'genes');
                        },
    

    And:

     new Plottable.Components.Table([
            [null, titleLabel],
            [null, histogram.axisLabel],
            [yAxis, plot],
            [null, xAxis]
        ]).renderTo(targetElement);
    

    PS I also changed a bit how plotSampleHistogram receives the arguments - instead of going through all fields of histogram I pass the object directly.

    "use strict";
    
    var histograms,
      thresholds = [];
    
    var input_data = [{
      "threshold": 1.5,
      "histograms": [{
        "sample": "Sample1",
        "nof_genes": 26,
        "values": [{
          "score": 6.7530200000000002,
          "celltype": "Bcells"
        }, {
          "score": 11.432763461538459,
          "celltype": "DendriticCells"
        }, {
          "score": 25.823089615384621,
          "celltype": "Macrophages"
        }, {
          "score": 9.9911211538461551,
          "celltype": "gdTCells"
        }, {
          "score": 7.817228076923076,
          "celltype": "StemCells"
        }, {
          "score": 17.482806923076922,
          "celltype": "StromalCells"
        }, {
          "score": 29.335427692307697,
          "celltype": "Monocytes"
        }, {
          "score": 28.914959615384621,
          "celltype": "Neutrophils"
        }, {
          "score": 13.818888461538467,
          "celltype": "NKCells"
        }, {
          "score": 9.5030688461538464,
          "celltype": "abTcells"
        }]
      }]
    }, {
      "threshold": 2,
      "histograms": [{
        "sample": "Sample1",
        "nof_genes": 30,
        "values": [{
          "score": 5.1335499999999996,
          "celltype": "Bcells"
        }, {
          "score": 16.076072499999999,
          "celltype": "DendriticCells"
        }, {
          "score": 46.182032499999998,
          "celltype": "Macrophages"
        }, {
          "score": 6.5895700000000001,
          "celltype": "gdTCells"
        }, {
          "score": 5.3218800000000002,
          "celltype": "StemCells"
        }, {
          "score": 53.643625,
          "celltype": "StromalCells"
        }, {
          "score": 85.1618225,
          "celltype": "Monocytes"
        }, {
          "score": 55.559129999999996,
          "celltype": "Neutrophils"
        }, {
          "score": 7.6717524999999984,
          "celltype": "NKCells"
        }, {
          "score": 6.3277800000000006,
          "celltype": "abTcells"
        }]
      }]
    }];
    
    processData(input_data);
    
    function processData(data) {
      histograms = data[0].histograms.map(function(data) {
        return {
          title: data.sample,
          dataset: new Plottable.Dataset(),
          axisLabel: new Plottable.Components.AxisLabel(),
          dataByThreshold: {},
                    nofGenesByThreshold: {},
                    load_nof_genes: function (threshold) {
                        this.axisLabel.text(this.nofGenesByThreshold[threshold] + 'genes');
                    },
          load: function(threshold) {
            this.dataset.data(this.dataByThreshold[threshold]);
          }
        };
      });
    
      data.forEach(function(data) {
        var threshold = data.threshold;
        thresholds.push(threshold);
        data.histograms.forEach(function(histogram, i) {
          histograms[i].dataByThreshold[threshold] = histogram.values;
           histograms[i].nofGenesByThreshold[threshold] = histogram.nof_genes;
        });
      });
    
    
    
      // Here we generalize the slide bar maximum threshold
      $('#threshold').attr('max', thresholds.length - 1);
      updateDatasets(thresholds[0]);
      updateNofGenes(thresholds[0]);
      buildPlots();
      updateThreshold();
    
    
    
    }
    
    
    
    $('#threshold').change(updateThreshold);
    
    function updateThreshold() {
    
      // This is where the user input updating slider
      // takes place and where the QTIP is in action.
    
      var thresholdIndex = parseInt($('#threshold').val(), 10);
      $("#foldchange_threshold").html(thresholds[thresholdIndex]);
      updateDatasets(thresholds[thresholdIndex]);
       updateNofGenes(thresholds[thresholdIndex]);
      $(".tooltipped .content rect").qtip({
        overwrite: true,
        position: {
          my: "bottom middle",
          at: "top middle"
        },
        style: {
          classes: "qtip-light"
        },
        content: {
          text: function() {
            return $(this).attr("qtip2-title");
          }
        }
      });
    }
    
    function updateDatasets(threshold) {
      histograms.forEach(function(histogram) {
        histogram.load(threshold);
      });
    }
    
        function updateNofGenes (threshold) {
            histograms.forEach(function (histogram) {
               
                histogram.load_nof_genes(threshold);
            });
        }
    
    
    function buildPlots() {
      var $histogramContainer = $('#sample-histograms');
    
      histograms.forEach(function(histogram, index) {
        var elementId = "sample-histogram-" + index;
    
    
        $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
          .css({
            width: '200px',
            height: '200px',
            display: 'inline-block'
          })
          .attr('id', elementId)
          .appendTo($histogramContainer);
    
        plotSampleHistogram(histogram, '#' + elementId);
      });
    
    }
    
    function plotSampleHistogram(histogram, targetElement) {
      var xScale = new Plottable.Scales.Category(),
        yScale = new Plottable.Scales.Linear(),
        colorScale = new Plottable.Scales.Color();
    
        var xAxis = new Plottable.Axes.Category(xScale, "bottom"),
            yAxis = new Plottable.Axes.Numeric(yScale, "left"),
            titleLabel = new Plottable.Components.TitleLabel(histogram.title);
    
        xAxis.tickLabelAngle(-90)
        yScale.domainMin(0);
    
        var plot = new Plottable.Plots.Bar()
          .addDataset(histogram.dataset)
          .x(function(d) { return d.celltype; }, xScale)
          .y(function(d) { return d.score; }, yScale)
          .attr("fill", function(d) { return d.celltype; }, colorScale)
          .attr("qtip2-title", function(d) { return '<div class="bartip">' + d.celltype + " (" + d.score.toFixed(2) + ') </div>'; })
          .addClass("tooltipped");
    
    
    
        new Plottable.Components.Table([
            [null, titleLabel],
            [null, histogram.axisLabel],
            [yAxis, plot],
            [null, xAxis]
        ]).renderTo(targetElement);
    }
    
    function drawHistogramLegend(targetElement) {
        new Plottable.Components.Legend(colorScale)
            .renderTo(targetElement);
    }
    <html>
    
    <head>
      <link href="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.css" rel="stylesheet" />
      <link href="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.css" rel="stylesheet" />
    
    
    </head>
    
    <body>
    
      <!-- Display the sliding bar -->
      <input id="threshold" type="range" min="0" max="1" step="1" value="0" />
      <br>
    
      <!-- Show foldchange threshold -->
      <div id="foldchange_threshold" style="display: inline-block; align:center;"></div>
    
    
    
      <!-- Show histograms -->
      <div id="sample-histograms"></div>
    
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.js"></script>
    
    
    
    
    </body>
    
    </html>