If you search Apple stock on Google you will be taken to this page. On the chart, you can left-click and hold it and move to the right or left. If you do so, you get the change of percentage as well as value change which is shown to you as you move around.
Is it possible to create the above capability exactly as described in Python? I tried with the Plotly package, but I could not do it.
I want to do it on the following graph:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
x = np.random.randn(2000)
y = np.cumsum(x)
df = pd.DataFrame(y, columns=['value'])
fig, ax = plt.subplots(figsize=(20, 4))
df['value'].plot(ax=ax)
plt.show()
In the comment section below, Joseph suggested using PyQtgraph pool of examples, but this is my first time using this package and I am not sure how to do it.
Here is a solution that uses
matplotlibwith mouse events rather thanpyqtgraph. It provides the move/click-and-drag behavior of the original stock plot, showing the results in the plot's legend:It does not plot the vertical lines and shaded areas from the original stock plot though. While this would also be possible (we could use
vlines()for drawing lines and follow this article for filling/shading areas), the code would get a bit too long probably.Some notes on the code:
closest_index_for(), we determine the index in our data frame that is closest to our event's x value. The result will beNoneif the event's x value isNone, meaning we are outside the plot area.str_move_from()andstr_drag_from()we prepare the result texts for mouse moving and click-and-drag, respectively. Some essential steps instr_drag_from():percent = np.nan if y_start == 0 ...).if x_current < x_start ...).update_legend(), we update the legend with the determined result text and redraw the canvas. The text depends on whether we move or click and drag (... if x_start is None else ...).PlotStateclass, we keep track of whether we move the mouse only (self.x_startisNone) or whether we click and drag instead, and provide the corresponding event handlers (theon_*()methods). We could avoid the class by providing a globalx_startvariable and corresponding handler functions instead, but I think the current solution is a bit cleaner.matplotlib's event handling is initialized by connecting to the necessary events ("button_press_event", "button_release_event", "motion_notify_event") viampl_connect().df,fig,leg,line_labelinupdate_legend()) in an actual application. I tried to find a compromise between brevity and legibility here.