0
import pandas as pd
import matplotlib.pyplot as plt
import mplcursors

df = pd.DataFrame(
    {'Universe': ['Darvel', 'MC', 'MC', 'Darvel', 'MC', 'Other', 'Darvel'],
     'Value': [10, 11, 13, 12, 9, 7, 10],
     'Upper': [12.5, 11.3, 15.4, 12.2, 13.1, 8.8, 11.5],
     'Lower': [4.5, 9.6, 11.8, 6, 6.5, 5, 8]})
df['UpperError'] = df['Upper'] - df['Value']
df['LowerError'] = df['Value'] - df['Lower']

colors = ['r', 'g', 'b']

fig, ax = plt.subplots()
for i, universe in enumerate(df['Universe'].unique()):
    to_plot = df[df['Universe'] == universe]
    ax.scatter(to_plot.index, to_plot['Value'], s=16, c=colors[i])
    error = to_plot[['LowerError', 'UpperError']].transpose().to_numpy()
    ax.errorbar(to_plot.index, to_plot['Value'], yerr=error, fmt='o',
                markersize=0, capsize=6, color=colors[i])
    ax.scatter(to_plot.index, to_plot['Upper'], c='w', zorder=-1)
    ax.scatter(to_plot.index, to_plot['Lower'], c='w', zorder=-1)
    
mplcursors.cursor(hover=True)

plt.show()

这完成了我想要的大部分工作,但我想要进行以下更改。

  1. 我不希望 mplcursors 光标与误差条交互,而只是散点图,包括误差条顶部和底部的不可见散点图。

  2. 我只想显示 y 值。例如,第一个栏应该在顶部显示“12.5”,在中间显示“10.0”,在底部显示“4.5”。

4

1 回答 1

2

要让 mplcursors 只与某些元素交互,可以将这些元素的列表作为第一个参数提供给mplcursors.cursor(). 该列表可以根据调用的返回值构建ax.scatter

要修改显示的注释文本,可以连接自定义函数。在下面的示例中,标签和 y 位置从所选元素中提取并放入注释文本中。可以通过添加此类标签ax.scatter(..., label=...)

(选择'none'“不可见”元素的颜色使它们真正不可见。为了使代码更“Pythonic”,可以避免显式索引,使用 withzip而不是 with enumerate。)

import matplotlib.pyplot as plt
import mplcursors
import pandas as pd

def show_annotation(sel):
    text = f'{sel.artist.get_label()}\n y={sel.target[1]:.1f}'
    sel.annotation.set_text(text)

df = pd.DataFrame(
    {'Universe': ['Darvel', 'MC', 'MC', 'Darvel', 'MC', 'Other', 'Darvel'],
     'Value': [10, 11, 13, 12, 9, 7, 10],
     'Upper': [12.5, 11.3, 15.4, 12.2, 13.1, 8.8, 11.5],
     'Lower': [4.5, 9.6, 11.8, 6, 6.5, 5, 8]})
df['UpperError'] = df['Upper'] - df['Value']
df['LowerError'] = df['Value'] - df['Lower']

colors = ['r', 'g', 'b']

fig, ax = plt.subplots()
all_scatters = []
for universe, color in zip(df['Universe'].unique(), colors):
    to_plot = df[df['Universe'] == universe]
    all_scatters.append(ax.scatter(to_plot.index, to_plot['Value'], s=16, c=color, label=universe))
    error = to_plot[['LowerError', 'UpperError']].transpose().to_numpy()
    ax.errorbar(to_plot.index, to_plot['Value'], yerr=error, fmt='o',
                markersize=0, capsize=6, color=color)
    all_scatters.append(ax.scatter(to_plot.index, to_plot['Upper'], c='none', zorder=-1, label=universe))
    all_scatters.append(ax.scatter(to_plot.index, to_plot['Lower'], c='none', zorder=-1, label=universe))

cursor = mplcursors.cursor(all_scatters, hover=True)
cursor.connect('add', show_annotation)
plt.show()

带有自定义文本的 mplcursors

PS:您还可以'Universe'通过 x 刻度显示:

ax.set_xticks(df.index)
ax.set_xticklabels(df['Universe'])

如果您愿意,对于短函数,您可以使用 lambda 表示法而不是编写单独的函数:

cursor.connect('add',
               lambda sel: sel.annotation.set_text(f'{sel.artist.get_label()}\n y={sel.target[1]:.1f}'))
于 2021-09-03T17:49:15.357 回答