时间序列指能在任何能在时间上观测到的数据。很多时间序列是有固定频率(fixed frequency)的,意思是数据点会遵照某种规律定期出现,比如每15秒,每5分钟,或每个月。时间序列也可能是不规律的(irregular),没有一个固定的时间规律。如何参照时间序列数据取决于我们要做什么样的应用,我们可能会遇到下面这些:
import pandas as pd
import numpy as np
from datetime import datetime
# 获取时间
now = datetime.now()
now
datetime.datetime(2018, 5, 10, 13, 31, 51, 898458)
print(now.year,'年')
print(now.month,'月')
print(now.day,'日')
print(now.minute,'分')
print(now.minute,'秒')
2018 年
5 月
10 日
31 分
31 秒
datetime能保存日期和时间到微妙级别。timedelta表示两个不同的datetime对象之间的时间上的不同:
# 时间对比
delta = datetime(2011, 1, 7) - datetime(2008, 6, 24, 8, 15)
delta
datetime.timedelta(926, 56700)
print('时间差多少天:',delta.days)
print('时间差多少秒:',delta.seconds)
时间差多少天: 926
时间差多少秒: 56700
我们可以在一个datetime对象上,添加或减少一个或多个timedelta,这样可以产生新的变化后的对象
from datetime import timedelta
start = datetime(2011, 1, 7)
start + timedelta(12) # 加12天
datetime.datetime(2011, 1, 19, 0, 0)
start - 2 * timedelta(12) # 减24天
datetime.datetime(2010, 12, 14, 0, 0)
下表汇总了一些datetime模块中的数据类型:
我们可以对datetime对象,以及pandas的Timestamp对象进行格式化,这部分之后会介绍,使用str或strftime方法,传入一个特定的时间格式就能进行转换:
# 时间转字符串
stamp = datetime(2011, 1, 3)
str(stamp)
'2011-01-03 00:00:00'
stamp.strftime('%Y-%m-%d')
'2011-01-03'
下表是关于日期时间类型的格式:
我们可以利用上面的format codes(格式码;时间日期格式)把字符串转换为日期,这要用到datetime.strptime:
# 字符串转时间
value = '2011-01-03'
datetime.strptime(value, '%Y-%m-%d')
datetime.datetime(2011, 1, 3, 0, 0)
datestrs = ['7/6/2011', '8/6/2011']
[datetime.strptime(x, '%m/%d/%Y') for x in datestrs]
[datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]
对于一个一直的时间格式,使用datetime.strptime来解析日期是很好的方法。但是,如果每次都要写格式的话很烦人,尤其是对于一些比较常见的格式。在这种情况下,我们可以使用第三方库dateutil中的parser.parse方法(这个库会在安装pandas的时候自动安装)
from dateutil.parser import parse
parse('2011-01-03')
datetime.datetime(2011, 1, 3, 0, 0)
# dateutil能够解析很多常见的时间表示格式
parse('Jan 31, 1997 10:45 PM')
datetime.datetime(1997, 1, 31, 22, 45)
在国际上,日在月之前是很常见的(译者:美国是把月放在日前面的),所以我们可以设置dayfirst=True来指明最前面的是否是日
parse('6/12/2011', dayfirst=True)
datetime.datetime(2011, 12, 6, 0, 0)
pandas通常可以用于处理由日期组成的数组,不论是否是DataFrame中的行索引或列。to_datetime方法能解析很多不同种类的日期表示。标准的日期格式,比如ISO 8601,能被快速解析
# 使用to_datetime
datestrs = ['2011-07-06 12:00:00', '2011-08-06 00:00:00']
pd.to_datetime(datestrs)
DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00'], dtype='datetime64[ns]', freq=None)
还能处理一些应该被判断为缺失的值(比如None, 空字符串之类的)
idx = pd.to_datetime(datestrs + [None])
idx
DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00', 'NaT'], dtype='datetime64[ns]', freq=None)
idx[2]
NaT
pd.isnull(idx)
array([False, False, True])
Nat(Not a Time)在pandas中,用于表示时间戳为空值(null value)。
dateutil.parse是一个很有用但不完美的工具。它可能会把一些字符串识别为日期,例如,’42’就会被解析为2042年加上今天的日期。
datetime对象还有一些关于地区格式(locale-specific formatting)的选项,用于处理不同国家或不同语言的问题。例如,月份的缩写在德国和法国,与英语是不同的。下表列出一些相关的选项:
在pandas中,一个基本的时间序列对象,是一个用时间戳作为索引的Series,在pandas外部的话,通常是用python 字符串或datetime对象来表示的
dates = [datetime(2011, 1, 2), datetime(2011, 1, 5),
datetime(2011, 1, 7), datetime(2011, 1, 8),
datetime(2011, 1, 10), datetime(2011, 1, 12)]
ts = pd.Series(np.random.randn(6), index=dates)
ts
2011-01-02 1.404005
2011-01-05 0.269604
2011-01-07 -0.558070
2011-01-08 0.876070
2011-01-10 0.694803
2011-01-12 -0.599207
dtype: float64
# 每隔两个元素选一个元素
ts[::2]
2011-01-02 1.404005
2011-01-07 -0.558070
2011-01-10 0.694803
dtype: float64
pandas中的时间戳,是按numpy中的datetime64数据类型进行保存的,可以精确到纳秒的级别
ts.index.dtype
dtype('
1、索引,选择,取子集
当我们基于标签进行索引和选择时,时间序列就像是pandas.Series
ts
2011-01-02 1.404005
2011-01-05 0.269604
2011-01-07 -0.558070
2011-01-08 0.876070
2011-01-10 0.694803
2011-01-12 -0.599207
dtype: float64
# 通过时间序列索引
stamp = ts.index[2]
ts[stamp]
-0.5580701255970213
为了方便,我们可以直接传入一个字符串用来表示日期
ts['1/10/2011']
0.6948026143470746
ts['20110110']
0.6948026143470746
对于比较长的时间序列,我们可以直接传入一年或一年一个月,来进行数据选取
longer_ts = pd.Series(np.random.randn(1000),
index=pd.date_range('1/1/2000', periods=1000)) # periods表示周期
longer_ts[::50]
2000-01-01 -1.434558
2000-02-20 0.199652
2000-04-10 0.396663
2000-05-30 -0.351714
2000-07-19 -1.464473
2000-09-07 0.113600
2000-10-27 -1.168503
2000-12-16 -0.395296
2001-02-04 0.109727
2001-03-26 -0.154458
2001-05-15 0.695305
2001-07-04 0.338459
2001-08-23 1.017848
2001-10-12 0.887390
2001-12-01 0.066979
2002-01-20 0.315712
2002-03-11 0.957264
2002-04-30 0.921923
2002-06-19 0.955736
2002-08-08 0.615709
Freq: 50D, dtype: float64
# 查看2001年的后5个数
longer_ts['2001'][-5:]
2001-12-27 0.553635
2001-12-28 0.900810
2001-12-29 -1.792167
2001-12-30 0.599491
2001-12-31 0.271903
Freq: D, dtype: float64
# 查看2002年8月的前5个数
longer_ts['2002-8'][:5]
2002-08-01 0.128209
2002-08-02 0.368129
2002-08-03 0.728122
2002-08-04 0.245300
2002-08-05 -0.685125
Freq: D, dtype: float64
# 利用datetime进行切片
longer_ts[datetime(2001, 1, 1)]
0.41985839386468266
# 按时间范围切片
longer_ts['12/28/2000':'1/3/2001']
2000-12-28 -0.756228
2000-12-29 -0.202390
2000-12-30 0.877150
2000-12-31 -1.073438
2001-01-01 0.419858
2001-01-02 -0.302687
2001-01-03 -0.777208
Freq: D, dtype: float64
记住,这种方式的切片得到的只是原来数据的一个视图,如果我们在切片的结果上进行更改的的,原来的数据也会变化。
有一个相等的实例方法(instance method)也能切片,truncate,能在两个日期上,对Series进行切片
longer_ts.truncate(before='12/28/2000', after='1/3/2001')
2000-12-28 -0.756228
2000-12-29 -0.202390
2000-12-30 0.877150
2000-12-31 -1.073438
2001-01-01 0.419858
2001-01-02 -0.302687
2001-01-03 -0.777208
Freq: D, dtype: float64
所有这些都适用于DataFrame,我们对行进行索引
dates = pd.date_range('1/1/2000', periods=100, freq='W-WED') # periods表示周期, freq表示频率
long_df = pd.DataFrame(np.random.randn(100, 4),
index=dates,
columns=['Colorado', 'Texas',
'New York', 'Ohio'])
long_df.iloc[::20]
Colorado
Texas
New York
Ohio
2000-01-05
-0.207091
-1.458642
-0.406117
-0.153867
2000-05-24
-0.047038
-0.496946
-0.091025
0.693195
2000-10-11
0.088201
-0.193686
-1.444394
-1.315864
2001-02-28
-0.654497
0.093796
-0.819060
0.755123
2001-07-18
1.885355
-0.206915
-1.392564
-1.514281
long_df.loc['2001-5']
Colorado
Texas
New York
Ohio
2001-05-02
-1.742909
-0.996392
0.824744
0.352815
2001-05-09
-0.951432
-0.326518
-0.767074
0.168574
2001-05-16
0.926389
0.064682
-0.253195
-0.081806
2001-05-23
1.592441
-2.418966
0.713259
-1.198133
2001-05-30
0.700246
0.593380
0.179941
0.127628
2、重复索引的时间序列
在某些数据中,可能会遇到多个数据在同一时间戳下的情况
dates = pd.DatetimeIndex(['1/1/2000', '1/2/2000', '1/2/2000',
'1/2/2000', '1/3/2000'])
dup_ts = pd.Series(np.random.randn(5), index=dates)
dup_ts
2000-01-01 1.350517
2000-01-02 -0.646460
2000-01-02 0.503535
2000-01-02 1.518830
2000-01-03 -0.276995
dtype: float64
我们通过is_unique属性来查看index是否是唯一值
# 判断索引值是否唯一
dup_ts.index.is_unique
False
对这个时间序列取索引的的话, 要么得到标量,要么得到切片,这取决于时间戳是否是重复的
dup_ts['1/2/2000']
2000-01-02 -0.646460
2000-01-02 0.503535
2000-01-02 1.518830
dtype: float64
# 索引的索引
dup_ts['1/2/2000'][2]
1.5188299993953172
3、生成日期范围
date_range默认会生成按日频度的时间戳,会保留开始或结束的时间戳。
pd.date_range(start='2012-04-01 12:56:3', periods=6)
DatetimeIndex(['2012-04-01 12:56:03', '2012-04-02 12:56:03',
'2012-04-03 12:56:03', '2012-04-04 12:56:03',
'2012-04-05 12:56:03', '2012-04-06 12:56:03'],
dtype='datetime64[ns]', freq='D')
pd.date_range(end='2012-04-06', periods=6)
DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04',
'2012-04-05', '2012-04-06'],
dtype='datetime64[ns]', freq='D')
# 设定频度
pd.date_range('2000-01-01', '2000-12-01', freq='BM')
DatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31', '2000-04-28',
'2000-05-31', '2000-06-30', '2000-07-31', '2000-08-31',
'2000-09-29', '2000-10-31', '2000-11-30'],
dtype='datetime64[ns]', freq='BM')
时间序列频度:
有些时候我们的时间序列数据带有小时,分,秒这样的信息,但我们想要让这些时间戳全部归一化到午夜(normalized to midnight, 即晚上0点),这个时候要用到normalize选项
nor_date = pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True)
nor_date
DatetimeIndex(['2012-05-02', '2012-05-03', '2012-05-04', '2012-05-05',
'2012-05-06'],
dtype='datetime64[ns]', freq='D')
# 可以看到小时,分,秒全部变为0
nor_date[2]
Timestamp('2012-05-04 00:00:00', freq='D')
# 设定频度为4小时
pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4H')
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00',
'2000-01-01 08:00:00', '2000-01-01 12:00:00',
'2000-01-01 16:00:00', '2000-01-01 20:00:00',
'2000-01-02 00:00:00', '2000-01-02 04:00:00',
'2000-01-02 08:00:00', '2000-01-02 12:00:00',
'2000-01-02 16:00:00', '2000-01-02 20:00:00',
'2000-01-03 00:00:00', '2000-01-03 04:00:00',
'2000-01-03 08:00:00', '2000-01-03 12:00:00',
'2000-01-03 16:00:00', '2000-01-03 20:00:00'],
dtype='datetime64[ns]', freq='4H')
# 设定频度为1.5小时
pd.date_range('2000-01-01', periods=10, freq='1h30min')
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 01:30:00',
'2000-01-01 03:00:00', '2000-01-01 04:30:00',
'2000-01-01 06:00:00', '2000-01-01 07:30:00',
'2000-01-01 09:00:00', '2000-01-01 10:30:00',
'2000-01-01 12:00:00', '2000-01-01 13:30:00'],
dtype='datetime64[ns]', freq='90T')
一个有用的类(class)是月中的第几周(Week of month),用WOM表示。我们想得到每个月的第三个星期五:
# 频度为每个月的第三个星期五
rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI')
rng
DatetimeIndex(['2012-01-20', '2012-02-17', '2012-03-16', '2012-04-20',
'2012-05-18', '2012-06-15', '2012-07-20', '2012-08-17'],
dtype='datetime64[ns]', freq='WOM-3FRI')
# 日期范围也能通过时区集合(time zone set)来创建
pd.date_range('3/9/2012 9:30', periods=10, freq='D', tz='UTC')
DatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00',
'2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00',
'2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00',
'2012-03-15 09:30:00+00:00', '2012-03-16 09:30:00+00:00',
'2012-03-17 09:30:00+00:00', '2012-03-18 09:30:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
4、数据偏移(提前与推后)
偏移(shifting)表示按照时间把数据向前或向后推移。Series和DataFrame都有一个shift方法实现偏移,索引(index)不会被更改
ts = pd.Series(np.random.randn(4),
index=pd.date_range('1/1/2000', periods=4, freq='M'))
ts
2000-01-31 -0.447984
2000-02-29 0.735111
2000-03-31 0.212321
2000-04-30 -0.987703
Freq: M, dtype: float64
# 位移时,会引入缺失值
ts.shift(-2)
2000-01-31 0.212321
2000-02-29 -0.987703
2000-03-31 NaN
2000-04-30 NaN
Freq: M, dtype: float64
shift的一个普通的用法是计算时间序列的百分比变化,可以表示为
ts / ts.shift(1) - 1
2000-01-31 NaN
2000-02-29 -2.640931
2000-03-31 -0.711171
2000-04-30 -5.651929
Freq: M, dtype: float64
因为普通的shift不会对index进行修改,一些数据会被丢弃。因此如果频度是已知的,可以把频度传递给shift,这样的话时间戳会自动变化
ts.shift(2)
2000-01-31 NaN
2000-02-29 NaN
2000-03-31 -0.447984
2000-04-30 0.735111
Freq: M, dtype: float64
ts.shift(2, freq='M')
2000-03-31 -0.447984
2000-04-30 0.735111
2000-05-31 0.212321
2000-06-30 -0.987703
Freq: M, dtype: float64
# 偏移到月底
from pandas.tseries.offsets import Day, MonthEnd
now = datetime(2011, 11, 17)
offset = MonthEnd() # 移到月底
now + offset
Timestamp('2011-11-30 00:00:00')
# 向前偏移一个月
offset.rollback(now)
Timestamp('2011-10-31 00:00:00')
offset.rollforward(now)
Timestamp('2011-11-30 00:00:00')
datetime.now()
datetime.datetime(2018, 5, 11, 8, 45, 22, 629877)
你可能感兴趣的:(数据分析,利用Python进行数据分析)