Search code examples
pythonplotlyvisualizationplotly-express

Plotly express box plot hover data not working


Trying to add data to hover of boxplot express in plotly and following the instructions here in plotly 5.4.1. It is mentioned in the tutorial that additional information to be shown in the hover can be added by hover_data and hover_name argument. However, The additional hover data, in this case information from continent column, is not presented in the hover. I am not sure what is going wrong here? (Here is the code I test in Google colab)

import plotly.express as px
import pandas as pd
import numpy as np
np.random.seed(1234)

df = pd.DataFrame(np.random.randn(20, 1),columns=['Col1'])
df['country']=['canada','france']*10
df['continent']=['america','europe']*10

fig = px.box(df, x="country", y="Col1",  hover_data=['continent'])

fig.show()

Here is what i get in google colab:

plotly boxplot in google colab

Error I get with suggested solution (this was solved with pip install plotly --upgrade): enter image description here

The solution offered by @Rob works but to make it a generic function, here is what I wrote out of it:

def box_with_hover(df,x,y,hover_data):    
  fig = px.box(df, x=x, y=y, hover_data=[hover_data])

  fig.add_traces(
      px.bar(
          df.groupby([x, hover_data], as_index=False).agg(
              base=(y, "min"), y=(y, lambda s: s.max() - s.min())
          ),
          x=x,
          base="base",
          y="y",
          hover_data={hover_data:True, x:True, "base":False, "y":False},
      )
      .update_traces(opacity=0.1)
      .data   ).update_layout(bargap=0.8)


  fig.show()

Solution

    • this is similar to Change Plotly Boxplot Hover Data
    • boxplot hover info is within javascript layer of plotly. Hence have overlayed a bar plot where hover can be controlled in way you require. When you hover over boxplot you get standard boxplot hover. bar different hover info
    import plotly.express as px
    import pandas as pd
    import numpy as np
    
    np.random.seed(1234)
    
    df = pd.DataFrame(np.random.randn(20, 1), columns=["Col1"])
    df["country"] = ["canada", "france"] * 10
    df["continent"] = ["america", "europe"] * 10
    
    fig = px.box(df, x="country", y="Col1", hover_data=["continent"])
    
    fig.add_traces(
        px.bar(
            df.groupby(["country", "continent"], as_index=False).agg(
                base=("Col1", "min"), y=("Col1", lambda s: s.max() - s.min())
            ),
            x="country",
            base="base",
            y="y",
            hover_data={"continent":True, "country":True, "base":False, "y":False},
        )
        .update_traces(opacity=0.1)
        .data
    ).update_layout(bargap=0.8)
    
    
    fig
    

    enter image description here

    generic function

    import plotly.express as px
    import pandas as pd
    import numpy as np
    
    np.random.seed(1234)
    
    df = pd.DataFrame(np.random.randn(20, 1), columns=["Col1"])
    df["country"] = ["canada", "france"] * 10
    df["continent"] = ["america", "europe"] * 10
    df["letter"] = list("AB") * 10
    
    
    def box_with_hover(*args, **kwargs):
        if isinstance(args[0], pd.DataFrame):
            kwargs["data_frame"] = args[0]
        fig = px.box(**kwargs)
    
        fig.add_traces(
            px.bar(
                kwargs["data_frame"]
                .groupby([kwargs["x"]], as_index=False)
                .agg(
                    **{
                        **{
                            "base": (kwargs["y"], "min"),
                            "y": (kwargs["y"], lambda s: s.max() - s.min()),
                        },
                        **{c: (c, "first") for c in kwargs["hover_data"]},
                    }
                ),
                x=kwargs["x"],
                base="base",
                y="y",
                hover_data={
                    **{c: True for c in kwargs["hover_data"]},
                    **{kwargs["x"]: True, "base": False, "y": False},
                },
            )
            .update_traces(opacity=0.1)
            .data
        ).update_layout(bargap=0.8)
        return fig
    
    
    box_with_hover(
        df.reset_index(), x="country", y="Col1", hover_data=["continent", "letter", "index"]
    )