Python 数据分析——Pandas 时间序列
liuian 2025-04-09 17:53 46 浏览
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])
Truedrop()删除指定标签对应的行或列,下面删除名为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参数指定该列为标识列:
相关推荐
-
- 驱动网卡(怎么从新驱动网卡)
-
网卡一般是指为电脑主机提供有线无线网络功能的适配器。而网卡驱动指的就是电脑连接识别这些网卡型号的桥梁。网卡只有打上了网卡驱动才能正常使用。并不是说所有的网卡一插到电脑上面就能进行数据传输了,他都需要里面芯片组的驱动文件才能支持他进行数据传输...
-
2026-01-30 00:37 liuian
- win10更新助手装系统(微软win10更新助手)
-
1、点击首页“系统升级”的按钮,给出弹框,告诉用户需要上传IMEI码才能使用升级服务。同时给出同意和取消按钮。华为手机助手2、点击同意,则进入到“系统升级”功能华为手机助手华为手机助手3、在检测界面,...
- windows11专业版密钥最新(windows11专业版激活码永久)
-
Windows11专业版的正版密钥,我们是对windows的激活所必备的工具。该密钥我们可以通过微软商城或者通过计算机的硬件供应商去购买获得。获得了windows11专业版的正版密钥后,我...
-
- 手机删过的软件恢复(手机删除过的软件怎么恢复)
-
操作步骤:1、首先,我们需要先打开手机。然后在许多图标中找到带有[文件管理]文本的图标,然后单击“文件管理”进入页面。2、进入页面后,我们将在顶部看到一行文本:手机,最新信息,文档,视频,图片,音乐,收藏,最后是我们正在寻找的[更多],单击...
-
2026-01-29 23:55 liuian
- 一键ghost手动备份系统步骤(一键ghost 备份)
-
步骤1、首先把装有一键GHOST装系统的U盘插在电脑上,然后打开电脑马上按F2或DEL键入BIOS界面,然后就选择BOOT打USDHDD模式选择好,然后按F10键保存,电脑就会马上重启。 步骤...
- 怎么创建局域网(怎么创建局域网打游戏)
-
1、购买路由器一台。进入路由器把dhcp功能打开 2、购买一台交换机。从路由器lan端口拉出一条网线查到交换机的任意一个端口上。 3、两台以上电脑。从交换机任意端口拉出网线插到电脑上(电脑设置...
- 精灵驱动器官方下载(精灵驱动手机版下载)
-
是的。驱动精灵是一款集驱动管理和硬件检测于一体的、专业级的驱动管理和维护工具。驱动精灵为用户提供驱动备份、恢复、安装、删除、在线更新等实用功能。1、全新驱动精灵2012引擎,大幅提升硬件和驱动辨识能力...
- 一键还原系统步骤(一键还原系统有哪些)
-
1、首先需要下载安装一下Windows一键还原程序,在安装程序窗口中,点击“下一步”,弹出“用户许可协议”窗口,选择“我同意该许可协议的条款”,并点击“下一步”。 2、在弹出的“准备安装”窗口中,可...
- 电脑加速器哪个好(电脑加速器哪款好)
-
我认为pp加速器最好用,飞速土豆太懒,急速酷六根本不工作。pp加速器什么网页都加速,太任劳任怨了!以上是个人观点,具体性能请自己试。ps:我家电脑性能很好。迅游加速盒子是可以加速电脑的。因为有过之...
- 任何u盘都可以做启动盘吗(u盘必须做成启动盘才能装系统吗)
-
是的,需要注意,U盘的大小要在4G以上,最好是8G以上,因为启动盘里面需要装系统,内存小的话,不能用来安装系统。内存卡或者U盘或者移动硬盘都可以用来做启动盘安装系统。普通的U盘就可以,不过最好U盘...
- u盘怎么恢复文件(u盘文件恢复的方法)
-
开360安全卫士,点击上面的“功能大全”。点击文件恢复然后点击“数据”下的“文件恢复”功能。选择驱动接着选择需要恢复的驱动,选择接入的U盘。点击开始扫描选好就点击中间的“开始扫描”,开始扫描U盘数据。...
- 系统虚拟内存太低怎么办(系统虚拟内存占用过高什么原因)
-
1.检查系统虚拟内存使用情况,如果发现有大量的空闲内存,可以尝试释放一些不必要的进程,以释放内存空间。2.如果系统虚拟内存使用率较高,可以尝试增加系统虚拟内存的大小,以便更多的应用程序可以使用更多...
-
- 剪贴板权限设置方法(剪贴板访问权限)
-
1、首先打开iphone手机,触碰并按住单词或图像直到显示选择选项。2、其次,然后选取“拷贝”或“剪贴板”。3、勾选需要的“权限”,最后选择开启,即可完成苹果剪贴板权限设置。仅参考1.打开苹果手机设置按钮,点击【通用】。2.点击【键盘】,再...
-
2026-01-29 21:37 liuian
- 平板系统重装大师(平板重装win系统)
-
如果你的平板开不了机,但可以连接上电脑,那就能好办,楼主下载安装个平板刷机王到你的个人电脑上,然后连接你的平板,平板刷机王会自动识别你的平板,平板刷机王上有你平板的我刷机包,楼主点击下载一个,下载完成...
- 联想官网售后服务网点(联想官网售后服务热线)
-
联想3c服务中心是联想旗下的官方售后,是基于互联网O2O模式开发的全新服务平台。可以为终端用户提供多品牌手机、电脑以及其他3C类产品的维修、保养和保险服务。根据客户需求层次,联想服务针对个人及家庭客户...
- 一周热门
-
-
用什么工具在Win中查看8G大的log文件?
-
如何在 Windows 10 或 11 上通过命令行安装 Node.js 和 NPM
-
Trae IDE 如何与 GitHub 无缝对接?
-
如何修改图片拍摄日期?快速修改图片拍摄日期的6种方法
-
5步搞定动态考勤表!标记节假日、调休日?Excel自动变色!
-
RK3588-HDMIRX(瑞芯微rk3588芯片手册)
-
用纯Python轻松构建Web UI:Remi 动态更新,实时刷新界面内容
-
tplink无线路由器桥接教程(tplink路由器如何进行无线桥接)
-
R语言 | CNS绘图第1款——linkET万物皆可连
-
都说Feign是RPC,没有侵入性,为什么我的代码越来越像 C++
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
