MPLD3: label information of barplot

1.1k views Asked by At

I'm trying to extend the example provided here:

from mpld3 import utils

class ClickInfo(plugins.PluginBase):
    """Plugin for getting info on click"""

    JAVASCRIPT = """
    mpld3.register_plugin("clickinfo", ClickInfo);
    ClickInfo.prototype = Object.create(mpld3.Plugin.prototype);
    ClickInfo.prototype.constructor = ClickInfo;
    ClickInfo.prototype.requiredProps = ["id"];
    function ClickInfo(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };

    ClickInfo.prototype.draw = function(){
        var obj = mpld3.get_element(this.props.id);
        obj.elements().on("mousedown",
                          function(d, i){alert("clicked on points[" + i + "]");});
    }
    """
    def __init__(self, points):
        self.dict_ = {"type": "clickinfo",
                      "id": utils.get_id(points)}

fig, ax = plt.subplots()
points = ax.scatter(np.random.rand(50), np.random.rand(50),
                    s=500, alpha=0.3)

plugins.connect(fig, ClickInfo(points))

My purpose is to do the same thing (display label when the object is clicked) but with barplot instead of scatterplot.

It doesn't work with the same Javascript code:

from mpld3 import utils

class ClickInfo(plugins.PluginBase):
    """Plugin for getting info on click"""

    JAVASCRIPT = """
    mpld3.register_plugin("clickinfo", ClickInfo);
    ClickInfo.prototype = Object.create(mpld3.Plugin.prototype);
    ClickInfo.prototype.constructor = ClickInfo;
    ClickInfo.prototype.requiredProps = ["id"];
    function ClickInfo(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };

    ClickInfo.prototype.draw = function(){
        var obj = mpld3.get_element(this.props.id);
        obj.elements().on("mousedown",
                          function(d, i){alert("clicked on bar[" + i + "]");});
    }
    """
    def __init__(self, bars):
        self.dict_ = {"type": "clickinfo",
                      "id": utils.get_id(bars)}
x = range(0,10)
y = np.random.rand(10)

fig, ax = plt.subplots()
bars = ax.bar(x, y)

plugins.connect(fig, ClickInfo(bars))

However, I can obtain a working behavior for one of the bar. For example, with plugins.connect(fig, ClickInfo(bars[0])), a click on the first bar will trigger the alert Javascript code.

Question:

How can I have the same behavior for each bar ?

Moreover, as I am inexperienced with D3 and Javascript a short explanation of how the code works would be very helpful. Any ressource to learn is also welcome as I can't find MPLD3 tutorials.

2

There are 2 answers

2
Abraham D Flaxman On BEST ANSWER

You are on the right track. Here is a way to make what you have work:

from mpld3 import utils, plugins

class ClickInfo(plugins.PluginBase):
    """Plugin for getting info on click"""

    JAVASCRIPT = """
    mpld3.register_plugin("clickinfo", ClickInfo);
    ClickInfo.prototype = Object.create(mpld3.Plugin.prototype);
    ClickInfo.prototype.constructor = ClickInfo;
    ClickInfo.prototype.requiredProps = ["ids"];
    function ClickInfo(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };

    ClickInfo.prototype.draw = function(){
        this.props.ids.forEach(function(id, i) {
            var obj = mpld3.get_element(id);
            obj.elements().on("mousedown",
                              function(d){alert("clicked on bar[" + i + "]");});
                              });
    }
    """
    def __init__(self, bars):
        self.dict_ = {"type": "clickinfo",
                      "ids": [utils.get_id(bar) for bar in bars]}
x = range(0,10)
y = np.random.rand(10)

fig, ax = plt.subplots()
bars = ax.bar(x, y)

plugins.connect(fig, ClickInfo(bars))

You can see it in action here. Perhaps someone else will have time to extend this answer with more of an explanation of how the code works.

1
Robert On

I ran into the same problem and I expanded the answer for a stacked barplot with floating labels, which you can find here:

http://nbviewer.ipython.org/gist/Iggam/416520098460b057c208

The code can be found here:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import mpld3
from mpld3 import plugins, utils

class BarLabelToolTip(plugins.PluginBase):    
    JAVASCRIPT = """
    mpld3.register_plugin("barlabeltoolTip", BarLabelToolTip);
    BarLabelToolTip.prototype = Object.create(mpld3.Plugin.prototype);
    BarLabelToolTip.prototype.constructor = BarLabelToolTip;
    BarLabelToolTip.prototype.requiredProps = ["ids","labels"];
    BarLabelToolTip.prototype.defaultProps = {
        hoffset: 0,
        voffset: 10,
        location: 'mouse'
    };
    function BarLabelToolTip(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };

    BarLabelToolTip.prototype.draw = function(){
        var svg = d3.select("#" + this.fig.figid);
        var objs = svg.selectAll(".mpld3-path");
        var loc = this.props.location;
        var labels = this.props.labels

        test = this.fig.canvas.append("text")
            .text("hello world")
            .style("font-size", 72)
            .style("opacity", 0.5)
            .style("text-anchor", "middle")
            .attr("x", this.fig.width / 2)
            .attr("y", this.fig.height / 2)
            .style("visibility", "hidden");

        function mousemove(d) {
            if (loc === "mouse") {
                var pos = d3.mouse(this.fig.canvas.node())
                this.x = pos[0] + this.props.hoffset;
                this.y = pos[1] - this.props.voffset;
            }

            test
                .attr("x", this.x)
                .attr("y", this.y);
        };

        function mouseout(d) {
            test.style("visibility", "hidden")
        };

        this.props.ids.forEach(function(id, i) {


            var obj = mpld3.get_element(id);

            function mouseover(d) {
                test.style("visibility", "visible")
                    .style("font-size", 24)
                    .style("opacity", 0.7)
                    .text(labels[i])
            };

            obj.elements().on("mouseover", mouseover.bind(this))

        });

       objs.on("mousemove", mousemove.bind(this)) 
           .on("mouseout", mouseout.bind(this));     

    }       
    """
    def __init__(self, ids, labels=None, location="mouse"):

        self.dict_ = {"type": "barlabeltoolTip",
                      "ids": ids,
                      "labels": labels,
                      "location": location}

fig, ax = plt.subplots()
x = range(0,10)
y = np.random.rand(10)
bars = ax.bar(x, y)

labels = [round(bar.get_height(),2) for bar in bars]
ids = [utils.get_id(bar) for bar in bars]

plugins.connect(fig, BarLabelToolTip(ids, labels))