如何在Matplotlib中处理带有时区的时间?

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

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

更改matplotlibrc中的timezone不会改变显示的图形中的任何内容(我可能误解了Matplotlib文档中关于时区的discussion on time zones)。

我尝试了一下使用plot()(而不是scatter())。当给定单个日期时,它会绘制并忽略时区。然而,当给定多个日期时,它使用固定的时区,但是它是如何确定的?我在文档中找不到任何信息。

最后,plot_date()是否应该是解决这些时区问题的解决方案?


4
看起来axes.xaxis_data(tz)会将所有要显示的日期设置为 那个 时区。如果您没有明确设置时区,它看起来会将时间转换为本地时区(我只是粗略浏览了代码,可能有偏差)。 - tacaswell
1
是的,看起来文档是错误的... - tacaswell
2
谢谢。当使用scatter()绘制单个时间时,它会忽略时区(不会使用本地时区)...文档中的哪里存在“谎言”?我只看到缺乏有关显示时区的信息。 - Eric O. Lebigot
1
我认为是虚假的说法是它关注rcparam,从代码来看它默认使用datetime的任何设置(我原以为是本地时区,但显然不对)。 - tacaswell
实际使用的方法是axes.xaxis_date(tz), 此方法的文档 - sberder
1
请参考以下链接:https://dev59.com/aF8e5IYBdhLWcg3whqvl#45728316 - Andre Holzner
2个回答

12
问题已经在评论中得到了解答。不过,我仍然在处理时区的问题。为了搞清楚它,我尝试了所有的组合。我认为您有两种主要方法,具体取决于您的日期时间对象是否已经在所需的时区内或处于不同的时区,我尝试在下面描述它们。可能我还错过了一些东西。
时间戳(日期时间对象):在UTC中 期望显示:在特定时区中
  • xaxis_date()设置为所需的显示时区(默认为rcParam['timezone'],对我来说是UTC)
时间戳(日期时间对象):在特定时区中 期望显示:在不同的特定时区中
  • 向绘图函数提供带有相应时区的日期时间对象(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()

3
投票支持DateFormatter提醒:必须指定tz参数。 - Robin Loxley
似乎重要的是设置日期的“tzinfo”? - Eric O. Lebigot
3
例如6,我建议向格式化程序提供时区 from pytz import timezone; formatter = mdates.DateFormatter("%H:%M(%d)") ; formatter.set_tzinfo(timezone('US/Pacific')); plt.gca().xaxis.set_major_formatter(formatter) - pseyfert

9
如果你和我一样,在尝试让一个带有时区信息的 pandas DataFrame 正确绘图时遇到这个问题,@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:

不确定为什么有些日期(EST的那些)看起来像是加粗了,但很可能是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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接