0

I want to show specific data (not the columns I used in plotting my scatter plot but other columns in the dataframe) whenever I hover my mouse on each data point using matpotlib. I don't want to use plotly because I need to implement another part of matplotlib. I wrote the following script but it is not implementing the hovering part when I run it in my jupyter notebook.

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib widget
# Load sample data
diamonds = sns.load_dataset('diamonds')

# Create a scatter plot with hue and use matplotlib to display the specific column values when hovering over a datapoint
sns.scatterplot(x="carat", y="price", hue="cut", data=diamonds)

def hover(event):
    # Get the current mouse position
    ax = event.inaxes
    x, y = event.xdata, event.ydata

    # Find the nearest datapoint
    distances = ((diamonds['carat'] - x)**2 + (diamonds['price'] - y)**2)
    idx = distances.idxmin()
    
    # Display the specific column values in a pop-up window
    text = f"Cut: {diamonds.loc[idx, 'cut']}\n" \
           f"Clarity: {diamonds.loc[idx, 'clarity']}\n" \
           f"Color: {diamonds.loc[idx, 'color']}"
    plt.gcf().text(x, y, text, ha='left', va='bottom', fontsize=10, backgroundcolor='white', alpha=0.7)

# Connect the hover function to the figure
fig = plt.gcf()
fig.canvas.mpl_connect("motion_notify_event", hover)

# Show the plot
plt.show()

Taiwo
  • 65
  • 8

1 Answers1

0

This is easier with mplcursors. There's a similar example of extracting the data and labels for the annotation from a dataframe to plug into. Implemented with your example it is:

%matplotlib ipympl
# based on https://mplcursors.readthedocs.io/en/stable/examples/dataframe.html
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.patheffects import withSimplePatchShadow
import mplcursors

df = sns.load_dataset('diamonds')[:10]
sbsp = sns.scatterplot(x="carat", y="price", hue="cut", data=df)

def show_hover_panel(get_text_func=None):
    cursor = mplcursors.cursor(
        hover=2,  # Transient
        annotation_kwargs=dict(
            bbox=dict(
                boxstyle="square,pad=0.5",
                facecolor="wheat",
                edgecolor="#ddd",
                linewidth=0.5,
                path_effects=[withSimplePatchShadow(offset=(1.5, -1.5))],
            ),
            linespacing=1.5,
            arrowprops=None,
        ),
        highlight=True,
        highlight_kwargs=dict(linewidth=2),
    )
    if get_text_func:
        cursor.connect(
            event="add",
            func=lambda sel: sel.annotation.set_text(get_text_func(sel.index)),
        )
    return cursor

def on_add(index):
    item = df.iloc[index]
    parts = [
        f"Cut: {item.cut}", # f"Cut: {diamonds.loc[idx, 'cut']}\n"
        f"Clarity: {item.clarity}", #  f"Clarity: {diamonds.loc[idx, 'clarity']}\n"
        f"Color: {item.color}", #f"Color: {diamonds.loc[idx, 'color']}"
    ]

    return "\n".join(parts)

sbsp.figure.canvas.header_visible = False # Hide the Figure name at the top of the figure;based on https://matplotlib.org/ipympl/examples/full-example.html
show_hover_panel(on_add)
plt.show();

That's for running it in JupyterLab where ipympl has been installed.

For running it in Jupyter Notebook at present, change the first line to %matplotlib notebook.

I saw it be smoother in JupyterLab.

I'll point to ways to try the code in both interfaces without installing anything on your system below.

(Your approach may be able to work with more incorporation of things here. However, there's already a very similar answer with mplcursors)



(The places and steps to run the code outlined below were worked out primarily for the Matplotlib option here yesterday. Noting that here as there were some quirks to the ipympl offering launching and I worry that I may miss updating each place if something changes.)

Try it in JupyterLab in conjunction with ipympl without touching your system

This will allow you to try the code in JupyterLab without installing anything on your own system.

  • Go to here. Sadly the link currently goes to a dead end and doesn't seem to build a new image right now. Fortunately, right now this offering works for launching a session where ipympl is already installed.

  • When that session opens, run in a notebook %pip install mplcursors seaborn. Let that installation command run to install both mplcursors and seaborn and then restart the kernel.

  • Make a new cell and paste in the code from above and run it.

Try it Jupyter Notebook classic interface without touching your system

This will allow you to try the code in the present classic Jupyter Notebook interface (soon-to-be more-often-referenced as 'the document-centric interface' as the underlying machinery for Jupyter Notebook 7 will use JupyterLab components) without installing anything on your own system.

  • Go here and press 'launch binder'. A temporary, remote session will spin up served via MyBinder.

  • Make a new cell and simply add %matplotlib notebook at the top of the cell. Then add the code above to the cell, leaving off the first line of the suggested code, so that you effectively substitute %matplotlib notebook in place of %matplotlib ipympl.

  • Run the cell.

Wayne
  • 6,607
  • 8
  • 36
  • 93