日期与时间#
与大多数科学学科一样,日期和时间在金融领域扮演着重要角色。本附录介绍在Python编程中与此主题相关的不同方面。当然,它不可能面面俱到,但它提供了对Python生态系统中支持日期和时间信息建模的主要领域的介绍。
Python#
Python标准库中的datetime模块允许实现大多数重要的日期和时间相关任务:
In [1]: from pylab import mpl, plt
plt.style.use('seaborn')
mpl.rcParams['font.family'] = 'serif'
%matplotlib inline
In [2]: import datetime as dt
In [3]: dt.datetime.now() ①
Out[3]: datetime.datetime(2018, 10, 19, 15, 17, 32, 164295)
In [4]: to = dt.datetime.today() ①
to
Out[4]: datetime.datetime(2018, 10, 19, 15, 17, 32, 177092)
In [5]: type(to)
Out[5]: datetime.datetime
In [6]: dt.datetime.today().weekday() ②
Out[6]: 4返回当前精确日期和系统时间。
⑥ ... day ...⑤ ... month ...以数字形式返回星期几,其中0 = 星期一。
当然,datetime对象可以自由定义:
In [7]: d = dt.datetime(2020, 10, 31, 10, 5, 30, 500000)
d
Out[7]: datetime.datetime(2020, 10, 31, 10, 5, 30, 500000)
In [8]: str(d)
Out[8]: '2020-10-3110:05:30.500000'
In [9]: print(d)
2020-10-3110:05:30.500000
In [10]: d.year
Out[10]: 2020
In [11]: d.month
Out[11]: 10
In [12]: d.day
Out[12]: 31
In [13]: d.hour
Out[13]: 10自定义datetime对象。
字符串表示。
打印该对象。
年属性……
……和时属性。
转换和拆分也很容易实现:
In [14]: o = d.toordinal( ) ①
o
Out[14]: 737729
In [15]: dt.datetime.fromordinal(o) ②
Out[15]: datetime.datetime(2020, 10, 31, 0, 0)
In [16]: t = dt.datetime.time(d) ③t
Out[16]: datetime.time(10, 5, 30, 500000)
In [17]: type(t)
Out[17]: datetime.time
In [18]: dd = dt.datetime.date(d) ④
dd
Out[18]: datetime.date(2020, 10, 31)
In [19]: d.replace(second=0, microsecond=0) ⑤
Out[19]: datetime.datetime(2020, 10, 31, 10, 5)转换为序数(ordinal number)。
从序数转换回来。
拆分时间部分。
拆分日期部分。
将选定的值设为0。
timedelta对象由datetime对象上的算术运算(即求两个此类对象之间的差)等操作产生:
In [20]: td = d - dt.datetime.now() ①
td
Out[20]: datetime.timedelta(days=742, seconds=67678, microseconds=169720)
In [21]: type(td) ②
Out[21]: datetime.timedelta
In [22]: td.days
Out[22]: 742
In [23]: td.seconds
Out[23]: 67678
In [24]: td.microseconds
Out[24]: 169720
In [25]: td.total_seconds() ③
Out[25]: 64176478.16972两个datetime对象之差……
……得到一个timedelta对象。
以秒为单位的差值。
有多种方法可以将datetime对象转换为不同表示形式,也可以从str对象等生成datetime对象。详细内容请参见datetime模块的文档。以下是一些示例:
In [26]: d.isoformat() ①
Out[26]: '2020-10-31T10:05:30.500000'
In [27]: d.strftime('%A, %d. %B %Y %I:%M%p') ②
Out[27]: 'Saturday, 31. October 202010:05AM'
In [28]: dt.datetime.strptime('2017-03-31', '%Y-%m-%d') ③
Out[28]: datetime.datetime(2017, 3, 31, 0, 0)
In [29]: dt.datetime.strptime('30-4-16', '%d-%m-%y') ③
Out[29]: datetime.datetime(2016, 4, 30, 0, 0)
In [30]: ds = str(d)
ds
Out[30]: '2020-10-3110:05:30.500000'
In [31]: dt.datetime.strptime(ds, '%Y-%m-%d %H:%M:%S.%f') ③
Out[31]: datetime.datetime(2020, 10, 31, 10, 5, 30, 500000)ISO格式字符串表示。
字符串表示的精确模板。
基于模板从str对象创建datetime对象。
除了now()和today()函数外,还有utcnow()函数,它以UTC(协调世界时,Coordinated Universal Time,以前称为格林威治标准时间GMT)格式返回精确的日期和时间信息。这比作者所在时区(欧洲中部时间CET或欧洲中部夏令时间CEST)早一或两个小时:
In [32]: dt.datetime.now()
Out[32]: datetime.datetime(2018, 10, 19, 15, 17, 32, 438889)In [33]: dt.datetime.utcnow() ①
Out[33]: datetime.datetime(2018, 10, 19, 13, 17, 32, 448897)In [34]: dt.datetime.now() - dt.datetime.utcnow() ②
Out[34]: datetime.timedelta(seconds=7199, microseconds=999995)返回当前UTC时间。
返回本地时间与UTC时间之差。
datetime模块的另一个类是tzinfo类,一个通用的时区类,包含utcoffset()、dst()和tzname()方法。UTC和CEST时间的定义可能如下所示:
In [35]: class UTC(dt.tzinfo):
def utcoffset(self, d):
return dt.timedelta(hours=0) ①
def dst(self, d):
return dt.timedelta(hours=0) ①
def tzname(self, d):
return 'UTC'
In [36]: u = dt.datetime.utcnow()
In [37]: u
Out[37]: datetime.datetime(2018, 10, 19, 13, 17, 32, 474585)
In [38]: u = u.replace(tzinfo=UTF()) ②
In [39]: u
Out[39]: datetime.datetime(2018, 10, 19, 13, 17, 32, 474585, tzinfo=<__main__.UTC object at 0x11c9a2320>)
In [40]: class CEST(dt.tzinfo):
def utcoffset(self, d):
return dt_timedelta(hours=2) ③
def dst(self, d):
return dt_timedelta(hours=1) ③
def tzname(self, d):
return 'CEST'
In [41]: c = u.astimezone(CEST()) ④
c
Out[41]: datetime.datetime(2018, 10, 19, 15, 17, 32, 474585, tzinfo=<__main__.CEST object at 0x11c9a2cc0>)
In [42]: c - c.dst() ⑤
Out[42]: datetime.datetime(2018, 10, 19, 14, 17, 32, 474585, tzinfo=<__main__.CEST object at 0x11c9a2cc0>)UTC没有偏移量。
通过replace()方法附加dt.tzinfo对象。
CEST的常规和夏令时(Daylight Saving Time, DST)偏移量。
将UTC时区转换为CEST时区。
给出转换后datetime对象的夏令时时间。
有一个名为pytz的Python模块,实现了世界上最重要的时区:
In [43]: import pytz
In [44]: pytz.country_names['US'] ①
Out[44]: 'United States'
In [45]: pytz.country_timezones['BE'] ②
Out[45]: ['Europe/Brussels']
In [46]: pytz.common_timezones[-10:] ③
Out[46]: ['Pacific/Wake', 'Pacific/Wallis', 'US/Alaska', 'US/Arizona', 'US/Central', 'US/Eastern', 'US/Hawaii', 'US/Mountain', 'US/Pacific', 'UTC']单个国家。
单个时区。
一些常见时区。
使用pytz,通常不需要自定义tzinfo对象:
In [47]: u = dt.datetime.utcnow()
In [48]: u = u.replace(tzinfo=pytz.utc) ①
In [49]: u
Out[49]: datetime.datetime(2018, 10, 19, 13, 17, 32, 611417, tzinfo=<UTC>)
In [50]: u.astimezone(pytz.timezone('CET')) ②
Out[50]: datetime.datetime(2018, 10, 19, 15, 17, 32, 611417, tzinfo=<DstTzInfo 'CET' CEST+2:00:00 DST>)
In [51]: u.astimezone(pytz.timezone('GMT')) ②
Out[51]: datetime.datetime(2018, 10, 19, 13, 17, 32, 611417, tzinfo=<StaticTzInfo 'GMT'>)
In [52]: u.astimezone(pytz.timezone('US/Central')) ②
Out[52]: datetime.datetime(2018, 10, 19, 8, 17, 32, 611417, tzinfo=<DstTzInfo 'US/Central' CDT-1 day, 19:00:00 DST>)通过pytz定义tzinfo对象。
将datetime对象转换到不同时区。
NumPy#
NumPy也提供了处理日期和时间信息的功能:
In [53]: import numpy as np
In [54]: nd = np.datetime64('2020-10-31') ①
nd
Out[54]: numpy.datetime64('2020-10-31')
In [55]: np.datetime_as_string(nd) ①
Out[55]: '2020-10-31'
In [56]: np.datetime_data(nd) ②
Out[56]: ('D', 1)
In [57]: d
Out[57]: datetime.datetime(2020, 10, 31, 10, 5, 30, 500000)
In [58]: nd = np.datetime64(d) ③
nd
Out[58]: numpy.datetime64('2020-10-31T10:05:30.500000')
In [59]: nd.astype(dt.datetime) ④
Out[59]: datetime.datetime(2020, 10, 31, 10, 5, 30, 500000)从str对象构建及字符串表示。
关于数据本身的元信息(类型、大小)。
从datetime对象构建。
转换为datetime对象。
另一种构造此类对象的方式是提供str对象,例如包含年月和频率信息。请注意,对象值默认为该月的第一天。基于列表对象构建ndarray对象也是可行的:
In [60]: nd = np.datetime64('2020-10', 'D')
nd
Out[60]: numpy.datetime64('2020-10-01')In [61]: np.datetime64('2020-10') == np.datetime64('2020-10-01')
Out[61]: TrueIn [62]: np.array(['2020-06-10', '2020-07-10', '2020-08-10'], dtype='datetime64')Out[62]: array(['2020-06-10', '2020-07-10', '2020-08-10'], dtype='datetime64[D]')
In [63]: np.array(['2020-06-10T12:00:00', '2020-07-10T12:00:00', '2020-08-10T12:00:00'], dtype='datetime64[s]')
Out[63]: array(['2020-06-10T12:00:00', '2020-07-10T12:00:00', '2020-08-10T12:00:00'], dtype='datetime64[s]')也可以使用np.arange()函数生成日期范围。不同的频率(例如天、周或秒)也很容易处理:
In [64]: np.arange('2020-01-01', '2020-01-04', dtype='datetime64') ①
Out[64]: array(['2020-01-01', '2020-01-02', '2020-01-03'], dtype='datetime64[D]')
In [65]: np.arange('2020-01-01', '2020-10-01', dtype='datetime64[M]') ②
Out[65]: array(['2020-01', '2020-02', '2020-03', '2020-04', '2020-05', '2020-06', '2020-07', '2020-08', '2020-09'], dtype='datetime64[M]')In [66]: np.arange('2020-01-01', '2020-10-01', dtype='datetime64[W]')[:10]
Out[66]: array(['2019-12-26', '2020-01-02', '2020-01-09', '2020-01-16', '2020-01-23', '2020-01-30', '2020-02-06', '2020-02-13', '2020-02-20', '2020-02-27'], dtype='datetime64[W]')In [67]: dtl = np.arange('2020-01-01T00:00:00', '2020-01-02T00:00:00',
dtype='datetime64[h]') ④
dtl[:10]
Out[67]: array(['2020-01-01T00', '2020-01-01T01', '2020-01-01T02',
'2020-01-01T03', '2020-01-01T04', '2020-01-01T05', '2020-01-01T06',
'2020-01-01T07', '2020-01-01T08', '2020-01-01T09'],
dtype='datetime64[h]')In [68]: np.arange('2020-01-01T00:00:00', '2020-01-02T00:00:00', dtype='datetime64[s]')[:10] ⑤
Out[68]: array(['2020-01-01T00:00:00', '2020-01-01T00:00:01', '2020-01-01T00:00:02', '2020-01-01T00:00:03', '2020-01-01T00:00:04', '2020-01-01T00:00:05', '2020-01-01T00:00:06', '2020-01-01T00:00:07', '2020-01-01T00:00:08', '2020-01-01T00:00:09'], dtype='datetime64[s]')In [69]: np.arange('2020-01-01T00:00:00', '2020-01-02T00:00:00',
dtype='datetime64[ms]'):[10] ⑥
Out[69]: array(['2020-01-01T00:00:00.000', '2020-01-01T00:00:00.001',
'2020-01-01T00:00:00.002', '2020-01-01T00:00:00.003',
'2020-01-01T00:00:00.004', '2020-01-01T00:00:00.005',
'2020-01-01T00:00:00.006', '2020-01-01T00:00:00.007',
'2020-01-01T00:00:00.008', '2020-01-01T00:00:00.009'],
dtype='datetime64[ms]')日频率。
月频率。
周频率。
小时频率。
秒频率。
毫秒频率。
绘制日期时间数据和/或时间序列数据有时可能比较棘手。matplotlib支持标准的datetime对象。将NumPy的datetime64信息转换为Python的datetime信息通常可以解决问题,如下例所示(结果如图A-1所示):
In [70]: import matplotlib.pyplot as plt
%matplotlib inline
In [71]: np.random.seed(3000)
rnd = np.random.standard_normal(len(dtl)).cumsum() ** 2
In [72]: fig = plt.figure(figsize=(10, 6))
plt.plot(dtl.astype(dt.datetime), rnd) ①
fig.autofmt_xdate(); ②使用datetime信息作为x值。
自动格式化x轴上的日期时间刻度。
图A.1 自动格式化日期时间x轴刻度的绘图
In [74]: ts = pd.Timestamp('2020-06-30')
ts
Out[74]: Timestamp('2020-06-3000:00:00')pandas#
pandas包在设计时至少在一定程度上考虑了时间序列数据。因此,该包提供了能够高效处理日期和时间信息的类,例如用于时间索引的DatetimeIndex类(参见 http://bit.ly/timeseries_doc 上的文档)。
pandas引入了Timestamp对象,作为datetime和datetime64对象的另一种替代:
In [73]: import pandas as pdIn [75]: d = ts.to_pydatetime() ②
d
Out[75]: datetime.datetime(2020, 6, 30, 0, 0)In [76]: pd.Timestamp(d) 3
Out[76]: Timestamp('2020-06-3000:00:00')In [77]: pd.Timestamp(nd) 4
Out[77]: Timestamp('2020-10-0100:00:00')从str对象创建Timestamp对象。
从Timestamp对象创建datetime对象。
从datetime对象创建Timestamp对象。
从datetime64对象创建Timestamp对象。
另一个重要的类是前面提到的DatetimeIndex类,它是Timestamp对象的集合,附带许多有用的方法。可以使用pd.date_range()函数创建DatetimeIndex对象,该函数在构建时间索引方面相当灵活且强大(更多细节请参见第8章)。常见的转换也是可行的:
In [78]: dti = pd.date_range('2020/01/01', freq='M', periods=12)
dti
Out[78]: DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31', '2020-04-30', '2020-05-31', '2020-06-30', '2020-07-31', '2020-08-31', '2020-09-30', '2020-10-31', '2020-11-30', '2020-12-31'], dtype='datetime64[ns]', freq='M')
In [79]: dti[6]
Out[79]: Timestamp('2020-07-3100:00:00', freq='M')In [80]: pdi = dti.to_pydatetime() ②
pdi
Out[80]: array([datetime.datetime(2020, 1, 31, 0, 0),
datetime.datetime(2020, 2, 29, 0, 0),
datetime.datetime(2020, 3, 31, 0, 0),
datetime.datetime(2020, 4, 30, 0, 0),
datetime.datetime(2020, 5, 31, 0, 0),
datetime.datetime(2020, 6, 30, 0, 0),
datetime.datetime(2020, 7, 31, 0, 0),
datetime.datetime(2020, 8, 31, 0, 0),
datetime.datetime(2020, 9, 30, 0, 0),
datetime.datetime(2020, 10, 31, 0, 0),
datetime.datetime(2020, 11, 30, 0, 0),
datetime.datetime(2020, 12, 31, 0, 0)], dtype=object)In [81]: datetimeIndex(('2020-01-31', '2020-02-29', '2020-03-31', '2020-04-30', '2020-05-31', '2020-06-30', '2020-07-31', '2020-08-31', '2020-09-30', '2020-10-31', '2020-11-30', '2020-12-31'], dtype='datetime64[ns]', freq=None)In [82]: pd.DatetimeIndex(dtl) ④
Out[82]: DatetimeIndex(['2020-01-0100:00:00', '2020-01-0101:00:00',
'2020-01-0102:00:00', '2020-01-0103:00:00',
'2020-01-0104:00:00', '2020-01-0105:00:00',
'2020-01-0106:00:00', '2020-01-0107:00:00',
'2020-01-0108:00:00', '2020-01-0109:00:00',
'2020-01-0110:00:00', '2020-01-0111:00:00',
'2020-01-0112:00:00', '2020-01-0113:00:00',
'2020-01-0114:00:00', '2020-01-0115:00:00',
'2020-01-0116:00:00', '2020-01-0117:00:00',
'2020-01-0118:00:00', '2020-01-0119:00:00',
'2020-01-0120:00:00', '2020-01-0121:00:00',
'2020-01-0122:00:00', '2020-01-0123:00:00'],
dtype='datetime64[ns]', freq=None)每月频率、12个周期的DatetimeIndex对象。
转换为包含datetime对象的ndarray对象。
从包含datetime对象的ndarray对象创建DatetimeIndex对象。
从包含datetime64对象的ndarray对象创建DatetimeIndex对象。
pandas能够正确处理日期时间信息的绘图(见图A-2及第8章):
In [83]: rnd = np.random.standard_normal(len(dti)).cumsum() ** 2In [84]: df = pd.DataFrame(rnd, columns=['data'], index=dti)In [85]: df.plot(figsize=(10, 6));图A.2 pandas自动格式化Timestamp x轴刻度的绘图
pandas还与pytz模块良好集成,用于管理时区:
In [86]: pd.date_range('2020/01/01', freq='M', periods=12, tz=pytz.timezone('CET'))
Out[86]: DatetimeIndex(['2020-01-3100:00:00+01:00', '2020-02-2900:00:00+01:00',
'2020-03-3100:00:00+02:00', '2020-04-3000:00:00+02:00',
'2020-05-3100:00:00+02:00', '2020-06-3000:00:00+02:00',
'2020-07-3100:00:00+02:00', '2020-08-3100:00:00+02:00',
'2020-09-3000:00:00+02:00', '2020-10-3100:00:00+01:00',
'2020-11-3000:00:00+01:00', '2020-12-3100:00:00+01:00'],
dtype='datetime64[ns, CET]', freq='M')
In [87]: dti = pd.date_range('2020/01/01', freq='M', periods=12, tz='US/Eastern')
dti
Out[87]: DatetimeIndex(['2020-01-3100:00:00-05:00', '2020-02-2900:00:00-05:00',
'2020-03-3100:00:00-04:00', '2020-04-3000:00:00-04:00',
'2020-05-3100:00:00-04:00', '2020-06-30T00:00:00-04:00',
'2020-07-31T00:00:00-04:00', '2020-08-31T00:00:00-04:00',
'2020-09-30T00:00:00-04:00', '2020-10-31T00:00:00-04:00',
'2020-11-30T00:00:00-05:00', '2020-12-31T00:00:00-05:00'],
dtype='datetime64[ns, US/Eastern]', freq='M')
In [88]: dti.tz_convert('GMT')Out[88]: DatetimeIndex(['2020-01-3105:00:00+00:00', '2020-02-2905:00:00+00:00', '2020-03-3104:00:00+00:00', '2020-04-3004:00:00+00:00', '2020-05-3104:00:00+00:00', '2020-06-3004:00:00+00:00', '2020-07-3104:00:00+00:00', '2020-08-3104:00:00+00:00', '2020-09-3004:00:00+00:00', '2020-10-3104:00:00+00:00', '2020-11-3005:00:00+00:00', '2020-12-3105:00:00+00:00'], dtype='datetime64[ns, GMT]', freq='M')