前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >时间序列 | pandas时间序列基础

时间序列 | pandas时间序列基础

作者头像
数据STUDIO
发布2021-06-24 10:23:56
发布2021-06-24 10:23:56
1.5K00
代码可运行
举报
文章被收录于专栏:数据STUDIO数据STUDIO
运行总次数:0
代码可运行

时间序列(time series)数据是一种重要的结构化数据形式,应用于多个领域,包括金融学、经济学、生态学、神经科学、物理学等。在多个时间点观察或测量到的任何事物都可以形成一段时间序列。很多时间序列是固定频率的,也就是说,数据点是根据某种规律定期出现的(比如每15秒、每5分钟、每月出现一次)。时间序列也可以是不定期的,没有固定的时间单位或单位之间的偏移量。时间序列数据的意义取决于具体的应用场景,主要有以下几种:

  • 时间戳(timestamp),特定的时刻。
  • 固定时期(period),如2008年1月或2020年全年。
  • 时间间隔(interval),由起始和结束时间戳表示。时期(period)可以被看做间隔(interval)的特例。

本文内容包括,索引、选取、子集构造,日期的范围、频率以及移动基础等。

首先回顾下datetime模块

代码语言:javascript
代码运行次数:0
运行
复制
>>> from datetime import 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   -0.162712
2011-01-05    1.876604
2011-01-07   -0.016393
2011-01-08    0.239276
2011-01-10   -0.704732
2011-01-12   -1.502936
dtype: float64
  
# 这些datetime对象实际上是被放在一个DatetimeIndex中
>>> ts.index 
DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', 
               '2011-01-08','2011-01-10', '2011-01-12']
              ,dtype='datetime64[ns]', freq=None)

# DatetimeIndex中的各个标量值是pandas的Timestamp对象
>>> stamp = ts.index[0] 
>>> stamp
Timestamp('2011-01-02 00:00:00')

索引、选取、子集构造

根据标签索引
代码语言:javascript
代码运行次数:0
运行
复制
>>> stamp = ts.index[2]
>>> stamp
Timestamp('2011-01-07 00:00:00')
传入一个可以被解释为日期的字符串
代码语言:javascript
代码运行次数:0
运行
复制
>>> ts['1/10/2011']
-0.7047322514407551
>>> ts['20110110']
-0.7047322514407551
对于较长的时间序列,只需传入“年”或“年月”即可轻松选取数据的切片
代码语言:javascript
代码运行次数:0
运行
复制
>>> long_ts = pd.Series(np.random.randn(1000)
                        ,index = pd.date_range('01/01/2000'
                                               ,periods = 1000))
>>> long_ts
2000-01-01    1.054099
2000-01-02   -1.554446
2000-01-03    0.576552
2000-01-04    0.785388
2000-01-05   -1.674210
                ...   
2002-09-22   -0.836620
2002-09-23    0.561945
2002-09-24   -0.196348
2002-09-25   -0.937816
2002-09-26   -0.431839
Freq: D, Length: 1000, dtype: float64
            
>>> long_ts['2002']
2002-01-01   -1.084543
2002-01-02   -2.452728
2002-01-03    0.415411
2002-01-04    1.284758
2002-01-05   -0.449588
                ...   
2002-09-22   -0.836620
2002-09-23    0.561945
2002-09-24   -0.196348
2002-09-25   -0.937816
2002-09-26   -0.431839
Freq: D, Length: 269, dtype: float64
            
>>> long_ts['2002-08']
2002-08-01    1.061041
2002-08-02    0.181386
2002-08-03    0.697954
2002-08-04    0.729076
2002-08-05   -0.573171
                ...   
2002-08-27   -1.240861
2002-08-28    0.397729
2002-08-29   -2.765702
2002-08-30   -2.228761
2002-08-31   -0.386724
Freq: D, dtype: float64
datetime对象也可以进行切
代码语言:javascript
代码运行次数:0
运行
复制
>>> long_ts[datetime(2001,5,10)::100]
2001-05-10   -1.412274
2001-08-18   -0.051782
2001-11-26   -0.948275
2002-03-06    0.565756
2002-06-14    0.040260
2002-09-22   -0.836620
Freq: 100D, dtype: float64
        
>>> '''由于大部分时间序列数据都是按照时间先后排序的,
... 因此你也可以用不存在于该时间序列中的时间戳对其进行切片(即范围查询)'''
>>> ts['1/6/2011':'1/11/2011'] 
2011-01-07   -0.016393
2011-01-08    0.239276
2011-01-10   -0.704732
dtype: float64
truncate()等价的实例方法也可以截取两个日期之间TimeSeries:
代码语言:javascript
代码运行次数:0
运行
复制
>>> ts.truncate(after = '1/9/2011')
2011-01-02   -0.162712
2011-01-05    1.876604
2011-01-07   -0.016393
2011-01-08    0.239276
dtype: float64
对DataFrame的行进行索引
代码语言:javascript
代码运行次数:0
运行
复制
>>> dates = pd.date_range('2/1/2020', periods = 10, freq = 'W-WED')
>>> dates_1 = pd.date_range('2/1/2020', periods = 10, freq = 'W-SUN')
>>> dates
DatetimeIndex(['2020-02-05', '2020-02-12', '2020-02-19', '2020-02-26',
               '2020-03-04', '2020-03-11', '2020-03-18', '2020-03-25',
               '2020-04-01', '2020-04-08'],
              dtype='datetime64[ns]', freq='W-WED')
>>> dates_1
DatetimeIndex(['2020-02-02', '2020-02-09', '2020-02-16', '2020-02-23',
               '2020-03-01', '2020-03-08', '2020-03-15', '2020-03-22',
               '2020-03-29', '2020-04-05'],
              dtype='datetime64[ns]', freq='W-SUN')

>>> long_df = pd.DataFrame(np.random.randn(10, 4)
                           , index=dates
                           , columns=['Colorado', 'Texas','New York', 'Ohio'])
>>> long_df

Colorado

TexasNew York

New YorkOhio

Ohio

2020-02-02

-0.029801

0.514341

-0.141655

1.715595

2020-02-09

-0.824540

-0.296288

-0.827877

0.245774

2020-02-16

-0.084484

0.126171

0.423891

-1.709223

2020-02-23

-1.688280

1.032951

-0.062944

-0.521174

2020-03-01

0.322167

0.226218

0.515736

0.098784

2020-03-08

2.117617

1.840786

-0.283062

-1.414009

2020-03-15

-0.853829

0.101525

0.395773

-0.938408

2020-03-22

0.022767

-1.761823

0.361345

-1.015794

2020-03-29

-1.075985

-0.403549

2.053879

-0.075490

2020-04-05

-0.468759

1.591693

0.012494

1.013237

同样只需传入“年”或“年月”即可轻松选取数据的切片

代码语言:javascript
代码运行次数:0
运行
复制
long_df.loc['3-2020']

Colorado

Texas

New York

Ohio

2020-03-01

-0.983091

-0.359375

0.014695

1.123173

2020-03-08

0.506222

-0.188421

-1.298069

-1.769189

2020-03-15

-1.773529

-1.664837

0.045604

0.365199

2020-03-22

-2.059035

-1.386787

0.402163

-1.967558

2020-03-29

-1.254208

-0.272317

-1.595532

1.349384

带有重复索引的时间序列
代码语言:javascript
代码运行次数:0
运行
复制
>>> dates = pd.DatetimeIndex(['1/1/2000', '1/2/2000', '1/2/2000','1/2/2000', '1/3/2000'])
>>> dup_ts = pd.Series(np.arange(5), index = dates)
>>> dup_ts
2000-01-01    0
2000-01-02    1
2000-01-02    2
2000-01-02    3
2000-01-03    4
dtype: int64
    
>>> dup_ts.index.is_unique
False
>>> dup_ts['1/2/2000']
2000-01-02    1
2000-01-02    2
2000-01-02    3
dtype: int64
    
>>> """假设你想要对具有非唯一时间戳的数据进行聚合。
... 一个办法是使用groupby,并传入level=0 """
>>> group = dup_ts.groupby(level=0)
>>> group.mean()
2000-01-01    0
2000-01-02    2
2000-01-03    4
dtype: int64

日期的范围、频率以及移动

pandas中的原生时间序列一般被认为是不规则的,也就是说,它们没有固定的频率。对于大部分应用程序而言,这是无所谓的。但是,它常常需要以某种相对固定 的频率进行分析,比如每日、每月、每15分钟等(这样自然会在时间序列中引入缺失值)。幸运的是,pandas有一整套标准时间序列频率以及用于重采样、频率推断、生成固定频率日期范围的工具。例如,我们可以将之前那个时间序列转换为一 个具有固定频率(每日)的时间序列,只需调用resample即可


pandas.date_range() 生成日期范围

  • pandas.date_range可用于根据指定的频率生成指定长度的DatetimeIndex
  • 默认情况下,date_range会产生按天计算的时间点。
  • 如果只传入起始或结束日期,那就还得传入一个表示一段时间的数字,起始和结束日期定义了日期索引的严格边界
代码语言:javascript
代码运行次数:0
运行
复制
>>> pd.date_range(start='2012-04-01', periods=20)
DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04',
               '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08',
               '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12',
               '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16',
               '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20'],
              dtype='datetime64[ns]', freq='D')

>>> pd.date_range(end ='2012-04-01', periods=20)
DatetimeIndex(['2012-03-13', '2012-03-14', '2012-03-15', '2012-03-16',
               '2012-03-17', '2012-03-18', '2012-03-19', '2012-03-20',
               '2012-03-21', '2012-03-22', '2012-03-23', '2012-03-24',
               '2012-03-25', '2012-03-26', '2012-03-27', '2012-03-28',
               '2012-03-29', '2012-03-30', '2012-03-31', '2012-04-01'],
              dtype='datetime64[ns]', freq='D')

如果你想要生成一个由每月最后一个工作日组成的日期索引,可以传入"BM"频率(表示business end of month,下表是频率列表),这样就只会包含时间间隔内(或刚好在边界上的)符合频率要求的日期:

别名

便宜量类型

说明

D

Day

每日历日

B

BusinessDay

每工作日

H

Hour

每小时

T 或 min

Minute

每分

S

Second

每秒

L或ms

Milli

每毫秒(即千万分之一秒)

U

Micro

每微秒(即百万分之一秒)

M

MonthEnd

每月最后一个日历日

BM

BusinessMonthEnd

每月最后一个工作日

MS

MonthBegin

每月第一个日历日

BMS

BusinessMonthBegin

每月第一个工作日

W-MON、W-TUE ...

Week

从指定的星期几(MON、TUE、WED、THU、FRI、SAT、SUN)开始算起,每周

WON-1MON、WOM-2MON...

WeekOfMonth

产生每月第一、第二、第三或第四周的星期即。例如,WOM-3FRI表示每月第3个星期五

Q-JAN、Q-FEB...

QuarterEnd

对于以指定月份(JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV、EDC)结束的年度,每季度最后一个月的最后一个日历日

BQ-JAN、BQ-FEB...

BusinessQuarterEnd

对于以指定月份结束的年度,每季度最后一月的最后一个工作日

QS-JAN、QS-FEB...

QuarterBegin

对于以指定月份结束的年度、每季度最后一月的第一个日历日

BQS-JAN、BQS-FEB...

BusinessQuarterBegin

对于以指定月份结束的年度、每季度最后一月的第一个工作日

A-JAN、A-FEB...

YearEnd

每年指定月份(JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV、EDC)的最后一个日历日

BA-JAN、BA-FEB...

BusinessYearEnd

每年指定月份的最后一个工作日

AS-JAN、As-FEB

YearBegin

每年指定月份的第一个日历日

BAS-JAN、BAS-FEB...

BusinessYearnBegin

每年指定月份的第一个工作日

有时,虽然起始和结束日期带有时间信息,但你希望产生一组被规范化 (normalize)到午夜的时间戳。normalize选项即可实现该功能:

代码语言:javascript
代码运行次数:0
运行
复制
>>> pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True)
>>> DatetimeIndex(['2012-05-02', '2012-05-03', '2012-05-04', '2012-05-05',
                   '2012-05-06'],
                  dtype='datetime64[ns]', freq='D')

频率和日期偏移量

pandas中的频率是由一个基础频率(base frequency)和一个乘数组成的。基础频 率通常以一个字符串别名表示,比如"M"表示每月,"H"表示每小时。对于每个基础 频率,都有一个被称为日期偏移量(date offset)的对象与之对应

代码语言:javascript
代码运行次数:0
运行
复制
>>> from pandas.tseries import offsets
>>> offsets.Hour()
<Hour>
# 传入一个整数即可定义偏移量的倍数:
>>> offsets.Hour(2)  
<2 * Hours>

# 大部分偏移量对象都可通过加法进行连接
>>> Hour(2) + Minute(30) 
<150 * Minutes>

#在创建日期范围时,给freq传入参数即可实现偏移频率
>>> 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')

WOM 日期 (Week Of Month)

WOM(Week Of Month)是一种非常实用的频率类,它以WOM开头。它使你能获得诸如“每月第3个星期五”之类的日期:

代码语言:javascript
代码运行次数:0
运行
复制
>>> rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI') # 每月第3个星期五
>>> list(rng)
[Timestamp('2012-01-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-02-17 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-03-16 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-04-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-05-18 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-06-15 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-07-20 00:00:00', freq='WOM-3FRI'),
 Timestamp('2012-08-17 00:00:00', freq='WOM-3FRI')]

shfit() -- 移动(超前和滞后)数据

移动(shifting)指的是沿着时间轴将数据前移或后移。SeriesDataFrame都有一个shift方法用于执行单纯的前移或后移操作,保持索引不变:

代码语言:javascript
代码运行次数:0
运行
复制
>>> ts
2011-01-02   -0.162712
2011-01-05    1.876604
2011-01-07   -0.016393
2011-01-08    0.239276
2011-01-10   -0.704732
2011-01-12   -1.502936
dtype: float64
  
# 往前移动2格,索引不变  
>>> ts.shift(2)  
2011-01-02         NaN
2011-01-05         NaN
2011-01-07   -0.162712
2011-01-08    1.876604
2011-01-10   -0.016393
2011-01-12    0.239276
dtype: float64
  
>>> ts.shift(-2)
2011-01-02   -0.016393
2011-01-05    0.239276
2011-01-07   -0.704732
2011-01-08   -1.502936
2011-01-10         NaN
2011-01-12         NaN
dtype: float64

shift通常用于计算一个时间序列或多个时间序列(如DataFrame的列)中的百分比变化。

代码语言:javascript
代码运行次数:0
运行
复制
>>> ts/ts.shift(1)-1
2011-01-02          NaN
2011-01-05   -12.533276
2011-01-07    -1.008735
2011-01-08   -15.596514
2011-01-10    -3.945270
2011-01-12     1.132634
dtype: float64
  
>>> ts
2011-01-02   -0.162712
2011-01-05    1.876604
2011-01-07   -0.016393
2011-01-08    0.239276
2011-01-10   -0.704732
2011-01-12   -1.502936
dtype: float64
  
>>> ts.shift(2) 
2011-01-02         NaN
2011-01-05         NaN
2011-01-07   -0.162712
2011-01-08    1.876604
2011-01-10   -0.016393
2011-01-12    0.239276
dtype: float64
  
>>> ts.shift(2,freq = 'D')
2011-01-04   -0.162712
2011-01-07    1.876604
2011-01-09   -0.016393
2011-01-10    0.239276
2011-01-12   -0.704732
2011-01-14   -1.502936
dtype: float64

>>> ts.shift(2, freq = 'BM')
2011-02-28   -0.162712
2011-02-28    1.876604
2011-02-28   -0.016393
2011-02-28    0.239276
2011-02-28   -0.704732
2011-02-28   -1.502936
dtype: float64

通过偏移量对日期进行位移

代码语言:javascript
代码运行次数:0
运行
复制
>>> from pandas.tseries.offsets import *
>>> now = datetime(2020,2,20)
>>> now + 3 * Day()
Timestamp('2020-02-23 00:00:00')

如果加的是锚点偏移量(比如MonthEnd),第一次增量会将原日期向前滚动到符合频率规则的下一个日期:

代码语言:javascript
代码运行次数:0
运行
复制
>>> now + MonthEnd()
Timestamp('2020-02-29 00:00:00')

通过锚点偏移量的rollforwardrollback方法,可明确地将日期向前或向后“滚动”:

代码语言:javascript
代码运行次数:0
运行
复制
>>> now
datetime.datetime(2020, 2, 20, 0, 0)

>>> me_offset = MonthEnd()
>>> MonthEnd().rollforward(now)
Timestamp('2020-02-29 00:00:00')

>>> me_offset.rollback(now)
Timestamp('2020-01-31 00:00:00')

结合groupby运用‘滚动’

代码语言:javascript
代码运行次数:0
运行
复制
>>> ts = pd.Series(np.random.randn(20), index = pd.date_range('1/15/2020',periods = 20, freq = '5d'))>>> ts
2020-01-15    1.036827
2020-01-20    0.013990
2020-01-25    0.833330
2020-01-30    0.377223
2020-02-04   -0.275511
2020-02-09   -0.331296
2020-02-14   -0.433451
2020-02-19   -0.518679
2020-02-24    0.828722
2020-02-29    0.158464
2020-03-05    0.737144
2020-03-10    0.318404
2020-03-15   -0.240118
2020-03-20   -0.910773
2020-03-25    0.416353
2020-03-30   -1.265480
2020-04-04    0.882785
2020-04-09    2.628588
2020-04-14    0.777044
2020-04-19   -1.821877
Freq: 5D, dtype: float64
    
>>> ts.groupby(MonthEnd().rollforward).mean()
2020-01-31    0.565343
2020-02-29   -0.095292
2020-03-31   -0.157412
2020-04-30    0.616635
dtype: float64

更简单、更快速地实现该功能的办法是使用resample

代码语言:javascript
代码运行次数:0
运行
复制
>>> ts.resample('M').mean()
2020-01-31    0.565343
2020-02-29   -0.095292
2020-03-31   -0.157412
2020-04-30    0.616635
Freq: M, dtype: float64
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据STUDIO 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 索引、选取、子集构造
    • 根据标签索引
    • 传入一个可以被解释为日期的字符串
    • 对于较长的时间序列,只需传入“年”或“年月”即可轻松选取数据的切片
    • datetime对象也可以进行切
    • truncate()等价的实例方法也可以截取两个日期之间TimeSeries:
    • 对DataFrame的行进行索引
    • 带有重复索引的时间序列
  • 日期的范围、频率以及移动
    • pandas.date_range() 生成日期范围
    • 频率和日期偏移量
    • WOM 日期 (Week Of Month)
    • shfit() -- 移动(超前和滞后)数据
    • 通过偏移量对日期进行位移
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档