在日常的数据分析工作中,常常会有根据日期来对数据进行分析。比如我们需要通过用户的下单时间来分析用户在不同时间段对商品的喜好;如通过访问日志的访问时间来分析系统的访问周期和负载,为不同时间段的资源调配提供依据;如通过用户刷短视频的时间来分析用户的行为特征和工作时间和工种;由此可以看出,在数据分析中,根据日期(时间)来分析的情况还是不少的。
同时,根据数据记录方式的不同,时间的保存格式也是不同的。比如有的系统记录是按照 2024/2/1 格式记录;有的系统是按照 2024-02-01 09:31:52 的格式来记录;也有的会直接存一个时间戳(1698724307189)来记录;时间的存储格式有很多,这里我们就不再一一举例说明咯。
如果要完全实现针对不同格式的兼容,往往需要书写大量琐碎的代码。而这还只是最简单的提取年月日。其他比如时间的加减,都不是简单就能够完成的。pandas 作为数据分析最强大的工具集,自然也提供了一套非常强大的处理时间数据的工具,下面我们就一起来看看。
1、pandas 常用的时间类
-
Timestamp:代表某个时间点,比如用户下单的时间,或是访问系统的时间。 -
DatetimeIndex:代表一个时间点的序列,相当于是多个 Timestamp 构成的列表。 -
Timedelta:单个时长。比如 3 分钟,5 分钟等都算时长,时长具有不同单位。常见的有天、时、分等。 -
TimedeltaIndex:多个时长数据的序列,类似 DatetimeIndex 和 Timestamp 的关系。 -
DataOffset:时间在日历维度的偏移。比如时间是 2024 年 2 月 1 日,在日历偏移一天就是 2024 年 1 月 31 日。DataOffset 提供了多种偏移方式,比如按工作日偏移,那么周五早上 10 点偏移到下一个工作日就是下周一早上 10 点。
在 pandas 中做时间处理的时候,用的最多的是以下几个场景:
❝1、将各种格式的时间数据转换成 timestamp 类型。
❝2、使用 Timestamp 类型来获取时间的各种属性。比如日期、时间、星期几等。
❝3、使用 Timestamp 和 Timedelta 来做时间的计算,如加减等。比如当前时间加 3 分钟。 如果需要做日历维度的偏移,就需要和 Offset 一起使用。
❝4、按时间筛选 DataFrame 里面的记录。则需将时间列设置为 DateTimeIndex,然后按照普通索引的用法通过时间来筛选。
2、解析时间数据
在 Python 中,对时间数据的解析本质上就是将数据先转换为 pandas 的 Timestamp 类型,因为只有转换后才能进行后续的操作。
pandas 提供了 to_datetime 的方法来将不同类型的时间数据转换为 Timestamp 类型。
(1)字符串解析
字符串是常见的时间存储格式,to_datetime 函数几乎支持所有的主流标记法,比如
import pandas as pd
# 常见的日期+时间的表示方法
pd_time = pd.to_datetime("2023-08-29 17:17:22")
print(type(pd_time),pd_time)
# 时间简写,并用12小时制的表示方法
pd_time1 = pd.to_datetime("2023-08-29 5:17pm")
print(type(pd_time1), pd_time1)
# / 表示法
pd_time2 = pd.to_datetime("08/29/2023")
print(type(pd_time2), pd_time2)
# 结合英文月份的表示方法
pd_time3 = pd.to_datetime("Aug 29, 2023")
print(type(pd_time3), pd_time3)
执行后输出:
从上面输出可以看出 to_datetime 函数返回的都是 Timestamp 类型。 如果是中文环境,类似于“2024 年 2 月 1 日”这样的格式,也同样是可以解析的,我们可以通过 to_datetime 的自定义格式字符串来解析。比如下面的代码:
# 使用自定义格式字符串解析任意时间字符串
pd_time4 = pd.to_datetime("2024年2月1日", format="%Y年%m月%d日")
print(type(pd_time4), pd_time4)
输出如下:
(2)浮点型(整型)解析
这里就比如我们上面聊到的数据源存储是时间戳的,那我们的转换方式如下:
time_value = 1620565604
# 将数字时间戳转换为 Timestamp 类型,并指定单位为秒
pd_time5 = pd.to_datetime(time_value, unit="s")
print(type(pd_time5), pd_time5)
输出如下:
❝在这里有点需要注意:如果需要加入时区,我们可以使用tz_localize("Asia/Shanghai")来指定。
(3)构造Timestamp对象
# 通过单独指定年月日等信息来创建 Timestamp 对象
pd_time7 = pd.Timestamp(year=2024, month=2, day=1, hour=21)
print(type(pd_time7),pd_time7)
# 获取当前时间
pd_time8 = pd.Timestamp("now")
print(type(pd_time8),pd_time8)
输出如下:
3、获取时间内的时间属性
当我们获取到Timestamp对象后,就可以通过Timestamp对象提供的方法来获取各种时间属性了,常用的属性获取方法如下:
print("当前时间对象:", pd_time8)
print("星期几,星期一为0:", pd_time8.dayofweek)
print("星期几,字符串表示:", pd_time8.day_name())
print("一年中的第几天:", pd_time8.dayofyear)
print("这个月的有几天:",pd_time8.daysinmonth)
print("今年是否是闰年", pd_time8.is_leap_year)
print("当前日期是否是本月最后一天", pd_time8.is_month_end)
print("当前日期是否是本月第一天", pd_time8.is_month_start)
print("当前日期是否是本季度最后一天", pd_time8.is_quarter_end)
print("当前日期是否是本季第一天", pd_time8.is_quarter_start)
print("当前日期是否是本年度最后一天", pd_time8.is_year_end)
print("当前日期是否是本年度第一天", pd_time8.is_year_start)
print("当前第几季度:", pd_time8.quarter)
print("当前的时区:", pd_time8.tz)
print("本年第几周:", pd_time8.week)
print("年:", pd_time8.year)
print("月:", pd_time8.month)
print("日:",pd_time8.day)
print("小时:", pd_time8.hour)
print("分钟:", pd_time8.minute)
print("秒:", pd_time8.second)
输出如下:
4、时间计算
简单来说,时间计算就是在当前时间后的几分钟、几小时或几秒后的时间是多少。因为时间的数据计算比较特殊,涉及到月份的天数、分钟折算成秒,需要除以60,小时折算成分钟,也需要除以60,如果自己手动计算,逻辑就会比较复杂。那么我们就可以通过pandas来进行时间计算。pandas的时间计算是通过Timestamp对象和Timedelta对象混合运算来实现的。
(1)创建Timedelta对象
❝①从字符串来创建
delta1 = pd.Timedelta('0.5 days')
print("半天:", delta1)
delta2 = pd.Timedelta("2 days 3 hour 20 minutes")
print("2天零3小时20分钟", delta2)
delta3 = pd.Timedelta("1 days 20:36:00")
print("1天零8小时36分钟:", delta3)
❝②从单元时间来创建
delta4 = pd.Timedelta(days = 1.5)
print("1天半:", delta4)
delta5 = pd.Timedelta(days = 10, hours= 9)
print("十天零九小时:", delta5)
❝③从时间缩写来创建
-
W:代表周、星期
-
D:代表天
-
H:代表小时
-
M:代表分钟
-
S:代表秒
delta6 = pd.Timedelta("2W3D")
print("两周零三天:", delta6)
delta7 = pd.Timedelta("6H30M12S")
print("6小时30分钟12秒:", delta7)
(2)时间计算
# 获得当前的时间
current_time = pd.Timestamp("now")
print("当前时间:", current_time)
# 获得当前时间减去两周的时间
two_week_ago = current_time - pd.Timedelta("2W")
print("两周前:", two_week_ago)
# 获得当前时间30天零7小时之后的时间
future_time = current_time + pd.Timedelta("30D7H")
print("30天零7小时之后的时间:",future_time)
执行后输出如下:
❝除了计算Timedelta和Timestamp外,两个Timestamp也能相减,得到一个时长:
# 创建去年国庆节上午八点的时间
national_day = pd.to_datetime("2023-10-01 08:00:00")
# 计算当前时间和国庆时间的 Timedelta
delta8 = current_time - national_day
print("距离去年国庆已经过了:", delta8)
输出为:
5、以时间为索引查询
接下来我们来看一下,针对DataFrame中的数据,我们如何利用时间来查询。
(1)准备示例数据
❝示例数据大家可以自行准备,可以直接新建csv文件来做模拟数据,不想做的可以公众号留言哈!
(2)设置DatetimeIndex
将示例数据加载到DataFrame中后,需要设置time字段转换为datetimeIndex。转换分两步:
第一步:将时间一列转换为Timestamp对象。
# 将 time 列转化为 Timestamp对象
df_log["time"] = pd.to_datetime(df_log["time"])
# 查看 time 列
df_log["time"]
执行之后输出:
0 2018-08-29 17:17:22.300959410
1 2018-08-29 20:59:58.841378430
2 2018-08-01 19:20:06.479644547
3 2018-08-01 17:25:58.912202131
4 2018-06-02 11:00:51.123221777
...
995 2018-11-08 11:10:13.586269568
996 2018-11-08 19:10:55.214335543
997 2018-11-08 16:54:37.687285776
998 2018-11-08 17:46:17.253211617
999 2018-06-26 20:38:07.950590072
Name: time, Length: 1000, dtype: datetime64[ns]
第二步:将新的time列设置成索引。
# 设置 time 一列为 df_log 的索引
df_log.set_index("time", inplace=True)
# 查看最新的 DataFrame
df_log
执行后输出:
从图中可以看到,时间列已代替了之前的序号,成为了DataFrame的索引。
(3)根据时间筛选数据
以上步骤做完之后,我们就可以通过时间来筛选DataFrame中的数据了。
❝①查询9月1日至9月15日的数据
df_log.loc["2018-09-01" : "2018-09-15",:]
❝②选择从 8月到9月的数据
df_log.loc["2018-08" : "2018-09", :]
❝③选择从 8 月 1 日到 9 月 2 日下午两点之前的数据
df_log.loc["2018-08-01" : "2018-09-02 14:00:00", :]
执行上述代码后,我们就可以看到根据时间筛选的DataFrame对应的数据了。
❝欢迎关注公众号:服务端技术精选
❝如果有疑问或者是其他需求,可公众号留言