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

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

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

  • Pandas时序数据系列博客
  • Pandas时间序列数据处理
    • 1.好用的Python库
    • 2.Pandas历史
    • 3.时序数据处理
      • 3.1 时序中的基本对象
      • 3.2 python中的datetime模块
        • 1. date对象:
        • 2. datetime对象
        • 3. datetime.timedelta:时间差
        • 4. parser.parse:日期字符串转换
      • 3.3. 时间戳(Date times)的构造与属性
        • 1.Timestamp对象
        • 2 Datetime序列的生成
          • 1. to_datetime方法
          • 2. date_range方法
        • 3. dt对象
        • 4. 时间戳的切片和索引

备注:如果感觉有帮助,可以点赞评论收藏~~

Pandas时序数据系列博客

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

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

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

Pandas时间序列数据处理

1.好用的Python库

Python很强大,有很多的好用的库:

2.Pandas历史

本文主要介绍Pandas库,Pandas在数据科学中十分常用,Pandas的位置如下:

Pandas诞生于2008年,它的开发者是Wes McKinney,一个量化金融分析工程师。因为疲于应付繁杂的财务数据,Wes McKinney便自学Python,并开发了Pandas

2009年底,开源,今天得到了来自世界各地志同道合的个人社区的积极支持。

自2015年以来,pandas是NumFOCUS赞助的项目。

为什么叫作Pandas,其实这是“Python data analysis”的简写,同时也衍生自计量经济学术语“panel data”(面板数据)。

主要的贡献者

3.时序数据处理

Pandas可以处理很多的数据类型,其中最初始也最有趣的数据类型之一就是时间序列数据。Pandas 基本上是为分析金融时间序列数据而开发的,并为处理时间、日期和时间序列数据提供了一整套全面的框架。时间序列数据在很对行业都有应用,如股票价格变化、天气记录、患者健康指标、和应用程序性能监控等。

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

3.1 时序中的基本对象

时间序列的概念在日常生活中十分常见,但对于一个具体的时序事件而言,可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课,这个课会在当天早上10点结束,其中包含了哪些时间概念?

第一,会出现时间戳(Date times)的概念,即'2020-9-7 08:00:00'和'2020-9-7 10:00:00'这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp。同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64ns,如果有涉及时区则为datetime64ns, tz,其中tz是timezone的简写。

第二,会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64ns。

第三,会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period。

第四,会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。

通过这个简单的例子,就能够容易地总结出官方文档中的这个表格:

概念

单元素类型

数组类型

pandas数据类型

Date times

Timestamp

DatetimeIndex

datetime64ns

Time deltas

Timedelta

TimedeltaIndex

timedelta64ns

Time spans

Period

PeriodIndex

periodfreq

Date offsets

DateOffset

None

None

由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。

我们可以将时间序列数据定义为在不同时间间隔获得并按时间顺序排列的数据点的集合

3.2 python中的datetime模块

  • datetime模块,主要掌握:datetime.date(), datetime.datetime(), datetime.timedelta()
  • 日期解析方法:parser.parse
1. date对象:
代码语言:javascript复制
# datetime.date:date对象
import datetime  # 也可以写 from datetime import date
today = datetime.date.today()
print(today,type(today))
# datetime.date.today 返回今日
# 输出格式为 date类 (年,月,日) → 直接得到当时日期
t = datetime.date(2022,10,1)
print(t)

输出为:

代码语言:javascript复制
2022-10-21 <class 'datetime.date'>
2022-10-01
2. datetime对象
代码语言:javascript复制
# datetime.datetime:datetime对象

now = datetime.datetime.now()
print(now,type(now))
# .now()方法,输出当前时间
# 输出格式为 datetime类
# 可通过str()转化为字符串

t1 = datetime.datetime(2022,10,1)
t2 = datetime.datetime(2022,10,1,1,0,0)
print(t1,t2)
# (年,月,日,时,分,秒),至少输入年月日
print("*"*10)
print(t2-t1)
t2-t1 # 输出为 datetime.timedelta(0, 3600)
# 相减得到时间差 —— timedelta

输出为:

代码语言:javascript复制
2022-10-21 19:43:08.461654 <class 'datetime.datetime'>
2022-10-01 00:00:00 2022-10-01 01:00:00
**********
1:00:00
datetime.timedelta(0, 3600)
3. datetime.timedelta:时间差
代码语言:javascript复制
# datetime.timedelta:时间差

today = datetime.datetime.today()  # datetime.datetime也有today()方法
yestoday = today - datetime.timedelta(1)  # 
print(today)
print(yestoday)
print(today - datetime.timedelta(7))
# 时间差主要用作时间的加减法,相当于可被识别的时间“差值”

输出为:

代码语言:javascript复制
2022-10-21 19:44:23.097770
2022-10-20 19:44:23.097770
2022-10-14 19:44:23.097770
4. parser.parse:日期字符串转换
代码语言:javascript复制
# parser.parse:日期字符串转换
from dateutil.parser import parse

date = '10-01-2022'
t = parse(date)
print(t,type(t))
# 直接将str转化成datetime.datetime

print(parse('2022-10-1'),'n',
     parse('10/2/2021'),'n',
     parse('10/3/2022', dayfirst = True),'n',  # 国际通用格式中,日在月之前,可以通过dayfirst来设置
     parse('10/4/2022'),'n',
     parse('Jan 31, 2022 10:45 PM'))
# 各种格式可以解析,但无法支持中文

输出为:

代码语言:javascript复制
2022-10-01 00:00:00 <class 'datetime.datetime'>
2022-10-01 00:00:00 
 2021-10-02 00:00:00 
 2022-03-10 00:00:00 
 2022-10-04 00:00:00 
 2022-01-31 22:45:00

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

概念

单元素类型

数组类型

pandas数据类型

Date times

Timestamp

DatetimeIndex

datetime64ns

Time deltas

Timedelta

TimedeltaIndex

timedelta64ns

Time spans

Period

PeriodIndex

periodfreq

Date offsets

DateOffset

None

None

1.Timestamp对象

单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:

创建时间戳:

代码语言:javascript复制
import datetime
import numpy as np
import pandas as pd

date1 = datetime.datetime(2020,1,2,3,4,5)  # 创建一个datetime.datetime
date2 = '2020-1-2 03:04:05'
# ts = pd.Timestamp(date1) # 这几种效果一样
# ts = pd.Timestamp(date2) # 这几种效果一样
ts = pd.Timestamp('2020-1-2 03:04:05') # 这几种效果一样
ts

输出为:

代码语言:javascript复制
Timestamp('2020-01-02 03:04:05')

通过year, month, day, hour, min, second可以获取具体的数值:

代码语言:javascript复制
# 通过year, month, day, hour, min, second可以获取具体的数值:
print("*"*10)
print(ts.year)
print("*"*10)
print(ts.month)
print("*"*10)
print(ts.day)
print("*"*10)
print(ts.hour)
print("*"*10)
print(ts.minute)
print("*"*10)
print(ts.second)

输出为:

代码语言:javascript复制
**********
2020
**********
1
**********
2
**********
3
**********
4
**********
5

pandas中,时间戳的最小精度为纳秒ns,由于使用了64位存储,可以表示的时间范围大约可以如下计算:

rm Time,Range = frac{2^{64}}{10^9times 60times 60times 24times 365} approx 585 (Years)

通过pd.Timestamp.maxpd.Timestamp.min可以获取时间戳表示的范围,可以看到确实表示的区间年数大小正如上述计算结果:

代码语言:javascript复制
print(pd.Timestamp.max)
# Timestamp('2262-04-11 23:47:16.854775807')
print(pd.Timestamp.min)
# Timestamp('1677-09-21 00:12:43.145225')
pd.Timestamp.max.year - pd.Timestamp.min.year
# 585

输出为:

2 Datetime序列的生成

一组时间戳可以组成时间序列,可以用to_datetime和date_range来生成。其中,to_datetime能够把一列时间戳格式的对象转换成为datetime64ns类型的时间序列.

datetime64[ns]本质上可以理解为一个大整数,对于一个该类型的序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳

下面先对to_datetime方法进行演示:

1. to_datetime方法

单个时间数据,生成Timestamp:

代码语言:javascript复制
# pd.to_datetime
from datetime import datetime
# pd.to_datetime():如果是单个时间数据,转换成pandas的时刻数据,数据类型为Timestamp
date1 = datetime(2020,1,2,3,4,5)
t1 = pd.to_datetime(date1)
print(t1,type(t1))
t1
# 2020-01-02 03:04:05 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
# Timestamp('2020-01-02 03:04:05')

输出为:

多个时间数据生成DatetimeIndex:

代码语言:javascript复制
lst_date = [ '2020-12-21', '2020-12-22', '2020-12-23']
t3 = pd.to_datetime(lst_date)
print(t3,type(t3))
t3
# DatetimeIndex(['2020-12-21', '2020-12-22', '2020-12-23'], dtype='datetime64[ns]', freq=None) <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
# 多个时间数据,将会转换为pandas的DatetimeIndex

输出为:

时间戳格式转换

在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:

代码语言:javascript复制
temp = pd.to_datetime(['2020\1\1','2020\1\3'],format='%Y\%m\%d')
temp 
# DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)

输出为:

传入列表和series的返回值:

注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64ns的序列,需要显式用Series转化:

代码语言:javascript复制
# DatetimeIndex
# DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)
temp = pd.to_datetime(['2020\1\1','2020\1\3'],format='%Y\%m\%d')
print(temp)

# datetime64[ns]序列
pd.Series(temp).head()

输出为:

时间序列夹杂其他格式errors参数:

代码语言:javascript复制
# 当一组时间序列中夹杂其他格式数据,可用errors参数返回
# errors = 'ignore':不可解析时返回原始输入,这里就是直接生成一般数组
date1 = ['2020-2-1','2020-2-2','2020-2-3','hello world!','2020-2-5','2020-2-6']
t1 = pd.to_datetime(date1, errors = 'ignore')
print(t1,type(t1))

print("*"*10)

# errors = 'coerce':不可扩展,缺失值返回NaT(Not a Time),结果认为DatetimeIndex
t2 = pd.to_datetime(date1, errors = 'coerce')
print(t2,type(t2))

输出为:

2. date_range方法

date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。这里要注意,开始或结束日期如果作为端点则它会被包含:

代码语言:javascript复制
# pd.date_range()-日期范围:生成日期范围
# 2种生成方式:①start   end; ②start/end   periods
# 默认频率:day

# normalize:时间参数值正则化到午夜时间戳
rng1 = pd.date_range('1/1/2020','1/3/2020',name = 'index_name', normalize=True)
rng2 = pd.date_range(start = '1/1/2020 10:10', periods = 3, normalize=True)
rng3 = pd.date_range(end = '1/30/2020 15:00:00', periods = 3)  # 增加了时、分、秒
print(rng1)
print(rng2)
print(rng3)
print('-------')
# 直接生成DatetimeIndex
# pd.date_range(start=None, end=None, periods=None, freq='D', tz=None, normalize=False, name=None, closed=None, **kwargs)
# start:开始时间
# end:结束时间
# periods:偏移量
# freq:频率,默认天,pd.date_range()默认频率为日历日,pd.bdate_range()默认频率为工作日
# tz:时区
# normalize:时间参数值正则化到午夜时间戳(这里最后就直接变成0:00:00,并不是10:10:00)
# name:索引对象名称

# closed:默认为None的情况下,左闭右闭,left则左闭右开,right则左开右闭
print(pd.date_range('20200101','20200104'))  # 20170101也可读取
# print(pd.date_range('20200101','20200104',closed = 'right'))
# print(pd.date_range('20200101','20200104',closed = 'left'))
print('-------')
# closed:默认为None的情况下,左闭右闭,left则左闭右开,right则左开右闭

# pd.bdate_range()默认频率为工作日
print(pd.bdate_range('20200101','20200107'))
print('-------')
# pd.bdate_range()默认频率为工作日

输出为:

代码语言:javascript复制
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03'], dtype='datetime64[ns]', name='index_name', freq='D')
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03'], dtype='datetime64[ns]', freq='D')
DatetimeIndex(['2020-01-28 15:00:00', '2020-01-29 15:00:00',
               '2020-01-30 15:00:00'],
              dtype='datetime64[ns]', freq='D')
-------
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04'], dtype='datetime64[ns]', freq='D')
-------
DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',
               '2020-01-07'],
              dtype='datetime64[ns]', freq='B')
-------
[Timestamp('2020-01-01 00:00:00', freq='D'), Timestamp('2020-01-02 00:00:00', freq='D'), Timestamp('2020-01-03 00:00:00', freq='D')]

freq参数的设置

pd.date_range()-日期范围:频率(1) 默认freq = ‘D’:每日历日 B:每工作日 H:每小时 T/MIN:每分 S:每秒 L:每毫秒(千分之一秒) U:每微秒(百万分之一秒) freq = ‘W-MON’ # W-MON:从指定星期几开始算起,每周 星期几缩写:MON/TUE/WED/THU/FRI/SAT/SUN freq = ‘WOM-2MON’ WOM-2MON:每月的第几个星期几开始算,这里是每月第二个星期一 pd.date_range()-日期范围:频率(2) M:每月最后一个日历日 Q-月:Q-DEC指定月为季度末,每个季度末最后一月的最后一个日历日 A-月:A-DEC每年指定月份的最后一个日历日 月缩写:JAN/FEB/MAR/APR/MAY/JUN/JUL/AUG/SEP/OCT/NOV/DEC 所以Q-月只有三种情况:1-4-7-10,2-5-8-11,3-6-9-12 BM:每月最后一个工作日 BQ-月:BQ-DEC指定月为季度末,每个季度末最后一月的最后一个工作日 BA-月:BA-DEC每年指定月份的最后一个工作日,这里是12月 M:MS每月第一个日历日 Q-月:QS-DEC指定月为季度末,每个季度末最后一月的第一个日历日 A-月:AS-DEC每年指定月份的第一个日历日 BM:BMS每月第一个工作日 BQ-月:BQS-DEC指定月为季度末,每个季度末最后一月的第一个工作日 BA-月:BAS-DEC每年指定月份的第一个工作日 pd.date_range()-日期范围:复合频率 freq = ‘7D’ # 7天 freq = ‘2h30min’ # 2小时30分钟 freq = ‘2M’ # 2月,每月最后一个日历日

代码语言:javascript复制
# asfreq:时期频率转换

ts = pd.Series(np.random.rand(4),
              index = pd.date_range('20170101','20170104'))
print(ts)
print(ts.asfreq('12H',method = 'ffill'))
# 改变频率,这里是D改为12H
# method:插值模式,None不插值,ffill用之前值填充,bfill用之后值填充

输出为:

代码语言:javascript复制
# pd.date_range()-日期范围:超前/滞后数据

ts = pd.Series(np.arange(4),
              index = pd.date_range('20200101','20200104'))
print(ts)

print(ts.shift(2))
# print(ts.shift(-2))
print('------')
# 正数:数值后移(滞后);负数:数值前移(超前)

per = ts/ts.shift(1) - 1
print(per)
print('------')
# 计算变化百分比,这里计算:该时间戳与上一个时间戳相比,变化百分比

print(ts.shift(2, freq = 'D')) # 天
# print(ts.shift(2, freq = 'T')) # 小时
# 加上freq参数:对时间戳进行位移,而不是对数值进行位移

输出为:

3. dt对象

在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64ns类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作

第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。

代码语言:javascript复制
s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))
s.dt.date # 日期
# s.dt.time 时间

输出为:

代码语言:javascript复制
0    2020-01-01
1    2020-01-02
2    2020-01-03
dtype: object

此外,可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:

代码语言:javascript复制
print(s.dt.month_name())
print(s.dt.day_name())

输出为:

代码语言:javascript复制
0    January
1    January
2    January
dtype: object
0    Wednesday
1     Thursday
2       Friday
dtype: object

第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:

代码语言:javascript复制
# 第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:
print(s.dt.is_year_start) # 还可选 is_quarter/month_start
s.dt.is_year_end # 还可选 is_quarter/month_end

输出为:

代码语言:javascript复制
0     True
1    False
2    False
dtype: bool
0    False
1    False
2    False
dtype: bool

第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒)

代码语言:javascript复制
s = pd.Series(pd.date_range('2020-1-1 20:35:00', '2020-1-1 22:35:00', freq='45min'))
print(s)
print(s.dt.round('1H'))
print(s.dt.ceil('1H'))
print(s.dt.floor('1H'))

输出为:

代码语言:javascript复制
0   2020-01-01 20:35:00
1   2020-01-01 21:20:00
2   2020-01-01 22:05:00
dtype: datetime64[ns]
0   2020-01-01 21:00:00
1   2020-01-01 21:00:00
2   2020-01-01 22:00:00
dtype: datetime64[ns]
0   2020-01-01 21:00:00
1   2020-01-01 22:00:00
2   2020-01-01 23:00:00
dtype: datetime64[ns]
0   2020-01-01 20:00:00
1   2020-01-01 21:00:00
2   2020-01-01 22:00:00
dtype: datetime64[ns]
4. 时间戳的切片和索引

一般而言,时间戳序列作为索引使用。如果想要选出某个子时间戳序列,第一类方法是利用dt对象和布尔条件联合使用,另一种方式是利用切片,后者常用于连续时间戳。下面,举一些例子说明:

构建series:

代码语言:javascript复制
s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
s.head()

输出为:

利用dt对象和布尔条件过滤

代码语言:javascript复制
idx = pd.Series(s.index).dt
print(idx)
s[(idx.is_month_start|idx.is_month_end).values].head()

输出为:

取出5月到7月15日中间的数据

代码语言:javascript复制
s['2020-05':'2020-7-15'].head()

输出为:

未完待续,见后续系列

0 人点赞