Python【Matplotlib】交互式时间序列绘图,将x轴设置为日期时间格式并和鼠标拖动缩放相结合

这篇具有很好参考价值的文章主要介绍了Python【Matplotlib】交互式时间序列绘图,将x轴设置为日期时间格式并和鼠标拖动缩放相结合。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

上篇博客:python【matplotlib】鼠标拖动滚动缩放坐标范围和拖动图例共存,得到启发,我们已经可以通过鼠标拖动缩放坐标范围和移动图例,来实现动态交互式绘图了,对于x轴是时间序列的绘图需求,能否也实现动态交互式绘图呢?
答案是肯定的,接下来我将详细描述其实现的方式。

效果

python plot x轴日期跳变,Python,python,matplotlib

实现步骤

准备工作

首先,我们需要导入必要的库,包括datetime、timedelta、matplotlib.pyplot等。同时,我们设置中文显示字体为"Microsoft YaHei",并准备一些时间序列数据。

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', ]

接下来,我们定义一个函数get_time,用于将时间字符串列表转换为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 重点,把字符串转换成时间对象
    return date_object

然后,我们调用这个函数将时间字符串列表转换为datetime对象,并打印结果。

date_object = get_time(x)
print(date_object)

创建时间序列图

现在,我们开始创建一个示例时间序列图。在这个例子中,我们将时间序列数据与位移数据绘制在同一张图上。

# 创建一个示例图形
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'))#设置时间标签显示格式

为了更好地交互,我们设置了x轴的日期格式为"%H:%M:%S",并打印出一些关于时间范围的信息。

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()可以转为日期类型
print(x_min, x_max)
print("==============")
print(mdate.num2date(x_min), mdate.num2date(x_max))     # float 转 日期
print("=============")
print(mdate.datestr2num(x[0]), mdate.datestr2num(x[2]))     # 日期转 float
print(date_object[0].timestamp(), date_object[2].timestamp())   #  日期转 float 和ax.get_xlim()获取到的不一样

调整坐标轴范围和刻度

我们接下来进行一些坐标轴的调整,包括去除坐标轴两端的空白、设置整数刻度、旋转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)     # 调整坐标轴两端的空白为0
# ax.xaxis.set_major_locator(MaxNLocator(integer=True))     # (integer=True) 只有整数刻度才会显示

# 重新设置x轴的范围,这里不能直接用x_min, x_max,因为x_min, x_max是float类型,需要用mdate.num2date()转为日期类型
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)     #  设置x轴的刻度标签 旋转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事件的坐标,已经是在本次做了移动之后的坐标系了,所以要体现出来
            # 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)  # 调整坐标轴两端的空白
        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()展示交互式时间序列图。

plt.show()

通过以上步骤,我们成功创建了一个交互式的时间序列图,支持鼠标拖动和滚轮滚动操作,方便用户查看不同时间范围的数据。希望这篇博客对你理解Matplotlib的交互功能有所帮助。文章来源地址https://www.toymoban.com/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 重点,把字符串转换成时间对象
    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)

# 创建一个示例图形
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'))#设置时间标签显示格式


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()可以转为日期类型
print(x_min, x_max)
print("==============")
print(mdate.num2date(x_min), mdate.num2date(x_max))     # float 转 日期
print("=============")
print(mdate.datestr2num(x[0]), mdate.datestr2num(x[2]))     # 日期转 float
print(date_object[0].timestamp(), date_object[2].timestamp())   #  日期转 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)     # 调整坐标轴两端的空白为0
# ax.xaxis.set_major_locator(MaxNLocator(integer=True))     # (integer=True) 只有整数刻度才会显示

# 重新设置x轴的范围,这里不能直接用x_min, x_max,因为x_min, x_max是float类型,需要用mdate.num2date()转为日期类型
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)     #  设置x轴的刻度标签 旋转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事件的坐标,已经是在本次做了移动之后的坐标系了,所以要体现出来
            # 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)  # 调整坐标轴两端的空白
        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()

到了这里,关于Python【Matplotlib】交互式时间序列绘图,将x轴设置为日期时间格式并和鼠标拖动缩放相结合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python-OpenCV中的图像处理-GrabCut算法交互式前景提取

    cv2.grabCut(img: Mat, mask: typing.Optional[Mat], rect, bgdModel, fgdModel, iterCount, mode=…) img:输入图像 mask:掩模图像,用来确定那些区域是背景,前景,可能是前景/背景等。 可以设置为: cv2.GC_BGD,cv2.GC_FGD,cv2.GC_PR_BGD,cv2.GC_PR_FGD,或者直接输入 0,1,2,3 也行。 rect :包含前景的矩形,格式为

    2024年02月13日
    浏览(34)
  • python:基于GeoPandas和GeoViews库将GEDI激光高程数据映射到交互式地图

    作者:CSDN @ _养乐多_ 本文将介绍 GEDI(Global Ecosystem Dynamics Investigation)激光雷达数据某数据点波形数据提取,并绘制图表,添加其他图表元素并使图表具有交互性。 在本文中,我们将探索如何打开、读取和处理GEDI数据,并利用地理信息处理库GeoPandas和地理空间数据可视化库

    2024年02月15日
    浏览(32)
  • 基于python下selenium库实现交互式图片保存操作(批量保存浏览器中的图片)

    Selenium是最广泛使用的开源Web UI(用户界面)自动化测试套件之一,可以通过编程与浏览量的交互式操作对网页进行自动化控制。基于这种操作进行数据保存操作,尤其是在图像数据的批量保存上占据优势。本博文基于selenium 与jupyterlab实现批量保存浏览器搜索到的图片。 Se

    2024年01月24日
    浏览(49)
  • 交互式shell与非交互式shell,反弹shell

    交互shell就是shell等待你的输入,并且立即执行你提交的命令。 这种模式被称作交互式是因为shell与用户进行交互。 这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当签退后,shell也终止了。 需要进行信息交互,例如输入某个信息 会返回信息 你需要对其输

    2024年02月02日
    浏览(42)
  • 【OpenCV-Python】——单/多模板匹配&分水岭算法图像分割&图像金字塔&交互式前景提取

    目录 前言: 1、模板匹配 1.1 单目标匹配 1.2 多目标匹配 2、图像分割

    2024年02月07日
    浏览(36)
  • Selenium4+Python3 - Iframe、Select控件、交互式弹出框、执行JS、Cookie操作

    iframe识别: 语法: driver.switch_to.frame(‘方式’) 1、常见处理方法三种 index:下标 name:id或name属性的值 webelement:元素 2、通过下标进入 进入第一个iframe: 3、通过id或name属性的值进入 通过id或name属性的值进入指定的iframe: 4、通过iframe元素进入iframe 通过iframe元素进入指定i

    2024年02月04日
    浏览(44)
  • 人机交互学习-5 交互式系统的需求

    关于目标产品的一种陈述,它指定了产品应做什么,或者应如何工作 应该是具体、明确和无歧义的 搜集数据 解释数据 提取需求 注:了解 功能不同 智能冰箱:应能够提示黄油已用完 字处理器:系统应支持多种格式 物理条件不同 移动设备运行的系统应尽可能小,屏幕显示限

    2024年02月09日
    浏览(44)
  • 人机交互学习-6 交互式系统的设计

    Allan Cooper建议不要过早地把重点放在小细节、小部件和精细的交互上会妨碍产品的设计,应先站在一个高层次上关注用户界面和相关行为的整体结构 Allan Cooper提出的交互框架不仅 定义了高层次的屏幕布局 ,同时定义了 产品的工作流、行为和组织 。它包括了6个主要步骤:

    2024年02月09日
    浏览(45)
  • 交互式shell

    交互式模式就是shell等待用户的输入,并且执行用户提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当用户签退后,shell也终止了。 shell也可以运行在另外一种模式:非交互式模式。在这种模

    2024年02月02日
    浏览(36)
  • Pyspark交互式编程

    Pyspark交互式编程 有该数据集Data01.txt 该数据集包含了某大学计算机系的成绩,数据格式如下所示: 根据给定的数据集,在pyspark中通过编程来完成以下内容: 该系总共有多少学生; (提前启动好pyspark) 该系共开设了多少门课程; Tom同学的总成绩平均分是多少; 求每名同学的

    2023年04月08日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包