Python 如何在Matplotlib中使用时区处理时间?

Python 如何在Matplotlib中使用时区处理时间?,python,matplotlib,plot,timezone,python-datetime,Python,Matplotlib,Plot,Timezone,Python Datetime,我有一些数据点,其横坐标是带有时区的datetime.datetime对象(它们的tzinfo恰好是通过MongoDB获得的bson.tz_util.FixedOffset) 当我用scatter()绘制它们时,刻度标签的时区是什么 更改matplotlibrc中的时区不会更改显示的绘图中的任何内容(我一定误解了Matplotlib文档中的内容) 我用plot()(而不是scatter())做了一些实验。当给定一个日期时,它将绘制该日期并忽略时区。然而,当给定多个日期时,它使用固定的时区,但如何

我有一些数据点,其横坐标是带有时区的
datetime.datetime
对象(它们的
tzinfo
恰好是通过MongoDB获得的
bson.tz_util.FixedOffset

当我用
scatter()
绘制它们时,刻度标签的时区是什么

更改
matplotlibrc
中的
时区
不会更改显示的绘图中的任何内容(我一定误解了Matplotlib文档中的内容)

我用
plot()
(而不是
scatter()
)做了一些实验。当给定一个日期时,它将绘制该日期并忽略时区。然而,当给定多个日期时,它使用固定的时区,但如何确定?我在文档中找不到任何东西


最后,
plot\u date()
应该是这些时区问题的解决方案吗?

这个问题已经在评论中得到了回答。然而,我自己仍在与时区作斗争。为了弄清楚,我尝试了所有的组合。我认为您有两种主要的方法,这取决于您的datetime对象是否已经在所需的时区或在不同的时区,我尝试在下面描述它们。有可能我还是漏掉了什么

时间戳(datetime对象):UTC格式 所需显示:在特定时区

  • 将设置为所需的显示时区(默认设置为
    rcParam['timezone']
    ,这对我来说是UTC)
时间戳(datetime对象):在特定时区中 所需显示:在不同的特定时区中

  • 为绘图函数datetime对象提供相应的时区(
    tzinfo=
  • 将rcParams['timezone']设置为所需的显示时区
  • 使用dateformatter(即使您对格式感到满意,)
如果您使用的是plot_date(),也可以传入tz关键字,但对于散点图,这是不可能的

当源数据包含unix时间戳时,如果要使用matplotlib时区功能,请务必明智地从
datetime.datetime.utcfromtimestamp()
和不使用utc:
fromtimestamp()
中进行选择

这是我做的实验(在本例中是在scatter()上),可能有点难以理解,但这里只是写给任何关心的人。注意第一个点出现的时间(对于每个子批次,x轴不会在同一时间开始):

源代码:

import time,datetime,matplotlib
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from dateutil import tz


#y
data = np.array([i for i in range(24)]) 

#create a datetime object from the unix timestamp 0 (epoch=0:00 1 jan 1970 UTC)
start = datetime.datetime.fromtimestamp(0)  
# it will be the local datetime (depending on your system timezone) 
# corresponding to the epoch
# and it will not have a timezone defined (standard python behaviour)

# if your data comes as unix timestamps and you are going to work with
# matploblib timezone conversions, you better use this function:
start = datetime.datetime.utcfromtimestamp(0)   

timestamps = np.array([start + datetime.timedelta(hours=i) for i in range(24)])
# now add a timezone to those timestamps, US/Pacific UTC -8, be aware this
# will not create the same set of times, they do not coincide
timestamps_tz = np.array([
    start.replace(tzinfo=tz.gettz('US/Pacific')) + datetime.timedelta(hours=i)
    for i in range(24)])


fig = plt.figure(figsize=(10.0, 15.0))


#now plot all variations
plt.subplot(711)
plt.scatter(timestamps, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().set_title("1 - tzinfo NO, xaxis_date = NO, formatter=NO")


plt.subplot(712)
plt.scatter(timestamps_tz, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().set_title("2 - tzinfo YES, xaxis_date = NO, formatter=NO")


plt.subplot(713)
plt.scatter(timestamps, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().xaxis_date('US/Pacific')
plt.gca().set_title("3 - tzinfo NO, xaxis_date = YES, formatter=NO")


plt.subplot(714)
plt.scatter(timestamps, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().xaxis_date('US/Pacific')
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M(%d)'))
plt.gca().set_title("4 - tzinfo NO, xaxis_date = YES, formatter=YES")


plt.subplot(715)
plt.scatter(timestamps_tz, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().xaxis_date('US/Pacific')
plt.gca().set_title("5 - tzinfo YES, xaxis_date = YES, formatter=NO")


plt.subplot(716)
plt.scatter(timestamps_tz, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().set_title("6 - tzinfo YES, xaxis_date = NO, formatter=YES")
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M(%d)'))


plt.subplot(717)
plt.scatter(timestamps_tz, data)
plt.gca().set_xlim([datetime.datetime(1970,1,1), datetime.datetime(1970,1,2,12)])
plt.gca().xaxis_date('US/Pacific')
plt.gca().set_title("7 - tzinfo YES, xaxis_date = YES, formatter=YES")
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%H:%M(%d)'))

fig.tight_layout(pad=4)
plt.subplots_adjust(top=0.90)

plt.suptitle(
    'Matplotlib {} with rcParams["timezone"] = {}, system timezone {}"
    .format(matplotlib.__version__,matplotlib.rcParams["timezone"],time.tzname))

plt.show()

如果像我一样,您在试图让时区感知数据帧正确绘制时遇到了这个问题,@pseyfert关于使用带时区的格式化程序的评论也是正确的。以下是熊猫图的一个示例,显示了从EST过渡到EDT时的一些点:

df = pd.DataFrame(
    dict(y=np.random.normal(size=5)),
    index=pd.DatetimeIndex(
        start='2018-03-11 01:30',
        freq='15min',
        periods=5,
        tz=pytz.timezone('US/Eastern')))
请注意,当我们过渡到夏令时,时区是如何变化的:

> [f'{t:%T %Z}' for t in df.index]
['01:30:00 EST',
 '01:45:00 EST',
 '03:00:00 EDT',
 '03:15:00 EDT',
 '03:30:00 EDT']
现在,绘制它:

df.plot(style='-o')
formatter = mdates.DateFormatter('%m/%d %T %Z', tz=df.index.tz)
plt.gca().xaxis.set_major_formatter(formatter)
plt.show()


PS

不确定为什么某些日期(最早的日期)看起来像粗体,但matplotlib的内部可能会多次渲染标签,并且位置会改变一到两个像素。。。以下内容确认对相同的时间戳多次调用格式化程序:

class Foo(mdates.DateFormatter):
    def __init__(self, *args, **kwargs):
        super(Foo, self).__init__(*args, **kwargs)

    def strftime(self, dt, fmt=None):
        s = super(Foo, self).strftime(dt, fmt=fmt)
        print(f'out={s} for dt={dt}, fmt={fmt}')
        return s
并查看以下内容的输出:

df.plot(style='-o')
formatter = Foo('%F %T %Z', tz=df.index.tz)
plt.gca().xaxis.set_major_formatter(formatter)
plt.show()

它看起来像是轴。xaxis_数据(tz)将设置该时区中要显示的所有日期。如果你不显式设置时区,它看起来会将时间转换为你的本地时区(我只是浏览了一下代码,我可能有点不对劲)。是的,看起来文档是假的……谢谢。当通过
scatter()
绘制单个时间时,它只会忽略时区(它不使用本地时区)…文档的“位置”在哪里?我只看到缺少关于显示时区的信息。我认为谎言在于它关注rcparam,从代码看,它似乎默认为datetime所做的(我假设是本地时区,但这显然是错误的)。请参见日期格式化程序提示的als投票:必须指定tz参数。重要的是设置日期的
tzinfo
?例如6,我建议从pytz导入时区向格式化程序
提供时区;formatter=mdates.DateFormatter(“%H:%M(%d)”;格式化程序.set_tzinfo(时区(“美国/太平洋”);plt.gca().xaxis.set\u major\u格式化程序(格式化程序)