参考书目:《深入浅出Pandas:利用Python进行数据处理与分析》
pandas的索引可以用时间来替代,然后基于时间序列数据会有很多用法,了解一下。
时间对象有:
还是先导入包:
import numpy as np
import pandas as pd
import datetime
#创建时间索引
pd里面的对象,或者字符串,np对象,和Python的datetime对象都可以直接创建时间索引。
pd.to_datetime(['11/1/2020',np.datetime64('2020-11-02'),datetime.datetime(2020,11,3)])
date_range创建一组时间,即时间序列
#默认频率为天
pd.date_range('2020-01-01',periods=10)
pd.date_range('2020-01-01','2020-01-10') #同上
pd.date_range(end='2020-01-10',periods=10)#同上
pd.date_range('2022-01-01', periods=3, freq='H')#频率为小时
#跳过星期六天
pd.bdate_range('2020-11-1',periods=10)
#通常时间序列数据作为 Series 或 DataFrame 的索引,以方便对时间数据进行操作。
# 指定开始时间和频率,周期数
pd.Series(range(3), index=pd.date_range('2000', freq='D', periods=3))
和其他种类数据一样,时间序列也是可以用[],.loc[]方法进行切片选取数据的。
生成案例数据
rng = pd.date_range('1/1/2021', '12/1/2021', freq='BM')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
#时间索引切片
#DatetimeIndex 作为时间索引,同样也支持数据切片:
ts[2:4]
ts[0:4].index #ts[::2].index
#还支持传入时间字符和各种时间对象
ts['2021'] #z只筛选2021年的
ts['1/29/2021']#查询这一天的值
ts['2021-1-29']#同上
ts['20210129'] #同上
ts[datetime.datetime(2021, 9, 30):]
ts[pd.Timestamp(2021,9,30)]
ts[pd.Timestamp('2021-9-30')]
ts[np.datetime64('2021-9-30')]
#也可以使用部分字符查询一定范围的数据:
ts['2021'] # 查询整个2021年的
ts['2021-6'] # 查询 2021年6月的
ts['2021-6':'2021-10'] # 6月到10月的
dft['2013-1':'2013-2-28 00:00:00'] # 精确时间
dft['2013-1-15':'2013-1-15 12:30:00']
dft2.loc['2013-01-05']
#由于时间格式样式比较多,很多情况下 Padnas 并不能自动识别为时间类型,所以我们在处理前的数据清洗过程中,需要专门对数据进行时间类型转换。
#astype 是最简单的时间转换方法,它只能针对相对标准的时间格式,如:
s = pd.Series(['2016-01-31', '2016-02-29', '2016-03-31'])
s.astype('datetime64[ns]')
#修改频率
s.astype('datetime64[D]')
pd.to_datetime(s)
#在上文中,我们转换时间时使用的是 ns (纳秒),最大程度地保留了最细的时间颗粒的供我们未来使用,但有时间我们不需要这么精确的细小粒度,如何操作呢?这就需要在 datetime64 后的括号里指定我们想要的粒度,如:
# 一个时间文本序列
s = pd.Series(['2016-01-31 10:18:04', '2016-02-29 12:18:09'])
s
'''
0 2016-01-31 10:18:04
1 2016-02-29 12:18:09
dtype: object
'''
# 纳秒级
s.astype('datetime64[ns]')
'''
0 2016-01-31 10:18:04
1 2016-02-29 12:18:09
dtype: datetime64[ns]
'''
# 日级
s.astype('datetime64[D]')
'''
0 2016-01-31
1 2016-02-29
dtype: datetime64[ns]
'''
# 小时级
s.astype('datetime64[h]')
'''
0 2016-01-31 10:00:00
1 2016-02-29 12:00:00
dtype: datetime64[ns]
'''
# 秒级
s.astype('datetime64[s]')
'''
0 2016-01-31 10:18:04
1 2016-02-29 12:18:09
dtype: datetime64[ns]
'''
# 月级,当月第一天
s.astype('datetime64[M]')
'''
0 2016-01-01
1 2016-02-01
dtype: datetime64[ns]
'''
# 年级,当年第一天
s.astype('datetime64[Y]')
'''
0 2016-01-01
1 2016-01-01
dtype: datetime64[ns]
'''
# 周级,当周周四(原因如下)
s.astype('datetime64[W]')
'''
0 2016-01-28
1 2016-02-25
dtype: datetime64[ns]
'''
'datetime64[W]' 的结果为周四是因为这只是四舍五入,是经过设计的。括号里还可以为 2W、7D等开工,如 7D 的意思是“自纪元(1970-01-01T00:00Z )以来7D的倍数”,就像 D 的意思是“自纪元以来一天的倍数”,7D 和 W 是同义词。
我们可以看到,除了周、月、年等,其他粒度仍然以纳秒存储,不过精确度都会损失。这个规划在下边我们介绍的 pd.to_datetime() 中也适用。
#Pandas 提供的 pd.to_datetime() 是识别转换时间的主要工具。接下来看一些例子。
#从 DataFrame 的多个列中组合一个日期时间。 键可以是常见的缩写,例如['year','month','day','minute','second','ms','us','ns']): 必须: year, month, day 可选: hour, minute, second, millisecond, microsecond, nanosecond
df = pd.DataFrame({'year': [2015, 2016],
'month': [2, 3],
'day': [4, 5]})
df
pd.to_datetime(df)
pd.to_datetime(df[['year', 'month', 'day']]) # 同上
#对Series智能解析时间:
pd.to_datetime(pd.Series(['Jul 31, 2009', '2010-01-10', None]))
#列表也可以智能
pd.to_datetime(['2005/11/23', '2010.12.31'])
# DatetimeIndex(['2005-11-23', '2010-12-31'], dtype='datetime64[ns]', freq=None)
pd.to_datetime(['04-01-2012 10:00'], dayfirst=True) # 日期在前
# DatetimeIndex(['2012-01-04 10:00:00'], dtype='datetime64[ns]', freq=None)
#pd.DatetimeIndex 也可以转换:
pd.DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'])
# DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq=None)
pd.DatetimeIndex(['20180101', '20180103', '20180105'], freq='infer') #自动推断频率
# DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns
#可以使用 pd.Timestamp() 进行转换单个时间点
pd.to_datetime('2010/11/12')
# Timestamp('2010-11-12 00:00:00')
pd.Timestamp('2020/11/12')
# Timestamp('2020-11-12 00:00:00')
#不规则格式转化时间
pd.to_datetime('2020_11_12',format='%Y_%m_%d',errors='ignore')
pd.to_datetime('20200101', format='%Y%m%d', errors='ignore')
# 可以让系统自己推断时间格式
pd.to_datetime('20200101', infer_datetime_format=True, errors='ignore')
# coerce 将不会忽略错误,返回空值
pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')
# 有时间需要字段转为字符,再转为时间
pd.to_datetime(df.d.astype(str), format='%m/%d/%Y')
# 其他
pd.to_datetime('2010/11/12', format='%Y/%m/%d')
# Timestamp('2010-11-12 00:00:00')
pd.to_datetime('12-11-2010 00:00', format='%d-%m-%Y %H:%M')
# Timestamp('2010-11-12 00:00:00')
#对时间戳进行转换,需要给出时间单位,一般为秒:
pd.to_datetime(1490195805, unit='s')
# Timestamp('2017-03-22 15:16:45')
pd.to_datetime(1490195805433502912, unit='ns')
# Timestamp('2017-03-22 15:16:45.433502912')
#对周期数据(数字列表)进行转换:
pd.to_datetime([1, 2, 3], unit='D',origin=pd.Timestamp('1960-01-01'))
# DatetimeIndex(['1960-01-02', '1960-01-03', '1960-01-04'], dtype='datetime64[ns]', fre
有时候不想要时间后面的小时,可以这样转化
pd.date_range('2022-1-1 00:10:00','2022-8-6 00:10:00').floor(freq='D') #忽略小时
上面的时间粒度方法也可以转化
s=pd.Series(pd.date_range('2022-1-1','2022-8-6')).astype('datetime64[M]')
pd.to_datetime(s.astype(str),format='%Y-%m') #实在不行字符串切。。
如果不想要20222-08-01后面的-01,可以用字符串去切,然后再变成时间。。
def time_t(time):
t=time.split('-')
return t[0]+'-'+t[1]
pd.to_datetime(s.astype('str').apply(time_t))
#对于时间序列数据,可以使用 s.dt.xxx 的形式来访问它们的属性和调用它们的方法
s = pd.Series(pd.date_range('2020-01-01', periods=3, freq='d'))
s.dt.date
s.dt.time
s.dt.timetz
s.dt.year
s.dt.month
s.dt.day
s.dt.hour
s.dt.minute
s.dt.second
s.dt.microsecond
s.dt.nanosecond
#周月年相关
s.dt.week
s.dt.weekofyear
s.dt.dayofweek
s.dt.weekday
s.dt.dayofyear #一年中第几天
s.dt.quarter #季度数
s.dt.is_month_start #是否月第一天
s.dt.is_month_end #是否最后一天
s.dt.is_quarter_start#是否季度第一天
s.dt.is_quarter_end #是否季度最后一天
s.dt.is_year_start #是否年第一天
s.dt.is_year_end #是否年最后一天
s.dt.is_leap_year #是否闰年
s.dt.daysinmonth #当月多少天
s.dt.days_in_month #同上
s.dt.tz #时区
s.dt.freq #频率
#转化方法
s.dt.to_period
s.dt.to_pydatetime
s.dt.tz_localize
s.dt.tz_convert
s.dt.normalize
s.dt.strftime
s.dt.round(fred='D')#类似四舍五入
s.dt.floor(fred='D')#向下舍入为天
s.dt.ceil(fred='D') #向上舍入为天
s.dt.month_name #月份名称
s.dt.day_name #星期几名称
s.dt.qyear
s.dt.start_time #开始时间
s.dt.end_time #结束时间
s.dt.days #天数
s.dt.seconds #秒
s.dt.microseconds #毫秒
s.dt.nanoseconds #纳秒
s.dt.components #时间成分
s.dt.to_pytimedelta #转为py时间
s.dt.total_seconds #总秒数
#可能需要将时间序列中的值在时间上前后移动或滞后。 shift() 方法也可以在时序对象上使用。
index = pd.date_range('2020-06-01', '2020-06-03')
ts = pd.Series(range(len(rng)), index=index)
ts
ts.shift(-1)
ts.shift(-1,freq='B')
有时候需要将低频率数据改为高频数据
#更改频率的主要功能是 asfreq() 方法。
dr = pd.date_range('1/1/2022', periods=3, freq=3 * pd.offsets.BDay())
ts = pd.Series(np.random.randn(3), index=dr)
ts
日期频率
ts.asfreq(pd.offsets.BDay())
半天频率
ts.asfreq(pd.offsets.Hour(12))
#asfreq 提供了更多便利,因此您可以为频率转换后可能出现的任何间隙指定插值方法。
ts.asfreq(pd.offsets.BDay(), method='pad') #扩展填充
# 对空值进行固定值填充
ts.asfreq(freq='12h', fill_value=9.0)
#以使用几个选项之一({None, ‘backfill’/’bfill’, ‘pad’/’ffill’, ‘nearest’})来填写缺失值。
#例如,要反向传播最后一个有效值以填充NaN值,请将 bfill 作为参数传递给method关键字。
ts.reindex(pd.date_range('2022-1-1', periods=15, freq='D'), method='ffill')
可以看到有效的将三个数据扩充到更多的额时间天上,并且值填充保持不变。