2020-08-06--Pandas-06--时间序列详解

基础概述

下面列出了 Pandas中 和时间日期相关常用的类以及创建方法。

备注 创建方法
Timestamp 时刻数据 to_datetime,Timestamp
DatetimeIndex Timestamp的索引 to_datetime,date_range,DatetimeIndex
Period 时期数据 Period
PeriodIndex Period period_range,PeriodIndex

Pandas 中关于时间序列最常见的类型就是时间戳(Timestamp)了,创建时间戳的方法有很多种,我们分别来看一看。

time = pd.Timestamp(2018, 5, 21)
print(time)
# 2018-05-21 00:00:00
time = pd.Timestamp("2018-5-21")
print(time)
# 2018-05-21 00:00:00

除了时间戳之外,另一个常见的结构是时间跨度(Period)。

periods = pd.Period("2018-01")
print(periods)
# 2018-01
periods = pd.Period("2018-05", freq="D")
print(periods)
# 2018-05-01

Timestamp 和 Period 可以是索引。将Timestamp 和 Period 作为 Series 或 DataFrame的索引后会自动强制转为为 DatetimeIndex 和 PeriodIndex。

dates = [pd.Timestamp("2018-05-01"), pd.Timestamp("2018-05-02"), pd.Timestamp("2018-05-03"), pd.Timestamp("2018-05-04")]
ts = pd.Series(data=["Tom", "Bob", "Mary", "James"], index=dates)
print(ts)
# 2018-05-01      Tom
# 2018-05-02      Bob
# 2018-05-03     Mary
# 2018-05-04    James
# dtype: object
print(ts.index)
# DatetimeIndex(['2018-05-01', '2018-05-02', '2018-05-03', '2018-05-04'], dtype='datetime64[ns]', freq=None)

periods = [pd.Period("2018-01"), pd.Period("2018-02"), pd.Period("2018-03"), pd.Period("2018-4")]
ts = pd.Series(data=["Tom", "Bob", "Mary", "James"], index=periods)
print(ts)
# 2018-01      Tom
# 2018-02      Bob
# 2018-03     Mary
# 2018-04    James
# Freq: M, dtype: object
print(ts.index)
# PeriodIndex(['2018-01', '2018-02', '2018-03', '2018-04'], dtype='period[M]', freq='M')

转换时间戳

你可能会想到,我们经常要和文本数据(字符串)打交道,能否快速将文本数据转为时间戳呢?

答案是可以的,通过 to_datetime 能快速将字符串转换为时间戳。当传递一个Series时,它会返回一个Series(具有相同的索引)。

timeSeries = pd.to_datetime(pd.Series(["Jul 31, 2018", "2018-05-10", None]))
print(timeSeries)
# 0   2018-07-31
# 1   2018-05-10
# 2          NaT
# dtype: datetime64[ns]

而传递一个list列表的则转换为DatetimeIndex。

timeList = pd.to_datetime(["2005/11/23", "2010.12.31"])
print(timeList)
# DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)

unix转换时间戳

除了可以将文本数据转为时间戳外,还可以将 unix 时间转为时间戳。

sunixTime = pd.to_datetime([1349720105, 1349806505, 1349892905], unit="s")
print(sunixTime)
# DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05',
#                '2012-10-10 18:15:05'],
#               dtype='datetime64[ns]', freq=None)

msunixTime = pd.to_datetime([1349720105100, 1349720105200, 1349720105300], unit="ms")
print(msunixTime)
# DatetimeIndex(['2012-10-08 18:15:05.100000', '2012-10-08 18:15:05.200000',
#                '2012-10-08 18:15:05.300000'],
#               dtype='datetime64[ns]', freq=None)

unix的单位可以是s,也可以是ms。该数值是从1970-1-1日开始计算的,通常获取时间最先获取的数值就是unix值,所以用的还是比较多的。

生成时间戳范围

有时候,我们可能想要生成某个范围内的时间戳。例如,我想要生成 "2018-6-26" 这一天之后的8天时间戳,如何完成呢?我们可以使用 date_range 和 bdate_range 来完成时间戳范围的生成。

  • date_range 默认使用的频率是 日历日
  • bdate_range 默认使用的频率是 营业日
data_range = pd.date_range("2018-6-26", periods=8)
print(data_range)
# DatetimeIndex(['2018-06-26', '2018-06-27', '2018-06-28', '2018-06-29',
#                '2018-06-30', '2018-07-01', '2018-07-02', '2018-07-03'],
#               dtype='datetime64[ns]', freq='D')

bdate_range = pd.bdate_range("2018-6-26", periods=8)
print(bdate_range)
# DatetimeIndex(['2018-06-26', '2018-06-27', '2018-06-28', '2018-06-29',
#                '2018-07-02', '2018-07-03', '2018-07-04', '2018-07-05'],
#               dtype='datetime64[ns]', freq='B')

当然了,我们可以自己指定频率.

比如,我们可以按周来生成时间戳范围。

week = pd.date_range("2018-6-26", periods=8, freq="W")
print(week)
# DatetimeIndex(['2018-07-01', '2018-07-08', '2018-07-15', '2018-07-22',
#                '2018-07-29', '2018-08-05', '2018-08-12', '2018-08-19'],
#               dtype='datetime64[ns]', freq='W-SUN')

显示的是7-01后的每周的周日。

DataTimeIndex---时间索引

在上边,当Timestamp 和 Period 作为 Series 或 DataFrame的索引后会自动强制转为为 DatetimeIndex 和 PeriodIndex。

所以DatetimeIndex 的主要作用是之一是用作 Pandas 对象的索引,使用它作为索引除了拥有普通索引对象的所有基本功能外,还拥有简化频率处理的高级时间序列方法。

首先创建一个时间戳作为索引的Series对象

# 创建时间戳作为索引的Series对象
rng = pd.date_range("2018-6-24", periods=4, freq="W")
ts = pd.Series(range(len(rng)), index=rng)
print(ts)
# 2018-06-24    0
# 2018-07-01    1
# 2018-07-08    2
# 2018-07-15    3
# Freq: W-SUN, dtype: int64

访问数据

通过日期访问数据

data  =ts['2018-06-24']
print(data)      # 0
切片访问

切片操作时前开后开的。

data1 = ts["2018-07-08": "2018-07-22"]
print(data1)
# 2018-07-08    2
# 2018-07-15    3
# Freq: W-SUN, dtype: int64
print(type(data1))
# <class 'pandas.core.series.Series'>

返回的是Series对象类型。

年份访问

data2 = ts['2018']
print(data2)
# 2018-06-24    0
# 2018-07-01    1
# 2018-07-08    2
# 2018-07-15    3
# Freq: W-SUN, dtype: int64

会把该年份中的所有数据全部查出来。

使用datetime类型进行 数据访问

除了可以使用字符串对 DateTimeIndex 进行索引外,还可以使用 datetime(日期时间)对象来进行索引。

from datetime import datetime

c = ts[datetime(2018, 7, 8) : datetime(2018, 7, 22)]
print(c)
# 2018-07-08    2
# 2018-07-15    3
# Freq: W-SUN, dtype: int64

根据DateTimeIndex或Timestamp访问属性

我们可以通过 TimestampDateTimeIndex 访问一些时间/日期的属性。这里列举一些常见的,想要查看所有的属性见官方链接:Time/Date Components(http://pandas.pydata.org/pandas-docs/stable/timeseries.html#time-date-components)

获取所有数据的年份:

year = ts.index.year
print(year)
# Int64Index([2018, 2018, 2018, 2018], dtype='int64')

获取索引是星期几

week = ts.index.dayofweek
print(week)
# Int64Index([6, 6, 6, 6], dtype='int64')

获取一年中的第几周

weeknum = ts.index.isocalendar()
print(weeknum)
#             year  week  day
# 2018-06-24  2018    25    7
# 2018-07-01  2018    26    7
# 2018-07-08  2018    27    7
# 2018-07-15  2018    28    7

DateOffset对象

DateOffset 从名称中就可以看出来是要做日期偏移的,
工作方式如下:

from pandas.tseries.offsets import *
d = pd.Timestamp("2018-06-25")
d = d + DateOffset(weeks=2, days=5)
print(d)
# 2018-07-14 00:00:00

除了可以使用 DateOffset 完成上面的功能外,还可以使用偏移量实例来完成。

print(d)
# 2018-07-14 00:00:00

d = d + Week(2) + Day(5)
print(d)
# 2018-08-02 00:00:00

与时间序列相关的方法

在做时间序列相关的工作时,经常要对时间做一些移动/滞后、频率转换、采样等相关操作,我们来看下这些操作如何使用吧。

1.移动

如果你想移动或滞后时间序列的数据部分,你可以使用 shift 方法。

ts = ts.shift(2)
print(ts)
# 2018-06-24    NaN
# 2018-07-01    NaN
# 2018-07-08    0.0
# 2018-07-15    1.0
# Freq: W-SUN, dtype: float64

可以看到,Series 所有的值都都移动了 2 个距离。如果不想移动值,而是移动日期索引,可以使用 freq 参数

c = ts.shift(2,freq=Day())
print(c)
# 2018-06-26    0
# 2018-07-03    1
# 2018-07-10    2
# 2018-07-17    3
# dtype: int64

可以看到,现在日期索引移动了 2 天的间隔。通过 tshift 同样可以达到相同的效果。

c = ts.tshift(2,freq=Day())
print(c)
# 2018-06-26    0
# 2018-07-03    1
# 2018-07-10    2
# 2018-07-17    3
# dtype: int64

但是建议使用shift()进行移动。

2.频率转换

asfrea()

频率转换可以使用 asfreq 函数来实现。下面演示了将频率由周转为了天。

print(ts)
# 2018-06-24    0
# 2018-07-01    1
# 2018-07-08    2
# 2018-07-15    3
# Freq: W-SUN, dtype: int64
ts = ts.asfreq(Day())
print(ts)
# 2018-06-24    0.0
# 2018-06-25    NaN
# 2018-06-26    NaN
# 2018-06-27    NaN
# 2018-06-28    NaN
# 2018-06-29    NaN
# 2018-06-30    NaN
# 2018-07-01    1.0
# 2018-07-02    NaN
# 2018-07-03    NaN
# 2018-07-04    NaN
# 2018-07-05    NaN
# 2018-07-06    NaN
# 2018-07-07    NaN
# 2018-07-08    2.0
# 2018-07-09    NaN
# 2018-07-10    NaN
# 2018-07-11    NaN
# 2018-07-12    NaN
# 2018-07-13    NaN
# 2018-07-14    NaN
# 2018-07-15    3.0
# Freq: D, dtype: float64

聪明的你会发现出现了缺失值,因此 Pandas 为你提供了 method 参数来填充缺失值。几种不同的填充方法参考 Pandas 缺失值处理)) 中 fillna 介绍。

print(ts)
# 2018-06-24    0
# 2018-07-01    1
# 2018-07-08    2
# 2018-07-15    3
# Freq: W-SUN, dtype: int64
ts = ts.asfreq(Day(),method='ffill')
print(ts)
# 2018-06-24    0
# 2018-06-25    0
# 2018-06-26    0
# 2018-06-27    0
# 2018-06-28    0
# 2018-06-29    0
# 2018-06-30    0
# 2018-07-01    1
# 2018-07-02    1
# 2018-07-03    1
# 2018-07-04    1
# 2018-07-05    1
# 2018-07-06    1
# 2018-07-07    1
# 2018-07-08    2
# 2018-07-09    2
# 2018-07-10    2
# 2018-07-11    2
# 2018-07-12    2
# 2018-07-13    2
# 2018-07-14    2
# 2018-07-15    3
# Freq: D, dtype: int64

重采样

resample 表示根据日期维度进行数据聚合,可以按照分钟、小时、工作日、周、月、年等来作为日期维度,更多的日期维度见 Offset Aliases(http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases)。

这里我们先以月来作为时间维度来进行聚合。

求出每个月和每周数据的总和:

print(c)
# 2018-06-24    0
# 2018-06-25    0
# 2018-06-26    0
# 2018-06-27    0
# 2018-06-28    0
# 2018-06-29    0
# 2018-06-30    0
# 2018-07-01    1
# 2018-07-02    1
# 2018-07-03    1
# 2018-07-04    1
# 2018-07-05    1
# 2018-07-06    1
# 2018-07-07    1
# 2018-07-08    2
# 2018-07-09    2
# 2018-07-10    2
# 2018-07-11    2
# 2018-07-12    2
# 2018-07-13    2
# 2018-07-14    2
# 2018-07-15    3
# Freq: D, dtype: int64

re = c.resample('1M').sum()
print(re)
# 2018-06-30     0
# 2018-07-31    24
# Freq: M, dtype: int64

re = c.resample('1W').sum()
print(re)
# 2018-06-24     0
# 2018-07-01     1
# 2018-07-08     8
# 2018-07-15    15
# Freq: W-SUN, dtype: int64

求出每个月和每周数据的平均值:

re = c.resample("1M").mean()
print(re)
# 2018-06-30    0.0
# 2018-07-31    1.6
# Freq: M, dtype: float64

re = c.resample("1W").mean()
print(re)
# 2018-06-24    0.000000
# 2018-07-01    0.142857
# 2018-07-08    1.142857
# 2018-07-15    2.142857
# Freq: W-SUN, dtype: float64
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352