背景
上篇博客:python【matplotlib】鼠標拖動滾動縮放坐標范圍和拖動圖例共存,得到啟發(fā),我們已經(jīng)可以通過鼠標拖動縮放坐標范圍和移動圖例,來實現(xiàn)動態(tài)交互式繪圖了,對于x軸是時間序列的繪圖需求,能否也實現(xiàn)動態(tài)交互式繪圖呢?
答案是肯定的,接下來我將詳細描述其實現(xiàn)的方式。
效果
實現(xiàn)步驟
準備工作
首先,我們需要導(dǎo)入必要的庫,包括datetime、timedelta、matplotlib.pyplot等。同時,我們設(shè)置中文顯示字體為"Microsoft YaHei",并準備一些時間序列數(shù)據(jù)。
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import matplotlib.dates as mdate
from matplotlib.ticker import MaxNLocator
plt.rcParams["font.family"] = "Microsoft YaHei"
x = ['2023/12/20 15:23:01', '2023/12/20 15:23:02', '2023/12/20 15:23:03', ]
接下來,我們定義一個函數(shù)get_time,用于將時間字符串列表轉(zhuǎn)換為datetime對象。
def get_time(time_list):
date_object = []
for time in time_list:
date_object.append(datetime.strptime(time, "%Y/%m/%d %H:%M:%S")) # TODO 重點,把字符串轉(zhuǎn)換成時間對象
return date_object
然后,我們調(diào)用這個函數(shù)將時間字符串列表轉(zhuǎn)換為datetime對象,并打印結(jié)果。
date_object = get_time(x)
print(date_object)
創(chuàng)建時間序列圖
現(xiàn)在,我們開始創(chuàng)建一個示例時間序列圖。在這個例子中,我們將時間序列數(shù)據(jù)與位移數(shù)據(jù)繪制在同一張圖上。
# 創(chuàng)建一個示例圖形
fig, ax = plt.subplots()
ax.plot(date_object, [2, 0, 6, ], label=f'位移', ls='-') # TODO 重點,直接把datetime類型的時間賦值為x軸即可
ax.xaxis.set_major_formatter(mdate.DateFormatter('%H:%M:%S'))#設(shè)置時間標簽顯示格式
為了更好地交互,我們設(shè)置了x軸的日期格式為"%H:%M:%S",并打印出一些關(guān)于時間范圍的信息。
one_minutes = timedelta(minutes=1)
one_second = timedelta(seconds=1)
dates = mdate.drange(date_object[-1], date_object[-1] + one_minutes, one_second) # TODO 取某個時間范圍,類似range()
print([mdate.num2date(t).strftime("%Y-%m-%d %H:%M:%S") for t in dates])
x_min, x_max = ax.get_xlim() # 這里獲取到的x軸的范圍是時間的float格式,用mdate.num2date()可以轉(zhuǎn)為日期類型
print(x_min, x_max)
print("==============")
print(mdate.num2date(x_min), mdate.num2date(x_max)) # float 轉(zhuǎn) 日期
print("=============")
print(mdate.datestr2num(x[0]), mdate.datestr2num(x[2])) # 日期轉(zhuǎn) float
print(date_object[0].timestamp(), date_object[2].timestamp()) # 日期轉(zhuǎn) float 和ax.get_xlim()獲取到的不一樣
調(diào)整坐標軸范圍和刻度
我們接下來進行一些坐標軸的調(diào)整,包括去除坐標軸兩端的空白、設(shè)置整數(shù)刻度、旋轉(zhuǎn)x軸標簽等。
xticks = ax.get_xticks()
yticks = ax.get_yticks()
# 計算相鄰刻度位置之間的差異
xtick_size = xticks[1] - xticks[0]
ytick_size = yticks[1] - yticks[0]
print(">>>>>>>")
print(xticks)
print(xtick_size)
print(type(mdate.num2date(x_min)))
ax.margins(0) # 調(diào)整坐標軸兩端的空白為0
# ax.xaxis.set_major_locator(MaxNLocator(integer=True)) # (integer=True) 只有整數(shù)刻度才會顯示
# 重新設(shè)置x軸的范圍,這里不能直接用x_min, x_max,因為x_min, x_max是float類型,需要用mdate.num2date()轉(zhuǎn)為日期類型
one_second = timedelta(seconds=1) # 1秒的時間
ax.set(xlim=(mdate.num2date(x_min)-one_second, mdate.num2date(x_max) + one_second)) # 左右分別增大1秒
plt.xticks(rotation=30) # 設(shè)置x軸的刻度標簽 旋轉(zhuǎn)30度,防止標簽重疊
print(ax.get_xticks())
添加交互功能
現(xiàn)在,我們?yōu)閳D表添加鼠標拖動和滾輪滾動的交互功能。這部分代碼包括處理鼠標事件的函數(shù)和連接事件的操作。
startx = 0
starty = 0
mPress = False
# 鼠標拖動 處理事件
def call_move(event):
# print(event.name)
global mPress
global startx
global starty
mouse_x = event.x
mouse_y = event.y
axtemp = event.inaxes
if event.name == 'button_press_event':
if axtemp and event.button == 1:
if axtemp.get_legend():
legend_bbox = axtemp.get_legend().get_window_extent()
left_bottom = legend_bbox.get_points()[0]
right_top = legend_bbox.get_points()[1]
if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]:
# print("在圖例上按下鼠標")
# 在圖例上按下鼠標
mPress = False
return
# 沒有圖例的情況
# print("在 Axes 上按下鼠標")
# 在 Axes 上按下鼠標
mPress = True
startx = event.xdata
starty = event.ydata
return
elif event.name == 'button_release_event':
if axtemp and event.button == 1:
mPress = False
elif event.name == 'motion_notify_event':
if axtemp and event.button == 1 and mPress:
if axtemp.get_legend():
legend_bbox = axtemp.get_legend().get_window_extent()
left_bottom = legend_bbox.get_points()[0]
right_top = legend_bbox.get_points()[1]
if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]:
print("在圖例上移動鼠標")
# 在圖例上按下鼠標
mPress = False
return
# 沒有圖例的情況
# print("在Axes上移動鼠標")
x_min, x_max = axtemp.get_xlim()
y_min, y_max = axtemp.get_ylim()
w = x_max - x_min
h = y_max - y_min
# print(event)
# 移動
mx = event.xdata - startx
my = event.ydata - starty
# 注意這里, -mx, 因為下一次 motion事件的坐標,已經(jīng)是在本次做了移動之后的坐標系了,所以要體現(xiàn)出來
# startx=event.xdata-mx startx=event.xdata-(event.xdata-startx)=startx, 沒必要再賦值了
# starty=event.ydata-my
# print(mx,my,x_min,y_min,w,h)
axtemp.set(xlim=(x_min - mx, x_min - mx + w))
axtemp.set(ylim=(y_min - my, y_min - my + h))
fig.canvas.draw_idle() # 繪圖動作實時反映在圖像上
return
# 滾輪滾動 處理事件
def call_scroll(event):
# print(event.name)
axtemp = event.inaxes
# print('event:', event)
# print(event.xdata, event.ydata)
# 計算放大縮小后, xlim 和ylim
if axtemp:
x_min, x_max = axtemp.get_xlim()
y_min, y_max = axtemp.get_ylim()
print(x_min, x_max)
w = x_max - x_min
h = y_max - y_min
curx = event.xdata
cury = event.ydata
curXposition = (curx - x_min) / w
curYposition = (cury - y_min) / h
if event.button == 'down':
# print('befor:', w, h)
w = w * 1.1 # 1.1
h = h * 1.1
# print('down', w, h)
elif event.button == 'up':
# print('befor:', w, h)
w = w / 1.1
h = h / 1.1
# print('up', w, h)
# print(curXposition, curYposition)
newx = curx - w * curXposition
newy = cury - h * curYposition
axtemp.set(xlim=(newx, newx + w))
axtemp.set(ylim=(newy, newy + h))
# axtemp.margins(0) # 調(diào)整坐標軸兩端的空白
fig.canvas.draw_idle() # 繪圖動作實時反映在圖像上
fig.canvas.mpl_connect('scroll_event', call_scroll)
fig.canvas.mpl_connect('button_press_event', call_move)
fig.canvas.mpl_connect('button_release_event', call_move)
# fig.canvas.mpl_connect('draw_event', call_move)
fig.canvas.mpl_connect('motion_notify_event', call_move)
最后,通過plt.show()展示交互式時間序列圖。文章來源:http://www.zghlxwxcb.cn/news/detail-839411.html
plt.show()
通過以上步驟,我們成功創(chuàng)建了一個交互式的時間序列圖,支持鼠標拖動和滾輪滾動操作,方便用戶查看不同時間范圍的數(shù)據(jù)。希望這篇博客對你理解Matplotlib的交互功能有所幫助。文章來源地址http://www.zghlxwxcb.cn/news/detail-839411.html
全部代碼
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import matplotlib.dates as mdate
from matplotlib.ticker import MaxNLocator
plt.rcParams["font.family"] = "Microsoft YaHei"
x = ['2023/10/24 15:23:01', '2023/10/24 15:23:02', '2023/10/24 15:23:03', ]
def get_time(time_list):
date_object = []
for time in time_list:
date_object.append(datetime.strptime(time, "%Y/%m/%d %H:%M:%S")) # TODO 重點,把字符串轉(zhuǎn)換成時間對象
return date_object
date_object = get_time(x)
# 從字符串到時間對象
# date_string = "2023-01-01 12:30:00"
# date_object = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
print(date_object)
# 創(chuàng)建一個示例圖形
fig, ax = plt.subplots()
ax.plot(date_object, [2, 0, 6, ], label=f'位移', ls='-') # TODO 重點,直接把datetime類型的時間賦值為x軸即可
ax.xaxis.set_major_formatter(mdate.DateFormatter('%H:%M:%S'))#設(shè)置時間標簽顯示格式
one_minutes = timedelta(minutes=1)
one_second = timedelta(seconds=1)
dates = mdate.drange(date_object[-1], date_object[-1] + one_minutes, one_second) # TODO 取某個時間范圍,類似range()
print([mdate.num2date(t).strftime("%Y-%m-%d %H:%M:%S") for t in dates])
x_min, x_max = ax.get_xlim() # 這里獲取到的x軸的范圍是時間的float格式,用mdate.num2date()可以轉(zhuǎn)為日期類型
print(x_min, x_max)
print("==============")
print(mdate.num2date(x_min), mdate.num2date(x_max)) # float 轉(zhuǎn) 日期
print("=============")
print(mdate.datestr2num(x[0]), mdate.datestr2num(x[2])) # 日期轉(zhuǎn) float
print(date_object[0].timestamp(), date_object[2].timestamp()) # 日期轉(zhuǎn) float 和ax.get_xlim()獲取到的不一樣
xticks = ax.get_xticks()
yticks = ax.get_yticks()
# 計算相鄰刻度位置之間的差異
xtick_size = xticks[1] - xticks[0]
ytick_size = yticks[1] - yticks[0]
print(">>>>>>>")
print(xticks)
print(xtick_size)
print(type(mdate.num2date(x_min)))
ax.margins(0) # 調(diào)整坐標軸兩端的空白為0
# ax.xaxis.set_major_locator(MaxNLocator(integer=True)) # (integer=True) 只有整數(shù)刻度才會顯示
# 重新設(shè)置x軸的范圍,這里不能直接用x_min, x_max,因為x_min, x_max是float類型,需要用mdate.num2date()轉(zhuǎn)為日期類型
one_second = timedelta(seconds=1) # 1秒的時間
ax.set(xlim=(mdate.num2date(x_min)-one_second, mdate.num2date(x_max) + one_second)) # 左右分別增大1秒
plt.xticks(rotation=30) # 設(shè)置x軸的刻度標簽 旋轉(zhuǎn)30度,防止標簽重疊
print(ax.get_xticks())
startx = 0
starty = 0
mPress = False
# 鼠標拖動 處理事件
def call_move(event):
# print(event.name)
global mPress
global startx
global starty
mouse_x = event.x
mouse_y = event.y
axtemp = event.inaxes
if event.name == 'button_press_event':
if axtemp and event.button == 1:
if axtemp.get_legend():
legend_bbox = axtemp.get_legend().get_window_extent()
left_bottom = legend_bbox.get_points()[0]
right_top = legend_bbox.get_points()[1]
if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]:
# print("在圖例上按下鼠標")
# 在圖例上按下鼠標
mPress = False
return
# 沒有圖例的情況
# print("在 Axes 上按下鼠標")
# 在 Axes 上按下鼠標
mPress = True
startx = event.xdata
starty = event.ydata
return
elif event.name == 'button_release_event':
if axtemp and event.button == 1:
mPress = False
elif event.name == 'motion_notify_event':
if axtemp and event.button == 1 and mPress:
if axtemp.get_legend():
legend_bbox = axtemp.get_legend().get_window_extent()
left_bottom = legend_bbox.get_points()[0]
right_top = legend_bbox.get_points()[1]
if left_bottom[0] <= mouse_x <= right_top[0] and left_bottom[1] <= mouse_y <= right_top[1]:
print("在圖例上移動鼠標")
# 在圖例上按下鼠標
mPress = False
return
# 沒有圖例的情況
# print("在Axes上移動鼠標")
x_min, x_max = axtemp.get_xlim()
y_min, y_max = axtemp.get_ylim()
w = x_max - x_min
h = y_max - y_min
# print(event)
# 移動
mx = event.xdata - startx
my = event.ydata - starty
# 注意這里, -mx, 因為下一次 motion事件的坐標,已經(jīng)是在本次做了移動之后的坐標系了,所以要體現(xiàn)出來
# startx=event.xdata-mx startx=event.xdata-(event.xdata-startx)=startx, 沒必要再賦值了
# starty=event.ydata-my
# print(mx,my,x_min,y_min,w,h)
axtemp.set(xlim=(x_min - mx, x_min - mx + w))
axtemp.set(ylim=(y_min - my, y_min - my + h))
fig.canvas.draw_idle() # 繪圖動作實時反映在圖像上
return
# 滾輪滾動 處理事件
def call_scroll(event):
# print(event.name)
axtemp = event.inaxes
# print('event:', event)
# print(event.xdata, event.ydata)
# 計算放大縮小后, xlim 和ylim
if axtemp:
x_min, x_max = axtemp.get_xlim()
y_min, y_max = axtemp.get_ylim()
print(x_min, x_max)
w = x_max - x_min
h = y_max - y_min
curx = event.xdata
cury = event.ydata
curXposition = (curx - x_min) / w
curYposition = (cury - y_min) / h
if event.button == 'down':
# print('befor:', w, h)
w = w * 1.1 # 1.1
h = h * 1.1
# print('down', w, h)
elif event.button == 'up':
# print('befor:', w, h)
w = w / 1.1
h = h / 1.1
# print('up', w, h)
# print(curXposition, curYposition)
newx = curx - w * curXposition
newy = cury - h * curYposition
axtemp.set(xlim=(newx, newx + w))
axtemp.set(ylim=(newy, newy + h))
# axtemp.margins(0) # 調(diào)整坐標軸兩端的空白
fig.canvas.draw_idle() # 繪圖動作實時反映在圖像上
fig.canvas.mpl_connect('scroll_event', call_scroll)
fig.canvas.mpl_connect('button_press_event', call_move)
fig.canvas.mpl_connect('button_release_event', call_move)
# fig.canvas.mpl_connect('draw_event', call_move)
fig.canvas.mpl_connect('motion_notify_event', call_move)
plt.show()
到了這里,關(guān)于Python【Matplotlib】交互式時間序列繪圖,將x軸設(shè)置為日期時間格式并和鼠標拖動縮放相結(jié)合的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!