数据导入与预处理-拓展-pandas时间数据处理02

2022-11-12 17:17:51 浏览数 (1)

数据导入与预处理-拓展-pandas时间数据处理02

  • Pandas时序数据系列博客
  • Pandas时间序列数据处理
    • 1.好用的Python库
    • 2.Pandas历史
    • 3.时序数据处理
      • 3.1 时序中的基本对象
      • 3.2 python中的datetime模块
      • 3.3. 时间戳(Date times)的构造与属性
      • 3.4. 时间差(Timedelta)的构造与属性
        • 1.Timedelta生成
          • 1.通过pd.Timedelta来构造
          • 2 to_timedelta生成
          • 3. timedelta_range生成
          • 4. dt对象
        • 2. Timedelta的运算
      • 3.5 时间段Time spans的构造与属性:Period - 1. 通过Period生成 - 2. 通过period_range方法生成 - 3. asfreq:频率转换
      • 3.6 相互转换
      • 3.7 日期偏置DateOffset的构造与属性
      • 3.8 时序中的滑窗与分组
        • 1. 滑动窗口
        • 2.重采样

Pandas时序数据系列博客

数据导入与预处理-拓展-pandas时间数据处理01

数据导入与预处理-拓展-pandas时间数据处理02

数据导入与预处理-拓展-pandas时间数据处理03

备注:如果有帮助,欢迎点赞收藏评论一键三联哈~~

Pandas时间序列数据处理

1.好用的Python库

见系列博客1

2.Pandas历史

见系列博客1

3.时序数据处理

见系列博客1

本文部分内容来源为:joyful-pandas

3.1 时序中的基本对象

见系列博客1

3.2 python中的datetime模块

见系列博客1

3.3. 时间戳(Date times)的构造与属性

见系列博客1

3.4. 时间差(Timedelta)的构造与属性

概念

单元素类型

数组类型

pandas数据类型

Date times

Timestamp

DatetimeIndex

datetime64ns

Time deltas

Timedelta

TimedeltaIndex

timedelta64ns

Time spans

Period

PeriodIndex

periodfreq

Date offsets

DateOffset

None

None

1.Timedelta生成
1.通过pd.Timedelta来构造

时间差可以理解为两个时间戳的差,这里也可以通过pd.Timedelta来构造:

通过Timestamp构建时间差Timedelta

代码语言:javascript复制
import numpy as np
import pandas as pd
pd.Timestamp('20220102 08:00:00')-pd.Timestamp('20220101 07:35:00')

输出为:

代码语言:javascript复制
Timedelta('1 days 00:25:00')

通过Timedelta生成

代码语言:javascript复制
pd.Timedelta(days=1, minutes=25) # 需要注意加s
# pd.Timedelta('1 days 25 minutes') # 字符串生成 同上一样

输出:

代码语言:javascript复制
Timedelta('1 days 00:25:00')
2 to_timedelta生成

to_timedelta生成-精确到 20.5us

代码语言:javascript复制
# 精确到 20.5us
pd.to_timedelta('20.5us')

输出为:

代码语言:javascript复制
Timedelta('0 days 00:00:00.000020500')

to_timedelta生成-构建一个Timedelta序列

代码语言:javascript复制
pd.to_timedelta(['2 days 04:06:10.00006', '15.5us', 'nan'])

输出为:

代码语言:javascript复制
TimedeltaIndex(['2 days 04:06:10.000060', '0 days 00:00:00.000015500', NaT], dtype='timedelta64[ns]', freq=None)

to_timedelta生成-指定单位

代码语言:javascript复制
pd.to_timedelta(np.arange(6), unit='d')

输出为:

代码语言:javascript复制
TimedeltaIndex(['0 days', '1 days', '2 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq=None)
3. timedelta_range生成

与date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:

代码语言:javascript复制
import numpy as np
import pandas as pd
pd.timedelta_range(start='2 day', periods=5, freq='6H', closed='right')

输出为:

代码语言:javascript复制
TimedeltaIndex(['2 days 06:00:00', '2 days 12:00:00', '2 days 18:00:00','3 days 00:00:00'],
dtype='timedelta64[ns]', freq='6H')
4. dt对象

对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds, nanoseconds,它们分别返回了对应的时间差特征。

2. Timedelta的运算

时间差支持的常用运算有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:

代码语言:javascript复制
# 初始化Timedelta
td1 = pd.Timedelta(days=1) # Timedelta('1 days 00:00:00')
td2 = pd.Timedelta(days=3) # Timedelta('3 days 00:00:00')

# 与标量的计算
td1 * 2 # Timedelta('2 days 00:00:00')

# 与时间差的计算
td2 - td1 # Timedelta('2 days 00:00:00')

# 与时间戳的计算
ts = pd.Timestamp('20200101')
td1 = pd.Timedelta(days=1) # Timedelta('1 days 00:00:00') 
ts   td1 # Timestamp('2020-01-02 00:00:00')

时间差序列计算:

代码语言:javascript复制
# 定义时间差
td1 = pd.timedelta_range(start='1 days', periods=5)
td1 
"""
# TimedeltaIndex(['1 days', '2 days', '3 days', '4 days', '5 days'], 
                 dtype='timedelta64[ns]', freq='D')
"""

td2 = pd.timedelta_range(start='12 hours', freq='2H', periods=5)
td2 
"""
TimedeltaIndex(['0 days 12:00:00', '0 days 14:00:00', '0 days 16:00:00',
                '0 days 18:00:00', '0 days 20:00:00'],
               dtype='timedelta64[ns]', freq='2H')
"""
ts = pd.date_range('20200101', '20200105')
ts
"""
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
               '2020-01-05'],

时间差序列与标量计算:

代码语言:javascript复制
td1 * 5
# TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')

输出为:

代码语言:javascript复制
TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')

时间差序列与series计算

代码语言:javascript复制
td1 * pd.Series(list(range(5))) # 逐个相乘

输出为:

代码语言:javascript复制
0    0 days
1    2 days
2    6 days
3   12 days
4   20 days
dtype: timedelta64[ns]

时间差序列直接计算

代码语言:javascript复制
td1 - td2

输出为:

代码语言:javascript复制
TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',
                '3 days 06:00:00', '4 days 04:00:00'],
               dtype='timedelta64[ns]', freq=None)

时间差序列与时间戳计算:

代码语言:javascript复制
td1   pd.Timestamp('20200101')

输出为:

代码语言:javascript复制
DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
               '2020-01-06'],
              dtype='datetime64[ns]', freq='D')

3.5 时间段Time spans的构造与属性:Period

概念

单元素类型

数组类型

pandas数据类型

Date times

Timestamp

DatetimeIndex

datetime64ns

Time deltas

Timedelta

TimedeltaIndex

timedelta64ns

Time spans

Period

PeriodIndex

periodfreq

Date offsets

DateOffset

None

None

1. 通过Period生成
代码语言:javascript复制
# 生成一个以2022-01开始,月为频率的时间构造器
# pd.Period()参数:一个时间戳   freq 参数 → freq 用于指明该 period 的长度,时间戳则说明该 period 在时间轴上的位置
period_d = pd.Period('2022', freq = 'M')
print(period_d, type(period_d))

# 通过加减整数,将周期整体移动
# 这里是按照 月、年 移动
print('period_d   1的结果为:',period_d   1)
print('period_d - 2的结果为:',period_d - 2)
print(pd.Period('2022', freq = 'A-DEC') - 1)

输出为:

代码语言:javascript复制
2022-01 <class 'pandas._libs.tslibs.period.Period'>
period_d   1的结果为: 2022-02
period_d - 2的结果为: 2021-11
2021
2. 通过period_range方法生成
代码语言:javascript复制
# pd.period_range()创建时期范围
prng = pd.period_range('1/1/2021', '1/1/2022', freq='M')
print(prng,type(prng))
print(prng[0],type(prng[0]))
# 数据格式为PeriodIndex,单个数值为Period

输出为:

代码语言:javascript复制
PeriodIndex(['2021-01', '2021-02', '2021-03', '2021-04', '2021-05', '2021-06',
             '2021-07', '2021-08', '2021-09', '2021-10', '2021-11', '2021-12',
             '2022-01'],
            dtype='period[M]', freq='M') <class 'pandas.core.indexes.period.PeriodIndex'>
2021-01 <class 'pandas._libs.tslibs.period.Period'>

构建series,指定索引为PeriodIndex

代码语言:javascript复制
ts = pd.Series(np.arange(len(prng)), index = prng)
print(ts,type(ts))
print(ts.index)
# 时间序列

输出为:

代码语言:javascript复制
2021-01     0
2021-02     1
2021-03     2
2021-04     3
2021-05     4
2021-06     5
2021-07     6
2021-08     7
2021-09     8
2021-10     9
2021-11    10
2021-12    11
2022-01    12
Freq: M, dtype: int32 <class 'pandas.core.series.Series'>
PeriodIndex(['2021-01', '2021-02', '2021-03', '2021-04', '2021-05', '2021-06',
             '2021-07', '2021-08', '2021-09', '2021-10', '2021-11', '2021-12',
             '2022-01'],
            dtype='period[M]', freq='M')
3. asfreq:频率转换
代码语言:javascript复制
# asfreq:频率转换
# Period('2020', freq = 'A-DEC')可以看成多个时间期的时间段中的游标
# Timestamp表示一个时间戳,是一个时间截面;Period是一个时期,是一个时间段!!但两者作为index时区别不大
p = pd.Period('2020','A-DEC')
print("p--->",p)
print("p--->",p.asfreq('M', how = 'start'))  # 也可写 how = 's'
print("p--->",p.asfreq('D', how = 'end'))  # 也可写 how = 'e'
# 通过.asfreq(freq, method=None, how=None)方法转换成别的频率

print('*'*10)
prng = pd.period_range('2020','2021',freq = 'M')
ts1 = pd.Series(np.random.rand(len(prng)), index = prng)
ts2 = pd.Series(np.random.rand(len(prng)), index = prng.asfreq('D', how = 'start'))
print(ts1.head(),len(ts1))
print(ts2.head(),len(ts2))
# asfreq也可以转换TIMESeries的index

输出为:

代码语言:javascript复制
p---> 2020
p---> 2020-01
p---> 2020-12-31
**********
2020-01    0.602249
2020-02    0.470631
2020-03    0.515769
2020-04    0.221421
2020-05    0.959175
Freq: M, dtype: float64 13
2020-01-01    0.115775
2020-02-01    0.309005
2020-03-01    0.738583
2020-04-01    0.785310
2020-05-01    0.574895
Freq: D, dtype: float64 13

3.6 相互转换

时间戳与时期之间的转换

时间戳转时间段

代码语言:javascript复制
# 时间戳与时期之间的转换:pd.to_period()、pd.to_timestamp()

# 每月最后一日,转化为每月
rng = pd.date_range('2020/1/1', periods = 3, freq = 'M')
ts1 = pd.Series(np.arange(len(rng)), index = rng)
print(ts1.head())
print(ts1.to_period().head())
print(type(ts1.index),type(ts1.to_period().index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'> <class 'pandas.core.indexes.period.PeriodIndex'>

输出为:

代码语言:javascript复制
2020-01-31    0
2020-02-29    1
2020-03-31    2
Freq: M, dtype: int32
2020-01    0
2020-02    1
2020-03    2
Freq: M, dtype: int32
<class 'pandas.core.indexes.datetimes.DatetimeIndex'> <class 'pandas.core.indexes.period.PeriodIndex'>
print('*'*10)

时间段转时间戳

代码语言:javascript复制
# 每月,转化为每月第一天
prng = pd.period_range('2020','2021', freq = 'M')
ts2 = pd.Series(np.arange(len(prng)), index = prng)
print(ts2.head())
print(ts2.to_timestamp().head())
print(type(ts2.index),type(ts2.to_timestamp().index))

3.7 日期偏置DateOffset的构造与属性

概念

单元素类型

数组类型

pandas数据类型

Date times

Timestamp

DatetimeIndex

datetime64ns

Time deltas

Timedelta

TimedeltaIndex

timedelta64ns

Time spans

Period

PeriodIndex

periodfreq

Date offsets

DateOffset

None

None

Offset对象

日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。

求下一个月的第几周的周几,week=0表示为第1周,weekday=0表示为周日

代码语言:javascript复制
# week=0表示为第1周,weekday=0表示为周日
pd.Timestamp('20220905')   pd.offsets.WeekOfMonth(week=0,weekday=0)

输出为:

代码语言:javascript复制
Timestamp('2022-10-03 00:00:00')

偏移30天

代码语言:javascript复制
pd.Timestamp('20220907')   pd.offsets.BDay(30)

输出为:

代码语言:javascript复制
Timestamp('2022-10-19 00:00:00')

常用的日期偏置如下可以查阅这里的文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay,其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,前者传入了需要过滤的日期列表,后者传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期。

3.8 时序中的滑窗与分组

1. 滑动窗口

所谓时序的滑窗函数,即把滑动窗口用freq关键词代替,下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30的BOLL指标可以如下写出:

代码语言:javascript复制
import matplotlib.pyplot as plt
idx = pd.date_range('20200101', '20201231', freq='B')
np.random.seed(2020)
data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列
s = pd.Series(data,index=idx)
s

输出为:

代码语言:javascript复制
2020-01-01    -1
2020-01-02    -2
2020-01-03    -1
2020-01-06    -1
2020-01-07    -2
              ..
2020-12-25    17
2020-12-28    18
2020-12-29    19
2020-12-30    19
2020-12-31    18
Freq: B, Length: 262, dtype: int32
代码语言:javascript复制
r = s.rolling('30D')
r

输出为:

代码语言:javascript复制
Rolling [window=2592000000000000,min_periods=1,center=False,win_type=freq,axis=0]

可视化:

代码语言:javascript复制
plt.plot(s,label='30D')
plt.title('BOLL LINES')
plt.plot(r.mean(),label='mean')
plt.plot(r.mean() r.std()*2,label='mean std()*2')
plt.plot(r.mean()-r.std()*2,label='mean-std()*2')
plt.legend()
plt.show()

输出为:

对于shift函数而言,作用在datetime64为索引的序列上时,可以指定freq单位进行滑动:

代码语言:javascript复制
s.shift(freq='1D')

输出为:

2.重采样

重采样对象resample和分组对象groupby的用法类似,前者是针对时间序列的分组计算而设计的分组对象。

例如,对上面的序列计算每个月的均值:

代码语言:javascript复制
s.resample('1M').mean()

输出为:

代码语言:javascript复制
2020-01-31    -3.000000
2020-02-29    -0.750000
2020-03-31     3.090909
2020-04-30     6.818182
2020-05-31     5.952381
2020-06-30     8.954545
2020-07-31    11.217391
2020-08-31     7.952381
2020-09-30    10.727273
2020-10-31     8.863636
2020-11-30    14.952381
2020-12-31    19.695652
Freq: M, dtype: float64
代码语言:javascript复制
s1 = s.copy()
s1.index = s_month
s1

输出为:

代码语言:javascript复制
1     -1
1     -2
1     -1
1     -1
1     -2
      ..
12    17
12    18
12    19
12    19
12    18
Length: 262, dtype: int32

采用分组的办法求均值

代码语言:javascript复制
s1.reset_index().groupby(by='index').mean()

输出为:

同时,如果没有内置定义的处理函数,可以通过apply方法自定义:

代码语言:javascript复制
s.resample('1M').apply(lambda x:x.max()-x.min()) # 极差

输出为

代码语言:javascript复制
2020-01-31     4
2020-02-29     7
2020-03-31     5
2020-04-30     3
2020-05-31     5
2020-06-30     7
2020-07-31     5
2020-08-31     2
2020-09-30     5
2020-10-31     4
2020-11-30    10
2020-12-31     4
Freq: M, dtype: int32

在resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。

基本知识描述完毕,系列博文中提供一个对应的案例,见系列文章3

0 人点赞