Monday 23 December 2019

matplotlib mouse move event cursor

values display at upper left

cursor response to mouse move, cursor center always snaps to the plot

import matplotlib.pyplot as plt
import numpy as np
import datetime as dt
import pandas_datareader.data as web

class SnaptoCursor(object):
    """
    Like Cursor but the crosshair snaps to the nearest x, y point.
    For simplicity, this assumes that *x* is sorted.
    """

    def __init__(self, ax, x, y, numberIndex):
        self.ax = ax
        self.lx = ax.axhline(color='r')  # the horiz line
        self.ly = ax.axvline(color='r')  # the vert line
        self.x = x
        self.y = y
        self.nIndex = numberIndex
        # text location in axes coords
        self.txt = ax.text(0.05, 0.9, '', transform=ax.transAxes)

    def mouse_move(self, event):
        if not event.inaxes:
            return

        x, y = event.xdata, event.ydata
        indx = min(np.searchsorted(self.nIndex, x), len(self.x) - 1)
        x = self.x[indx]
        y = self.y[indx]
        # update the line positions
        self.lx.set_ydata(y)
        self.ly.set_xdata(indx)

        self.txt.set_text('x=%s\ny=%1.2f' % (x.strftime('%b %d %Y'), y))
        self.ax.figure.canvas.draw()

start = dt.datetime(2014, 1, 1)
end = dt.datetime(2019, 10, 30)
ticker = 'BNS'

df = web.DataReader(ticker, 'yahoo', start, end)
print(df[:5])
print(df['Adj Close'].values[:5])
print(df['Adj Close'].index[:5])

x = df['Adj Close'].index
y = df['Adj Close'].values
#number index is need for the cursor position, xaxis is mapped with integer first and converted to date later
x_num_index = np.arange(0, len(x), 1)

fig, ax = plt.subplots()
ax.plot(x_num_index, y)
snap_cursor = SnaptoCursor(ax, x, y, x_num_index)
fig.canvas.mpl_connect('motion_notify_event', snap_cursor.mouse_move)

#get default # of lables on x axis
n = len(ax.get_xticklabels())
print(n)

x_format = x.strftime('%b %d %Y')
xlabels = [x_format[int(i*len(x)/n)] for i in range(0, n)]
print(xlabels)
ax.set_xticklabels(xlabels)

ax.set_ylim([np.min(y), np.max(y)])
ax.set_title('Closing Price of %s' %(ticker))

plt.gcf().autofmt_xdate()
plt.show()

----------------------------
#logs
                 High        Low       Open      Close    Volume  Adj Close
Date
2014-01-02  62.750000  61.939999  62.549999  61.980000  425800.0  43.690678
2014-01-03  61.689999  61.060001  61.490002  61.500000  351100.0  43.790367
2014-01-06  61.360001  60.480000  61.340000  60.750000  427300.0  43.256340
2014-01-07  60.820000  59.869999  60.610001  59.889999  547600.0  42.643982
2014-01-08  59.840000  59.330002  59.770000  59.630001  540700.0  42.458855
[43.69067764 43.79036713 43.25634003 42.64398193 42.45885468]
DatetimeIndex(['2014-01-02', '2014-01-03', '2014-01-06', '2014-01-07',
               '2014-01-08'],
              dtype='datetime64[ns]', name='Date', freq=None)
10
['Jan 02 2014', 'Aug 01 2014', 'Mar 04 2015', 'Oct 01 2015', 'May 03 2016', 'Nov 30 2016', 'Jul 03 2017', 'Feb 01 2018', 'Aug 31 2018', 'Apr 04 2019']

reference:
http://chuanshuoge2.blogspot.com/2019/10/python-programming-for-finance-1.html
https://stackabuse.com/how-to-format-dates-in-python/
https://matplotlib.org/3.1.0/gallery/misc/cursor_demo_sgskip.html
https://stackoverflow.com/questions/11244514/modify-tick-label-text

No comments:

Post a Comment