如果您在 Google 上搜索 Apple 股票,您将被带到此页面。在图表上,您可以左键单击并按住并向右或向左移动。如果这样做,您会得到百分比的变化以及值的变化,当您四处移动时会显示这些变化。
问题
是否可以完全按照 Python 中的描述创建上述功能?我尝试使用 Plotly 包但无法做到。
我想在下图上做:
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()
在下面的评论部分,Joseph 建议使用 PyQtgraph 示例池,但这是我第一次使用这个包,我不知道该怎么做。
在不确切知道您要寻找什么的情况下,这是一个使用 PyQtgraph 的示例。这将创建一个带有数据光标的简单股票图表。当您左键单击并按住图表并向左或向右移动时,数据光标将跟随您的鼠标光标并显示该点股价的百分比变化。
import numpy as np
import pyqtgraph as pg
class StockChart(pg.PlotWidget):
def __init__(self):
super().__init__()
# Create a plot curve
self.plot_curve = self.plot()
# Set the plot range
self.setXRange(0, 2000)
self.setYRange(0, 10000)
# Create a data cursor
self.data_cursor = pg.LinearRegionItem()
self.addItem(self.data_cursor)
# Bind the data cursor to the plot curve
self.data_cursor.sigRegionChanged.connect(self.on_data_cursor_changed)
def on_data_cursor_changed(self):
# Get the x and y values of the data cursor
x, y = self.data_cursor.getRegion()
# Calculate the percentage change
percentage_change = (y - self.plot_curve.getData(x)[0]) / self.plot_curve.getData(x)[0] * 100
# Display the percentage change
print(f"Percentage change: {percentage_change:.2f}%")
if __name__ == "__main__":
# Create a StockChart object
stock_chart = StockChart()
# Generate some random data
x = np.random.randn(2000)
y = np.cumsum(x)
# Set the data for the plot curve
stock_chart.plot_curve.setData(x, y)
# Show the plot
stock_chart.show()
这是一个将
matplotlib
与鼠标事件结合使用的解决方案。它提供原始股票图的移动/单击和拖动行为,在图例中显示结果:
它确实不绘制原始库存图中的垂直线和阴影区域。虽然这也是可能的,但可能会有点太长了。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def set_text_for(x_start, event):
if event.xdata is not None: # Are we inside the graph?
x_current = int(np.round(event.xdata))
y_current = df["value"].at[x_current]
if x_start is None: # No click and drag: show y value and x value
value_str = f"{y_current:.2f} at {x_current})"
else: # Click and drag: show y value change and x value range
y_start = df["value"].at[x_start]
if x_current < x_start: # Swap start and current if we drag to the left
x_start, x_current, y_start, y_current = x_current, x_start, y_current, y_start
y_diff = y_current - y_start
y_diff_percent = np.nan if y_start == 0 else abs(100 * y_diff / y_start)
arrow = " " if y_diff == 0 else ("↑" if y_diff > 0 else "↓")
value_str = f"{y_diff:+.2f} ({y_diff_percent:.2f} %) {arrow} at {x_start}-{x_current}"
leg.texts[0].set_text(f"{line_label}: {value_str}")
fig.canvas.draw_idle()
class PlotState:
def __init__(self):
self.x_start = None
def on_press(self, event):
if event.xdata is not None:
self.x_start = int(np.round(event.xdata))
def on_release(self, event):
self.x_start = None
set_text_for(self.x_start, event)
def on_move(self, event):
set_text_for(self.x_start, event)
if __name__ == "__main__":
# Initialize data
np.random.seed(0)
df = pd.DataFrame(np.cumsum(np.random.randn(2000)), columns=["value"])
# Initialize plotting
fig, ax = plt.subplots(figsize=(20, 4))
df["value"].plot(ax=ax)
leg = plt.legend()
line_label = leg.texts[0].get_text()
# Initialize event handling
state = PlotState()
fig.canvas.mpl_connect("button_press_event", state.on_press)
fig.canvas.mpl_connect("button_release_event", state.on_release)
fig.canvas.mpl_connect("motion_notify_event", state.on_move)
plt.show()
代码上的一些注释:
PlotState
,我们可以跟踪是否仅移动鼠标(self.x_start
是None
)还是单击并拖动,并提供相应的事件处理。mpl_connect()
连接到必要的事件(“button_press_event”、“button_release_event”、“motion_notify_event”)来初始化事件处理。event.xdata
(光标下绘图的 x 坐标)进行舍入,以从数据框中获取最接近的 x 值。