27_Pandas按星期,月份,季度和年份的天计算时间序列数据的总计和平均值
将pandas.DataFrame的索引设置为DatetimeIndex对于处理具有日期和时间信息(例如日期和时间)的时间序列数据很有用。
由于DatetimeIndex可以获取诸如星期几,月份,季度和年份之类的信息作为属性,因此可以使用它们来计算每个时间序列数据的总和或平均值。此外,通过组合它们,例如可以计算每个季度的一周中的每一天的总计。
这里,将描述以下内容。
- 通过resample()汇总任何期间
- 星期几:weekday, dayofweek, day_name()
- 通过指定星期几来提取行
- 计算一周中每一天的总数和平均值
- 其他属性
- 年: year
- 季度:quarter
- 月:month, month_name()
- 周:week, weekofyear
- 通过组合多个属性(星期几,月份,季度,年份等)进行处理
有关如何将索引设置为datetime64 [ns]类型的DatetimeIndex,请参考以下文章。
使用以下csv数据作为示例
import pandas as pd
df = pd.read_csv('./data/27/sample_date.csv', index_col=0, parse_dates=True)
print(df)
# val_1 val_2
# date
# 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
# 2017-11-27 20 38
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2017-12-22 31 54
# 2017-12-29 21 8
# 2018-01-03 98 76
# 2018-01-08 48 64
# 2018-01-19 18 48
# 2018-01-23 86 70
print(type(df.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
通过resample()汇总任何期间
首先,显示了通过使用resample()而不是DatetimeIndex的属性对任意时间段进行总计(求和,平均值等)的方法。
在第一个参数中指定频率代码,例如W(星期),M(月),Q(季度),Y(年),并调用诸如sum()的方法。
print(df.resample('W').sum())
# val_1 val_2
# date
# 2017-11-05 65 76
# 2017-11-12 26 66
# 2017-11-19 47 47
# 2017-11-26 0 0
# 2017-12-03 20 38
# 2017-12-10 65 85
# 2017-12-17 4 29
# 2017-12-24 31 54
# 2017-12-31 21 8
# 2018-01-07 98 76
# 2018-01-14 48 64
# 2018-01-21 18 48
# 2018-01-28 86 70
print(df.resample('M').sum())
# val_1 val_2
# date
# 2017-11-30 158 227
# 2017-12-31 121 176
# 2018-01-31 250 258
print(df.resample('Q').sum())
# val_1 val_2
# date
# 2017-12-31 279 403
# 2018-03-31 250 258
print(df.resample('Y').sum())
# val_1 val_2
# date
# 2017-12-31 279 403
# 2018-12-31 250 258
还可以通过组合数字来指定时间段,例如10D(10天)和3W(3周).
print(df.resample('10D').sum())
# val_1 val_2
# date
# 2017-11-01 91 142
# 2017-11-11 47 47
# 2017-11-21 20 38
# 2017-12-01 65 85
# 2017-12-11 4 29
# 2017-12-21 52 62
# 2017-12-31 146 140
# 2018-01-10 18 48
# 2018-01-20 86 70
print(df.resample('3W').sum())
# val_1 val_2
# date
# 2017-11-05 65 76
# 2017-11-26 73 113
# 2017-12-17 89 152
# 2018-01-07 150 138
# 2018-01-28 152 182
除了sum()之外,还可以使用诸如mean()之类的方法。还可以使用agg()同时应用多种方法。
print(df.resample('Y').mean())
# val_1 val_2
# date
# 2017-12-31 34.875 50.375
# 2018-12-31 62.500 64.500
print(df.resample('Y').agg(['sum', 'mean', 'max', 'min']))
# val_1 val_2
# sum mean max min sum mean max min
# date
# 2017-12-31 279 34.875 65 4 403 50.375 85 8
# 2018-12-31 250 62.500 98 18 258 64.500 76 48
请注意,如果索引的值为D,则将是该期间的开始日期和时间,而W,M,Q,Y将是结束日期和时间。
星期几:weekday, dayofweek, day_name()
从这里开始,将说明使用DatetimeIndex属性和多索引进行总计的方法。
使用DatetimeIndex的weekday或dayofweek属性,可以获取星期几信息,该信息是整数,其中星期一为0,星期日为6。两者是相同的。
print(df.index.weekday)
# Int64Index([2, 1, 5, 0, 1, 1, 4, 4, 2, 0, 4, 1], dtype='int64', name='date')
print(df.index.dayofweek)
# Int64Index([2, 1, 5, 0, 1, 1, 4, 4, 2, 0, 4, 1], dtype='int64', name='date')
可以用英语获取ay_name()。请注意,这是一种方法,而不是属性。此外,在版本0.22之前,可以使用weekday_name属性获取星期几的名称,但是在0.23中已将其废除,并添加了新的day_name()。
print(df.index.day_name())
# Index(['Wednesday', 'Tuesday', 'Saturday', 'Monday', 'Tuesday', 'Tuesday',
# 'Friday', 'Friday', 'Wednesday', 'Monday', 'Friday', 'Tuesday'],
# dtype='object', name='date')
通过指定星期几来提取行
例如,如果要提取星期一(0)的行,请执行以下操作。
print(df[df.index.weekday == 0])
# val_1 val_2
# date
# 2017-11-27 20 38
# 2018-01-08 48 64
计算一周中每一天的总数和平均值
可以使用sum()和mean()方法来计算统计数据,例如在一周的指定天中提取的数据的总和和平均值。也可以通过agg()一次全部应用。
print(df[df.index.weekday == 0].sum())
# val_1 68
# val_2 102
# dtype: int64
print(df[df.index.weekday == 0].mean())
# val_1 34.0
# val_2 51.0
# dtype: float64
print(df[df.index.weekday == 0].agg(['sum', 'mean']))
# val_1 val_2
# sum 68.0 102.0
# mean 34.0 51.0
当要计算所有天的总和或平均值而不仅仅是特定日期时,多索引机制很有用。
如果使用set_index()将多个列指定为索引,则它将成为多索引。有关set_index()的信息,请参见以下文章。
df_w = df.set_index([df.index.weekday, df.index])
print(df_w)
# val_1 val_2
# date date
# 2 2017-11-01 65 76
# 1 2017-11-07 26 66
# 5 2017-11-18 47 47
# 0 2017-11-27 20 38
# 1 2017-12-05 65 85
# 2017-12-12 4 29
# 4 2017-12-22 31 54
# 2017-12-29 21 8
# 2 2018-01-03 98 76
# 0 2018-01-08 48 64
# 4 2018-01-19 18 48
# 1 2018-01-23 86 70
由于索引列的名称照原样重复,因此请重写index.names属性。
df_w.index.names = ['weekday', 'date']
print(df_w)
# val_1 val_2
# weekday date
# 2 2017-11-01 65 76
# 1 2017-11-07 26 66
# 5 2017-11-18 47 47
# 0 2017-11-27 20 38
# 1 2017-12-05 65 85
# 2017-12-12 4 29
# 4 2017-12-22 31 54
# 2017-12-29 21 8
# 2 2018-01-03 98 76
# 0 2018-01-08 48 64
# 4 2018-01-19 18 48
# 1 2018-01-23 86 70
さらに、sort_index()でソート。
df_w.sort_index(inplace=True)
print(df_w)
# val_1 val_2
# weekday date
# 0 2017-11-27 20 38
# 2018-01-08 48 64
# 1 2017-11-07 26 66
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2018-01-23 86 70
# 2 2017-11-01 65 76
# 2018-01-03 98 76
# 4 2017-12-22 31 54
# 2017-12-29 21 8
# 2018-01-19 18 48
# 5 2017-11-18 47 47
如果按原样使用sum(),则将计算总和,但是如果在参数级别中指定索引列的名称,则将计算每个索引的总数(在这种情况下,将计算一周中每一天的总数)。
print(df_w.sum())
# val_1 529
# val_2 661
# dtype: int64
print(df_w.sum(level='weekday'))
# val_1 val_2
# weekday
# 0 68 102
# 1 181 250
# 2 163 152
# 4 70 110
# 5 47 47
其他方法(例如mean())也是如此。
print(df_w.mean(level='weekday'))
# val_1 val_2
# weekday
# 0 34.000000 51.000000
# 1 45.250000 62.500000
# 2 81.500000 76.000000
# 4 23.333333 36.666667
# 5 47.000000 47.000000
如果是agg(),请将其与groupby()一起使用。
print(df_w.groupby(level='weekday').agg(['sum', 'mean']))
# val_1 val_2
# sum mean sum mean
# weekday
# 0 68 34.000000 102 51.000000
# 1 181 45.250000 250 62.500000
# 2 163 81.500000 152 76.000000
# 4 70 23.333333 110 36.666667
# 5 47 47.000000 47 47.000000
其他属性
年: year
可以使用DatetimeIndex属性year来获取年份。
print(df.index.year)
# Int64Index([2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2018, 2018, 2018,
# 2018],
# dtype='int64', name='date')
可以采用与上述每周几天相同的方式来计算每年的总计和平均值。
df_y = df.set_index([df.index.year, df.index])
df_y.index.names = ['year', 'date']
print(df_y)
# val_1 val_2
# year date
# 2017 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
# 2017-11-27 20 38
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2017-12-22 31 54
# 2017-12-29 21 8
# 2018 2018-01-03 98 76
# 2018-01-08 48 64
# 2018-01-19 18 48
# 2018-01-23 86 70
print(df_y.sum(level='year'))
# val_1 val_2
# year
# 2017 279 403
# 2018 250 258
季度:quarter
可以按日期时间索引的季度属性获得季度的数目。
df_q = df.set_index([df.index.quarter, df.index])
df_q.index.names = ['quarter', 'date']
print(df_q)
# val_1 val_2
# quarter date
# 4 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
# 2017-11-27 20 38
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2017-12-22 31 54
# 2017-12-29 21 8
# 1 2018-01-03 98 76
# 2018-01-08 48 64
# 2018-01-19 18 48
# 2018-01-23 86 70
print(df_q.sum(level='quarter'))
# val_1 val_2
# quarter
# 4 279 403
# 1 250 258
月:month, month_name()
可以使用DatetimeIndex属性month获得月份。
df_m = df.set_index([df.index.month, df.index])
df_m.index.names = ['month', 'date']
print(df_m)
# val_1 val_2
# month date
# 11 2017-11-01 65 76
# 2017-11-07 26 66
# 2017-11-18 47 47
# 2017-11-27 20 38
# 12 2017-12-05 65 85
# 2017-12-12 4 29
# 2017-12-22 31 54
# 2017-12-29 21 8
# 1 2018-01-03 98 76
# 2018-01-08 48 64
# 2018-01-19 18 48
# 2018-01-23 86 70
print(df_m.sum(level='month'))
# val_1 val_2
# month
# 11 158 227
# 12 121 176
# 1 250 258
还有month_name()返回月份的名称
print(df.index.month_name())
# Index(['November', 'November', 'November', 'November', 'December', 'December',
# 'December', 'December', 'January', 'January', 'January', 'January'],
# dtype='object', name='date')
周:week, weekofyear
可以使用DatetimeIndex的week或weekofyear属性获取星期数。
df_w2 = df.set_index([df.index.week, df.index])
df_w2.index.names = ['week', 'date']
print(df_w2)
# val_1 val_2
# week date
# 44 2017-11-01 65 76
# 45 2017-11-07 26 66
# 46 2017-11-18 47 47
# 48 2017-11-27 20 38
# 49 2017-12-05 65 85
# 50 2017-12-12 4 29
# 51 2017-12-22 31 54
# 52 2017-12-29 21 8
# 1 2018-01-03 98 76
# 2 2018-01-08 48 64
# 3 2018-01-19 18 48
# 4 2018-01-23 86 70
print(df_w2.sum(level='week'))
# val_1 val_2
# week
# 44 65 76
# 45 26 66
# 46 47 47
# 48 20 38
# 49 65 85
# 50 4 29
# 51 31 54
# 52 21 8
# 1 98 76
# 2 48 64
# 3 18 48
# 4 86 70
print(pd.date_range('2017-01-01', '2017-01-07').week)
# Int64Index([52, 1, 1, 1, 1, 1, 1], dtype='int64')
print(pd.date_range('2017-01-01', '2017-01-07').day_name())
# Index(['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
# 'Saturday'],
# dtype='object')
通过组合多个属性(星期几,月份,季度,年份等)进行处理
通过增加多索引中的列数,可以组合和处理多个属性(星期几,月份,季度,年份等)。
Resample()易于进行简单的年度和月度处理,但是使用包含多个属性的多索引进行处理很方便。
例如,按年份和星期几排序。
df_yw = df.set_index([df.index.year, df.index.weekday, df.index])
df_yw.index.names = ['year', 'weekday', 'date']
df_yw.sort_index(inplace=True)
print(df_yw)
# val_1 val_2
# year weekday date
# 2017 0 2017-11-27 20 38
# 1 2017-11-07 26 66
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2 2017-11-01 65 76
# 4 2017-12-22 31 54
# 2017-12-29 21 8
# 5 2017-11-18 47 47
# 2018 0 2018-01-08 48 64
# 1 2018-01-23 86 70
# 2 2018-01-03 98 76
# 4 2018-01-19 18 48
根据sum()或mean()的参数级别,可以汇总所有数据的星期几数据,也可以汇总每年的星期几数据。
print(df_yw.sum(level='weekday'))
# val_1 val_2
# weekday
# 0 68 102
# 1 181 250
# 2 163 152
# 4 70 110
# 5 47 47
print(df_yw.sum(level=['year', 'weekday']))
# val_1 val_2
# year weekday
# 2017 0 20 38
# 1 95 180
# 2 65 76
# 4 52 62
# 5 47 47
# 2018 0 48 64
# 1 86 70
# 2 98 76
# 4 18 48
在多索引的情况下,列选择和提取会有一些奇怪之处。有关详细信息,请参见以下文章。
这里有些例子。
print(df_yw.loc[(2017, 1), :])
# val_1 val_2
# date
# 2017-11-07 26 66
# 2017-12-05 65 85
# 2017-12-12 4 29
print(df_yw.xs(1, level='weekday'))
# val_1 val_2
# year date
# 2017 2017-11-07 26 66
# 2017-12-05 65 85
# 2017-12-12 4 29
# 2018 2018-01-23 86 70
print(df_yw.loc[(2017, [0, 4]), :])
# val_1 val_2
# year weekday date
# 2017 0 2017-11-27 20 38
# 4 2017-12-22 31 54
# 2017-12-29 21 8
print(df_yw.loc[pd.IndexSlice[:, [0, 4]], :])
# val_1 val_2
# year weekday date
# 2017 0 2017-11-27 20 38
# 4 2017-12-22 31 54
# 2017-12-29 21 8
# 2018 0 2018-01-08 48 64
# 4 2018-01-19 18 48
如果原始时间序列数据的范围很广,则可以方便地首先设置大量的多索引列,以便可以从各个角度汇总数据。
df_yqmw = df.set_index([df.index.year, df.index.quarter, df.index.month, df.index.weekday, df.index])
df_yqmw.index.names = ['year', 'quarter', 'month', 'weekday', 'date']
df_yqmw.sort_index(inplace=True)
print(df_yqmw)
# val_1 val_2
# year quarter month weekday date
# 2017 4 11 0 2017-11-27 20 38
# 1 2017-11-07 26 66
# 2 2017-11-01 65 76
# 5 2017-11-18 47 47
# 12 1 2017-12-05 65 85
# 2017-12-12 4 29
# 4 2017-12-22 31 54
# 2017-12-29 21 8
# 2018 1 1 0 2018-01-08 48 64
# 1 2018-01-23 86 70
# 2 2018-01-03 98 76
# 4 2018-01-19 18 48
print(df_yqmw.sum(level='month'))
# val_1 val_2
# month
# 11 158 227
# 12 121 176
# 1 250 258
可以轻松地按每年的一天分析趋势,而按季度的一周的一天分析趋势。
print(df_yqmw.sum(level='weekday'))
# val_1 val_2
# weekday
# 0 68 102
# 1 181 250
# 2 163 152
# 5 47 47
# 4 70 110
print(df_yqmw.sum(level=['quarter', 'weekday']))
# val_1 val_2
# quarter weekday
# 4 0 20 38
# 1 95 180
# 2 65 76
# 5 47 47
# 4 52 62
# 1 0 48 64
# 1 86 70
# 2 98 76
# 4 18 48
同样,由于在多索引情况下的列选择和提取有一些奇怪之处,请参阅以下文章以了解详细信息。
print(df_yqmw.xs(1, level='weekday'))
# val_1 val_2
# year quarter month date
# 2017 4 11 2017-11-07 26 66
# 12 2017-12-05 65 85
# 2017-12-12 4 29
# 2018 1 1 2018-01-23 86 70
print(df_yqmw.xs((1, 2017), level=('weekday', 'year')))
# val_1 val_2
# quarter month date
# 4 11 2017-11-07 26 66
# 12 2017-12-05 65 85
# 2017-12-12 4 29
print(df_yqmw.loc[pd.IndexSlice[2017, :, :, [0, 4]], :])
# val_1 val_2
# year quarter month weekday date
# 2017 4 11 0 2017-11-27 20 38
# 12 4 2017-12-22 31 54
# 2017-12-29 21 8