2 x轴,1个索引,1个使用时间戳点,y

时间:2017-03-07 10:35:45

标签: python matplotlib plot jupyter-notebook

我有一个时间序列,我希望有2个x轴,1个显示数据索引,1个是相应的时间戳。一种方法是使用plt.plot_date,然后再次对索引进行绘制。但是:

  1. 必须有更好的方法来实现它
  2. 针对日期的情节不会获取所有数据(您可以看到"差距"下面)。
  3. dataplot vs indexplot.

    红色是日期图,蓝色是指数图。

    • 有没有办法可以初步设置"日期" xaxis等于 "指数" xaxis,然后将tick标签更改为相应的 日期吗

    • 你也知道为什么我在使用时会得到间隙行为 plt.plot_date

    时间戳的原始格式位于strarray(['2017-02-14 05:48:00', '2017-02-14 05:49:00', '2017-02-14 05:50:00', '2017-02-14 05:51:00', '2017-02-14 05:52:00', '2017-02-14 05:53:00',...]

    x = [dt.datetime.strptime(d,'%Y-%m-%d %H:%M:%S') for d in dates_test]
    x = [mdates.date2num(i) for i in x]
    fig, ax = plt.subplots(nrows=3, figsize=(8, 6), sharex = True)
    
    ax[0].plot(xaxis, errors)
    ax4 = ax[0].twiny()
    ax4.plot_date(x, errors, fmt="r-")
    ax4.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m \n%H:%M'))
    

2 个答案:

答案 0 :(得分:1)

由于时间戳之间的差距,这比最初的想法更难。使用date2num而不是将时间戳视为浮动,可能更容易将它们视为str:

dates_test是str格式的时间戳数组。

fig, ax = plt.subplots(nrows=3, figsize=(8, 6), sharex = True)

ax[0].plot(xaxis, errors)
ax4 = ax[0].twiny()
ax4.plot(xaxis, errors, "r-")
ax4.xaxis.set_major_locator(majorLocator)
ax4.xaxis.set_minor_locator(minorLocator)

majorDates = [dates_test[i] for i in range(0,len(dates_test),250)]
majorDates = [dates_test[0]] + majorDates
ax4.set_xticklabels(majorDates, rotation = 'vertical')

enter image description here

答案 1 :(得分:0)

生成与您类似的数据后:

import numpy as np
import matplotlib.pyplot as plt
plt.ioff()
import matplotlib.dates as mdates
import datetime as dt
import time

# --- Build date blocks ---
now = time.time() # first time
delta_time = 60 # distance between points in seconds

n_jumps = 10 # number of pieces with no data
avg_jump_size = 60*60*24 # 86400 s = 1 day
jumps = abs(np.random.normal(avg_jump_size/2.,
                             avg_jump_size/2,
                             n_jumps+1)) + avg_jump_size/2.
# `abs` just to make sure the jump is positive, and ` + avg_jump_size/2.` to
# make sure it's larger than the time step.

avg_n_poins_per_block = 2*60*60/delta_time # 2 hours of acquisition per block
blocks_sizes = abs(np.random.normal(avg_n_poins_per_block/2.,
                                    avg_n_poins_per_block/2.,
                                    n_jumps+1)) + avg_n_poins_per_block/2.

times = np.array([]) # array to place all dates
for i in range(n_jumps):
    block = np.arange(now, now+delta_time*blocks_sizes[i], delta_time)
    times = np.concatenate((times, block))
    now += jumps[i]
# last block
block = np.arange(now, now+delta_time*blocks_sizes[-1], delta_time)
times = np.concatenate((times, block))

def time2mdate_str(number=None):
    """
    Convert a time given by `time.time()` to a `datetime` instance
    from `matplotlib.mdate`.
    """
    if number is None:
        number = time.time()
    # time.time() returns the number of seconds since the Epoch
    # (1970-01-01 00:00:00).
    # But mdate represents time in days since 0001-01-01 00:00:00, plus 1 day.
    # (http://matplotlib.org/api/dates_api.html)
    # So we convert to days:
    number /= 60*60*24
    # and then we add the 1969 years:
    # http://www.rapidtables.com/calc/time/days-in-year.htm
    number += 1969*365.2425
    # and now it should be off by only (!) ~11h (close enough)
    a = mdates.num2date(number)
    return a

# list of time strings:
dates_test = [time2mdate_str(t).strftime("%Y-%m-%d %H:%M:%S") for t in times]
# some random data:
errors = np.random.normal(0.025, 0.01, len(times))
xaxis = np.arange(len(errors)) # omiter

# Original code:
x = [dt.datetime.strptime(d,'%Y-%m-%d %H:%M:%S') for d in dates_test]
x = [mdates.date2num(i) for i in x]
fig, ax = plt.subplots(nrows=3, figsize=(8, 6), sharex = True)

ax[0].plot(xaxis, errors)
ax4 = ax[0].twiny()
ax4.plot_date(x, errors, fmt="r-")
ax4.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m \n%H:%M'))

fig.tight_layout()
fig.show()

enter image description here

我的第一个想法是分隔每个数据块(您仍然可以减去初始小时,使第一个x值为00:00):

# break into blocks:
x = np.array(x)
deltas = x[1:] - x[:-1]
# assume there's no break right after the first value and
# find where the difference between consecutive times is larger
# than 1.1*deltas[0] (margin of 1.1* for float comparison)
break_indexes = np.where(deltas > deltas[0]*1.1)[0]+1
# add borders (will be useful for iterating over the list):
break_indexes = np.concatenate(([0],break_indexes,[-1]))
n_jumps = len(break_indexes) - 1
# offset to make sure each line does not overlap with another:
offset = 1.5*np.max(errors)

fig2, ax2 = plt.subplots(figsize=(8, 6))
for i in range(n_jumps):
    i_0 = break_indexes[i]
    slice_ = slice(i_0, break_indexes[i+1])
    ax2.plot(x[slice_]-x[i_0]+x[0], errors[slice_]+offset*i, label=dates_test[i_0])
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
ax2.set_xlim(x[0])
ax2.legend()

fig2.tight_layout()
fig2.show()

Pieces separated

然后我认为你可以发现将每个时间块的开头用作刻度是有用的,尽管这会导致标签的叠加:

# use blocks as ticks:
fig3, ax3 = plt.subplots(nrows=3, figsize=(8, 6), sharex = True)
ax3[0].plot(xaxis, errors)
ax3 = ax3[0].twiny()
ax3.plot_date(x, errors, fmt="r-")
ax3.set_xticks(x[break_indexes][:-1])
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m \n%H:%M'))

fig3.tight_layout()
fig3.show()

Useful ticks

最后我想你可能想要删除这些空白但是将标签保存在正确的位置:

# use blocks as ticks and eliminate the gaps:
# (could have used the inverse of this to create those gaps)
x_without_gaps = x.copy()
delta = x[1] - x[0]
for i in range(1,n_jumps):
    i0 = break_indexes[i]
    i1 = break_indexes[i+1]
    if i1 == -1:
        i1 = None
    x_without_gaps[i0:i1] -= x_without_gaps[i0] - x_without_gaps[i0-1] - delta

#x_without_gaps += x[0]
fig4, ax4 = plt.subplots(nrows=3, figsize=(8, 6), sharex = True)
ax4[0].plot(xaxis, errors)
ax4[0].set_xlim(0,len(errors)-1)
ax5 = ax4[0].twiny()
ax5.plot_date(x_without_gaps, errors, fmt="r-")
ax5.set_xticks(x_without_gaps[break_indexes][:-1])
ax5.set_xticklabels([date.strftime('%d/%m \n%H:%M') for date in 
                 mdates.num2date(x[break_indexes][:-1])])
# the following line would clear the values placed!
#ax5.xaxis.set_major_formatter(mdates.DateFormatter('%d/%m \n%H:%M'))
fig4.tight_layout()
fig4.show()

enter image description here

错误的刻度标签的旧结果(数据在每次运行时随机化,因此新示例上的标签明显不同,但我不想重新上传每个图像 - 信任或尝试自己并验证= op) : Probably what is wanted 请注意,您无法真正看到红色背后的蓝线,因此匹配似乎没问题 - 只要您设置xlim以确保这一点。 Detail of previous picture