-
Notifications
You must be signed in to change notification settings - Fork 0
/
mplcursors_tooltip.py
104 lines (87 loc) · 2.99 KB
/
mplcursors_tooltip.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
from collections.abc import Callable
from typing import Optional
from matplotlib.collections import PathCollection
from matplotlib.lines import Line2D
from matplotlib.patheffects import withSimplePatchShadow
import matplotlib.pyplot as plt
import mplcursors
import pandas as pd
# Added in #605
def add_mplcursors_tooltip(
get_text: Callable[[mplcursors.Selection], Optional[str]] = None,
pickables=None, # Added in #802
):
tooltip_cursor = mplcursors.cursor(
pickables=pickables, # Added in #802
hover=mplcursors.HoverMode.Transient,
annotation_kwargs=dict(
bbox=dict(
alpha=0.8,
path_effects=[withSimplePatchShadow(offset=(2, -2))],
),
linespacing=1.4,
multialignment="left",
),
highlight=True,
highlight_kwargs=dict(
edgecolor=plt.rcParams["text.color"],
color=plt.rcParams["text.color"],
),
bindings=dict( # Added in #803
toggle_enabled="ctrl+alt+e",
toggle_visible="ctrl+alt+v",
),
)
if get_text:
def set_text(sel: mplcursors.Selection):
text = get_text(sel)
if text is not None:
sel.annotation.set_text(text)
tooltip_cursor.connect("add", set_text)
if __name__ == "__main__":
import mpl_utils
mpl_utils.setup()
fig, ax = plt.subplots(num="mplcursors", clear=True)
mpl_utils.clear_events()
df = pd.read_csv("../data/crop-data.csv")
df = df[df.Region.eq("Europe") & df.Crop.eq("Rice")].dropna()
for country_name, country_df in df.groupby("Country"):
if "n" in country_name:
ax.plot(country_df.Year, country_df.Yield, label=country_name)
else:
ax.scatter(country_df.Year, country_df.Yield, label=country_name)
mock_events = [
[1964, "Drought"],
[1973, "Storms"],
[1975, "Major drought"],
[2003, "Heat wave"],
[2015, "Locust plague"],
[2018, "Drought"],
]
for year, label in mock_events:
ax.axvline(
x=year,
label=f"_{label}",
color="#444",
linestyle=":",
linewidth=3,
zorder=0,
)
ax.legend(loc="upper left")
def get_text(sel: mplcursors.Selection):
label = sel.artist.get_label()
if isinstance(sel.artist, PathCollection):
x_value = sel.target[0]
elif isinstance(sel.artist, Line2D):
x_data = sel.artist.get_xdata()
x_value = x_data[round(sel.index)]
if len(set(x_data)) == 1: # Vertical line
label = label.removeprefix("_")
return f"{x_value}\n{label}"
else:
return
row = df[df.Country.eq(label) & df.Year.eq(x_value)]
assert len(row) == 1, "There should only be one match"
row = row.squeeze()
return mpl_utils.series_to_string(row)
add_mplcursors_tooltip(get_text=get_text)