同步操作将从 张亚飞/轻松掌握Pandas 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
时间是一个比较特殊的事物,不像数字有具体的进制,可以做相关的计算。比如时间分秒时是60进制,时和天又是24进制,一周又有7天,一月又有28到31天不等,等等。这就给我们处理时间问题带来了麻烦。为了更好地表达现实世界中的时间,计算机设计者提出了一些概念来进行表达。
这个非常简单,它是一个独立的时间,不在任何周期时,比如中午 13:30 睡觉,不代表你每半个小时就睡一次。其他的如,2020 年、农历四月十八、19世纪等等。
固定的时间有一定的属性,如所在年、月、日、毫秒等。
Unix时间戳(Unix timestamp),时间戳是指格林威治时间1970年01月01日00时00分00秒起至当下的总秒数。它是一个非常大的数字,一直以一秒的步长在增加。如:1591684854 代表北京时间 2020/6/9 14:40:54。那么 1970年 年以前的时间怎么表示呢?用负数,如-1591684957 代表1919/7/26 2:17:23。
代表一个时间长度,但它没有指定的开始时间和结束时间,比如一首歌4分钟,你不管从什么时候开始,它都会占用4分钟。
由于原始时间数据可能是时间戳,也有可能是其他类型的数据,我们在使用时想按照一定的格式进行显示,就需要做格式化处理。如时间戳 1591684854 和 2020/6/9 14:40:54 转换为为 2020年06月09日 这样的中文格式。
有开始、有结束,并且有时间单位周期的时间,比如从大学毕业后,我开始工作,每周一到周五去工作,持续到65岁退休,这个就是工作日周期。另如钟表,每半小时响一次,开始时间是我安装好它,结束时间是它坏掉的时候。
周期时间中有一个频率,一个频率是一个单位,日期偏移是基于这个频率,如一个周期的频率是小时,那偏移三个频率就是三小时。比如偏移一个工作周,这个工作周期是自己定义的。
每个固定的时间都会有一个时区,一般为你电脑本地的时区,当然你根据需要可以进行调整。
在数据分析时,经常会与是否工作日相关,除了正常的周末,每个国家和地区节假日,甚至具体到一些公司都会有一些特定的作息,一般需要自己来指定这个工作日的周期。
时间转换包括时间表达格式的转换,也会有一些时间计算,比如给一个时间,转为去年的当天、100天前的当天、转换为特定时区的时间等等。
时间序列(Time series)数据是非常重要的数据类型,在各个领域的研究中,都与时间相关,因此对于时间数据的处理就越来越重要。Pandas 提供了多种时间数据类型和处理方法。
时间概念 | 类型表示 | Array 类 | pandas 类型 | 创建方法 |
---|---|---|---|---|
固定时间 (Date times) | Timestamp | DatetimeIndex | datetime64[ns] or datetime64[ns, tz] | to_datetime or date_range |
时长 (Time deltas) | Timedelta | TimedeltaIndex | timedelta64[ns] | to_timedelta or timedelta_range |
周期 (Time spans) | Period | PeriodIndex | period[freq] | Period or period_range |
日期偏移 (Date offsets) | DateOffset | None | None | DateOffset |
通常时间序列数据作为 Series 或 DataFrame 的索引,以方便对时间数据进行操作。
带时间戳的数据是时间序列数据的最基本类型,它将值与时间点相关联,对它可以进行相关的时间操作,如何定义转换当非常关键。同时一个具体的时间包含了非常丰富的信息,如年份、在周几、在几月、在哪个季度,需要我们进行属性的读取。
导入相关模块
import pandas as pd
import numpy as np
import datetime
# 至少需要年月日
pd.Timestamp(datetime.datetime(2021, 2, 1))
Timestamp('2021-02-01 00:00:00')
# 指定是时分秒
pd.Timestamp(datetime.datetime(2021, 2, 1, 18, 39, 32))
Timestamp('2021-02-01 18:39:32')
pd.Timestamp('2018-08-01')
Timestamp('2018-08-01 00:00:00')
pd.Timestamp('2021-01-01T12')
Timestamp('2021-01-01 12:00:00')
pd.Timestamp(2008, 8, 8)
Timestamp('2008-08-08 00:00:00')
pd.Timestamp(2008, 8, 8, 8)
Timestamp('2008-08-08 08:00:00')
pd.Timestamp(year=2008, month=8, day=8, hour=8)
Timestamp('2008-08-08 08:00:00')
pd.Timestamp(1612176206.2, unit='s') # 单位为秒
Timestamp('2021-02-01 10:43:26.200000048')
pd.Timestamp(1612176206, unit='s', tz='US/Pacific')
Timestamp('2021-02-01 02:43:26-0800', tz='US/Pacific')
# 指定为北京时间
pd.Timestamp(1612176206, unit='s', tz='Asia/Shanghai')
Timestamp('2021-02-01 18:43:26+0800', tz='Asia/Shanghai')
pd.Timestamp('today')
Timestamp('2021-02-01 18:46:09.627130')
pd.Timestamp('now')
Timestamp('2021-02-01 18:46:15.060057')
pd.Timestamp('today').date() # 只取日期
datetime.date(2021, 2, 1)
也可以计算出昨天、明天等信息:
# 昨天
pd.Timestamp('now')-pd.Timedelta(days=1)
Timestamp('2021-01-31 18:46:50.276669')
# 明天
pd.Timestamp('now')+pd.Timedelta(days=1)
Timestamp('2021-02-02 18:46:56.115898')
# 当月初,一日
pd.Timestamp('now').replace(day=1)
Timestamp('2021-02-01 18:47:07.699907')
pd.Timestamp.min
Timestamp('1677-09-21 00:12:43.145225')
pd.Timestamp.max
Timestamp('2262-04-11 23:47:16.854775807')
以上为支持的最早最晚时间。
time = pd.Timestamp('now')
time
Timestamp('2021-02-01 18:50:17.041362')
time.asm8 # 返回 numpy datetime64格式(以纳秒为单位)。
numpy.datetime64('2021-02-01T18:50:17.041362000')
其他属性:
time = pd.Timestamp('now', tz='Asia/Shanghai')
time
Timestamp('2021-02-01 18:55:06.578998+0800', tz='Asia/Shanghai')
# 转换为指定时区
time.astimezone('UTC')
Timestamp('2021-02-01 10:55:06.578998+0000', tz='UTC')
# 转换单位,向上舍入
time.ceil('s') # 转为以秒为单位
Timestamp('2021-02-01 18:55:07+0800', tz='Asia/Shanghai')
time.ceil('ns') # 转为以纳秒为单位
time.ceil('d') # 保留日
time.ceil('h') # 保留时
Timestamp('2021-02-01 19:00:00+0800', tz='Asia/Shanghai')
# 转换单位, 为向下舍入
time.floor('h') # 保留时
Timestamp('2021-02-01 18:00:00+0800', tz='Asia/Shanghai')
# 类似四舍五入
time.round('h') # 保留时
Timestamp('2021-02-01 19:00:00+0800', tz='Asia/Shanghai')
# 返回星期名
time.day_name()
'Monday'
# 月份名称
time.month_name() # 'June'
# 将时间戳规范化为午夜,保留tz信息。
time.normalize()
Timestamp('2021-02-01 00:00:00+0800', tz='Asia/Shanghai')
# 时间元素替换 datetime.replace,可处理纳秒。
time.replace(year=2019) # 年份换为2019年
Timestamp('2019-02-01 18:55:06.578998+0800', tz='Asia/Shanghai')
time.replace(month=8) # 月份换为8月
Timestamp('2021-08-01 18:55:06.578998+0800', tz='Asia/Shanghai')
# 转为周期类型,将丢失时区
time.to_period(freq='h') # 周期为小时
<ipython-input-38-1ba4d122f88a>:2: UserWarning: Converting to Period representation will drop timezone information. time.to_period(freq='h') # 周期为小时
Period('2021-02-01 18:00', 'H')
# 转为指定时区
time.tz_convert('UTC') # 转为 utc 时间
Timestamp('2021-02-01 10:55:06.578998+0000', tz='UTC')
# 本地化时区转换
time = pd.Timestamp('now')
time.tz_localize('Asia/Shanghai')
Timestamp('2021-02-01 18:58:00.359104+0800', tz='Asia/Shanghai')
# Timestamp('2020-06-09 17:32:47.388726+0800', tz='Asia/Shanghai')
time.tz_localize(None) # 删除时区
Timestamp('2021-02-01 18:58:00.359104')
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
s.dt.floor
s.dt.ceil
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
s.dt.total_seconds
# 个别用法举例
s.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
s.dt.strftime('%Y/%m/%d')
对于固定时间会返回固定的结果,对于这序列会返回对应值的序列。
s = pd.Series(['2020-01-31', '2020-02-29', '2020-03-31'])
s.astype('datetime64[ns]')
0 2020-01-31 1 2020-02-29 2 2020-03-31 dtype: datetime64[ns]
指定时区
s.astype('datetime64[ns, US/Eastern]')
0 2020-01-31 00:00:00-05:00 1 2020-02-29 00:00:00-05:00 2 2020-03-31 00:00:00-04:00 dtype: datetime64[ns, US/Eastern]
Pandas 提供的 pd.to_datetime() 是识别转换时间的主要工具。接下来我们看一些例子。
从 DataFrame 的多个列中组合一个日期时间。 键可以是常见的缩写,例如['year','month','day','minute','second','ms','us','ns']):
df = pd.DataFrame({'year': [2020, 2021],
'month': [2, 3],
'day': [4, 5]})
df
year | month | day | |
---|---|---|---|
0 | 2020 | 2 | 4 |
1 | 2021 | 3 | 5 |
pd.to_datetime(df)
0 2020-02-04 1 2021-03-05 dtype: datetime64[ns]
pd.to_datetime(df[['year', 'month', 'day']]) # 同上
0 2020-02-04 1 2021-03-05 dtype: datetime64[ns]
智能解析时间:
pd.to_datetime(pd.Series(['Dec 31, 2008', '2018-01-10', None]))
0 2008-12-31 1 2018-01-10 2 NaT dtype: datetime64[ns]
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.Timestamp() 进行转换:
pd.to_datetime('2010/11/12')
Timestamp('2010-11-12 00:00:00')
pd.Timestamp('2010/11/12')
Timestamp('2010-11-12 00:00:00')
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(['2018-01-01', '2018-01-03', '2018-01-05'], freq='infer')
DatetimeIndex(['2018-01-01', '2018-01-03', '2018-01-05'], dtype='datetime64[ns]', freq='2D')
对于有格式的数据,需要指定数据的格式:
pd.to_datetime('13000101', format='%Y%m%d', errors='ignore')
# 可以让系统自己推断时间格式
pd.to_datetime('13000101', infer_datetime_format=True, errors='ignore')
# datetime.datetime(1300, 1, 1, 0, 0)
# coerce 将不会忽略错误,返回空值
pd.to_datetime('13000101', format='%Y%m%d', errors='coerce')
# NaT
# 有时间需要字段转为字符
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]', freq
python中时间日期格式化符号:
dates = [datetime.datetime(2012, 5, 1),
datetime.datetime(2012, 5, 2),
datetime.datetime(2012, 5, 3)]
# 无频率
pd.DatetimeIndex(dates)
# pd.Index(dates) # 效果同上
DatetimeIndex(['2012-05-01', '2012-05-02', '2012-05-03'], dtype='datetime64[ns]', freq=None)
pd.date_range(start='1/1/2018', end='1/08/2018')
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'], dtype='datetime64[ns]', freq='D')
pd.date_range(start='1/1/2018', periods=8)
DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08'], dtype='datetime64[ns]', freq='D')
pd.date_range(end='1/1/2018', periods=8)
DatetimeIndex(['2017-12-25', '2017-12-26', '2017-12-27', '2017-12-28', '2017-12-29', '2017-12-30', '2017-12-31', '2018-01-01'], dtype='datetime64[ns]', freq='D')
pd.date_range(start='2018-04-24', end='2018-04-27', periods=3)
DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00', '2018-04-27 00:00:00'], dtype='datetime64[ns]', freq=None)
date_range 的默认频率是 D 日,可以指定为其他频率,
# 指定频率为月
pd.date_range(start='1/1/2018', periods=5, freq='M')
DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30', '2018-05-31'], dtype='datetime64[ns]', freq='M')
start = datetime.datetime(2011, 1, 1)
end = datetime.datetime(2012, 1, 1)
pd.date_range(start, end)
DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05', '2011-01-06', '2011-01-07', '2011-01-08', '2011-01-09', '2011-01-10', ... '2011-12-23', '2011-12-24', '2011-12-25', '2011-12-26', '2011-12-27', '2011-12-28', '2011-12-29', '2011-12-30', '2011-12-31', '2012-01-01'], dtype='datetime64[ns]', length=366, freq='D')
多个单位频率
# 三个月
pd.date_range(start='1/1/2018', periods=5, freq='3M')
DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31', '2019-01-31'], dtype='datetime64[ns]', freq='3M')
频率也可以使用时间偏移
# 三个月,取月最后一天
pd.date_range(start='1/1/2018', periods=5, freq=pd.offsets.MonthEnd(3))
DatetimeIndex(['2018-01-31', '2018-04-30', '2018-07-31', '2018-10-31', '2019-01-31'], dtype='datetime64[ns]', freq='3M')
可以指定时区:
# 东京时间
pd.date_range(start='1/1/2018', periods=5, tz='Asia/Tokyo')
DatetimeIndex(['2018-01-01 00:00:00+09:00', '2018-01-02 00:00:00+09:00', '2018-01-03 00:00:00+09:00', '2018-01-04 00:00:00+09:00', '2018-01-05 00:00:00+09:00'], dtype='datetime64[ns, Asia/Tokyo]', freq='D')
closed 可以做开始和结束时间开闭区间的控制:
# None 两边都包含
pd.date_range(start='2017-01-01', end='2017-01-04', closed=None)
DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04'], dtype='datetime64[ns]', freq='D')
# left 不包含右边
pd.date_range(start='2017-01-01', end='2017-01-04', closed='left')
DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03'], dtype='datetime64[ns]', freq='D')
# right 不包含左边
pd.date_range(start='2017-01-01', end='2017-01-04', closed='right')
DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04'], dtype='datetime64[ns]', freq='D')
pd.bdate_range(start='1/1/2021', end='1/08/2021')
DatetimeIndex(['2021-01-01', '2021-01-04', '2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'], dtype='datetime64[ns]', freq='B')
在四个参数中:开始,结束,周期和频率,必须精确指定三个。 指定频率是 bdate_range 的要求。 如果不需要指定频率,请使用 date_range。
周期数据有一个基础频率,或者叫分辨率、粒度,时间偏移由基础频率和乘数组成,这对我们处理周期数据非常有用。
Pandas 专门提供了一个时间偏移对象(Date offsets)来完成时间偏移工作,本文将介绍 Date offsets 的构成和使用方法。DateOffset 基础操作类似于 dateutil.relativedelta(relativedelta 文档),可以按真实的日历进行时间偏移,并用算数运算符(+)或 apply 进行日期偏移操作。
DateOffset 类似于时间差 Timedelta ,但它使用日历中时间日期的规则,而不是直接进行时间性质的算术计算,让时间更符合实际生活。比如,有些地区使用夏令时时,每日偏移时间有可能是 23 或 24 小时,甚至 25 个小时。
DataOffset与Timedelta的区别
# 生成一个指定的时间,芬兰赫尔辛基时间执行夏令时
ts = pd.Timestamp('2016-10-30 00:00:00', tz='Europe/Helsinki')
ts
Timestamp('2016-10-30 00:00:00+0300', tz='Europe/Helsinki')
# 增加一天
ts + pd.Timedelta(days=1)
Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')
# 按日历时间
ts + pd.DateOffset(days=1)
Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')
这似乎有些令人头大,但只要把tz(time zone)去除就可以不用管它了,两者保持一致,除非要使用到时区变换
# 增加一天
ts + pd.Timedelta(days=1)
Timestamp('2016-10-31 00:00:00')
# 按日历时间
ts + pd.DateOffset(days=1)
Timestamp('2016-10-31 00:00:00')
以下是增加工作日,出现跨周末的情况:
# 生成一个指定的时间,芬兰赫尔辛基时间执行夏令时
ts = pd.Timestamp('2016-10-30 00:00:00')
ts
Timestamp('2016-10-30 00:00:00')
friday = pd.Timestamp('2018-01-05')
friday.day_name()
'Friday'
# 增加两个工作日,从周五到周二
two_business_days = 2 * pd.offsets.BDay()
two_business_days.apply(friday)
Timestamp('2018-01-09 00:00:00')
friday + two_business_days
Timestamp('2018-01-09 00:00:00')
(friday + two_businbess_days).day_name()
'Tuesday'
DateOffset的可选参数包括years/months/weeks/days/hours/minutes/seconds
所有的日期偏移对象都在 pandas.tseries.offsets 下,其中 pandas.tseries.offsets.DateOffset 是标准的日期范围时间偏移类型,用于日期范围的标准日期增量类型。它默认是一个日历日。
ts = pd.Timestamp('2017-01-01 09:10:11')
ts + pd.DateOffset(months=3)
Timestamp('2017-04-01 09:10:11')
ts + pd.DateOffset(hours=2)
Timestamp('2017-01-01 11:10:11')
ts + pd.DateOffset()
Timestamp('2017-01-02 09:10:11')
freq | D/B | W | (B)M/(B)Q/(B)Y | (B)MS/(B)QS/(B)YS | H | T | S | C |
---|---|---|---|---|---|---|---|---|
offset | DateOffset/BDay | Week | (B)MonthEnd/(B)QuarterEnd/(B)YearEnd | (B)MonthBegin/(B)QuarterBegin/(B)YearBegin | Hour | Min |
pd.Timestamp('2020-01-01') + pd.offsets.Week(2)
Timestamp('2020-01-15 00:00:00')
pd.Timestamp('2020-01-01') + pd.offsets.BQuarterBegin(1)
Timestamp('2020-03-02 00:00:00')
DateOffset 基本都支持频率字符串或偏移别名,传入 freq 参数。以下都是时间偏移的子类、子对象,都支持时间偏移的相关操作。有效的日期偏移及频率字符串有:
日期偏移对象 | 频率字符串 | 说明 |
---|---|---|
DateOffset | 无 | 通用偏移类,默认一个日历日 |
BDay 或 BusinessDay | 'B' | 工作日 |
CDay 或 CustomBusinessDay | 'C' | 自定义工作日 |
Week | 'W' | 一周,可选周内固定某日 |
WeekOfMonth | 'WOM' | 每月第几周的第几天 |
LastWeekOfMonth | 'LWOM' | 每月最后一周的第几天 |
MonthEnd | 'M' | 日历日月末 |
MonthBegin | 'MS' | 日历日月初 |
BMonthEnd 或 BusinessMonthEnd | 'BM' | 工作日月末 |
BMonthBegin 或 BusinessMonthBegin | 'BMS' | 工作日月初 |
CBMonthEnd 或 CustomBusinessMonthEnd | 'CBM' | 自定义工作日月末 |
CBMonthBegin 或 CustomBusinessMonthBegin | 'CBMS' | 自定义工作日月初 |
SemiMonthEnd | 'SM' | 某月第 15 天(或其它半数日期)与日历日月末 |
SemiMonthBegin | 'SMS' | 日历日月初与第 15 天(或其它半数日期) |
QuarterEnd | 'Q' | 日历日季末 |
QuarterBegin | 'QS' | 日历日季初 |
BQuarterEnd | 'BQ | 工作日季末 |
BQuarterBegin | 'BQS' | 工作日季初 |
FY5253Quarter | 'REQ' | 零售季,又名 52-53 周 |
YearEnd | 'A' | 日历日年末 |
YearBegin | 'AS' 或 'BYS' | 日历日年初 |
BYearEnd | 'BA' | 工作日年末 |
BYearBegin | 'BAS' | 工作日年初 |
FY5253 | 'RE' | 零售年(又名 52-53 周) |
Easter | 无 | 复活节假日 |
BusinessHour | 'BH' | 工作小时 |
CustomBusinessHour | 'CBH' | 自定义工作小时 |
Day | 'D' | 一天 |
Hour | 'H' | 一小时 |
Minute | 'T' 或 'min' | 一分钟 |
Second | 'S' | 一秒 |
Milli | 'L' 或 'ms' | 一毫秒 |
Micro | 'U' 或 'us' | 一微秒 |
Nano | 'N' | 一纳秒 |
Offset 支持向前或向后偏移:
ts = pd.Timestamp('2020-06-06 00:00:00')
ts.day_name()
# 'Saturday'
# 定义一个工作小时偏移,默认是周一到周五 9-17 点,我们从 10点开始
offset = pd.offsets.BusinessHour(start='10:00')
# 向前偏移一个工作小时,是一个周一,跳过了周日
offset.rollforward(ts)
# Timestamp('2020-06-08 10:00:00')
# 向前偏移至最近的工作日,小时也会增加
ts + offset
# Timestamp('2020-06-08 11:00:00')
# 向后偏移,会在周五下班前的一个小时
offset.rollback(ts)
# Timestamp('2020-06-05 17:00:00')
ts - pd.offsets.Day(1) # 昨日
ts - pd.offsets.Day(2) # 前日
ts - pd.offsets.Week(weekday=0) - pd.offsets.Day(14) # 上周一
ts - pd.offsets.MonthEnd() - pd.offsets.MonthBegin() # 上月一日
时间偏移操作会保留小时和分钟,有时候我们不在意具体的时间只开始从哪天开始,可以使用 normalize() 进行标准化到午夜 0 点:
offset.rollback(ts).normalize()
# Timestamp('2020-06-05 00:00:00')
apply 可以使用偏移对象应用到一个时间上:
ts = pd.Timestamp('2020-06-01 09:00')
day = pd.offsets.Day() # 定义偏移对象
day.apply(ts) # 偏移对象应用到时间上
# Timestamp('2020-06-02 09:00:00')
day.apply(ts).normalize() # 标准化/归一化
# Timestamp('2020-06-02 00:00:00')
ts = pd.Timestamp('2020-06-01 22:00')
hour = pd.offsets.Hour()
hour.apply(ts)
# Timestamp('2020-06-01 23:00:00')
hour.apply(ts).normalize()
# Timestamp('2020-06-01 00:00:00')
hour.apply(pd.Timestamp("2014-01-01 23:30")).normalize()
# Timestamp('2014-01-02 00:00:00')
上边我们偏移时只偏移了偏移对象的一个单位,可以传入参数支持多个单位和对象中的其他单位:
import datetime
d = datetime.datetime(2020, 6, 1, 9, 0)
# datetime.datetime(2020, 6, 1, 9, 0)
d + pd.offsets.Week() # 偏移一周
# Timestamp('2020-06-08 09:00:00')
d + pd.offsets.Week(weekday=4) # 偏移4个周中的日期
# Timestamp('2020-06-05 09:00:00')
# 取一周第几天
(d + pd.offsets.Week(weekday=4)).weekday()
# 4
d - pd.offsets.Week() # 向后一周
# Timestamp('2020-05-25 09:00:00')
# 参数也支持归一标准化 normalize:
d + pd.offsets.Week(normalize=True)
# Timestamp('2020-06-08 00:00:00')
d - pd.offsets.Week(normalize=True)
# Timestamp('2020-05-25 00:00:00')
# 再比如,YearEnd 支持 month 指定月份:
d + pd.offsets.YearEnd()
# Timestamp('2020-12-31 09:00:00')
d + pd.offsets.YearEnd(month=6)
# Timestamp('2020-06-30 09:00:00')
# 不同的偏移对象支持不同的参数,可以通过代码编辑器的代码提示进行查询。
当使用日期作为索引的DataFrame时,此函数可以基于日期偏移量选择最后几行:
i = pd.date_range('2018-04-09', periods=4, freq='2D')
ts = pd.DataFrame({'A': [1, 2, 3, 4]}, index=i)
ts
'''
A
2018-04-09 1
2018-04-11 2
2018-04-13 3
2018-04-15 4
'''
# 取最后三天, 请注意,返回的是最近3天的数据
# 而不是数据集中最近3天的数据,因此未返回2018-04-11的数据
ts.last('3D')
'''
A
2018-04-13 3
2018-04-15 4
'''
# 前三天
ts.first('3D')
'''
A
2018-04-09 1
2018-04-11 2
'''
# 指定时间
ts.at_time('12:00')
'''
A
2018-04-09 12:00:00 2
2018-04-10 12:00:00 4
'''
ts.between_time('0:15', '0:45')
'''
A
2018-04-10 00:20:00 2
2018-04-11 00:40:00 3
'''
ts.between_time('0:45', '0:15')
'''
A
2018-04-09 00:00:00 1
2018-04-12 01:00:00 4
'''
rng = pd.date_range('2012-01-01', '2012-01-03')
s = pd.Series(rng)
rng
DatetimeIndex(['2012-01-01', '2012-01-02', '2012-01-03'], dtype='datetime64[ns]', freq='D')
rng + pd.DateOffset(months=2)
DatetimeIndex(['2012-03-01', '2012-03-02', '2012-03-03'], dtype='datetime64[ns]', freq=None)
s + pd.DateOffset(months=2)
0 2012-03-01 1 2012-03-02 2 2012-03-03 dtype: datetime64[ns]
s - pd.DateOffset(months=2)
0 2011-11-01 1 2011-11-02 2 2011-11-03 dtype: datetime64[ns]
s - pd.offsets.Day(2)
0 2011-12-30 1 2011-12-31 2 2012-01-01 dtype: datetime64[ns]
td = s - pd.Series(pd.date_range('2011-12-29', '2011-12-31'))
td
0 3 days 1 3 days 2 3 days dtype: timedelta64[ns]
td + pd.offsets.Minute(15)
0 3 days 00:15:00 1 3 days 00:15:00 2 3 days 00:15:00 dtype: timedelta64[ns]
需要注意的是,有些时间偏移对象不支持以上操作,还有是就算支持性能会很差,执行很慢,会及抛出性能警告。
rng = pd.date_range('1/1/2021', '12/1/2021', freq='BM')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
2021-01-29 0.494045 2021-02-26 0.662493 2021-03-31 2.131822 2021-04-30 -0.826927 2021-05-31 -0.822046 2021-06-30 -1.207667 2021-07-30 0.993799 2021-08-31 -0.711776 2021-09-30 -0.617836 2021-10-29 0.130349 2021-11-30 -0.473077 Freq: BM, dtype: float64
ts[2:4].index
DatetimeIndex(['2021-03-31', '2021-04-30'], dtype='datetime64[ns]', freq='BM')
ts[::2].index
DatetimeIndex(['2021-01-29', '2021-03-31', '2021-05-31', '2021-07-30', '2021-09-30', '2021-11-30'], dtype='datetime64[ns]', freq='2BM')
ts['1/29/2021']
0.49404517681343435
ts[datetime.datetime(2021, 9, 30):]
2021-09-30 -0.617836 2021-10-29 0.130349 2021-11-30 -0.473077 Freq: BM, dtype: float64
ts['5/31/2021':'8/31/2021']
2021-05-31 -0.822046 2021-06-30 -1.207667 2021-07-30 0.993799 2021-08-31 -0.711776 Freq: BM, dtype: float64
也可以使用部分字符查询一定范围的数据
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']
# 索引选择器
idx = pd.IndexSlice
dft2.loc[idx[:, '2013-01-05'], :]
# 带时区,原数据时区可能不是这个
df['2019-01-01 12:00:00+04:00':'2019-01-01 13:00:00+04:00']
series_minute = pd.Series([1, 2, 3],
pd.DatetimeIndex(['2011-12-31 23:59:00',
'2012-01-01 00:00:00',
'2012-01-01 00:02:00']))
series_minute.index.resolution
'minute'
# 按小时取出
series_minute['2011-12-31 23']
2011-12-31 23:59:00 1 dtype: int64
series_minute['2011-12-31 23:59']
1
series_minute['2011-12-31 23:59:00']
1
series_second = pd.Series([1, 2, 3],
pd.DatetimeIndex(['2011-12-31 23:59:59',
'2012-01-01 00:00:00',
'2012-01-01 00:00:01']))
series_second
2011-12-31 23:59:59 1 2012-01-01 00:00:00 2 2012-01-01 00:00:01 3 dtype: int64
# 最小粒度为秒
series_second.index.resolution
'second'
series_second['2011-12-31 23:59']
2011-12-31 23:59:59 1 dtype: int64
rng2 = pd.date_range('2011-01-01', '2012-01-01', freq='W')
ts2 = pd.Series(np.random.randn(len(rng2)), index=rng2)
ts2.truncate(before='2011-11', after='2011-12')
2011-11-06 -0.690541 2011-11-13 0.598374 2011-11-20 1.781615 2011-11-27 -0.950292 Freq: W-SUN, dtype: float64
# 对比以上操作的不同
ts2['2011-11':'2011-12']
2011-11-06 -0.690541 2011-11-13 0.598374 2011-11-20 1.781615 2011-11-27 -0.950292 2011-12-04 0.220030 2011-12-11 0.519082 2011-12-18 -0.974609 2011-12-25 0.413335 Freq: W-SUN, dtype: float64
# 也可以通过自然索引来取
ts2[[0, 2, 6]].index
DatetimeIndex(['2011-01-02', '2011-01-16', '2011-02-13'], dtype='datetime64[ns]', freq=None)
df = pd.DataFrame({'A': ['a', 'b', 'c', 'd', 'e'],
'B': ['f', 'g', 'h', 'i', 'j'],
'C': ['k', 'l', 'm', 'n', 'o']},
index=[1, 2, 3, 4, 5])
df
A | B | C | |
---|---|---|---|
1 | a | f | k |
2 | b | g | l |
3 | c | h | m |
4 | d | i | n |
5 | e | j | o |
df.truncate(before=2, after=4)
A | B | C | |
---|---|---|---|
2 | b | g | l |
3 | c | h | m |
4 | d | i | n |
# 按列取
df.truncate(before="A", after="B", axis="columns")
A | B | |
---|---|---|
1 | a | f |
2 | b | g |
3 | c | h |
4 | d | i |
5 | e | j |
df['A'].truncate(before=2, after=4)
2 b 3 c 4 d Name: A, dtype: object
截取时间的例子:
dates = pd.date_range('2016-01-01', '2016-02-01', freq='s')
df = pd.DataFrame(index=dates, data={'A': 1})
df.tail()
A | |
---|---|
2016-01-31 23:59:56 | 1 |
2016-01-31 23:59:57 | 1 |
2016-01-31 23:59:58 | 1 |
2016-01-31 23:59:59 | 1 |
2016-02-01 00:00:00 | 1 |
df.truncate(before=pd.Timestamp('2016-01-05'),
after=pd.Timestamp('2016-01-10')).tail()
A | |
---|---|
2016-01-09 23:59:56 | 1 |
2016-01-09 23:59:57 | 1 |
2016-01-09 23:59:58 | 1 |
2016-01-09 23:59:59 | 1 |
2016-01-10 00:00:00 | 1 |
df.truncate('2016-01-05', '2016-01-10').tail()
A | |
---|---|
2016-01-09 23:59:56 | 1 |
2016-01-09 23:59:57 | 1 |
2016-01-09 23:59:58 | 1 |
2016-01-09 23:59:59 | 1 |
2016-01-10 00:00:00 | 1 |
df.loc['2016-01-05':'2016-01-10', :].tail()
A | |
---|---|
2016-01-10 23:59:55 | 1 |
2016-01-10 23:59:56 | 1 |
2016-01-10 23:59:57 | 1 |
2016-01-10 23:59:58 | 1 |
2016-01-10 23:59:59 | 1 |
rng = pd.date_range('2020-06-01', '2020-06-03')
ts = pd.Series(range(len(rng)), index=rng)
ts
2020-06-01 0 2020-06-02 1 2020-06-03 2 Freq: D, dtype: int64
ts.shift(1)
2020-06-01 NaN 2020-06-02 0.0 2020-06-03 1.0 Freq: D, dtype: float64
shift 方法接受 freq 参数,该参数可以接受 DateOffset 类或其他类似 timedelta 的对象,也可以接受偏移别名。
# 工作日
ts.shift(3, freq=pd.offsets.BDay())
2020-06-04 0 2020-06-05 1 2020-06-08 2 dtype: int64
# 工作日月末
ts.shift(3, freq='BM')
2020-08-31 0 2020-08-31 1 2020-08-31 2 dtype: int64
除了更改数据和索引的对齐方式之外,ts方法将索引中的所有日期更改指定的偏移量(只移动索引):
ts.shift(3, freq='D')
2020-06-04 0 2020-06-05 1 2020-06-06 2 Freq: D, dtype: int64
请注意,使用shift时,最前边的 条目将不再是NaN,因为不会重新对齐数据。
dr = pd.date_range('1/1/2010', periods=3, freq=3 * pd.offsets.BDay())
ts = pd.Series(np.random.randn(3), index=dr)
ts
2010-01-01 2.367982 2010-01-06 -0.047885 2010-01-11 -0.528319 Freq: 3B, dtype: float64
# 从3个工作日转为一个工作日
ts.asfreq(pd.offsets.BDay())
2010-01-01 2.367982 2010-01-04 NaN 2010-01-05 NaN 2010-01-06 -0.047885 2010-01-07 NaN 2010-01-08 NaN 2010-01-11 -0.528319 Freq: B, dtype: float64
asfreq 提供了更多便利,因此您可以为频率转换后可能出现的任何间隙指定插值方法。
ts.asfreq(pd.offsets.BDay(), method='pad')
2010-01-01 2.367982 2010-01-04 2.367982 2010-01-05 2.367982 2010-01-06 -0.047885 2010-01-07 -0.047885 2010-01-08 -0.047885 2010-01-11 -0.528319 Freq: B, dtype: float64
# 对空值进行填充
ts.asfreq(freq='30S', fill_value=9.0)
2010-01-01 00:00:00 2.367982 2010-01-01 00:00:30 9.000000 2010-01-01 00:01:00 9.000000 2010-01-01 00:01:30 9.000000 2010-01-01 00:02:00 9.000000 ... 2010-01-10 23:58:00 9.000000 2010-01-10 23:58:30 9.000000 2010-01-10 23:59:00 9.000000 2010-01-10 23:59:30 9.000000 2010-01-11 00:00:00 -0.528319 Freq: 30S, Length: 28801, dtype: float64
对于 DatetimeIndex,这基本上只是reindex() 的一个很方便的包装器,该包装器生成 date_range 并调用 reindex。
date_index = pd.date_range('1/1/2010', periods=6, freq='D')
df2 = pd.DataFrame({"prices": [100, 101, np.nan, 100, 89, 88]},
index=date_index)
df2
prices | |
---|---|
2010-01-01 | 100.0 |
2010-01-02 | 101.0 |
2010-01-03 | NaN |
2010-01-04 | 100.0 |
2010-01-05 | 89.0 |
2010-01-06 | 88.0 |
# 假设我们决定扩展数据框以覆盖更大的日期范围
date_index2 = pd.date_range('12/29/2009', periods=10, freq='D')
df2.reindex(date_index2)
prices | |
---|---|
2009-12-29 | NaN |
2009-12-30 | NaN |
2009-12-31 | NaN |
2010-01-01 | 100.0 |
2010-01-02 | 101.0 |
2010-01-03 | NaN |
2010-01-04 | 100.0 |
2010-01-05 | 89.0 |
2010-01-06 | 88.0 |
2010-01-07 | NaN |
默认情况下,原始数据框中没有值的索引条目(例如,“ 2009-12-29”)将用NaN填充。 如果需要,我们可以使用几个选项之一({None, ‘backfill’/’bfill’, ‘pad’/’ffill’, ‘nearest’})来填写缺失值。
例如,要反向传播最后一个有效值以填充NaN值,请将 bfill 作为参数传递给method关键字。
df2.reindex(date_index2, method='bfill')
prices | |
---|---|
2009-12-29 | 100.0 |
2009-12-30 | 100.0 |
2009-12-31 | 100.0 |
2010-01-01 | 100.0 |
2010-01-02 | 101.0 |
2010-01-03 | NaN |
2010-01-04 | 100.0 |
2010-01-05 | 89.0 |
2010-01-06 | 88.0 |
2010-01-07 | NaN |
# DatetimeIndex 转为 Python 原生 datetime.datetime 类型
df2.index.to_pydatetime()
array([datetime.datetime(2010, 1, 1, 0, 0), datetime.datetime(2010, 1, 2, 0, 0), datetime.datetime(2010, 1, 3, 0, 0), datetime.datetime(2010, 1, 4, 0, 0), datetime.datetime(2010, 1, 5, 0, 0), datetime.datetime(2010, 1, 6, 0, 0)], dtype=object)
rng = pd.date_range('1/1/2012', periods=1000, freq='S')
ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
ts
2012-01-01 00:00:00 244 2012-01-01 00:00:01 174 2012-01-01 00:00:02 18 2012-01-01 00:00:03 87 2012-01-01 00:00:04 435 ... 2012-01-01 00:16:35 33 2012-01-01 00:16:36 72 2012-01-01 00:16:37 23 2012-01-01 00:16:38 364 2012-01-01 00:16:39 308 Freq: S, Length: 1000, dtype: int32
# 每5分钟进行一次聚合
ts.resample('5Min').sum()
2012-01-01 00:00:00 76011 2012-01-01 00:05:00 77349 2012-01-01 00:10:00 73123 2012-01-01 00:15:00 26538 Freq: 5T, dtype: int32
重采样功能非常灵活,您可以指定许多不同的参数来控制频率转换和重采样操作。
通过分派可用的任何函数(类似于 grouby)都可以作为返回对象的方法,包括 sum,mean,std,sem,max,min,mid,median,first,last,ohlc:
ts.resample('5Min').mean() # 平均
2012-01-01 00:00:00 253.370000 2012-01-01 00:05:00 257.830000 2012-01-01 00:10:00 243.743333 2012-01-01 00:15:00 265.380000 Freq: 5T, dtype: float64
ts.resample('5Min').max() # 最大值
2012-01-01 00:00:00 497 2012-01-01 00:05:00 497 2012-01-01 00:10:00 495 2012-01-01 00:15:00 491 Freq: 5T, dtype: int32
美国线(英语:Open-High-Low-Close chart,OHLC chart),以竖立的线条表现股票价格的变化,可以呈现“开盘价、最高价、最低价、收盘价”,竖线呈现最高价和最低价间的价差间距,左侧横线代表开盘价,右侧横线代表收盘价,绘制上较K线简单。另有一种美国线仅呈现“最高价、最低价、收盘价”(HLC)三项讯息。
ts.resample('5Min').ohlc()
open | high | low | close | |
---|---|---|---|---|
2012-01-01 00:00:00 | 407 | 497 | 0 | 51 |
2012-01-01 00:05:00 | 302 | 497 | 2 | 161 |
2012-01-01 00:10:00 | 125 | 495 | 0 | 338 |
2012-01-01 00:15:00 | 370 | 491 | 3 | 132 |
ts.resample('5Min', closed='right').mean()
2011-12-31 23:55:00 407.000000 2012-01-01 00:00:00 253.020000 2012-01-01 00:05:00 257.240000 2012-01-01 00:10:00 244.560000 2012-01-01 00:15:00 264.323232 Freq: 5T, dtype: float64
ts.resample('5Min', closed='left').mean()
2012-01-01 00:00:00 253.370000 2012-01-01 00:05:00 257.830000 2012-01-01 00:10:00 243.743333 2012-01-01 00:15:00 265.380000 Freq: 5T, dtype: float64
ts.resample('5Min').mean() # 默认 label='left'
2012-01-01 00:00:00 253.370000 2012-01-01 00:05:00 257.830000 2012-01-01 00:10:00 243.743333 2012-01-01 00:15:00 265.380000 Freq: 5T, dtype: float64
ts.resample('5Min', label='left').mean()
2012-01-01 00:00:00 253.370000 2012-01-01 00:05:00 257.830000 2012-01-01 00:10:00 243.743333 2012-01-01 00:15:00 265.380000 Freq: 5T, dtype: float64
上采样又称图像插值,其主要目的是通过放大原图像,进而可以在更高分辨率的设备上显示。图像放大几乎都是采用内插值方法,即在原有图像像素的基础上在像素点之间采用合适的插值算法插入新的元素。
对于上采样,您可以指定一种上采样的方法,并指定 limit 参数以对创建的间隙进行插值:
# 从每秒到每250毫秒
ts[:2].resample('250L').asfreq()
2012-01-01 00:00:00.000 407.0 2012-01-01 00:00:00.250 NaN 2012-01-01 00:00:00.500 NaN 2012-01-01 00:00:00.750 NaN 2012-01-01 00:00:01.000 176.0 Freq: 250L, dtype: float64
ts[:2].resample('250L').ffill()
2012-01-01 00:00:00.000 407 2012-01-01 00:00:00.250 407 2012-01-01 00:00:00.500 407 2012-01-01 00:00:00.750 407 2012-01-01 00:00:01.000 176 Freq: 250L, dtype: int32
ts[:2].resample('250L').ffill(limit=2)
2012-01-01 00:00:00.000 407.0 2012-01-01 00:00:00.250 407.0 2012-01-01 00:00:00.500 407.0 2012-01-01 00:00:00.750 NaN 2012-01-01 00:00:01.000 176.0 Freq: 250L, dtype: float64
压缩感知(Compressed sensing),也被称为压缩采样(Compressive sampling)或稀疏采样(Sparse sampling),是一种寻找欠定线性系统的稀疏解的技术。压缩感知被应用于电子工程尤其是信号处理中,用于获取和重构稀疏或可压缩的信号。
相对于要重采样的时间量,稀疏时间序列的点要少得多。 天真地对稀疏序列进行升采样可能会产生很多中间值。 当您不想使用一种方法来填充这些值时,例如 fill_method 为 None,则中间值将用 NaN 填充。由于重采样是基于时间的分组依据,因此以下是仅对并非全部为NaN的组进行有效重采样的方法。
rng = pd.date_range('2014-1-1', periods=100, freq='D') + pd.Timedelta('1s')
ts = pd.Series(range(100), index=rng)
# 如果我们想对整个系列重新采样:
ts.resample('3T').sum()
2014-01-01 00:00:00 0 2014-01-01 00:03:00 0 2014-01-01 00:06:00 0 2014-01-01 00:09:00 0 2014-01-01 00:12:00 0 .. 2014-04-09 23:48:00 0 2014-04-09 23:51:00 0 2014-04-09 23:54:00 0 2014-04-09 23:57:00 0 2014-04-10 00:00:00 99 Freq: 3T, Length: 47521, dtype: int64
取而代之的是,我们只能对那些我们拥有点的组重新采样,如下所示:
from functools import partial
from pandas.tseries.frequencies import to_offset
def round(t, freq):
freq = to_offset(freq)
return pd.Timestamp((t.value // freq.delta.value) * freq.delta.value)
ts.groupby(partial(round, freq='3T')).sum()
2014-01-01 0 2014-01-02 1 2014-01-03 2 2014-01-04 3 2014-01-05 4 .. 2014-04-06 95 2014-04-07 96 2014-04-08 97 2014-04-09 98 2014-04-10 99 Length: 100, dtype: int64
类似于 aggregating API, groupby API, 和窗口方法 api, Resampler 也适用相关方法。重新采样DataFrame时,默认值是对具有相同功能的所有列进行操作:
df = pd.DataFrame(np.random.randn(1000, 3),
index=pd.date_range('1/1/2012', freq='S', periods=1000),
columns=['A', 'B', 'C'])
r = df.resample('3T')
r.mean()
A | B | C | |
---|---|---|---|
2012-01-01 00:00:00 | -0.033312 | 0.048403 | 0.122693 |
2012-01-01 00:03:00 | 0.057612 | 0.138748 | -0.005933 |
2012-01-01 00:06:00 | -0.020865 | 0.039067 | -0.023592 |
2012-01-01 00:09:00 | 0.011191 | -0.013045 | 0.030350 |
2012-01-01 00:12:00 | -0.136724 | 0.023454 | -0.049614 |
2012-01-01 00:15:00 | 0.032506 | 0.070861 | 0.124264 |
我们可以选择一个或多个特定列:
r['A'].mean()
2012-01-01 00:00:00 -0.033312 2012-01-01 00:03:00 0.057612 2012-01-01 00:06:00 -0.020865 2012-01-01 00:09:00 0.011191 2012-01-01 00:12:00 -0.136724 2012-01-01 00:15:00 0.032506 Freq: 3T, Name: A, dtype: float64
r[['A', 'B']].mean()
A | B | |
---|---|---|
2012-01-01 00:00:00 | -0.033312 | 0.048403 |
2012-01-01 00:03:00 | 0.057612 | 0.138748 |
2012-01-01 00:06:00 | -0.020865 | 0.039067 |
2012-01-01 00:09:00 | 0.011191 | -0.013045 |
2012-01-01 00:12:00 | -0.136724 | 0.023454 |
2012-01-01 00:15:00 | 0.032506 | 0.070861 |
多个聚合方式:
r['A'].agg([np.sum, np.mean, np.std])
sum | mean | std | |
---|---|---|---|
2012-01-01 00:00:00 | -5.996128 | -0.033312 | 0.937740 |
2012-01-01 00:03:00 | 10.370077 | 0.057612 | 0.934882 |
2012-01-01 00:06:00 | -3.755667 | -0.020865 | 0.953072 |
2012-01-01 00:09:00 | 2.014386 | 0.011191 | 1.056193 |
2012-01-01 00:12:00 | -24.610344 | -0.136724 | 0.976037 |
2012-01-01 00:15:00 | 3.250625 | 0.032506 | 0.998992 |
r.agg([np.sum, np.mean]) # 每个列
A | B | C | ||||
---|---|---|---|---|---|---|
sum | mean | sum | mean | sum | mean | |
2012-01-01 00:00:00 | -5.996128 | -0.033312 | 8.712526 | 0.048403 | 22.084827 | 0.122693 |
2012-01-01 00:03:00 | 10.370077 | 0.057612 | 24.974586 | 0.138748 | -1.067948 | -0.005933 |
2012-01-01 00:06:00 | -3.755667 | -0.020865 | 7.032081 | 0.039067 | -4.246644 | -0.023592 |
2012-01-01 00:09:00 | 2.014386 | 0.011191 | -2.348185 | -0.013045 | 5.463043 | 0.030350 |
2012-01-01 00:12:00 | -24.610344 | -0.136724 | 4.221691 | 0.023454 | -8.930492 | -0.049614 |
2012-01-01 00:15:00 | 3.250625 | 0.032506 | 7.086119 | 0.070861 | 12.426425 | 0.124264 |
# 不同的聚合方式
r.agg({'A': np.sum,
'B': lambda x: np.std(x, ddof=1)})
A | B | |
---|---|---|
2012-01-01 00:00:00 | -5.996128 | 0.935210 |
2012-01-01 00:03:00 | 10.370077 | 0.968908 |
2012-01-01 00:06:00 | -3.755667 | 0.983241 |
2012-01-01 00:09:00 | 2.014386 | 0.982057 |
2012-01-01 00:12:00 | -24.610344 | 1.047875 |
2012-01-01 00:15:00 | 3.250625 | 1.013443 |
# 用字符指定
r.agg({'A': 'sum', 'B': 'std'})
A | B | |
---|---|---|
2012-01-01 00:00:00 | -5.996128 | 0.935210 |
2012-01-01 00:03:00 | 10.370077 | 0.968908 |
2012-01-01 00:06:00 | -3.755667 | 0.983241 |
2012-01-01 00:09:00 | 2.014386 | 0.982057 |
2012-01-01 00:12:00 | -24.610344 | 1.047875 |
2012-01-01 00:15:00 | 3.250625 | 1.013443 |
r.agg({'A': ['sum', 'std'], 'B': ['mean', 'std']})
A | B | |||
---|---|---|---|---|
sum | std | mean | std | |
2012-01-01 00:00:00 | -5.996128 | 0.937740 | 0.048403 | 0.935210 |
2012-01-01 00:03:00 | 10.370077 | 0.934882 | 0.138748 | 0.968908 |
2012-01-01 00:06:00 | -3.755667 | 0.953072 | 0.039067 | 0.983241 |
2012-01-01 00:09:00 | 2.014386 | 1.056193 | -0.013045 | 0.982057 |
2012-01-01 00:12:00 | -24.610344 | 0.976037 | 0.023454 | 1.047875 |
2012-01-01 00:15:00 | 3.250625 | 0.998992 | 0.070861 | 1.013443 |
迭代采样对象:
small = pd.Series(range(6),
index=pd.to_datetime(['2017-01-01T00:00:00',
'2017-01-01T00:30:00',
'2017-01-01T00:31:00',
'2017-01-01T01:00:00',
'2017-01-01T03:00:00',
'2017-01-01T03:05:00'])
)
resampled = small.resample('H')
for name, group in resampled:
print("Group: ", name)
print("-" * 27)
print(group, end="\n\n")
Group: 2017-01-01 00:00:00 --------------------------- 2017-01-01 00:00:00 0 2017-01-01 00:30:00 1 2017-01-01 00:31:00 2 dtype: int64 Group: 2017-01-01 01:00:00 --------------------------- 2017-01-01 01:00:00 3 dtype: int64 Group: 2017-01-01 02:00:00 --------------------------- Series([], dtype: int64) Group: 2017-01-01 03:00:00 --------------------------- 2017-01-01 03:00:00 4 2017-01-01 03:05:00 5 dtype: int64
Pandas 窗口函数,为了处理数字数据,Pandas 提供几种窗口函数,如移动窗口函数(rolling()),扩展窗口函数(expanding()),指数加权滑动(ewm()),同时可在基基础上调用适合的统计函数,如求和、中位数、均值、协方差、方差、相关性等。
理解窗口
可以把“窗口”(windows)这个理解一个集合,一个窗口就是一个集合,在统计分析中有需要不同的「窗口」,比如一个部门分成不同组,在统计时会按组进行平均、排名等操作。再比如,在一些像时间这种有顺序的数据,我们可能5天分一组、一月分一组再进行排序、求中位数等计算。
rolling(10) 与 groupby 很像,但并没有进行分组,而是创建了一个按移动 10(天)位的滑动窗口对象。我们再对每个对象进行统计操作。
expanding,「扩展」从数据(大多情况下是时间)的起始处开始窗口,增加窗口直到指定的大小。
以下是 Expanding vs. rolling window 图示:
以下是 rolling 动图示例:
以下是 expanding 动图示例:
指数加权函数
指数加权函数(Exponential weighted moving),在上述两个统计方法中分组中的所有数值的权重都是一样的,指数加权就是我们对分组中的数据给予不同的权重用于后边的计算中。如指数移动平均(Exponential Moving Average)也叫权重移动平均(Weighted Moving Average),是以指数式递减加权的移动平均,各数值的加权影响力随时间呈指数式递减,时间越靠近当前时刻的数据加权影响力越大。
梯度下降法,就是计算了梯度的指数加权平均数,并以此来更新权重,它的运行速度几乎总是快于标准的梯度下降算法。
Pandas 中的窗口
滚动对象通过.rolling调用返回:pandas.DataFrame.rolling(),pandas.Series.rolling() 等。扩展对象通过.expanding 调用返回:pandas.DataFrame.expanding(),pandas.Series.expanding() 等。EWM 对象由.ewm调用返回:pandas.DataFrame.ewm(),pandas.Series.ewm() 等。
s = pd.Series(np.random.randn(1000),
index=pd.date_range('1/1/2000', periods=1000))
s = s.cumsum()
r = s.rolling(window=60)
r
Rolling [window=60,center=False,axis=0]
在.rolling()
创建对象时,它支持以下参数:
参数 | 说明 |
---|---|
window | 可选参数,表示时间窗的大小,注意有两种形式(int 或 offset)。如果使用 int,则数值表示计算统计量的观测值的数量即向前几个数据,如果是offset类型,表示时间窗的大小。 |
min_periods | 每个窗口最少包含的观测值数量,小于这个值的窗口结果为NaN,值可以是int,默认None,offset情况下,默认为1。 |
center | 把窗口的标签设置为居中。布尔型,默认False,居右 |
win_type | 窗口的类型,截取窗的各种函数,字符串类型,默认为None。 |
on | 可选参数,对于dataframe而言,指定要计算滚动窗口的列,值为列名。 |
axis | int、字符串,默认为0,即对列进行计算 |
closed | 定义区间的开闭,支持int类型的window。对于offset类型默认是左开右闭,默认为right,可以根据情况指定为left、both等。 |
窗口的类型 win_type 将win_type 传递给.rolling 会生成一个通用的滚动窗口计算,该计算将根据 win_type进行加权。
win_type 参数支持以下类型:
s.rolling(window=5, win_type='triang').mean()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 NaN 2000-01-05 -0.421702 ... 2002-09-22 16.374412 2002-09-23 16.199573 2002-09-24 16.354381 2002-09-25 16.715682 2002-09-26 16.921977 Freq: D, Length: 1000, dtype: float64
s.rolling(window=5, win_type='gaussian').mean(std=0.1)
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 NaN 2000-01-05 -0.043657 ... 2002-09-22 16.919401 2002-09-23 15.615150 2002-09-24 15.611811 2002-09-25 17.326237 2002-09-26 17.551819 Freq: D, Length: 1000, dtype: float64
# 以下相同
s.rolling(window=5, win_type='boxcar').mean()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 NaN 2000-01-05 -0.444738 ... 2002-09-22 16.270742 2002-09-23 16.406929 2002-09-24 16.604883 2002-09-25 16.525007 2002-09-26 16.669580 Freq: D, Length: 1000, dtype: float64
s.rolling(window=5).mean()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 NaN 2000-01-05 -0.444738 ... 2002-09-22 16.270742 2002-09-23 16.406929 2002-09-24 16.604883 2002-09-25 16.525007 2002-09-26 16.669580 Freq: D, Length: 1000, dtype: float64
r.mean()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 NaN 2000-01-05 NaN ... 2002-09-22 21.061487 2002-09-23 20.917474 2002-09-24 20.800376 2002-09-25 20.661929 2002-09-26 20.528112 Freq: D, Length: 1000, dtype: float64
s.plot(style='k--') # 绘图看一下变化,原数据
<AxesSubplot:>
r.mean().plot(style='k') # 绘图看一下变化,计算后的
<AxesSubplot:>
df = pd.DataFrame(np.random.randn(1000, 4),
index=pd.date_range('1/1/2000', periods=1000),
columns=['A', 'B', 'C', 'D'])
df = df.cumsum()
# 操作并绘图
df.rolling(window=60).sum().plot(subplots=True)
array([<AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>, <AxesSubplot:>], dtype=object)
提供许多常见的统计功能:
Method | Description |
---|---|
count() | Number of non-null observations |
sum() | Sum of values |
mean() | Mean of values |
median() | Arithmetic median of values |
min() | Minimum |
max() | Maximum |
std() | Bessel-corrected sample standard deviation |
var() | Unbiased variance |
skew() | Sample skewness (3rd moment) |
kurt() | Sample kurtosis (4th moment) |
quantile() | Sample quantile (value at %) |
apply() | Generic apply |
cov() | Unbiased covariance (binary) |
corr() | Correlation (binary) |
apply() 函数接受一个额外的 func 参数,并执行通用滚动计算。 func 参数应该是一个从 ndarray 输入生成单个值的函数。 假设我们要滚动计算平均绝对偏差:
def mad(x):
return np.fabs(x - x.mean()).mean()
s.rolling(window=60).apply(mad, raw=True).plot(style='k')
<AxesSubplot:>
dft = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]},
index=pd.date_range('20130101 09:00:00',
periods=5,
freq='s'))
dft
B | |
---|---|
2013-01-01 09:00:00 | 0.0 |
2013-01-01 09:00:01 | 1.0 |
2013-01-01 09:00:02 | 2.0 |
2013-01-01 09:00:03 | NaN |
2013-01-01 09:00:04 | 4.0 |
dft.rolling(2).sum()
B | |
---|---|
2013-01-01 09:00:00 | NaN |
2013-01-01 09:00:01 | 1.0 |
2013-01-01 09:00:02 | 3.0 |
2013-01-01 09:00:03 | NaN |
2013-01-01 09:00:04 | NaN |
dft.rolling(2, min_periods=1).sum()
B | |
---|---|
2013-01-01 09:00:00 | 0.0 |
2013-01-01 09:00:01 | 1.0 |
2013-01-01 09:00:02 | 3.0 |
2013-01-01 09:00:03 | 2.0 |
2013-01-01 09:00:04 | 4.0 |
指定移动频率
dft.rolling('2s').sum()
B | |
---|---|
2013-01-01 09:00:00 | 0.0 |
2013-01-01 09:00:01 | 1.0 |
2013-01-01 09:00:02 | 3.0 |
2013-01-01 09:00:03 | 2.0 |
2013-01-01 09:00:04 | 4.0 |
时间感知滚动与重采样
将 .rolling() 与基于时间的索引一起使用非常类似于重采样。它们既对时间索引的 Pandas 对象进行操作又执行还原操作。
当使用带有偏移量的.rolling() 时。偏移量是时间增量。取得一个向后看的窗口,并汇总该窗口中的所有值(包括终点,但不包括起点)。这是结果中此时的新值。对于输入的每个点,这些都是时空可变大小的窗口,将获得与输入相同大小的结果。
当使用带有偏移量的.resample() 时。构造一个新的索引,它是偏移的频率。对于每个频率仓,在该时间仓中的时间倒退窗口内,从输入汇总点。聚合的结果是该频率点的输出。窗口在频率空间中是固定大小的。您的结果将具有介于原始输入对象的最小值和最大值之间的规则频率形状。
总而言之,.rolling() 是基于时间的窗口操作,而.resample() 是基于频率的窗口操作。
它们遵循与.rolling 类似的接口,其中.expanding 方法返回 Expanding 对象。
由于这些计算是滚动统计的特殊情况,因此它们在 Pandas 中实现,因此以下两个调用是等效的:
df.rolling(window=len(df), min_periods=1).mean()[:5]
A | B | C | D | |
---|---|---|---|---|
2000-01-01 | 1.049313 | 0.111910 | 0.322994 | -1.091590 |
2000-01-02 | 0.919457 | -0.534175 | 0.146955 | -1.443932 |
2000-01-03 | 0.876989 | -0.459435 | 0.209525 | -1.924145 |
2000-01-04 | 0.963262 | -0.896879 | 0.056904 | -2.227882 |
2000-01-05 | 0.960253 | -0.964472 | 0.224515 | -2.138985 |
df.expanding(min_periods=1).mean()[:5]
A | B | C | D | |
---|---|---|---|---|
2000-01-01 | 1.049313 | 0.111910 | 0.322994 | -1.091590 |
2000-01-02 | 0.919457 | -0.534175 | 0.146955 | -1.443932 |
2000-01-03 | 0.876989 | -0.459435 | 0.209525 | -1.924145 |
2000-01-04 | 0.963262 | -0.896879 | 0.056904 | -2.227882 |
2000-01-05 | 0.960253 | -0.964472 | 0.224515 | -2.138985 |
具有与.rolling方法类似的方法:
Method | Description |
---|---|
count() | Number of non-null observations |
sum() | Sum of values |
mean() | Mean of values |
median() | Arithmetic median of values |
min() | Minimum |
max() | Maximum |
std() | Bessel-corrected sample standard deviation |
var() | Unbiased variance |
skew() | Sample skewness (3rd moment) |
kurt() | Sample kurtosis (4th moment) |
quantile() | Sample quantile (value at %) |
apply() | Generic apply |
cov() | Unbiased covariance (binary) |
corr() | Correlation (binary) |
s.cumsum().head()
2000-01-01 -0.763718 2000-01-02 -2.799295 2000-01-03 -2.842953 2000-01-04 -2.291686 2000-01-05 -2.223691 Freq: D, dtype: float64
s.cumsum().head()
2000-01-01 -0.763718 2000-01-02 -2.799295 2000-01-03 -2.842953 2000-01-04 -2.291686 2000-01-05 -2.223691 Freq: D, dtype: float64
shift/diff/pct_change都是涉及到了元素关系
①shift是指序列索引不变,但值向后移动
②diff是指前后元素的差,period参数表示间隔,默认为1,并且可以为负
③pct_change是值前后元素的变化百分比,period参数与diff类似
s.shift(2).head()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 -0.763718 2000-01-04 -2.035577 2000-01-05 -0.043657 Freq: D, dtype: float64
s.diff(3).head()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 1.314986 2000-01-05 2.103571 Freq: D, dtype: float64
s.pct_change(3).head()
2000-01-01 NaN 2000-01-02 NaN 2000-01-03 NaN 2000-01-04 -1.721820 2000-01-05 -1.033403 Freq: D, dtype: float64
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。