Matplotlib:使用日期时间轴进行绘图时,如何跳过几个小时的范围? [英] Matplotlib: How to skip a range of hours when plotting with a datetime axis?

查看:155
本文介绍了Matplotlib:使用日期时间轴进行绘图时,如何跳过几个小时的范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一份金融工具的逐笔报价数据,我正在尝试使用 matplotlib 进行绘制.我正在使用 pandas ,并且数据已通过 DatetimeIndex 进行索引.

问题是,当我尝试绘制多个交易日时,我无法跳过收市价至第二天开盘之间的时间范围(请参见示例),当然我对此不感兴趣.

是否有一种方法可以使matplotlib忽略此问题,而仅将收盘报价与第二天的开盘价粘在一起"?我尝试过一个自定义的时间范围:

  plt.xticks(time_range) 

但是结果是一样的.任何想法如何做到这一点?

 #示例数据工具= pd.DataFrame(数据= {'约会时间': [dt.datetime.strptime('2018-01-11 11:00:11','%Y-%m-%d%H:%M:%S'),dt.datetime.strptime('2018-01-11 13:02:17','%Y-%m-%d%H:%M:%S'),dt.datetime.strptime('2018-01-11 16:59:14','%Y-%m-%d%H:%M:%S'),dt.datetime.strptime('2018-01-12 11:00:11','%Y-%m-%d%H:%M:%S'),dt.datetime.strptime('2018-01-12 13:15:24','%Y-%m-%d%H:%M:%S'),dt.datetime.strptime('2018-01-12 16:58:43','%Y-%m-%d%H:%M:%S')],'价格':[127.6、128.1、127.95、129.85、129.7、131.2],'音量':[725、146、48、650、75、160]}).set_index('Datetime')plt.figure(figsize =(10,5))顶部= plt.subplot2grid((4,4),(0,0),行跨= 3,列跨= 4)底部= plt.subplot2grid((4,4),(3,0),rowspan = 1,colspan = 4)top.plot(instrument.index,工具['价格'])bottom.bar(instrument.index,instrument ['Volume'],0.005)top.xaxis.get_major_ticks()top.axes.get_xaxis().set_visible(False)top.set_title('范例')top.set_ylabel('价格')bottom.set_ylabel('音量') 

解决方案

TL; DR

替换matplotlib绘图功能:

  top.plot(instrument.index,工具['价格'])bottom.bar(instrument.index,instrument ['Volume'],0.005) 

有了这些:

  top.plot(range(instrument.index.size),工具['Price'])bottom.bar(range(instrument.index.size),instrument ['Volume'],width = 1) 

或者使用这些熊猫绘图功能(只有x轴限制看起来有所不同):

  instrument ['Price'].plot(use_index = False,ax = top)instrument ['Volume'].plot.bar(width = 1,ax = bottom) 

通过与 sharex = True 共享x轴来对齐两个图,并使用数据框索引根据需要设置刻度线,如下面的示例所示.




首先让我创建一个样本数据集,并显示使用matplotlib绘图函数对其进行绘图时的外观,例如在您的示例中,使用 DatetimeIndex 作为x变量.


创建示例数据集

示例数据是使用



通过使用一定范围的整数,使用matplotlib绘制数据而没有任何间隙

可以通过简单地忽略 DatetimeIndex 并使用整数范围来解决这些差距的问题.然后,大部分工作在于创建适当的刻度标签.这是一个示例:

 #使用一些其他格式创建图形和matplotlib图图,(顶部,机器人)= plt.subplots(2,1,sharex = True,图大小=(10,5),gridspec_kw = dict(height_ratios = [0.75,0.25]))top.plot(range(df.index.size),df ['Price'])top.set_title('Matplotlib绘图无间隙',pad = 20,size = 14,weight ='semibold')top.set_ylabel('Price',labelpad = 10)top.grid(axis ='x',alpha = 0.3)bot.bar(范围(df.index.size),df ['Volume'],width = 1)bot.set_ylabel('Volume',labelpad = 10)#设置固定的主要和次要刻度线位置ticks_date = df.index.indexer_at_time('09:30')ticks_time = np.arange(df.index.size)[df.index.minute == 0] [:: 2]#小时bot.set_xticks(ticks_date)bot.set_xticks(ticks_time,minor = True)#设置主要和次要刻度标签labels_date = [maj_tick.strftime('\ n%d-%b').replace('\ n0','\ n')df.index [ticks_date]中的maj_ticklabels_time = [min_tick.strftime('%I%p').lstrip('0').lower()为df.index [ticks_time]中的min_tickbot.set_xticklabels(labels_date)bot.set_xticklabels(labels_time,minor = True)bot.figure.autofmt_xdate(rotation = 0,ha ='center',which ='both') 



为交互式绘图创建动态刻度线

如果您想使用matplotlib的交互界面(带有pan/zoom),则需要使用 matplotlib报价器模块.这是一个如何设置刻度线的示例,其中主要的刻度线是固定的,并且格式如上,但是次要的刻度线是在您放大/缩小绘图时自动生成的:

 #设置固定的主要刻度线位置和自动的次要刻度线位置ticks_date = df.index.indexer_at_time('09:30')bot.set_xticks(ticks_date)bot.xaxis.set_minor_locator(ticker.AutoLocator())#格式化主要刻度标签labels_date = [maj_tick.strftime('\ n%d-%b').replace('\ n0','\ n')df.index [ticks_date]中的maj_tickbot.set_xticklabels(labels_date)#格式化次要刻度标签def min_label(x,pos):如果0< = x<df.index.size:返回df.index [int(x)].strftime('%H:%M')min_fmtr = ticker.FuncFormatter(最小标签)bot.xaxis.set_minor_formatter(min_fmtr)bot.figure.autofmt_xdate(rotation = 0,ha ='center',which ='both') 


文档:日期时间字符串格式代码

I have tick-by-tick data of a financial instrument, which I am trying to plot using matplotlib. I am working with pandas and the data is indexed with DatetimeIndex.

The problem is, when I try to plot multiple trading days I can't skip the range of time between the market closing time and next day's opening (see the example), which of course I am not interested in.

Is there a way to make matplotlib ignore this and just "stick" together the closing quote with the following day's opening? I tried to pass a custom range of time:

plt.xticks(time_range)

But the result is the same. Any ideas how to do this?

# Example data
instrument = pd.DataFrame(data={
    'Datetime': [
        dt.datetime.strptime('2018-01-11 11:00:11', '%Y-%m-%d %H:%M:%S'),
        dt.datetime.strptime('2018-01-11 13:02:17', '%Y-%m-%d %H:%M:%S'),
        dt.datetime.strptime('2018-01-11 16:59:14', '%Y-%m-%d %H:%M:%S'),

        dt.datetime.strptime('2018-01-12 11:00:11', '%Y-%m-%d %H:%M:%S'),
        dt.datetime.strptime('2018-01-12 13:15:24', '%Y-%m-%d %H:%M:%S'),
        dt.datetime.strptime('2018-01-12 16:58:43', '%Y-%m-%d %H:%M:%S')
    ],
    'Price': [127.6, 128.1, 127.95, 129.85, 129.7, 131.2],
    'Volume': [725, 146, 48, 650, 75, 160]
}).set_index('Datetime')

plt.figure(figsize=(10,5))
top = plt.subplot2grid((4,4), (0, 0), rowspan=3, colspan=4)
bottom = plt.subplot2grid((4,4), (3,0), rowspan=1, colspan=4)
top.plot(instrument.index, instrument['Price'])
bottom.bar(instrument.index, instrument['Volume'], 0.005) 

top.xaxis.get_major_ticks()
top.axes.get_xaxis().set_visible(False)
top.set_title('Example')
top.set_ylabel('Price')
bottom.set_ylabel('Volume')

解决方案

TL;DR

Replace the matplotlib plotting functions:

top.plot(instrument.index, instrument['Price'])
bottom.bar(instrument.index, instrument['Volume'], 0.005)

With these ones:

top.plot(range(instrument.index.size), instrument['Price'])
bottom.bar(range(instrument.index.size), instrument['Volume'], width=1)

Or with these pandas plotting functions (only the x-axis limits will look different):

instrument['Price'].plot(use_index=False, ax=top)
instrument['Volume'].plot.bar(width=1, ax=bottom)

Align both plots by sharing the x-axis with sharex=True and set up the ticks as you would like them using the dataframe index, as shown in the example further below.




Let me first create a sample dataset and show what it looks like if I plot it using matplotlib plotting functions like in your example where the DatetimeIndex is used as the x variable.


Create sample dataset

The sample data is created using the pandas_market_calendars package to create a realistic DatetimeIndex with a minute-by-minute frequency that spans several weekdays and a weekend.

import numpy as np                        # v 1.19.2
import pandas as pd                       # v 1.1.3
import matplotlib.pyplot as plt           # v 3.3.2
import matplotlib.ticker as ticker
import pandas_market_calendars as mcal    # v 1.6.1

# Create datetime index with a 'minute start' frequency based on the New
# York Stock Exchange trading hours (end date is inclusive)
nyse = mcal.get_calendar('NYSE')
nyse_schedule = nyse.schedule(start_date='2021-01-07', end_date='2021-01-11')
nyse_dti = mcal.date_range(nyse_schedule, frequency='1min', closed='left')\
               .tz_convert(nyse.tz.zone)
# Remove timestamps of closing times to create a 'period start' datetime index
nyse_dti = nyse_dti.delete(nyse_dti.indexer_at_time('16:00'))

# Create sample of random data consisting of opening price and
# volume of financial instrument traded for each period
rng = np.random.default_rng(seed=1234)  # random number generator
price_change = rng.normal(scale=0.1, size=nyse_dti.size)
price_open = 127.5 + np.cumsum(price_change)
volume = rng.integers(100, 10000, size=nyse_dti.size)
df = pd.DataFrame(data=dict(Price=price_open, Volume=volume), index=nyse_dti)

df.head()

#                             Price       Volume
#  2021-01-07 09:30:00-05:00  127.339616  7476
#  2021-01-07 09:31:00-05:00  127.346026  3633
#  2021-01-07 09:32:00-05:00  127.420115  1339
#  2021-01-07 09:33:00-05:00  127.435377  3750
#  2021-01-07 09:34:00-05:00  127.521752  7354

Plot data with matplotlib using the DatetimeIndex

This sample data can now be plotted using matplotlib plotting functions like in your example, but note that the subplots are created by using plt.subplots with the sharex=True argument. This aligns the line with the bars correctly and makes it possible to use the interactive interface of matplotlib with both subplots.

# Create figure and plots using matplotlib functions
fig, (top, bot) = plt.subplots(2, 1, sharex=True, figsize=(10,5),
                               gridspec_kw=dict(height_ratios=[0.75,0.25]))
top.plot(df.index, df['Price'])
bot.bar(df.index, df['Volume'], 0.0008)

# Set title and labels
top.set_title('Matplotlib plots with unwanted gaps', pad=20, size=14, weight='semibold')
top.set_ylabel('Price', labelpad=10)
bot.set_ylabel('Volume', labelpad=10);



Plot data with matplotlib without any gaps by using a range of integers

The problem of these gaps can be solved by simply ignoring the DatetimeIndex and using a range of integers instead. Most of the work then lies in creating appropriate tick labels. Here is an example:

# Create figure and matplotlib plots with some additional formatting
fig, (top, bot) = plt.subplots(2, 1, sharex=True, figsize=(10,5),
                               gridspec_kw=dict(height_ratios=[0.75,0.25]))
top.plot(range(df.index.size), df['Price'])
top.set_title('Matplotlib plots without any gaps', pad=20, size=14, weight='semibold')
top.set_ylabel('Price', labelpad=10)
top.grid(axis='x', alpha=0.3)
bot.bar(range(df.index.size), df['Volume'], width=1)
bot.set_ylabel('Volume', labelpad=10)

# Set fixed major and minor tick locations
ticks_date = df.index.indexer_at_time('09:30')
ticks_time = np.arange(df.index.size)[df.index.minute == 0][::2] # step in hours
bot.set_xticks(ticks_date)
bot.set_xticks(ticks_time, minor=True)

# Format major and minor tick labels
labels_date = [maj_tick.strftime('\n%d-%b').replace('\n0', '\n')
               for maj_tick in df.index[ticks_date]]
labels_time = [min_tick.strftime('%I %p').lstrip('0').lower()
               for min_tick in df.index[ticks_time]]
bot.set_xticklabels(labels_date)
bot.set_xticklabels(labels_time, minor=True)
bot.figure.autofmt_xdate(rotation=0, ha='center', which='both')



Create dynamic ticks for interactive plots

If you like to use the interactive interface of matplotlib (with pan/zoom), you will need to use locators and formatters from the matplotlib ticker module. Here is an example of how to set the ticks, where the major ticks are fixed and formatted like above but the minor ticks are generated automatically as you zoom in/out of the plot:

# Set fixed major tick locations and automatic minor tick locations
ticks_date = df.index.indexer_at_time('09:30')
bot.set_xticks(ticks_date)
bot.xaxis.set_minor_locator(ticker.AutoLocator())

# Format major tick labels
labels_date = [maj_tick.strftime('\n%d-%b').replace('\n0', '\n')
               for maj_tick in df.index[ticks_date]]
bot.set_xticklabels(labels_date)

# Format minor tick labels
def min_label(x, pos):
    if 0 <= x < df.index.size:
        return df.index[int(x)].strftime('%H:%M')
min_fmtr = ticker.FuncFormatter(min_label)
bot.xaxis.set_minor_formatter(min_fmtr)

bot.figure.autofmt_xdate(rotation=0, ha='center', which='both')


Documentation: example of an alternative solution; datetime string format codes

这篇关于Matplotlib:使用日期时间轴进行绘图时,如何跳过几个小时的范围?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆