关于《关卡1:Pandas处理时间数据》学习心得分享

devtools/2025/1/17 6:45:37/

Image Name

碎碎念:由于我在时间序列分析方面的知识尚显薄弱,因此参加了和鲸举办的时间序列数据处理训练营,期望能够提升相关技能。同时,我借助了GPT来补充一些内容,希望这些分享能对各位读者有所帮助。欢迎大家一起学习交流!

关卡1:Pandas处理时间数据

欢迎来到时间序列数据处理训练营关卡1:Pandas处理时间数据。在本节中,我们将深入探讨如何使用Pandas库来处理和操作时间序列数据。
Pandas是一个强大的数据分析工具,特别适用于处理时间相关的数据。通过本关卡的学习,你将掌握如何创建时间戳、日期时间索引、时间段、时间差等,并了解如何进行时区处理和数据加载。这些技能对于时间序列分析、数据预处理和特征工程至关重要。

一、Pandas在时间处理方面的应用

Pandas功能

在时间处理时,Pandas能够生成固定频率的日期和时间跨度序列;将时间序列调整或转换为特定的频率;根据非标准的时间间隔计算"相对"日期(例如,年底前的5个工作日),或者向前或向后滚动日期。

Pandas时间相关的数据类型

Image Name

import pandas as pd
import numpy as np

二、Timestamp (时间戳)

#创建表示特定日期的时间戳
pd.Timestamp('2016-07-10')
Timestamp('2016-07-10 00:00:00')
#增加细节 使时间戳更细致
pd.Timestamp('2016-07-10 10')
Timestamp('2016-07-10 10:00:00')
#再加细节
pd.Timestamp('2016-07-10 10:15')
Timestamp('2016-07-10 10:15:00')

思考: 一共可以增加多少细节?

pd.Timestamp('2016-07-10 10:15:30.123456789')
Timestamp('2016-07-10 10:15:30.123456789')

Timestamp 可以精确到纳秒级别。

  • 年月日:2016-07-10
  • 时分秒:10:15:30
  • 毫秒:123
  • 微秒:456
  • 纳秒:789
# 时间戳有哪些属性?
# 提示: http://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html
t = pd.Timestamp('2016-07-10 10:15')

时间/日期组件

可以从时间戳(Timestamp)或时间戳集合(如DateTimeIndex)中访问的几个时间/日期属性。

Image Name

属性/方法描述
year日期时间的年份
month日期时间的月份
day日期时间的日
hour日期时间的小时
minute日期时间的分钟
second日期时间的秒
microsecond日期时间的微秒
date()返回datetime.date对象(日期部分)
time()返回datetime.time对象(时间部分)
dayofyear一年中的第几天(序数)
week一年中的第几周(序数)
weekday()一周中的第几天,周一为0,周日为6
strftime(‘%A’)星期几的名称(例如:星期五)
day_name()获取星期几的名称,pandas支持的替代方法(例如:星期五)。替代了weekday_name
quarter季度:1-3月=1,4-6月=2,7-9月=3,10-12月=4。pandas中的Timestamp对象支持该属性
days_in_month返回当月的天数,pandas中的Timestamp对象支持该属性
is_month_start逻辑值,指示是否为月初(由频率定义),pandas中的Timestamp对象支持该属性
is_month_end逻辑值,指示是否为月末(由频率定义),pandas中的Timestamp对象支持该属性
is_quarter_start逻辑值,指示是否为季度开始(由频率定义),pandas中的Timestamp对象支持该属性
is_quarter_end逻辑值,指示是否为季度结束(由频率定义),pandas中的Timestamp对象支持该属性
is_year_start逻辑值,指示是否为年初(由频率定义),pandas中的Timestamp对象支持该属性
is_year_end逻辑值,指示是否为年末(由频率定义),pandas中的Timestamp对象支持该属性

已被替代或移除的方法

被移除/替代方法新方法描述
weekday_nameday_name()weekday_name方法在较新版本的pandas中被移除,使用day_name()方法替代。
weekofyearweekweekofyear方法在pandas旧版本中有效,但在新版中已被week替代,用于返回年内的第几周。

print(t.weekday_name)
Image Name
错误是因为pandas中的Timestamp对象在较新版本的pandas中不再支持weekday_name属性。weekday_name曾经存在于pandas的早期版本,但现在被移除或替代了其他方法。

print(t.day_name())
print('---')
print(t.second)
print('---')
print(t.dayofyear)
Sunday
---
0
---
192

上面只列举了一部分函数,我这里全部举个例子,方便学习理解。

ts = pd.Timestamp('2023-03-31 23:59:59')print(f"日期时间: {ts}\n")# 基本时间组件
print("基本时间组件:")
print(f"年份: {ts.year}")
print(f"月份: {ts.month}")
print(f"日: {ts.day}")
print(f"小时: {ts.hour}")
print(f"分钟: {ts.minute}")
print(f"秒: {ts.second}")
print(f"微秒: {ts.microsecond}")
print(f"纳秒: {ts.nanosecond}")# 日期和时间获取
print("\n日期和时间获取:")
print(f"日期: {ts.date()}")
print(f"时间: {ts.time()}")# 日期在年/周中的位置
print("\n日期在年/周中的位置:")
print(f"一年中的第几天: {ts.dayofyear}")
print(f"一年中的第几周: {ts.week}")  # 或 ts.weekofyear
print(f"一周中的第几天(0=周一,6=周日): {ts.dayofweek}")  # 或 ts.weekday
print(f"星期几: {ts.day_name()}")# 其他时间相关属性
print("\n其他时间相关属性:")
print(f"季度: {ts.quarter}")
print(f"当月的天数: {ts.days_in_month}")# 逻辑判断属性
print("\n逻辑判断属性:")# 月初/月末
ts_month_start = pd.Timestamp('2023-03-01')
ts_month_end = pd.Timestamp('2023-03-31')
print("月初/月末:")
print(f"2023-03-01 是否为月初: {ts_month_start.is_month_start}")
print(f"2023-03-31 是否为月末: {ts_month_end.is_month_end}")# 季度开始/结束
ts_quarter_start = pd.Timestamp('2023-04-01')
ts_quarter_end = pd.Timestamp('2023-06-30')
print("\n季度开始/结束:")
print(f"2023-04-01 是否为季度开始: {ts_quarter_start.is_quarter_start}")
print(f"2023-06-30 是否为季度结束: {ts_quarter_end.is_quarter_end}")# 年初/年末
ts_year_start = pd.Timestamp('2023-01-01')
ts_year_end = pd.Timestamp('2023-12-30')
print("\n年初/年末:")
print(f"2023-01-01 是否为年初: {ts_year_start.is_year_start}")
print(f"2023-12-30 是否为年末: {ts_year_end.is_year_end}")# 原始时间戳的逻辑判断
print("\n原始时间戳 (2023-03-31 23:59:59) 的逻辑判断:")
print(f"是否为月初: {ts.is_month_start}")
print(f"是否为月末: {ts.is_month_end}")
print(f"是否为季度开始: {ts.is_quarter_start}")
print(f"是否为季度结束: {ts.is_quarter_end}")
print(f"是否为年初: {ts.is_year_start}")
print(f"是否为年末: {ts.is_year_end}")
日期时间: 2023-03-31 23:59:59基本时间组件:
年份: 2023
月份: 3
日: 31
小时: 23
分钟: 59
秒: 59
微秒: 0
纳秒: 0日期和时间获取:
日期: 2023-03-31
时间: 23:59:59日期在年/周中的位置:
一年中的第几天: 90
一年中的第几周: 13
一周中的第几天(0=周一,6=周日): 4
星期几: Friday其他时间相关属性:
季度: 1
当月的天数: 31逻辑判断属性:
月初/月末:
2023-03-01 是否为月初: True
2023-03-31 是否为月末: True季度开始/结束:
2023-04-01 是否为季度开始: True
2023-06-30 是否为季度结束: True年初/年末:
2023-01-01 是否为年初: True
2023-12-30 是否为年末: False原始时间戳 (2023-03-31 23:59:59) 的逻辑判断:
是否为月初: False
是否为月末: True
是否为季度开始: False
是否为季度结束: True
是否为年初: False
是否为年末: False

三、DatetimeIndex(日期时间编码器)

日期时间索引的主要用途之一是作为Pandas对象的索引
日期时间索引类包含许多与时间序列相关的优化:
· 在底层预先计算并缓存了各种偏移量的大范围日期,以便快速生成后续的日期范围(只需获取一个切片)。
· 可以使用 shift 和 tshift 方法来对数据进行快速的移动或偏移操作。
· 对于具有相同频率的重叠的 DatetimeIndex 对象进行合并是非常快速的(这对于快速的数据对齐非常重要)。
· 可以通过年份、月份等属性快速访问日期字段。
· 包含正则化函数(snap,asof etc.)。

偏移别名

在 Pandas 中一些常见的时间序列频率被赋予了一些字符串别名,这些别名被称为偏移别名(在 v0.8.0 之前被称为时间规则)。

Image Name

别名描述
B工作日频率
C自定义工作日频率(实验性)
D日历日频率
W每周频率
M月末频率
BM工作日月末频率
CBM自定义工作日月末频率
MS月初频率
BMS工作日月初频率
CBMS自定义工作日月初频率
Q季度末频率
BQ工作日季度末频率
QS季度初频率
BQS工作日季度初频率
A年末频率
BA工作日年末频率
AS年初频率
BAS工作日年初频率
BH工作时间频率
H每小时频率
T, min每分钟频率
S每秒频率
L, ms毫秒
U, us微秒
N纳秒
#创建日期时间索引 rng,起始日期为'2016年7月1日',并且频率为每日一次('D'),共包含10个日期时间点
rng = pd.date_range('2016 Jul 1',periods = 10, freq = 'D')
rng
DatetimeIndex(['2016-07-01', '2016-07-02', '2016-07-03', '2016-07-04','2016-07-05', '2016-07-06', '2016-07-07', '2016-07-08','2016-07-09', '2016-07-10'],dtype='datetime64[ns]', freq='D')

思考:

  1. 以下哪种格式与其他的格式含义不同?
    ‘2016 Jul 1’ ‘7/1/2016’ ‘1/7/2016’ ‘July 1, 2016’ ‘2016-07-01’ ‘2016/07/01’

  2. 7/1/2016 是一月还是七月?

  3. date_range中保存的单个对象的类别是什么?

  4. 在列出的格式中,‘1/7/2016’ 的含义可能与其他格式不同。这是因为:

    • ‘2016 Jul 1’、‘July 1, 2016’、‘2016-07-01’、‘2016/07/01’ 都明确表示 2016 年 7 月 1 日。
    • ‘7/1/2016’ 在美国日期格式中表示 2016 年 7 月 1 日。
    • 但 ‘1/7/2016’ 可能有歧义:
      • 在美国格式中,它表示 2016 年 1 月 7 日。
      • 在许多其他国家(如英国、澳大利亚等),它表示 2016 年 7 月 1 日。
  5. ‘7/1/2016’ 的解释依赖于使用的日期格式约定:

    • 在美国格式(MM/DD/YYYY)中,它表示 2016 年 7 月 1 日。
    • 在其他许多国家使用的格式(DD/MM/YYYY)中,它会表示 2016 年 1 月 7 日。
    • 在 Pandas 中,默认情况下它通常被解释为 7 月 1 日,因为 Pandas 倾向于使用美国日期格式。但这可以通过设置来改变。
  6. 在 Pandas 的 date_range 函数中,生成的每个单个对象的类别是 Timestamp,例子如下代码。

# 创建一个 date_range
date_range = pd.date_range(start='2023-01-01', end='2023-01-05')
print(date_range)# 检查第一个元素的类型
print(type(date_range[0]))
DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04','2023-01-05'],dtype='datetime64[ns]', freq='D')
<class 'pandas._libs.tslibs.timestamps.Timestamp'>

日期偏移对象

在Pandas中,通过传递例如’M’、'W’和’BM’等频率字符串到freq关键字参数,我们创建了不同频率的DatetimeIndex对象。这些频率字符串在底层被转换为 pandas 的 DateOffset 实例,它代表了一种常规的频率增量。特定的偏移逻辑,如"月"、“工作日"或"一小时”,则在它的各种子类中表示。

Image Name

类名描述
DateOffset通用偏移对象,默认值是 1 个日历日,可以用来创建自定义的日期偏移量。
BDay工作日(不包括周末)。例如增加或减少若干个工作日。
CDay自定义工作日(实验性功能),支持根据特定规则自定义哪些天是工作日。
Week一周的偏移量,可以选择锚定在一周的某一天。例如,每周的周三。
WeekOfMonth每月第 y 周的第 x 天,例如每月的第二个周五(非常灵活)。
LastWeekOfMonth每月的最后一周的第 x 天,例如每月最后一周的周二。
MonthEnd日历月的月末,例如 1 月的 31 日、2 月的 28/29 日。
MonthBegin日历月的月初,例如 1 月的 1 日、2 月的 1 日。
BMonthEnd工作月的月末,即月中最后一个工作日。例如,2025 年 1 月的月末为 1 月 31 日,但如果这一天是周六,那么 BMonthEnd 会指向 1 月 30 日(周五)。
BMonthBegin工作月的月初,即月中第一个工作日。例如,2025 年 1 月的月初是 1 月 1 日,但如果这一天是周六,那么 BMonthBegin 会指向 1 月 2 日(周一)。
CBMonthEnd自定义商业月的月末,用于特定的商业规则。例如,你可以定义某些日子为商业工作日,然后用它来定位商业月的月末。
CBMonthBegin自定义商业月的月初,用于特定的商业规则。例如,你可以定义某些日子为商业工作日,然后用它来定位商业月的月初。
QuarterEnd日历季度的季度末,例如第一季度的 3 月 31 日,第二季度的 6 月 30 日。
QuarterBegin日历季度的季度初,例如第一季度的 1 月 1 日,第二季度的 4 月 1 日。
BQuarterEnd工作季度的季度末,即季度中最后一个工作日。例如,2025 年第一季度的季度末是 3 月 31 日,但如果这一天是周六,则指向 3 月 28 日(周五)。
BQuarterBegin工作季度的季度初,即季度中第一个工作日。例如,2025 年第二季度的季度初是 4 月 1 日,但如果这一天是周六,则指向 4 月 3 日(周一)。
FY5253Quarter零售季度,用于基于 52 周或 53 周的财务会计周期(广泛用于零售业)。
YearEnd日历年的年末,例如 2025 年的 12 月 31 日。
YearBegin日历年的年初,例如 2025 年的 1 月 1 日。
BYearEnd工作年的年末,即最后一个工作日。例如,2025 年的年末是 12 月 31 日,但如果这一天是周六,则指向 12 月 29 日(周五)。
BYearBegin工作年的年初,即第一个工作日。例如,2025 年的年初是 1 月 1 日,但如果这一天是周日,则指向 1 月 2 日(周一)。
FY5253零售年,用于基于 52 周或 53 周的财务会计周期,类似于 FY5253Quarter,但对应整个财年。
BusinessHour工作小时,例如增加或减少若干个标准工作小时(默认为上午 9 点到下午 5 点的时间段)。
CustomBusinessHour自定义的工作小时,可以设置工作时间的起止范围。例如上午 8 点到下午 6 点,适合非标准工作时间的场景。
Hour一小时的偏移,例如在原始时间上加减若干小时。
Minute一分钟的偏移,例如在原始时间上加减若干分钟。
Second一秒的偏移,例如在原始时间上加减若干秒。
Milli一毫秒的偏移,例如在原始时间上加减若干毫秒。
Micro一微秒的偏移,例如在原始时间上加减若干微秒。
Nano一纳秒的偏移,例如在原始时间上加减若干纳秒。
#DateOffset 示例,以下所示了夏令时的情况:
# 生成一个指定的时间,芬兰赫尔辛基时间执行夏令时
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.DateOffset(days=1)
# Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')
Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')

夏令时(DST, Daylight Saving Time)是一种人为调整时间的制度,为了更好地利用日照资源,在夏季时将时钟向前拨快 1 小时,从而使人们可以在下午拥有更多的自然光线时间。这种调整通常在春季开始(时钟向前拨 1 小时),在秋季结束(时钟向后拨回 1 小时),我国从1992年起,不使用夏时令,而统一使用 北京时间(UTC+8) 作为标准时间,所以这里我打算使用更加适合我国的时间来学习偏移,我相信这样也会便于读者理解。

# 设置时间戳,使用北京时间
ts = pd.Timestamp('2023-03-31 23:59:59', tz='Asia/Shanghai') # 因为 pandas 本身没有Beijing,但是和上海是同一时区,因此用上海来表示。
print(f"原始时间戳: {ts}\n")# 偏移操作示例
print("DateOffset 偏移操作:")# 偏移 10 天
offset_days = pd.DateOffset(days=10)
print(f"偏移 10 天: {ts + offset_days}")# 偏移 5 个工作日
offset_bdays = pd.offsets.BDay(5)
print(f"偏移 5 个工作日: {ts + offset_bdays}")# 偏移 1 周
offset_week = pd.offsets.Week(1)
print(f"偏移 1 周: {ts + offset_week}")# 偏移到最近的月末
offset_month_end = pd.offsets.MonthEnd()
print(f"偏移到最近的月末: {ts + offset_month_end}")# 偏移到最近的季度末
offset_quarter_end = pd.offsets.QuarterEnd()
print(f"偏移到最近的季度末: {ts + offset_quarter_end}")# 偏移到最近的年末
offset_year_end = pd.offsets.YearEnd()
print(f"偏移到最近的年末: {ts + offset_year_end}")# 偏移 5 个工作小时
offset_bhour = pd.offsets.BusinessHour(5)
print(f"偏移 5 个工作小时: {ts + offset_bhour}")# 偏移 1 小时、30 分钟、10 秒
offset_hour = pd.offsets.Hour(1)
offset_minute = pd.offsets.Minute(30)
offset_second = pd.offsets.Second(10)
print(f"偏移 1 小时: {ts + offset_hour}")
print(f"偏移 30 分钟: {ts + offset_minute}")
print(f"偏移 10 秒: {ts + offset_second}")# 偏移 5 毫秒、10 微秒、1 纳秒
offset_milli = pd.offsets.Milli(5)
offset_micro = pd.offsets.Micro(10)
offset_nano = pd.offsets.Nano(1)
print(f"偏移 5 毫秒: {ts + offset_milli}")
print(f"偏移 10 微秒: {ts + offset_micro}")
print(f"偏移 1 纳秒: {ts + offset_nano}")
原始时间戳: 2023-03-31 23:59:59+08:00DateOffset 偏移操作:
偏移 10 天: 2023-04-10 23:59:59+08:00
偏移 5 个工作日: 2023-04-07 23:59:59+08:00
偏移 1 周: 2023-04-07 23:59:59+08:00
偏移到最近的月末: 2023-04-30 23:59:59+08:00
偏移到最近的季度末: 2023-06-30 23:59:59+08:00
偏移到最近的年末: 2023-12-31 23:59:59+08:00
偏移 5 个工作小时: 2023-04-03 14:00:00+08:00
偏移 1 小时: 2023-04-01 00:59:59+08:00
偏移 30 分钟: 2023-04-01 00:29:59+08:00
偏移 10 秒: 2023-04-01 00:00:09+08:00
偏移 5 毫秒: 2023-03-31 23:59:59.005000+08:00
偏移 10 微秒: 2023-03-31 23:59:59.000010+08:00
偏移 1 纳秒: 2023-03-31 23:59:59.000000001+08:00

锚定后缀

在 Pandas 中,除了常见的时间序列频率别名(如’D’表示每日频率、'W’表示每周频率等),还可以在某些频率后面添加一个特定的后缀,以指定时间序列中数据点的锚定点或起始点。

Image Name

别名描述
W-SUN每周的频率,锚定在周日(和 ‘W’ 别名相同,默认锚定周日)
W-MON每周的频率,锚定在周一
W-TUE每周的频率,锚定在周二
W-WED每周的频率,锚定在周三
W-THU每周的频率,锚定在周四
W-FRI每周的频率,锚定在周五
W-SAT每周的频率,锚定在周六
QE-DEC季度频率,年度结束于 12 月(和 ‘Q’ 别名相同,默认锚定 12 月)
QE-JAN季度频率,年度结束于 1 月
QE-FEB季度频率,年度结束于 2 月
QE-MAR季度频率,年度结束于 3 月
BQE-DEC季度频率,仅包含工作日,年度结束于 12 月
BQE-JAN季度频率,仅包含工作日,年度结束于 1 月
BQE-FEB季度频率,仅包含工作日,年度结束于 2 月
BQE-MAR季度频率,仅包含工作日,年度结束于 3 月
BQS-DEC季度频率,仅包含工作日,起始日期为工作日,年度结束于 12 月
BQS-JAN季度频率,仅包含工作日,起始日期为工作日,年度结束于 1 月
BQS-FEB季度频率,仅包含工作日,起始日期为工作日,年度结束于 2 月
BQS-MAR季度频率,仅包含工作日,起始日期为工作日,年度结束于 3 月

时间序列分析中,“锚定”是指将时间间隔(如每周、每月或每季度)对齐到特定的时间点(如一周的某一天、一个月的某一天或一个季度的某个结束月份)。这种操作的目的在于确保生成的时间序列具有一致性和规律性。

#当n不为0时,如果给定日期不在锚点上,则它会捕捉到下一个(上一个)锚点,并向前或向后移动 |n|-1 步。
pd.Timestamp('2014-01-02') + pd.offsets.MonthBegin(n=1)
# Timestamp('2014-02-01 00:00:00')
Timestamp('2014-02-01 00:00:00')

解释:什么叫“给定日期不在锚点上”?
在使用 pandas 中的偏移对象(如 MonthBeginMonthEnd 等)时,锚点是指偏移对象的目标时间点。例如:

  • 对于 MonthBegin,锚点是每月的月初
  • 对于 MonthEnd,锚点是每月的月末
  • 对于 W-MON,锚点是每周的周一

“给定日期不在锚点上”,意思是当前的时间戳不在偏移对象的目标位置,例如:

  • 当前日期是 2014-01-02,而偏移对象是 MonthBegin,目标锚点是 2014 年 1 月的月初(即 2014-01-01),那么当前日期 2014-01-02 不在目标锚点上。
#如果给定的日期在锚点上,则将其| n |移动。 指向前进或后退:
pd.Timestamp('2014-01-01') + pd.offsets.MonthBegin(n=1)
# Timestamp('2014-02-01 00:00:00')
Timestamp('2014-02-01 00:00:00')
#对于n = 0的情况,如果在锚点上,则日期不会移动,否则它将前滚到下一个锚点。
pd.Timestamp('2014-01-02') + pd.offsets.MonthBegin(n=0)
# Timestamp('2014-02-01 00:00:00')
Timestamp('2014-02-01 00:00:00')
# 按每周周一生成时间范围
date_range_mon = pd.date_range(start='2023-01-01', periods=5, freq='W-MON')
print("每周锚定在周一:\n", date_range_mon)# 按每周周五生成时间范围
date_range_fri = pd.date_range(start='2023-01-01', periods=5, freq='W-FRI')
print("\n每周锚定在周五:\n", date_range_fri)
每周锚定在周一:DatetimeIndex(['2023-01-02', '2023-01-09', '2023-01-16', '2023-01-23','2023-01-30'],dtype='datetime64[ns]', freq='W-MON')每周锚定在周五:DatetimeIndex(['2023-01-06', '2023-01-13', '2023-01-20', '2023-01-27','2023-02-03'],dtype='datetime64[ns]', freq='W-FRI')
# 按季度结束于 12 月生成时间范围
date_range_q_dec = pd.date_range(start='2024-01-01', periods=4, freq='QE-DEC')
print("季度结束于 12 月:\n", date_range_q_dec)# 按季度结束于 3 月生成时间范围
date_range_q_mar = pd.date_range(start='2024-01-01', periods=4, freq='QE-MAR')
print("\n季度结束于 3 月:\n", date_range_q_mar)
季度结束于 12 月:DatetimeIndex(['2024-03-31', '2024-06-30', '2024-09-30', '2024-12-31'], dtype='datetime64[ns]', freq='QE-DEC')季度结束于 3 月:DatetimeIndex(['2024-03-31', '2024-06-30', '2024-09-30', '2024-12-31'], dtype='datetime64[ns]', freq='QE-MAR')
# 按季度结束于 12 月(工作日)生成时间范围
date_range_bq_dec = pd.date_range(start='2024-01-01', periods=4, freq='BQE-DEC')
print("季度结束于 12 月(仅工作日):\n", date_range_bq_dec)# 按季度结束于 3 月(工作日)生成时间范围
date_range_bq_mar = pd.date_range(start='2024-01-01', periods=4, freq='BQE-MAR')
print("\n季度结束于 3 月(仅工作日):\n", date_range_bq_mar)
季度结束于 12 月(仅工作日):DatetimeIndex(['2024-03-29', '2024-06-28', '2024-09-30', '2024-12-31'], dtype='datetime64[ns]', freq='BQE-DEC')季度结束于 3 月(仅工作日):DatetimeIndex(['2024-03-29', '2024-06-28', '2024-09-30', '2024-12-31'], dtype='datetime64[ns]', freq='BQE-MAR')
  1. 每周频率的锚定: 使用 W- 后缀可以指定每周的具体锚定日,例如 W-MON 锚定到周一,W-FRI 锚定到周五。

  2. 季度频率的锚定: 使用 (B)Q(S)- 后缀,可以指定季度的结束月份。例如 QE-DEC 是年度结束于 12 月的季度频率,QE-MAR 是年度结束于 3 月的季度频率。

  3. 工作日频率: 使用 B 表示仅包括工作日,例如 BQE-DEC 表示年度结束于 12 月,且只包含工作日。

节假日和节假日日历

在 Pandas 中,可以通过创建节假日日历类来定义一组特定的节假日规则。AbstractHolidayCalendar 类提供了必要的方法来返回节假日列表,而具体的规则则需要在特定的节假日日历类中定义。此外,start_date 和 end_date 类属性用于确定生成节假日的日期范围。应该在 AbstractHolidayCalendar 类上进行覆盖,以使这个范围适用于所有日历子类。

在 Pandas 中,已经预先定义了一些节假日日历,其中最常用的是 usFederalHolidayCalendar。它主要作为开发其他日历的示例。
对于那些在固定日期发生的节假日(例如美国的纪念日或7月4日),如果节假日恰逢周末或其他非工作日,则会根据一个观察规则来确定其观察日期。已定义的观察规则如下:

Image Name

规则描述
nearest_workday将周六的节假日调整为前一个周五,将周日的节假日调整为后一个周一
sunday_to_monday将周日的节假日调整为后一个周一
next_monday_or_tuesday将周六调整为下一个周一,周日或周一调整为下一个周二
previous_friday将周六和周日的节假日都调整为前一个周五
next_monday将周六和周日的节假日都调整为下一个周一

解释:将周六的节假日调整为前一个周五,将周日的节假日调整为后一个周一
这个规则主要出现在时间序列管理和节假日日历的定义中,目的是为了确保节假日总是落在工作日(通常是周一至周五)内,避免因为节假日落在周末(周六或周日)而影响到业务场景或分析计算。
规则描述

  1. 周六的节假日调整到前一个周五

    • 如果一个节假日原本是周六,就会将这个节假日的日期调整到它前面的最近一个工作日——也就是周五。
  2. 周日的节假日调整到后一个周一

    • 如果一个节假日原本是周日,就会将这个节假日的日期调整到它后面的最近一个工作日——也就是周一。

为什么要做这种调整?

在实际应用中,很多场景(如金融市场、公司考勤)只考虑工作日。如果一个节假日落在周末,通常需要将它调整到工作日,以确保数据处理或分析时不会漏掉重要的时间点。

例如:

  • 在美国,如果独立日(7 月 4 日)是周六,会提前到周五(7 月 3 日)放假。
  • 如果圣诞节(12 月 25 日)是周日,会延后到周一(12 月 26 日)放假。

pandas.tseries.holiday 中,nearest_workdayusFederalHolidayCalendar 的默认观察规则。即,当定义节假日日历时,默认规则会按照以下逻辑运行:

  • 周六的节假日 → 调整到前一个周五
  • 周日的节假日 → 调整到后一个周一

也可以通过修改 observance 参数,自定义节假日的调整规则。

from pandas.tseries.holiday import USFederalHolidayCalendar, nearest_workday# 创建美国联邦假日日历实例
calendar = USFederalHolidayCalendar()# 获取 2024 年的所有节假日
holidays = calendar.holidays(start='2024-01-01', end='2024-12-31')
print("2024 年美国联邦假日:")
print(holidays)
2024 年美国联邦假日:
DatetimeIndex(['2024-01-01', '2024-01-15', '2024-02-19', '2024-05-27','2024-06-19', '2024-07-04', '2024-09-02', '2024-10-14','2024-11-11', '2024-11-28', '2024-12-25'],dtype='datetime64[ns]', freq=None)

除了默认的 nearest_workday 规则,还可以指定其他观察规则,如 sunday_to_monday。以下示例展示如何创建一个自定义的节假日日历,并调整观察规则。

#生成节假日
from pandas.tseries.holiday import AbstractHolidayCalendar, Holiday
class ChinaHolidaysCalendar(AbstractHolidayCalendar):rules = [Holiday('元旦', month=1, day=1),Holiday('元旦', month=1, day=2),Holiday('元旦', month=1, day=3),Holiday('春节', month=1, day=21),Holiday('春节', month=1, day=22),# 同样的方法添加其他节假日]start_date = '2023-01-01'
end_date = '2023-12-31'china_holidays_calendar = ChinaHolidaysCalendar()
holidays = china_holidays_calendar.holidays(start_date, end_date)
print(holidays)
DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-21','2023-01-22'],dtype='datetime64[ns]', freq=None)

知识扩充——holidays库

holidays 库支持世界大多数国家的节假日,并且可以轻松扩展自定义节假日。

!pip install holidays -i https://pypi.tuna.tsinghua.edu.cn/simple/
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/
Collecting holidaysDownloading https://pypi.tuna.tsinghua.edu.cn/packages/90/9c/5235772fc9d2399f41401e6a054a26b4a993bd8a38e4ff849a6097a912a9/holidays-0.63-py3-none-any.whl (1.2 MB)
[2K     [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m1.2/1.2 MB[0m [31m4.1 MB/s[0m eta [36m0:00:00[0m00:01[0m00:01[0m
[?25hRequirement already satisfied: python-dateutil in /opt/conda/lib/python3.11/site-packages (from holidays) (2.9.0)
Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.11/site-packages (from python-dateutil->holidays) (1.16.0)
Installing collected packages: holidays
Successfully installed holidays-0.63
import holidays# 定义中国的节假日
china_holidays = holidays.China(years=2024)
print("2024 年的节假日:")
for date, name in sorted(china_holidays.items()):print(f"{date}: {name}")
2024 年的节假日:
2024-01-01: New Year's Day
2024-02-10: Chinese New Year (Spring Festival)
2024-02-11: Chinese New Year (Spring Festival)
2024-02-12: Chinese New Year (Spring Festival)
2024-02-13: Chinese New Year (Spring Festival) (observed)
2024-02-14: Chinese New Year (Spring Festival) (observed)
2024-02-15: Day off (substituted from 02/04/2024)
2024-02-16: Day off (substituted from 02/18/2024)
2024-04-04: Tomb-Sweeping Day
2024-04-05: Day off (substituted from 04/07/2024)
2024-05-01: Labor Day
2024-05-02: Day off (substituted from 04/28/2024)
2024-05-03: Day off (substituted from 05/11/2024)
2024-06-10: Dragon Boat Festival
2024-09-16: Day off (substituted from 09/14/2024)
2024-09-17: Mid-Autumn Festival
2024-10-01: National Day
2024-10-02: National Day
2024-10-03: National Day
2024-10-04: Day off (substituted from 09/29/2024)
2024-10-07: Day off (substituted from 10/12/2024)
# 判断某日期是否为节假日
date = '2024-10-04'
is_holiday = date in china_holidays
print(f"{date} 是否为节假日: {is_holiday}")
2024-10-04 是否为节假日: True
# 添加自定义节假日
custom_holidays = holidays.China(years=2024)
custom_holidays.append({'2024-04-13': 'Water Splashing Festival'})
print("添加后的节假日:")
for date, name in sorted(custom_holidays.items()):print(f"{date}: {name}")
添加后的节假日:
2024-01-01: New Year's Day
2024-02-10: Chinese New Year (Spring Festival)
2024-02-11: Chinese New Year (Spring Festival)
2024-02-12: Chinese New Year (Spring Festival)
2024-02-13: Chinese New Year (Spring Festival) (observed)
2024-02-14: Chinese New Year (Spring Festival) (observed)
2024-02-15: Day off (substituted from 02/04/2024)
2024-02-16: Day off (substituted from 02/18/2024)
2024-04-04: Tomb-Sweeping Day
2024-04-05: Day off (substituted from 04/07/2024)
2024-04-13: Water Splashing Festival
2024-05-01: Labor Day
2024-05-02: Day off (substituted from 04/28/2024)
2024-05-03: Day off (substituted from 05/11/2024)
2024-06-10: Dragon Boat Festival
2024-09-16: Day off (substituted from 09/14/2024)
2024-09-17: Mid-Autumn Festival
2024-10-01: National Day
2024-10-02: National Day
2024-10-03: National Day
2024-10-04: Day off (substituted from 09/29/2024)
2024-10-07: Day off (substituted from 10/12/2024)

翻译后:
2024-01-01: 元旦
2024-02-10: 春节
2024-02-11: 春节
2024-02-12: 春节
2024-02-13: 春节(补假)
2024-02-14: 春节(补假)
2024-02-15: 调休日(调休自 2024-02-04)
2024-02-16: 调休日(调休自 2024-02-18)
2024-04-04: 清明节
2024-04-05: 调休日(调休自 2024-04-07)
2024-04-13: 泼水节
2024-05-01: 劳动节
2024-05-02: 调休日(调休自 2024-04-28)
2024-05-03: 调休日(调休自 2024-05-11)
2024-06-10: 端午节
2024-09-16: 调休日(调休自 2024-09-14)
2024-09-17: 中秋节
2024-10-01: 国庆节
2024-10-02: 国庆节
2024-10-03: 国庆节
2024-10-04: 调休日(调休自 2024-09-29)
2024-10-07: 调休日(调休自 2024-10-12)

四、period(周期)

pandas.Periodpandas 中用于表示**时间段(period)**的一个对象,它可以精确表示时间的某个区间(如年、季度、月、日等)。Period 不仅仅表示时间点(像 Timestamp 那样),还可以表示一个时间区间,如 “2023 年第一季度” 或 “2024 年 5 月”。

#创建表示特定月份的时间段
pd.Period('2016-01')
Period('2016-01', 'M')

思考:
上面的额外信息是什么?它是如何设置的?

#创建表示特定日期的时间段
pd.Period('2016-01-01')
Period('2016-01-01', 'D')
#增加细节 使时间段更细致
pd.Period('2016-01-01 10')
Period('2016-01-01 10:00', 'h')
#增加细节 使时间段更细致
pd.Period('2016-01-01 10:10')
Period('2016-01-01 10:10', 'min')
#再加细节
pd.Period('2016-01-01 10:10:10')
Period('2016-01-01 10:10:10', 's')

思考:

  1. 你能得到的最详细的时间段是什么?
  2. 如何制作多个时间段? 提示:寻找与上面 pd.date_range() 的类比

timedelta

#创建表示时间差的 Timedelta 对象,表示一天的时间跨度
pd.Timedelta('1 day')
Timedelta('1 days 00:00:00')
#将时间段和时间差相加,得到新的时间段
pd.Period('2016-01-01 10:10') + pd.Timedelta('1 day')
Period('2016-01-02 10:10', 'min')
#将时间戳和时间差相加,得到新的时间戳
pd.Timestamp('2016-01-01 10:10') + pd.Timedelta('1 day')
Timestamp('2016-01-02 10:10:00')
pd.Timestamp('2016-01-01 10:10') + pd.Timedelta('15 ns')
Timestamp('2016-01-01 10:10:00.000000015')

五、PeriodIndex(周期索引)

#花式频率设置  
#在指定的时间范围内生成一系列时间段  
#只要工作日  
pd.period_range('2016-01-01 10:10',freq = 'B',periods = 10)  

该方法,官方会提示警告:

/tmp/ipykernel_51/4134041981.py:4: FutureWarning: Period with BDay freq is deprecated and will be removed in a future version. Use a DatetimeIndex with BDay freq instead.  pd.period_range('2016-01-01 10:10',freq = 'B',periods = 10)  
/tmp/ipykernel_51/4134041981.py:4: FutureWarning: PeriodDtype[B] is deprecated and will be removed in a future version. Use a DatetimeIndex with freq='B' instead  pd.period_range('2016-01-01 10:10',freq = 'B',periods = 10)  

因为 pandas 中的 Period 对象使用 B(工作日)频率已经被弃用(deprecated),并将在未来的版本中移除。官方建议改用 DatetimeIndex 配合 B 频率来生成工作日序列。

# 使用 DatetimeIndex 和工作日频率(B)
workday_index = pd.date_range('2016-01-01 10:10', freq='B', periods=10)
print(workday_index)
DatetimeIndex(['2016-01-01 10:10:00', '2016-01-04 10:10:00','2016-01-05 10:10:00', '2016-01-06 10:10:00','2016-01-07 10:10:00', '2016-01-08 10:10:00','2016-01-11 10:10:00', '2016-01-12 10:10:00','2016-01-13 10:10:00', '2016-01-14 10:10:00'],dtype='datetime64[ns]', freq='B')
# 可以组合频率。如果想每天提前 25 小时怎么办?有哪两种方法可以做到这一点?
p1 = pd.period_range('2016-01-01 10:10',freq = '25h',periods = 10)
p1
PeriodIndex(['2016-01-01 10:00', '2016-01-02 11:00', '2016-01-03 12:00','2016-01-04 13:00', '2016-01-05 14:00', '2016-01-06 15:00','2016-01-07 16:00', '2016-01-08 17:00', '2016-01-09 18:00','2016-01-10 19:00'],dtype='period[25h]')
p2 = pd.period_range('2016-01-01 10:10',freq = '1d1h',periods = 10)
p2
PeriodIndex(['2016-01-01 10:00', '2016-01-02 11:00', '2016-01-03 12:00','2016-01-04 13:00', '2016-01-05 14:00', '2016-01-06 15:00','2016-01-07 16:00', '2016-01-08 17:00', '2016-01-09 18:00','2016-01-10 19:00'],dtype='period[25h]')
# 使用 TIME 对象编制索引
# 创建包含 10 个日期的时间索引,然后创建以这些日期为索引、以对应位置的整数为值的序列
rng = pd.date_range('2016 Jul 1',periods = 10,freq = 'D')
rng
pd.Series(range(len(rng)),index = rng)
2016-07-01    0
2016-07-02    1
2016-07-03    2
2016-07-04    3
2016-07-05    4
2016-07-06    5
2016-07-07    6
2016-07-08    7
2016-07-09    8
2016-07-10    9
Freq: D, dtype: int64
# 也可以使用时间段索引
# 将索引视为一个时间跨度,而不是单个时间点
periods = [pd.Period('2016-01'),pd.Period('2016-02'),pd.Period('2016-03')]
ts = pd.Series(np.random.randn(len(periods)),index = periods)
ts
2016-01    0.448995
2016-02    0.798802
2016-03    1.033849
Freq: M, dtype: float64
# ts的索引类型
type(ts.index)
pandas.core.indexes.period.PeriodIndex

思考:
尝试各种索引 提示:ts[‘2016’] 有效吗?

# 提取 2016 年的所有时间段
print("ts['2016']:")
print(ts['2016'])
ts['2016']:
2016-01    0.448995
2016-02    0.798802
2016-03    1.033849
Freq: M, dtype: float64

ts[‘2016’] 有效,因为 PeriodIndex 支持基于较大的时间单位(如年份)的切片操作。这个特性使得你可以通过年份提取对应的时间段。

时间戳数据与周期索引的转换

# 时间戳数据可以转换为具有to_period的周期索引,反之亦然
ts = pd.Series(range(10),pd.date_range('07-10-16 8:00',periods = 10,freq = 'h'))
ts
2016-07-10 08:00:00    0
2016-07-10 09:00:00    1
2016-07-10 10:00:00    2
2016-07-10 11:00:00    3
2016-07-10 12:00:00    4
2016-07-10 13:00:00    5
2016-07-10 14:00:00    6
2016-07-10 15:00:00    7
2016-07-10 16:00:00    8
2016-07-10 17:00:00    9
Freq: h, dtype: int64
ts_period = ts.to_period()
ts_period
2016-07-10 08:00    0
2016-07-10 09:00    1
2016-07-10 10:00    2
2016-07-10 11:00    3
2016-07-10 12:00    4
2016-07-10 13:00    5
2016-07-10 14:00    6
2016-07-10 15:00    7
2016-07-10 16:00    8
2016-07-10 17:00    9
Freq: h, dtype: int64

六、数据切片

#使用时间段对时间序列数据进行切片操作
ts_period['2016-07-10 08:30':'2016-07-10 11:45']
2016-07-10 08:00    0
2016-07-10 09:00    1
2016-07-10 10:00    2
2016-07-10 11:00    3
Freq: h, dtype: int64
#使用时间戳对时间序列数据进行切片操作
ts['2016-07-10 08:30':'2016-07-10 11:45']
2016-07-10 09:00:00    1
2016-07-10 10:00:00    2
2016-07-10 11:00:00    3
Freq: h, dtype: int64

七、时区处理

#不设置时区
rng = pd.date_range('3/6/2012 00:00',periods = 15,freq = 'D')
rng.tz
#设置时区
rng_tz = pd.date_range('3/6/2012 00:00', periods = 15, freq = 'D', tz = 'Europe/London')
rng_tz.tz
<DstTzInfo 'Europe/London' LMT-1 day, 23:59:00 STD>
#使用pytz库来获取通用时区和所有时区的信息
from pytz import common_timezones, all_timezones
print(len(common_timezones))
print(len(all_timezones))
print(set(all_timezones) - set(common_timezones))
433
596
{'Europe/Kiev', 'Etc/GMT-12', 'America/Indianapolis', 'W-SU', 'MST7MDT', 'Mexico/BajaSur', 'Brazil/Acre', 'Singapore', 'Egypt', 'Etc/UCT', 'Portugal', 'Asia/Ulan_Bator', 'Etc/GMT+11', 'Etc/GMT+7', 'Australia/Canberra', 'Etc/GMT-3', 'America/Santa_Isabel', 'Atlantic/Faeroe', 'America/Buenos_Aires', 'Etc/GMT+4', 'MET', 'Zulu', 'America/Coral_Harbour', 'Etc/GMT0', 'America/Argentina/ComodRivadavia', 'Europe/Nicosia', 'GMT+0', 'Etc/GMT-0', 'GB', 'Libya', 'Iceland', 'Brazil/East', 'CET', 'Asia/Chongqing', 'America/Nipigon', 'Asia/Saigon', 'America/Rainy_River', 'GMT-0', 'Japan', 'Asia/Thimbu', 'Australia/Currie', 'Etc/GMT+2', 'Etc/GMT+5', 'Eire', 'PRC', 'Europe/Belfast', 'Etc/GMT+8', 'Canada/Saskatchewan', 'Asia/Tel_Aviv', 'Asia/Katmandu', 'Pacific/Yap', 'Israel', 'America/Jujuy', 'Etc/GMT-2', 'Greenwich', 'Europe/Zaporozhye', 'Australia/North', 'Etc/GMT+0', 'Asia/Kashgar', 'Australia/Yancowinna', 'Chile/Continental', 'America/Shiprock', 'Etc/Greenwich', 'Brazil/West', 'Pacific/Truk', 'WET', 'GB-Eire', 'Etc/GMT-14', 'America/Thunder_Bay', 'EST', 'Australia/ACT', 'Asia/Ujung_Pandang', 'America/Louisville', 'America/Catamarca', 'Etc/GMT+3', 'PST8PDT', 'Pacific/Johnston', 'Australia/LHI', 'Canada/Yukon', 'Etc/GMT', 'Iran', 'Mexico/General', 'Brazil/DeNoronha', 'US/Indiana-Starke', 'Atlantic/Jan_Mayen', 'UCT', 'Etc/GMT-4', 'Antarctica/South_Pole', 'Etc/Universal', 'America/Knox_IN', 'Etc/GMT-13', 'NZ-CHAT', 'Australia/Queensland', 'Etc/GMT-11', 'America/Ensenada', 'America/Fort_Wayne', 'America/Pangnirtung', 'Cuba', 'Africa/Asmera', 'Mexico/BajaNorte', 'Etc/GMT-6', 'America/Virgin', 'Europe/Tiraspol', 'EST5EDT', 'HST', 'America/Atka', 'Asia/Dacca', 'Africa/Timbuktu', 'Etc/GMT+10', 'Asia/Rangoon', 'ROK', 'US/Michigan', 'Hongkong', 'America/Cordoba', 'Poland', 'NZ', 'US/Samoa', 'Asia/Chungking', 'Etc/GMT-1', 'America/Mendoza', 'ROC', 'Australia/South', 'Australia/Victoria', 'Etc/GMT+6', 'CST6CDT', 'Etc/GMT-10', 'Etc/GMT-5', 'Etc/GMT+12', 'Turkey', 'Europe/Uzhgorod', 'Pacific/Samoa', 'Pacific/Enderbury', 'Universal', 'Etc/Zulu', 'America/Rosario', 'America/Porto_Acre', 'Etc/GMT-7', 'Kwajalein', 'Etc/GMT-9', 'Asia/Harbin', 'EET', 'Asia/Ashkhabad', 'Jamaica', 'Etc/UTC', 'America/Yellowknife', 'Etc/GMT+1', 'Asia/Macao', 'GMT0', 'Asia/Istanbul', 'MST', 'Australia/Tasmania', 'Australia/West', 'America/Godthab', 'Chile/EasterIsland', 'America/Montreal', 'US/Aleutian', 'Asia/Calcutta', 'US/East-Indiana', 'Etc/GMT-8', 'Navajo', 'Etc/GMT+9', 'Pacific/Ponape', 'Australia/NSW'}
#创建不带时区信息的时间戳
t_naive = pd.Timestamp('2016-07-10 08:50')
t_naive
Timestamp('2016-07-10 08:50:00')
#将本地时间转换为具有特定时区信息的时间
t = t_naive.tz_localize(tz = 'US/Central')
t
Timestamp('2016-07-10 08:50:00-0500', tz='US/Central')
#将已经具有时区信息的对象转换为另一个时区
t.tz_convert('Asia/Shanghai')
Timestamp('2016-07-10 21:50:00+0800', tz='Asia/Shanghai')
#如何处理夏令时?
#创建带有美国中部时区的日期时间索引,并将其用作一个时间序列的索引
rng = pd.date_range('2016-03-10', periods=10, tz='US/Central')
ts = pd.Series(range(10), index=rng)
ts
2016-03-10 00:00:00-06:00    0
2016-03-11 00:00:00-06:00    1
2016-03-12 00:00:00-06:00    2
2016-03-13 00:00:00-06:00    3
2016-03-14 00:00:00-05:00    4
2016-03-15 00:00:00-05:00    5
2016-03-16 00:00:00-05:00    6
2016-03-17 00:00:00-05:00    7
2016-03-18 00:00:00-05:00    8
2016-03-19 00:00:00-05:00    9
Freq: D, dtype: int64

1. 创建带有美国中部时区的时间序列

  • 创建一个时间序列Series),其中的时间点包含 美国中部时区(US/Central) 的时区信息。
  • 注意,夏令时的切换会在 2016 年 3 月 13 日发生:
    • 3 月 12 日之前的时间是标准时间(-06:00,UTC-6)。
    • 从 3 月 13 日开始,切换为夏令时(-05:00,UTC-5)。
  • 这是为了说明 pandas 会自动处理夏令时的切换。在时间序列中,当时区发生变化(如进入夏令时),pandas 会自动调整时间偏移量。
#创建带有协调世界时时区的日期时间索引,并将其用作一个时间序列的索引
rng = pd.date_range('2016-03-10', periods=10, tz='utc')
ts = pd.Series(range(10), index=rng)
ts
2016-03-10 00:00:00+00:00    0
2016-03-11 00:00:00+00:00    1
2016-03-12 00:00:00+00:00    2
2016-03-13 00:00:00+00:00    3
2016-03-14 00:00:00+00:00    4
2016-03-15 00:00:00+00:00    5
2016-03-16 00:00:00+00:00    6
2016-03-17 00:00:00+00:00    7
2016-03-18 00:00:00+00:00    8
2016-03-19 00:00:00+00:00    9
Freq: D, dtype: int64

2. 创建带有协调世界时(UTC)的时间序列

  • 创建一个时间序列,所有时间点都在 UTC(协调世界时)
  • UTC 不受夏令时的影响,所以每个时间点的时区偏移量始终是 +00:00
  • 使用 UTC 作为时间序列的标准时区,可以避免时区和夏令时切换的问题。在许多跨时区的应用中,通常使用 UTC 作为基础时间戳。
#将时间序列中的时间点转换为特定的时区,以便在特定时区进行处理和分析。
ts.tz_convert('US/Central')
2016-03-09 18:00:00-06:00    0
2016-03-10 18:00:00-06:00    1
2016-03-11 18:00:00-06:00    2
2016-03-12 18:00:00-06:00    3
2016-03-13 19:00:00-05:00    4
2016-03-14 19:00:00-05:00    5
2016-03-15 19:00:00-05:00    6
2016-03-16 19:00:00-05:00    7
2016-03-17 19:00:00-05:00    8
2016-03-18 19:00:00-05:00    9
Freq: D, dtype: int64

3. 将时间序列从 UTC 转换为其他时区

  • 将之前的 UTC 时间序列ts)转换为 美国中部时区(US/Central)
  • 观察到时间序列的时间点发生了变化:
    • 例如:2016-03-10 00:00:00+00:00 转换为 2016-03-09 18:00:00-06:00
    • 这是因为美国中部时区相对于 UTC 是 UTC-6(标准时间),在夏令时期间是 UTC-5
  • tz_convert 是用来转换时间序列的时区。
  • 转换后,时间点会自动调整到目标时区,并正确处理夏令时切换。
#创建包含12个时间点的日期时间索引,每个时间点相隔1小时,且带有美国东部时区的信息
pd.date_range('03-12-2016 22:00', periods = 12, freq = 'h', tz = 'US/Eastern')
/tmp/ipykernel_123/3463310447.py:2: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.pd.date_range('03-12-2016 22:00', periods = 12, freq = 'H', tz = 'US/Eastern')DatetimeIndex(['2016-03-12 22:00:00-05:00', '2016-03-12 23:00:00-05:00','2016-03-13 00:00:00-05:00', '2016-03-13 01:00:00-05:00','2016-03-13 03:00:00-04:00', '2016-03-13 04:00:00-04:00','2016-03-13 05:00:00-04:00', '2016-03-13 06:00:00-04:00','2016-03-13 07:00:00-04:00', '2016-03-13 08:00:00-04:00','2016-03-13 09:00:00-04:00', '2016-03-13 10:00:00-04:00'],dtype='datetime64[ns, US/Eastern]', freq='h')

4. 创建带有美国东部时区的时间序列

  • 创建一个以小时为频率的时间序列,从 2016 年 3 月 12 日晚上 10 点开始,持续 12 小时。
  • 包含 美国东部时区(US/Eastern) 的时区信息。
  • 夏令时切换:
    • 2016 年 3 月 13 日凌晨 2 点 -> 凌晨 3 点直接跳过,体现了夏令时的切换。
    • 3 月 13 日之后的时间偏移变为 -04:00,表示夏令时(UTC-4)。

思考:这段代码想干什么?

  1. 展示如何处理时区和夏令时

    • 不同时区(如 US/Central, US/Eastern, UTC)会对时间序列产生影响。
    • 夏令时(DST)会导致时间的偏移,pandas 可以自动处理这些复杂性。
  2. 说明时区转换的用法

    • 使用 tz_convert 方法将时间序列转换到其他时区,以便在特定时区下进行分析。
    • 例如:UTC 是常见的基础时区,但数据可能需要在本地时区中呈现。
  3. 模拟跨时区分析场景

    • 创建跨时区的时间序列,观察夏令时对时间序列的影响。
    • 这种场景常见于金融市场、航空调度、国际会议时间安排等应用中。

八、读取数据并制作数据框

#读取数据  
data = pd.read_fwf("climate.txt", parse_dates = [[0, 1]], infer_datetime_format = True, header = None,)  

警告如下:

/tmp/ipykernel_51/101956276.py:2: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.  data = pd.read_fwf("climate.txt", parse_dates = [[0, 1]], infer_datetime_format = True, header = None,)  

警告的原因

  • infer_datetime_format=True 的作用
    • pandas 尝试自动推断日期时间的格式,以便更高效地解析日期。
    • 如果数据中的日期时间格式不统一(如有些是 YYYY-MM-DD,有些是 DD/MM/YYYY),pandas 无法确定统一的格式,从而触发警告。
  • dateutil 的回退机制
    • 如果不能推断出格式,pandas 会使用 Python 内置的 dateutil 模块逐个解析日期。这种方法会较慢且容易出错。
# 指定日期时间格式
data = pd.read_fwf("climate.txt",parse_dates=[[0, 1]],  # 合并第 0 列和第 1 列为日期时间date_format='%Y %m',  # 指定日期时间格式header=None
)
#显示前几行数据
data.head()
0_12
01950-01-01-0.0603
11950-02-010.6268
21950-03-01-0.0081
31950-04-010.5551
41950-05-010.0716
data.columns = ['month','value'] #修改列名
data.index = data.month #将 'month' 列的值作为新的索引,替代原来的默认整数索引
data = data.drop('month',axis = 1) #删除原始的 'month' 列,避免出现重复列
#显示前几行数据
data.head()
value
month
1950-01-01-0.0603
1950-02-010.6268
1950-03-01-0.0081
1950-04-010.5551
1950-05-010.0716
#进行切片操作,选择索引值在 '1950' 到 '1952' 之间的所有行
#注意日期范围
data['1950':'1952']
value
month
1950-01-01-0.0603
1950-02-010.6268
1950-03-01-0.0081
1950-04-010.5551
1950-05-010.0716
1950-06-010.5386
1950-07-01-0.8025
1950-08-01-0.8510
1950-09-010.3580
1950-10-01-0.3789
1950-11-01-0.5151
1950-12-01-1.9281
1951-01-01-0.0850
1951-02-01-0.3999
1951-03-01-1.9341
1951-04-01-0.7765
1951-05-01-0.8628
1951-06-01-0.9179
1951-07-010.0900
1951-08-01-0.3774
1951-09-01-0.8178
1951-10-01-0.2129
1951-11-01-0.0685
1951-12-011.9872
1952-01-010.3682
1952-02-01-1.7472
1952-03-01-1.8595
1952-04-010.5385
1952-05-01-0.7735
1952-06-01-0.4409
1952-07-010.3831
1952-08-01-0.0304
1952-09-01-0.3834
1952-10-01-0.4372
1952-11-01-1.8909
1952-12-01-1.8267
#索引类型
type(data.index)
pandas.core.indexes.datetimes.DatetimeIndex
#进行切片操作,选择索引值在 '1951-11-11' 到 '1951-11-12' 之间的所有行
data['1951-11-11':'1951-11-12']
value
month
#获取周期索引
data_pd = data.to_period()
#切片
data_pd['1951-11-11':'1951-11-12']
value
month
1951-11-0.0685
#切片
data_pd['1951-11-11':'1952-01-12']
value
month
1951-11-0.0685
1951-121.9872
1952-010.3682
#各种数据加载是如何执行的?  
#测试 Pandas 的 read_fwf() 函数在不同参数配置下的性能  
import timeit  print("infer_datetime_format = True, no date parser")  
%timeit pd.read_fwf("climate.txt", parse_dates = [[0, 1]], infer_datetime_format = True, header = None,)  print("infer_datetime_format = False, no date parser")  
%timeit pd.read_fwf("climate.txt", parse_dates = [[0, 1]], infer_datetime_format = False, header = None,)  print("infer_datetime_format = True, date parser provided")  
dateparse = lambda x, y: pd.datetime.strptime('%s-%s'%(x,y), '%Y-%m')  
%timeit pd.read_fwf("climate.txt", parse_dates = [[0, 1]], infer_datetime_format = True, date_parser = dateparse,  header = None,)  print("infer_datetime_format = False, date parser provided")  
dateparse = lambda x, y: pd.datetime.strptime('%s-%s'%(x,y), '%Y-%m')  
%timeit pd.read_fwf("climate.txt", parse_dates = [[0, 1]], infer_datetime_format = False, date_parser = dateparse,  header = None,)  

这段代码,这里就不测试了,在新版中,date_parser 参数已经被标记为废弃(deprecated),将会在未来的 pandas 版本中被移除。这里运行会报特别多的警告。

# 在已经有了数据框的情况下解析列
df = pd.DataFrame({'year': [2015, 2016],'month': [2, 3],'day': [4, 5],'hour': [2, 3]})
df
yearmonthdayhour
02015242
12016353
#将包含日期时间信息的数据转换为 Pandas 中的 datetime 类型
pd.to_datetime(df)
0   2015-02-04 02:00:00
1   2016-03-05 03:00:00
dtype: datetime64[ns]
#将 'year'、'month' 和 'day' 列的数据合并成一个日期时间列,并将结果转换为 Pandas 中的 datetime 类型
pd.to_datetime(df[['year', 'month', 'day']])
0   2015-02-04
1   2016-03-05
dtype: datetime64[ns]

思考:

  1. 是否适用于其他列名
  2. 去获取你自己的时间序列数据并加载,能看到什么?
  3. (1)绘制图表 (2)获取时间范围 (3)在时间序列分析中,在时间戳和时间段之间进行转换。

1. 是否适用于其他列名?

答案:可以适用,但需要调整列名或显式指定列的顺序。

  • 默认行为: 如果列名为 'year''month''day'pd.to_datetime 可以自动识别并解析。

  • 非默认列名: 如果列名不是默认的(如 ['Year', 'Month', 'Day'] 或其他),需要手动指定列的顺序,例如:

    df = pd.DataFrame({'Year': [2015, 2016], 'Month': [2, 3], 'Day': [4, 5]})  
    pd.to_datetime(df[['Year', 'Month', 'Day']].rename(columns={'Year': 'year', 'Month': 'month', 'Day': 'day'}))  
    

    或者,直接传递列值的数组:

    pd.to_datetime({'year': df['Year'], 'month': df['Month'], 'day': df['Day']})  
    
  • 如果包含时间信息: 如果列还包含时间信息(如 'hour''minute''second'),pd.to_datetime 也支持解析。例如:

    df = pd.DataFrame({  'Year': [2015, 2016],  'Month': [2, 3],  'Day': [4, 5],  'Hour': [2, 3],  'Minute': [30, 45]  
    })  
    pd.to_datetime({'year': df['Year'], 'month': df['Month'], 'day': df['Day'], 'hour': df['Hour'], 'minute': df['Minute']})  
    

2. 获取你自己的时间序列数据并加载,能看到什么?

  1. 获取一个带有时间数据的数据集,可以是本地文件(如 CSV、Excel)或者在线数据源(如金融数据、气象数据)。
  2. 使用 pd.to_datetime 解析时间列,并探索数据的基本特性。

以一个带时间信息的 CSV 文件为例:

import pandas as pd  # 示例数据  
data = {  'date': ['2023-01-01', '2023-02-01', '2023-03-01'],  'value': [100, 150, 200]  
}  
df = pd.DataFrame(data)  # 将 'date' 列解析为 datetime  
df['date'] = pd.to_datetime(df['date'])  # 查看数据  
print(df.info())  
print(df)  

看到什么?

  • 时间列的解析与数据类型'date' 列会被转换为 datetime64[ns] 类型,方便进行时间序列分析。
  • 时间列的索引作用: 可以将时间列设置为索引,便于按时间进行切片、筛选等操作:
df.set_index('date', inplace=True)  
print(df)  

3. 探索时间序列数据的三个任务
(1)绘制图表

  • 使用 matplotlibpandas 的内置绘图功能:
import matplotlib.pyplot as plt  # 示例数据  
df = pd.DataFrame({  'date': pd.date_range(start='2023-01-01', periods=12, freq='M'),  'value': [100, 110, 105, 115, 120, 130, 125, 135, 140, 150, 155, 160]  
})  
df.set_index('date', inplace=True)  # 绘制时间序列
df.plot(y='value', title='Time Series Plot')  
plt.show()  

(2)获取时间范围

print("起始时间:", df.index.min())  
print("结束时间:", df.index.max())  
print("时间跨度:", df.index.max() - df.index.min())  

(3)时间戳和时间段的转换

  • 时间戳(Timestamp)转时间段(Period: 时间段表示一个时间跨度(如一个月、一年)。
# 时间戳转换为时间段  
df['period'] = df.index.to_period('M')  # 'M' 表示月,'Y' 表示年  
print(df)  
  • 时间段(Period)转时间戳(Timestamp: 如果需要具体的起始或结束时间:
# 时间段转换为时间戳  
df['start_of_period'] = df['period'].dt.start_time  # 时间段的开始  
df['end_of_period'] = df['period'].dt.end_time    # 时间段的结束  
print(df)  
#创建时间序列,并对该时间序列进行了截取操作
ts = pd.Series(range(10), index = pd.date_range('7/31/2015', freq = 'M', periods = 10))
ts.truncate(before='10/31/2015', after='12/31/2015')
/tmp/ipykernel_123/1076020219.py:2: FutureWarning: 'M' is deprecated and will be removed in a future version, please use 'ME' instead.ts = pd.Series(range(10), index = pd.date_range('7/31/2015', freq = 'M', periods = 10))2015-10-31    3
2015-11-30    4
2015-12-31    5
Freq: ME, dtype: int64
#在截断时间序列时,可能会破坏时间序列的频率。需要谨慎选择截断方式,确保截断后的时间序列仍然保持为需要的频率。
#返回索引为 0、2 和 6 的数据点对应的时间索引
ts[[0, 2, 6]].index
/tmp/ipykernel_123/1690724394.py:3: FutureWarning: Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`ts[[0, 2, 6]].indexDatetimeIndex(['2015-07-31', '2015-09-30', '2016-01-31'], dtype='datetime64[ns]', freq=None)
#对时间序列 ts 进行按位置的切片操作
ts.iloc[0:10:2].index
DatetimeIndex(['2015-07-31', '2015-09-30', '2015-11-30', '2016-01-31','2016-03-31'],dtype='datetime64[ns]', freq='2ME')

总结
在本关卡中,我们详细介绍了Pandas在时间数据处理方面的多种功能和应用。通过一系列的示例和练习,我们学习了如何创建和操作时间戳(Timestamp)、日期时间索引(DatetimeIndex)、时间段(Period)、时间差(Timedelta)以及周期索引(PeriodIndex)。此外,我们还探讨了时区处理、节假日日历的创建和数据加载的技巧。


http://www.ppmy.cn/devtools/151200.html

相关文章

深入理解 Entity、VO、QO、DTO 的区别及其在 MVC 架构中的应用

文章背景 在现代软件开发中&#xff0c;我们经常会接触到各种数据结构的概念&#xff0c;比如 Entity、VO&#xff08;Value Object&#xff09;、QO&#xff08;Query Object&#xff09;、DTO&#xff08;Data Transfer Object&#xff09;等。这些概念尽管看似相似&#xff…

【算法】图解二叉树的前中后序遍历

目录 1.递归序实现 2.非递归实现 二叉树的节点结构 public static class Node {public int value;public Node left;public Node right;public Node(int data) {this.value data;} } 1.递归序实现 递归的方法遍历二叉树时每一个节点都会被访问三次 public static void f…

【微信小程序】let和const-综合实训

let 和 const 都是用于声明变量的关键字&#xff0c;它们与传统的 var 关键字相比&#xff0c;有很多不同之处。 let 声明块级作用域变量&#xff0c;可再赋值&#xff1b;const 声明块级作用域常量&#xff0c;不可再赋值。 以下是它们的详细介绍&#xff1a; 一、基本概念…

Ruby语言的网络编程

Ruby语言的网络编程 引言 Ruby是一种高度抽象的动态编程语言&#xff0c;以其简洁的语法和强大而灵活的功能而闻名。自1995年由松本行弘&#xff08;Yukihiro Matsumoto&#xff09;发布以来&#xff0c;Ruby便吸引了无数开发者&#xff0c;尤其是在Web开发领域。随着互联网的…

【Uniapp-Vue3】pages.json页面路由globalStyle的属性

项目的全局配置在pages.json中。 一、导航栏设置 二、下拉刷新设置 下拉就可以看到设置的样式 三、上拉触底 这个页面中&#xff0c;向下滑动页面到底部就会输出“到底了” 现在将触底距离设置为500 走到半路就会输出“到底了”

FastDDS安装测试记录

1、安装依赖的软件 sudo apt install cmake g python3-pip wget git sudo apt install libasio-dev libtinyxml2-dev sudo apt install libssl-dev sudo apt install libp11-dev libengine-pkcs11-openssl sudo apt install softhsm22、安装foonathan_memory_vendor cd ~/Fas…

JVM远程调试原理剖析

一、如何开启JVM远程调试 当一个 Java 应用启动时&#xff0c;JVM 会根据启动参数配置其运行环境。使用 -agentlib:jdwp 参数启动远程调试功能&#xff0c;JVM 会初始化调试代理。 agentlib:jdwptransportdt_socket,servery,suspendn,address*:5005 -jar your_application.jar…

【数据结构】快排之三路划分+文件归并排序

排序 一.快排1.快排性能分析2.快排之三路划分3.快排之内省排序 二.归并1.外排序2.文件归并排序 一.快排 1.快排性能分析 决定快排性能的关键点是每次单趟排序后&#xff0c;key对数组的分割&#xff0c;如果每次选key基本二分居中&#xff0c;那么快排的递归树就是颗均匀的满…