第6章 时间序列
时间序列数据在很多领域都是重要的结构化数据形式,例如金融、经济、生态学、神经科学和物理学。在多个时间点观测或测量的数据形成了时间序列。
许多时间序列是固定频率的,也就是说数据是根据相同的规则定期出现的,例如每15秒、每5分钟或每月1次。时间序列也可以是不规则的,没有固定的时间单位或单位间的偏移量。如何标记和引用时间序列数据取决于应用程序,并且您可能有以下其中一项:·
- 时间戳,具体的时刻。
- 固定的时间区间,例如2007的1月或整个2010年。
- 时间间隔,由开始和结束时间戳表示。时间区间可以被认为是间隔的特殊情况。
- 实验时间或消耗时间。每个时间戳是相对于特定开始时间的时间的量度(例如,自从被放置在烤箱中每秒烘烤的饼干的直径)。
python有很多和时间相关的模块,如datetime,pandas,time,calendar
6.1 datetime
6.1.1 时间的类型
形式 | 实例 |
---|---|
字符串形式 | ‘2021/11/26’,‘16:34:20’ |
timestamp时间戳 | 在计算机中,时间实际上是用数字表示的。我们把1970年1月1日 00:00:00 UTC+00:00时区的时刻称为epoch time,记为0(1970年以前的时间timestamp为负数),当前时间就是相对于epoch time的秒数,称为timestamp。你可以认为:timestamp = 0 = 1970-1-1 00:00:00 UTC+0:00 ,对应的北京时间是:timestamp = 0 = 1970-1-1 08:00:00 UTC+8:00
|
本地时间 | 本地时间是指当前操作系统设定的时区。例如北京时区是东8区,则本地时间:2015-04-19 12:20:00 ,实际上就是UTC+8:00时区的时间:2015-04-19 12:20:00 UTC+8:00 ,而此刻的格林威治标准时间与北京时间差了8小时,也就是UTC+0:00时区的时间应该是:2015-04-19 04:20:00 UTC+0:00
|
utc时间 | 具有时区信息,同一时间点不同的时区,时间点不同,通过在时间后面加UTC+ 来表示,UTC时间指UTC+0:00时区的时间 |
datetime | datetime表示的时间需要时区信息才能确定一个特定的时间,否则只能视为本地时间。如果要存储datetime,最佳方法是将其转换为timestamp再存储,因为timestamp的值与时区完全无关. |
6.1.2 datetime主要类
类名 | 实例对象与解释 |
---|---|
datetime.date(year,month,day) | datetime.date(2021,11,26) 日期类,包含年月日信息 |
datetime.time(\[hour[, minute[, second[, microsecond[, tzinfo]]]]\]) | datetime.time(),时间类,包含小时,分钟,秒,微秒,时区信息 |
datetime.datetime(year, month, day\[, hour[, minute[, second[, microsecond[,tzinfo]]]]\]) | 既包含日期,也包含时间 |
datetime.timedelta() | timedelta类是用来计算二个datetime对象的差值的。 |
datetime.tzinfo() | 时区信息对象 |
6.1.2.1 .date()
datetime.date(year,month,day)
属性和方法 | 介绍 |
---|---|
.day,.month,.year | 天,月,年 |
.isocalendar() | 返回一个包含三个值的元组,三个值依次为:year年份,week number周数,weekday星期数(周一为1…周日为7) |
.isoformat(…) | 返回符合ISO 8601标准 (YYYY-MM-DD) 的日期字符串 |
.isoweekday(…) | 返回符合ISO标准的指定日期所在的星期数(周一为1…周日为7) |
.weekday(…) | 与.isoweekday(…)相似的还有一个weekday(…)方法,返回的周一为 0, 周日为 6 |
.timetuple(…) | 该方法为了兼容time.localtime(…)返回一个类型为time.struct_time的数组,但有关时间的部分元素值为0 |
.toordinal(…) | 返回公元公历开始到现在的天数。公元1年1月1日为1 |
.replace(…) | 返回一个替换指定日期字段的新date对象。参数3个可选参数,分别为year,month,day。注意替换是产生新对象,不影响原date对象。 |
.resolution | date对象表示日期的最小单位。这里是天。 |
.fromordinal(…) | 将Gregorian日历时间转换为date对象;Gregorian Calendar :一种日历表示方法,类似于我国的农历,西方国家使用比较多。 |
.fromtimestamp(…) | 根据给定的时间戮,返回一个date对象 |
.today(…) | 返回当前日期 |
.max | date类能表示的最大的年、月、日的数值 |
.min | date类能表示的最小的年、月、日的数值 |
6.1.2.2 .time()
datetime.time(\[hour[, minute[, second[, microsecond[, tzinfo]]]]\])
def time_atr_and_met(hour,minute,second):
import datetime
time1=datetime.time(hour,minute,second)
atr=[i for i in dir(time1) if i[0]!='_']
l=[]
for i in atr:
try:
a=eval('time1.'+i+'()')
except:
try:
a=eval('time1.'+i)
except:
a="pass"
l.append(['.'+i,a])
return l
pd.DataFrame(time_atr_and_met(15,42,30),columns=['方法或属性','实例']).pipe(lambda df:print(df.to_markdown(tablefmt="github")))
方法或属性 | 实例 | |
---|---|---|
0 | .dst | |
1 | .fold | 0 |
2 | .fromisoformat | <built-in method fromisoformat of type object at 0x00007FFE0F4894D0> |
3 | .hour | 15 |
4 | .isoformat | 15:42:30 |
5 | .max | 23:59:59.999999 |
6 | .microsecond | 0 |
7 | .min | 00:00:00 |
8 | .minute | 42 |
9 | .replace | 15:42:30 |
10 | .resolution | 0:00:00.000001 |
11 | .second | 30 |
12 | .strftime | <built-in method strftime of datetime.time object at 0x00000196E32B9290> |
13 | .tzinfo | |
14 | .tzname | |
15 | .utcoffset |
6.1.2.3 .datetime()
datetime(year, month, day\[, hour[, minute[, second[, microsecond[,tzinfo]]]]\])
def datetime_atr_and_met(year,month,day,hour,minute,second):
import datetime
datetime1=datetime.datetime(year,month,day,hour,minute,second)
atr=[i for i in dir(datetime1) if i[0]!='_']
l=[]
for i in atr:
try:
a=eval('datetime1.'+i+'()')
except:
try:
a=eval('datetime1.'+i)
except:
a="pass"
l.append(['.'+i,a])
return l
dt=datetime_atr_and_met(2021,11,26,16,0,0)
ndt=[i+j for i,j in zip(dt[20:39],dt[:19])]+[dt[19]+['']+['']]
pd.DataFrame(ndt,columns=['方法或属性','实例','方法或属性','实例']).pipe(lambda df:print(df.to_markdown(tablefmt="github")))
方法或属性 | 实例 | 方法或属性 | 实例 | |
---|---|---|---|---|
0 | .now | 2024-04-29 00:34:45.839126 | .astimezone | 2021-11-26 16:00:00+08:00 |
1 | .replace | 2021-11-26 16:00:00 | .combine | <built-in method combine of type object at 0x00007FFE0F489330> |
2 | .resolution | 0:00:00.000001 | .ctime | Fri Nov 26 16:00:00 2021 |
3 | .second | 0 | .date | 2021-11-26 |
4 | .strftime | <built-in method strftime of datetime.datetime object at 0x00000196E2D1C1E0> | .day | 26 |
5 | .strptime | <built-in method strptime of type object at 0x00007FFE0F489330> | .dst | |
6 | .time | 16:00:00 | .fold | 0 |
7 | .timestamp | 1637913600.0 | .fromisocalendar | <built-in method fromisocalendar of type object at 0x00007FFE0F489330> |
8 | .timetuple | time.struct_time(tm_year=2021, tm_mon=11, tm_mday=26, tm_hour=16, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=330, tm_isdst=-1) | .fromisoformat | <built-in method fromisoformat of type object at 0x00007FFE0F489330> |
9 | .timetz | 16:00:00 | .fromordinal | <built-in method fromordinal of type object at 0x00007FFE0F489330> |
10 | .today | 2024-04-29 00:34:45.839127 | .fromtimestamp | <built-in method fromtimestamp of type object at 0x00007FFE0F489330> |
11 | .toordinal | 738120 | .hour | 16 |
12 | .tzinfo | .isocalendar | datetime.IsoCalendarDate(year=2021, week=47, weekday=5) | |
13 | .tzname | .isoformat | 2021-11-26T16:00:00 | |
14 | .utcfromtimestamp | <built-in method utcfromtimestamp of type object at 0x00007FFE0F489330> | .isoweekday | 5 |
15 | .utcnow | 2024-04-28 16:34:45.839126 | .max | 9999-12-31 23:59:59.999999 |
16 | .utcoffset | .microsecond | 0 | |
17 | .utctimetuple | time.struct_time(tm_year=2021, tm_mon=11, tm_mday=26, tm_hour=16, tm_min=0, tm_sec=0, tm_wday=4, tm_yday=330, tm_isdst=0) | .min | 0001-01-01 00:00:00 |
18 | .weekday | 4 | .minute | 0 |
19 | .month | 11 |
6.1.2.4 .timedelta()
timedelta类是用来计算二个datetime对象的差值的。
创建对象:datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
使用timedelta你可以很容易地算出前几天和后几天的时刻。
def timedelta_atr_and_met(days,seconds,microseconds):
import datetime
timedelta=datetime.timedelta(days,seconds,microseconds)
atr=[i for i in dir(timedelta) if i[0]!='_']
l=[]
for i in atr:
try:
a=eval('timedelta.'+i+'()')
except:
try:
a=eval('timedelta.'+i)
except:
a="pass"
l.append(['.'+i,a])
return l
pd.DataFrame(timedelta_atr_and_met(2,42,300),columns=['方法或属性','实例']).pipe(lambda df:print(df.to_markdown(tablefmt="github")))
方法或属性 | 实例 | |
---|---|---|
0 | .days | 2 |
1 | .max | 999999999 days, 23:59:59.999999 |
2 | .microseconds | 300 |
3 | .min | -999999999 days, 0:00:00 |
4 | .resolution | 0:00:00.000001 |
5 | .seconds | 42 |
6 | .total_seconds | 172842.0003 |
datetime.date(2023, 3, 19)
6.1.2.5 .timezone()
创建时区
datetime.timezone(offset, name=None)
offset必须是一个timedelta对象,代表了与utc时间的偏移,范围为-timedelta(hours=24) 到 timedelta(hours=24)
name可以不给,给了话必须是datetime.tzname()生成的对象
datetime.timezone(datetime.timedelta(seconds=21600))
'UTC+06:00'
datetime.timezone.utc
datetime.timedelta(seconds=21600)
6.1.3 时间类型的转换
6.1.3.1 str与datetime
* 字符串形式转化为datetime `datetime.datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')`
* 只有datetime对象有该方法
* 注意转换后的datetime是没有时区信息的。
* datetime对象转化为字符串 `datetime.datetime().strftime('%a, %b %d %H:%M')`
* date对象和time对象也有该方法
* 如果你想将时间对象转化为字符串对象的话,可以用到__format__(...)方法以指定格式进行时间输出:
符号 | 说明 |
---|---|
%y | 两位数的年份表示(00-99) |
%Y | 四位数的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月内中的一天(0-31) |
%H | 24小时制小时数(0-23) |
%I | 12小时制小时数(01-12) |
%M | 分钟数(00=59) |
%S | 秒(00-59) |
%a | 本地简化星期名称 |
%A | 本地完整星期名称 |
%b | 本地简化的月份名称 |
%B | 本地完整的月份名称 |
%c | 本地相应的日期表示和时间表示 |
%j | 年内的一天(001-366) |
%p | 本地A.M.或P.M.的等价符 |
%U | 一年中的星期数(00-53)星期天为星期的开始 |
%w | 星期(0-6),星期天为星期的开始 |
%W | 一年中的星期数(00-53)星期一为星期的开始 |
%x | 本地相应的日期表示 |
%X | 本地相应的时间表示 |
%Z | 当前时区的名称 |
%% | %号本身 |
6.1.3.2 timestamp与datetime
- datetime 转化为timestamp
datetime.datetime.timestamp()
- timestamp 转化为 datetime
- 本地时间的datetime:
datetime.datetime.fromtimestamp(t)
- UTC时间的datetime:
datetime.datetime.utcfromtimestamp(t)
- 本地时间的datetime:
6.2 dateutil
datetime.strptime是在已知格式的情况下转换日期的好方式。然而,每次都必须编写一个格式代码可能有点烦人,特别是对于通用日期格式。在这种情况下,你可以使用第三方dateutil包的parser.parse方法(这个包在安装pandas时已经自动安装):
dateutil.parser是一个有用但并不完美的工具。值得注意的是,它会将一些字符串识别为 你并不想要的日期——例如,’42’将被解析为2042年的当前日期。
datetime.datetime(2011, 1, 3, 0, 0)
6.3 pandas时间日期操作
pd.to_datetime()
可以将缺失值转为NaT
6.3.1 pandas时间类
Concept | Scalar Class | Array Class | pandas Data Type | Primary Creation Method |
---|---|---|---|---|
Datetimes | Timestamp | DatetimeIndex | datetime64[ns] or datetime64[ns,tz] | to_datetime or date_range |
Timedeltas | Timedelta | TimedeltaIndex | timedelta64[ns] | to_timedelta or timedelta_range |
Timespans | Period | PeriodIndex | period[freq] | Period or period_range |
Dateoffsets | DateOffset | None | None | DateOffset |
6.3.1.1 datetime
pd.to_datetime(format=)
pd.date_range(start_time,ends_time,periods=)
pd.date_range(start_time,ends_time,freq=)
pd.date_range(start_time,periods=,freq=)
属性方法
Property |
Description |
---|---|
year |
The year of the datetime |
month |
The month of the datetime |
day |
The days of the datetime |
hour |
The hour of the datetime |
minute |
The minutes of the datetime |
second |
The seconds of the datetime |
microsecond |
The microseconds of the datetime |
nanosecond |
The nanoseconds of the datetime |
date |
Returns datetime.date (does not contain timezone information) |
time |
Returns datetime.time (does not contain timezone information) |
timetz |
Returns datetime.time as local time with timezone information |
dayofyear |
The ordinal day of year |
day_of_year |
The ordinal day of year |
weekofyear |
The week ordinal of the year |
week |
The week ordinal of the year |
dayofweek |
The number of the day of the week with Monday=0, Sunday=6 |
day_of_week |
The number of the day of the week with Monday=0, Sunday=6 |
weekday |
The number of the day of the week with Monday=0, Sunday=6 |
quarter |
Quarter of the date: Jan-Mar = 1, Apr-Jun = 2, etc. |
days_in_month |
The number of days in the month of the datetime |
is_month_start |
Logical indicating if first day of month (defined by frequency) |
is_month_end |
Logical indicating if last day of month (defined by frequency) |
is_quarter_start |
Logical indicating if first day of quarter (defined by frequency) |
is_quarter_end |
Logical indicating if last day of quarter (defined by frequency) |
is_year_start |
Logical indicating if first day of year (defined by frequency) |
is_year_end |
Logical indicating if last day of year (defined by frequency) |
is_leap_year |
Logical indicating if the date belongs to a leap year |
6.3.1.3 timespans
pd.period_range()
pd.period_range(start_time,end_time,freq)
生成一个periodIndex对象
每一个索引值是一个Period对象
6.3.1.4 dateoffsets
from pandas.tseries.offset import BMonthEnd
DateOffset代表规则的频率增量。 在具有DateOffset各种子类的 Pandas 中,可以表示特定的日期偏移逻辑,例如“月”,“工作日”或“小时”
可以将它们相加或相减以获得转换后的日期 可以将它们乘以整数(正数或负数),以便多次应用增量 它们具有rollforward和rollback方法,可以将日期向前或向后移动到下一个或上一个“偏移日期”
可以通过向datetime对象传递代表固定时间段的datetime对象或使用多个关键字参数来创建DateOffset对象。 关键字参数分为两大类。 第一类是代表绝对日期的关键字:年,月,日,小时,分钟,秒和微秒。 第二类代表相对持续时间,可以是负值:年,月,周,日,小时,分钟,秒和微秒。
特定DateOffset的多个单位可以通过乘法表示,时间加上偏移量可以实现时间的改变,或者使用这些对象的实例方法rollforward等实现时间的改变
Timestamp('2024-04-30 00:34:46.620385')
Timestamp('2024-02-29 00:34:46.620385')
6.3.2 indexing
<string>:1: FutureWarning: 'BM' is deprecated and will be removed in a future version, please use 'BME' instead.
-0.5061677483723529
2011-10-31 0.329107
2011-11-30 0.733076
2011-12-30 -1.845578
Freq: BME, dtype: float64
2011-06-30 -1.062127
Freq: BME, dtype: float64
2011-01-31 -0.506168
2011-02-28 0.754779
2011-03-31 -0.273464
2011-04-29 -0.199650
2011-05-31 -1.022645
2011-06-30 -1.062127
2011-07-29 -0.307864
2011-08-31 1.877916
2011-09-30 -3.059956
2011-10-31 0.329107
2011-11-30 0.733076
2011-12-30 -1.845578
Freq: BME, dtype: float64
6.3.3 时区处理
`.tz` 获取时区
`rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D',tz="utc")` 设置utc时区
`.tz_localize()` 方法将utc时区转为本地utc时区,单纯改了时区,没有换时间,所以可能时间不对
`.tz.convert()` 一旦时间序列被本地化为某个特定的时区,则可以通过tz_convert将其转换为另一个时区:
rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D',tz="utc")
ts = pd.Series(np.random.randn(len(rng)), index=rng)
print(ts.index.tz)
UTC
2012-03-09 04:30:00-05:00 0.559909
2012-03-10 04:30:00-05:00 0.680295
2012-03-11 05:30:00-04:00 0.829914
2012-03-12 05:30:00-04:00 -1.550127
2012-03-13 05:30:00-04:00 1.315035
2012-03-14 05:30:00-04:00 1.655262
Freq: D, dtype: float64
6.3.5 频率和频率转换



p.asfreq(freq,how=“start”)
从小间隔转到大间隔默认保留第一个值
从大间隔到小间隔可能会有缺失值,默认NAN,可以使用.asfreq()方法的method参数更改此默认行为。 该值可用于正向填充,反向填充或填充NaN值
ffill方法将向前填充最后一个已知值(pad也这样做)
bfill方法将从下一个已知值回填值:
6.3.6 resample
一天”1D”的级别比”1W”的级别要高
pandas对象都配有resample方法,该方法是所有频率转换的工具函数。resample拥有类似于groupby的API;你调用resample对数据分组,之后再调用聚合函数:

rng = pd.date_range('2000-01-01', periods=100, freq='D')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts.resample('M').mean()
<string>:1: FutureWarning: 'M' is deprecated and will be removed in a future version, please use 'ME' instead.
2000-01-31 0.198931
2000-02-29 0.151074
2000-03-31 -0.190714
2000-04-30 -0.542959
Freq: ME, dtype: float64
<string>:1: FutureWarning: The 'kind' keyword in Series.resample is deprecated and will be removed in a future version. Explicitly cast the index to the desired type instead
2000-01 0.198931
2000-02 0.151074
2000-03 -0.190714
2000-04 -0.542959
Freq: M, dtype: float64
6.3.7 窗口函数
统计和其他通过移动窗口或指数衰减而运行的函数是用于时间序列操作的数组变换的一个重要类别。这对平滑噪声或粗糙的数据非常有用。我称这些函数为移动窗口函数,尽管它也包含了一些没有固定长度窗口的函数,比如指数加权移动平均。与其他的统计函数类似,这些函数会自动排除缺失数据。
6.3.7.1 .rolling()
确定窗口大小,对窗口内所有值进行操作
`.rolling()
data.rolling("20D").mean(window,min_periods,closed)
center : bool, default False
If False, set the window labels as the right edge of the window index.
If True, set the window labels as the center of the window index
tind = pd.date_range("2023-01-01","2023-03-31")
ts = pd.Series(np.random.randint(1,101,len(tind)),index=tind)
ts.rolling("10D",min_periods=5).mean()
2023-01-01 NaN
2023-01-02 NaN
2023-01-03 NaN
2023-01-04 NaN
2023-01-05 27.8
...
2023-03-27 43.7
2023-03-28 48.3
2023-03-29 40.6
2023-03-30 37.5
2023-03-31 38.5
Freq: D, Length: 90, dtype: float64
2023-01-01 NaN
2023-01-02 NaN
2023-01-03 22.666667
2023-01-04 33.750000
2023-01-05 27.800000
...
2023-03-27 43.700000
2023-03-28 48.300000
2023-03-29 40.600000
2023-03-30 37.500000
2023-03-31 38.500000
Freq: D, Length: 90, dtype: float64
2023-01-01 34.666667
2023-01-02 38.285714
2023-01-03 40.500000
2023-01-04 42.000000
2023-01-05 41.900000
...
2023-03-27 41.111111
2023-03-28 42.250000
2023-03-29 46.000000
2023-03-30 48.000000
2023-03-31 55.600000
Freq: D, Length: 90, dtype: float64
6.3.7.2 .shift()
.shift(n,periods,freq,fill_value)
滞后n期,并不会改变原索引
2023-01-01 NaN
2023-01-02 13.0
2023-01-03 20.0
2023-01-04 35.0
2023-01-05 67.0
...
2023-03-27 10.0
2023-03-28 55.0
2023-03-29 57.0
2023-03-30 5.0
2023-03-31 66.0
Freq: D, Length: 90, dtype: float64
2023-01-01 NaN
2023-01-02 13.0
2023-01-03 20.0
2023-01-04 35.0
2023-01-05 67.0
...
2023-03-27 10.0
2023-03-28 55.0
2023-03-29 57.0
2023-03-30 5.0
2023-03-31 66.0
Freq: D, Length: 90, dtype: float64
2023-01-01 13.0
2023-01-02 13.0
2023-01-03 20.0
2023-01-04 35.0
2023-01-05 67.0
...
2023-03-27 10.0
2023-03-28 55.0
2023-03-29 57.0
2023-03-30 5.0
2023-03-31 66.0
Freq: D, Length: 90, dtype: float64
6.3.7.3 .diff()
2023-01-01 NaN
2023-01-02 7.0
2023-01-03 15.0
2023-01-04 32.0
2023-01-05 -63.0
...
2023-03-27 45.0
2023-03-28 2.0
2023-03-29 -52.0
2023-03-30 61.0
2023-03-31 29.0
Freq: D, Length: 90, dtype: float64
6.3.7.4 .first()
返回时间的索引值之间的极差是offset
offset:str,dateoffset
<string>:1: FutureWarning: first is deprecated and will be removed in a future version. Please create a mask and filter using `.loc` instead
2023-01-01 13
2023-01-02 20
2023-01-03 35
2023-01-04 67
2023-01-05 4
2023-01-06 69
2023-01-07 60
2023-01-08 56
2023-01-09 54
2023-01-10 41
Freq: D, dtype: int32
6.3.7.5 .last()
<string>:1: FutureWarning: last is deprecated and will be removed in a future version. Please create a mask and filter using `.loc` instead
2023-03-12 99
2023-03-13 12
2023-03-14 48
2023-03-15 68
2023-03-16 94
2023-03-17 62
2023-03-18 11
2023-03-19 82
2023-03-20 97
2023-03-21 85
2023-03-22 15
2023-03-23 32
2023-03-24 16
2023-03-25 34
2023-03-26 10
2023-03-27 55
2023-03-28 57
2023-03-29 5
2023-03-30 66
2023-03-31 95
Freq: D, dtype: int32