百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

Python 数据分析——Pandas 时间序列

liuian 2025-04-09 17:53 13 浏览

Pandas提供了表示时间点、时间段和时间间隔等三种与时间有关的类型,以及元素为这些类型的索引对象,并提供了许多时间序列相关的函数。

一、时间点、时间段、时间间隔

Timestamp对象从Python标准库中的datetime类继承,表示时间轴上的一个时刻。它提供了方便的时区转换功能。下面调用Timestamp.now()获取当前时间now,它是不包含时区信息的本地时间。调用其tz_localize()可以得到指定时区的Timestamp对象。而带时区信息的Timestamp对象可以通过其tz_convert()转换时区。下面的now_shanghai的时间以"+08:00"结尾,表示它是东八区的时间,将其转换为东京时间得到now_tokyo,它是东九区的时间:

now = pd.Timestamp.now()
now_shanghai = now.tz_localize("Asia/Shanghai")
now_tokyo = now_shanghai.tz_convert("Asia/Tokyo")
print u"本地时间:", now
print u"上海时区:", now_shanghai
print u"东京时区:", now_tokyo
本地时间: 2015-07-25 11:50:46.264000
上海时区: 2015-07-25 11:50:46.264000+08:00
东京时区: 2015-07-25 12:50:46.264000+09:00

不同时区的时间可以比较,而本地时间和时区时间无法比较:

now_shanghai == now_tokyo
True

通过pytz模块的common_timezones()可以获得常用的表示时区的字符串:

import pytz
pytz.common_timezones
['Africa/Abidjan',
'Africa/Accra',
'Africa/Addis_Ababa',
'Africa/Algiers',
...

Period对象表示一个标准的时间段,例如某年、某月、某日、某小时等。时间段的长短由freq属性决定。下面的程序调用Period.now(),分别获得包含当前时间的日周期时间段和小时周期时间段。

now_day = pd.Period.now(freq="D")
now_hour = pd.Period.now(freq="H")
now_day                       now_hour           
-------------------------  -------------------------------
Period('2015-07-25', 'D')  Period('2015-07-25 11:00', 'H')

freq属性是一个描述时间段的字符串,其可选值可以通过下面的代码获得:

from pandas.tseries import frequencies

frequencies._period_code_map.keys()

frequencies._period_alias_dictionary()

对于周期为年度和星期的时间段,可以通过freq指定开始的时间。例如"W"表示以星期天开始的星期时间段,而"W-MON"则表示以星期一开始的星期时间段:

now_week_sun = pd.Period.now(freq="W")
now_week_mon = pd.Period.now(freq="W-MON")
now_week_sun                              now_week_mon              
----------------------------------------  ----------------------------------------
Period('2015-07-20/2015-07-26', 'W-SUN')  Period('2015-07-21/2015-07-27', 'W-MON')

时间段的起点和终点可以通过start_time和end_time属性获得,它们都是表示时间点的Timestamp对象:

now_day.start_time                      now_day.end_time             
--------------------------------  ------------------------------------------
Timestamp('2015-07-25 00:00:00')  Timestamp('2015-07-25 23:59:59.999999999')

调用Timestamp对象的to_period()方法可以把时间点转换为包含该时间点的时间段。注意时间段不包含时区信息:

now_shanghai.to_period("H")
Period('2015-07-25 11:00', 'H')

Timestamp和Period对象可以通过其属性获得年、月、日等信息。下面分别获得年、月、日、星期几、一年中的第几天、小时等信息:

将两个时间点相减可以得到表示时间间隔的Timedelta对象,下面计算当前时刻离2015年国庆节还有多少时间:

national_day = pd.Timestamp("2015-10-1")
td = national_day - pd.Timestamp.now()
td
Timedelta('67 days 12:09:04.039000')

时间点和时间间隔之间可以进行加减运算:

national_day + pd.Timedelta("20 days 10:20:30")
Timestamp('2015-10-21 10:20:30')

Timedelta对象的days、seconds、microseconds和nanoseconds等属性分别获得它包含的天数、秒数、微秒数和纳秒数。注意这些值与对应的单位相乘并求和才是该对象表示的总时间间隔:

td.days  td.seconds  td.microseconds
-------  ----------  ---------------
67L     43744L      39000L  

也可以通过关键字参数直接指定时间间隔的天数、小时数、分钟数和秒数:

print pd.Timedelta(days=10, hours=1, minutes=2, seconds=10.5)
print pd.Timedelta(seconds=100000)
10 days 01:02:10.500000
1 days 03:46:40

二、时间序列

上节介绍的Timestamp、Period和Timedelta对象都是表示单个值的对象,这些值可以放在索引或数据列中。下面的程序调用random_timestamps()创建一个包含5个随机时间点的DatetimeIndex对象ts_index,然后通过ts_index创建PeriodIndex类型的索引对象pd_index和TimedeltaIndex类型的索引对象td_index。DatetimeIndex、PeriodIndex和TimedeltaIndex都从Index继承,可以作为Series或DataFrame的索引。

random_timestamps()中的date_range()函数创建以start为起点、以end为终点、周期为freq的DatetimeIndex对象。

下面查看这三种索引对象的dtype属性。其中M8[ns]和m8[ns]是NumPy中表示时间点和时间间隔的dtype类型,内部采用64位整数存储时间信息,其中[ns]表示时间的最小单位为纳秒,能表示的时间范围大约是公元1678年到公元 2262年。PeriodIndex也使用64位整数,但是最小时间单位由其freq属性决定。

ts_index.dtype   pd_index.dtype   td_index.dtype
----------------  --------------  ----------------
dtype('<M8[ns]')  dtype('int64')  dtype('<m8[ns]')

这三种索引对象都提供了许多与时间相关的属性,例如:

ts_index.weekday   pd_index.month        td_index.seconds      
----------------  ---------------  ----------------------------
[3, 6, 5, 3, 1]   [1, 2, 2, 8, 8]  [57120, 15960, 51000, 37980]

DatetimeIndex.shift(n, freq)可以移动时间点,将当前的时间移动n个freq时间单位。对于天数、小时这样的精确单位,结果相当于与指定的时间间隔相加:

ts_index.shift(1, "H")
DatetimeIndex(['2015-01-15 17:12:00', '2015-02-15 09:04:00',
'2015-02-28 13:30:00', '2015-08-06 03:40:00',
'2015-08-18 14:13:00'],
dtype='datetime64[ns]', freq=None, tz=None)

而对于月份这样不精确的时间单位,则移动一个单位相当于移到月头或月底:

ts_index.shift(1, "M")
DatetimeIndex(['2015-01-31 16:12:00', '2015-02-28 08:04:00',
'2015-03-31 12:30:00', '2015-08-31 02:40:00',
'2015-08-31 13:13:00'],
dtype='datetime64[ns]', freq=None, tz=None)

DatetimeIndex.normalize()将时刻修改为当天的凌晨零点,可以理解为按日期取整:

ts_index.normalize()
DatetimeIndex(['2015-01-15', '2015-02-15', '2015-02-28', '2015-08-06',
'2015-08-18'],
dtype='datetime64[ns]', freq=None, tz=None)

如果希望对任意的时间周期取整,可以先通过to_period()将其转换为PeriodIndex对象,然后再调用to_timestamp()方法转换回DatetimeIndex对象。to_timestamp()的how参数决定将时间段的起点还是终点转换为时间点,默认值为"start"。

ts_index.to_period("H").to_timestamp()
DatetimeIndex(['2015-01-15 16:00:00', '2015-02-15 08:00:00',
'2015-02-28 12:00:00', '2015-08-06 02:00:00',
'2015-08-18 13:00:00'],
dtype='datetime64[ns]', freq=None, tz=None)

下面的Series对象ts_series的索引为DatetimeIndex对象,这种Series对象被称为时间序列:

ts_series = pd.Series(range(5), index=ts_index)

时间序列提供一些专门用于处理时间的方法,例如between_time()返回所有位于指定时间范围之内的数据:

ts_series.between_time("9:00", "18:00")
2015-01-15 16:12:00    0
2015-02-28 12:30:00    2
2015-08-18 13:13:00    4
dtype: int64

而tshift()则将索引移动指定的时间:

ts_series.tshift(1, freq="D")
2015-01-16 16:12:00    0
2015-02-16 08:04:00    1
2015-03-01 12:30:00    2
2015-08-07 02:40:00    3
2015-08-19 13:13:00    4
dtype: int64

以PeriodIndex和TimedeltaIndex为索引的序列也可以使用tshift()对索引进行移动:

时间信息除了可以作为索引之外,还可以作为Series或DataFrame的列。下面分别将上述三种索引对象转换为Series对象,并查看其dtype属性:

可以看到Pandas的Series对象目前尚不支持使用64位整数表示时间段,因此使用对象数组保存所有的Period对象。而对于时间点和时间间隔数据则采用64位整数数组保存。

当序列的值为时间数据时,可以通过名字空间对象dt调用时间相关的属性和方法。例如:

三、与NaN相关的函数

Pandas使用NaN表示缺失的数据,由于整数列无法使用NaN,因此如果整数类型的列出现缺失数据,则会被自动转换为浮点数类型。下面将布尔类型的DataFrame对象传递给一个整数类型的DataFrame对象的where()方法。该方法将False对象的元素设置为NaN,注意其结果变成了浮点数类型,而没有NaN的列仍然为整数类型。

isnull()和notnull()用于判断元素值是否为NaN,它们返回全是布尔值的DataFrame对象。df.notnull()和~df.isnull()的结果相同,但是由于notnull()少创建一个临时对象,其运算效率更高一些。

count()返回每行或每列的非NaN元素的个数:

对于包含NaN元素的数据,最简单的办法就是调用dropna()以删除包含NaN的行或列,当全部使用默认参数时,将删除包含NaN的所有行。可以通过thresh参数指定NaN个数的阈值,删除所有NaN个数大于等于该阈值的行。

当行数据按照某种物理顺序(例如时间)排列时,可以使用NaN前后的数据对其进行填充。ffill()使用之前的数据填充,而bfill()则使用之后的数据填充。interpolate()使用前后数据进行插值填充:

interpolate()默认使用等距线性插值,可以通过其method参数指定插值算法。在下面的例子中,第0个元素和第2个元素的数值分别为3.0和7.0,因此当method参数默认省时下标为1的NaN被填充为前后两个元素的平均值5.0。而当method为"index"时,则使用索引值进行插值运算。由于第1个元素的索引与第2个元素的索引接近,因此其插值结果也接近第二个元素的值。

此外还可以使用字典参数让fillna()对不同的列使用不同的值填充NaN:

各种聚合方法的skipna参数默认为True,因此计算时将忽略NaN元素,注意每行或每列是单独运算的。如果需要忽略包含NaN的整行,需要先调用dropna()。若将skipna参数设置为False,则包含NaN的行或列的运算结果为NaN。

df.combine_first(other)使用other填充df中的NaN元素。它将df中的NaN元素替换为other中对应标签的元素。在下面的例子中,df_nan中索引为1、2、8、9的行中的NaN被替换为df_other中相应的值:


四、改变DataFrame的形状

本节介绍表1中的函数:

表1 本节要介绍的函数

函数名

功能

函数名

功能

concat

拼接多块数据

drop

删除行或列

set_index

设置索引

reset_index

将行索引转换为列

stack

将列索引转换为行索引

unstack

将行索引转换为列索引

reorder_levels

设置索引级别的顺序

swaplevel

交换索引中两个级别的顺序

sort_index

对索引排序

pivot

创建透视表

melt

透视表的逆变换

assign

返回添加新列之后的数据

DataFrame的shape属性和NumPy的二维数组相同,是一个有两个元素的元组。由于DataFrame的index和columns都支持MultiIndex索引对象,因此可以用DataFrame表示更高维的数据。

下面首先从CSV文件读入数据,并使用groupby()计算分组的平均值。关于groupby在后面的章节还会详细介绍。注意下面的soils_mean对象的行索引是多级索引:

1.添加删除列或行

由于DataFrame可以看作一个Series对象的字典,因此通过DataFrame[colname] = values即可添加新列。有时新添加的列是从已经存在的列计算而来,这时可以使用eval()方法计算。例如下面的代码添加一个名为N_percent的新列,其值为N列乘上100:

soils["N_percent"] = soils.eval("N * 100")

assign()方法添加由关键字参数指定的列,它返回一个新的DataFrame对象,原数据的内容保持不变:

append()方法用于添加行,它没有inplace参数,只能返回一个全新对象。由于每次调用append()都会复制所有的数据,因此在循环中使用append()添加数据会极大地降低程序的运算速度。可以使用一个列表缓存所有的分块数据,然后调用concat()将所有这些数据沿着指定轴拼贴到一起。下面的程序比较二者的运算速度:

def random_dataframe(n):
columns = ["A", "B", "C"]
for i in range(n):
nrow = np.random.randint(10, 20)
yield pd.DataFrame(np.random.randint(0, 100, size=(nrow, 3)), columns=columns)

df_list = list(random_dataframe(1000))
%%time
df_res1 = pd.DataFrame([])
for df in df_list:
df_res1 = df_res1.append(df)
Wall time: 1.37 s
%%time
df_res2 = pd.concat(df_list, axis=0)
Wall time: 118 ms

可以使用keys参数指定与每块数据对应的键,这样结果中的拼接轴将使用多级索引,方便快速获取原始的数据块。下面获取拼接之后的DataFrame对象中第0级标签为30的数据,并使用equals()方法判断它是否与原始数据中下标为30的数据块相同:

df_res3 = pd.concat(df_list, axis=0, keys=range(len(df_list)))
df_res3.loc[30].equals(df_list[30])
True

drop()删除指定标签对应的行或列,下面删除名为N和Group的两列:

2.行索引与列之间的相互转换

reset_index()可以将索引转换为列,通过level参数可以指定被转换为列的级别。如果只希望从索引中删除某个级别,可以设置drop参数为True。

set_index()将列转换为行索引,如果append参数为False(默认值),则删除当前的行索引;若为True,则为当前的索引添加新的级别。


3.行索引和列索引的相互转换

stack()方法把指定级别的列索引转换为行索引,而unstack()则把行索引转换为列索引。下面的程序将行索引中的第一级转换为列索引的第一级,所得到的结果中行索引为单级索引,而列索引为多级索引:

无论是stack()还是unstack(),当所有的索引被转换到同一个轴上时,将得到一个Series对象:

4.交换索引的等级

reorder_levels()和swaplevel()交换指定轴的索引级别。下面调用swaplevel()交换行索引的两个级别,然后调用sort_index()对新的索引进行排序:


5.透视表

pivot()可以将DataFrame中的三列数据分别作为行索引、列索引和元素值,将这三列数据转换为二维表格:

pivot()的三个参数index、columns和values只支持指定一列数据。若不指定values参数,就将剩余的列都当作元素值列,得到多级列索引:

melt()可以看作pivot()的逆变换。由于它不能对行索引进行操作,因此先调用reset_index()将行索引转换为列,然后用id_vars参数指定该列为标识列:

相关推荐

GANs为何引爆机器学习?这篇基于TensorFlow的实例教程为你解惑!

「机器人圈导览」:生成对抗网络无疑是机器学习领域近三年来最火爆的研究领域,相关论文层出不求,各种领域的应用层出不穷。那么,GAN到底如何实践?本文编译自Medium,该文作者以一朵玫瑰花为例,详细阐...

高丽大学等机构联合发布StarGAN:可自定义表情和面部特征

原文来源:arXiv、GitHub作者:YunjeyChoi、MinjeChoi、MunyoungKim、Jung-WooHa、SungKim、JaegulChoo「雷克世界」编译:嗯~...

TensorFlow和PyTorch相继发布最新版,有何变化

原文来源:GitHub「机器人圈」编译:嗯~阿童木呀、多啦A亮Tensorflow主要特征和改进在Tensorflow库中添加封装评估量。所添加的评估量列表如下:1.深度神经网络分类器(DNNCl...

「2022 年」崔庆才 Python3 爬虫教程 - 深度学习识别滑动验证码缺口

上一节我们使用OpenCV识别了图形验证码躯壳欧。这时候就有朋友可能会说了,现在深度学习不是对图像识别很准吗?那深度学习可以用在识别滑动验证码缺口位置吗?当然也是可以的,本节我们就来了解下使用深度...

20K star!搞定 LLM 微调的开源利器

LLM(大语言模型)微调一直都是老大难问题,不仅因为微调需要大量的计算资源,而且微调的方法也很多,要去尝试每种方法的效果,需要安装大量的第三方库和依赖,甚至要接入一些框架,可能在还没开始微调就已经因为...

大模型DeepSeek本地部署后如何进行自定义调整?

1.理解模型架构a)查看深度求索官方文档或提供的源代码文件,了解模型的结构、输入输出格式以及支持的功能。模型是否为预训练权重?如果是,可以在预训练的基础上进行微调(Fine-tuning)。是否需要...

因配置不当,约5000个AI模型与数据集在公网暴露

除了可访问机器学习模型外,暴露的数据还可能包括训练数据集、超参数,甚至是用于构建模型的原始数据。前情回顾·人工智能安全动态向ChatGPT植入恶意“长期记忆”,持续窃取用户输入数据多模态大语言模型的致...

基于pytorch的深度学习人员重识别

基于pytorch的深度学习人员重识别Torchreid是一个库。基于pytorch的深度学习人员重识别。特点:支持多GPU训练支持图像的人员重识别与视频的人员重识别端到端的训练与评估简单的re...

DeepSeek本地部署:轻松训练你的AI模型

引言:为什么选择本地部署?在AI技术飞速发展的今天,越来越多的企业和个人希望将AI技术应用于实际场景中。然而,对于一些对数据隐私和计算资源有特殊需求的用户来说,云端部署可能并不是最佳选择。此时,本地部...

谷歌今天又开源了,这次是Sketch-RNN

前不久,谷歌公布了一项最新技术,可以教机器画画。今天,谷歌开源了代码。在我们研究其代码之前,首先先按要求设置Magenta环境。(https://github.com/tensorflow/magen...

Tensorflow 使用预训练模型训练的完整流程

前面已经介绍了深度学习框架Tensorflow的图像的标注和训练数据的准备工作,本文介绍一下使用预训练模型完成训练并导出训练的模型。1.选择预训练模型1.1下载预训练模型首先需要在Tensorf...

30天大模型调优学习计划(30分钟训练大模型)

30天大模型调优学习计划,结合Unsloth和Lora进行大模型微调,掌握大模型基础知识和调优方法,熟练应用。第1周:基础入门目标:了解大模型基础并熟悉Unsloth等工具的基本使用。Day1:大模...

python爬取喜马拉雅音频,json参数解析

一.抓包分析json,获取加密方式1.抓包获取音频界面f12打开抓包工具,播放一个(非vip)视频,点击“媒体”单击打开可以复制URL,发现就是我们要的音频。复制“CKwRIJEEXn-cABa0Tg...

五、JSONPath使用(Python)(json数据python)

1.安装方法pipinstalljsonpath2.jsonpath与Xpath下面表格是jsonpath语法与Xpath的完整概述和比较。Xpathjsonpath概述/$根节点.@当前节点...

Python网络爬虫的时候json=就是让你少写个json.dumps()

大家好,我是皮皮。一、前言前几天在Python白银交流群【空翼】问了一个Python网络爬虫的问题,提问截图如下:登录请求地址是这个:二、实现过程这里【甯同学】给了一个提示,如下所示:估计很多小伙伴和...