Search code examples
pythonmatplotlibmplcursors

Changing type of annotation in mplcursors on matplotlib


The current format

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import ScalarFormatter
import mplcursors as mpl

class Compound():
    def accumulation(i,t):
        return (1+i)**t
    def discount(i,t):
        return (1-i)**(-t)
    
years= np.linspace(1,1000,12000)

%matplotlib widget

fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(ScalarFormatter(useMathText=True))

plt.plot(years,0.93*Compound.accumulation(0.0225,years))
plt.title('Interest')

mpl.cursor(hover=True).annotation_kwargs

I'm using a Jupiter notebook and I want to change the scientific format in the annotation that mplcursors creates when the cursor hovers above the lines


Solution

  • The mplcursors package uses the matplotlib Axes.format_coord to set the style of the formatters in the annotation box. So, you can define your own format_coord function and use that instead (see, e.g., here).

    For example,

    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.ticker import ScalarFormatter
    import mplcursors as mpl
    
    
    class Compound():
        def accumulation(i, t):
            return (1 + i)**t
        def discount(i, t):
            return (1 - i)**(-t)
        
    years = np.linspace(1, 1000, 12000)
    
    
    def format_coord(x, y):
        # output numbers (not in scientific notation) with one decimal place
        return f"x={x:.1f}, y={y:.1f}"
    
    
    fig, ax = plt.subplots()
    ax.yaxis.set_major_formatter(ScalarFormatter(useMathText=True))
    
    ax.plot(years, 0.93 * Compound.accumulation(0.0225, years))
    ax.set_title("Interest")
    
    # switch axes format_coord function to your own 
    ax.format_coord = format_coord
    
    mpl.cursor(hover=True)
    
    plt.show()
    

    Unfortunately, this doesn't seem to work with LaTeX/MathText style strings enclosed in $ signs, e.g., using:

    def format_coord(x, y):
        yscale = int(np.log10(y))
        
        # return LaTeX style scientific notation
        return rf"x={x:1.1f}, y=${y / 10**yscale:1.1f} \times 10^{yscale}$"
    

    keeps the dollars and does not render it as an equation. Doing that may required a more in-depth hack.