Python 数据分析——Pandas 时间序列
liuian 2025-04-09 17:53 39 浏览
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参数指定该列为标识列:
相关推荐
-
- 移动路由器怎么改wifi密码(移动网络路由器怎么改密码wifi密码)
-
1.打开手机设置,找到wifi点击进入,点击已连接的wifi。2.里面有一个路由器的选项,记住路由器后面一串数字。3.打开手机网页,在地址栏输入刚记住的那串数字,点击进入,选择继续访问网页版,输入管理员密码,点击确定。4.点击路由设置,点击...
-
2025-12-23 16:05 liuian
-
- 手机五笔输入法怎么使用(手机五笔怎么用新手教程)
-
手机使用五笔输入法操作方法1、打开手机,点击应用商店,下载搜狗输入法,并安装。2、打开下载后的搜狗输入法,在打开页面勾选搜狗输入法,然后点击启用。3、在弹出页面,点击搜狗输入法。在编辑文字页面,点击如图所示按钮。4、在弹出页面,点击其他输入...
-
2025-12-23 15:55 liuian
- 天翼网关怎么设置wifi(天翼网关怎么设置wifi密码)
-
天翼网关设置wifi的方法步骤如下:首先打开浏览器,在地址栏中输入198.168.1.1,确定。输入用户名、密码,如果以前没改过,这个密码就在网关背面的标签上面,可以去查看一下,输入后点击“确定”。点...
- 十大软件免费下载安装(软件大全下载)
-
以下是十大永久免费电视直播软件APP:央视频(手机端)):官方看电视直播软件,画质清晰,需要借助手机投屏操作。央视投屏助手(盒子端):与央视频配合使用,提供高清电视直播。电视家:智能电视、电视盒子或智...
- 电脑死机咋办(电脑开机蓝屏怎么办)
-
可按照如下方式重启:1、直接按下“CTRL+ALT+DEL”键,然后选择重启;2、如方式1无效,可按住电源键数秒进行强制关机,然后再次启动即可;3、最后一个办法就是,拔出电池和电源适配器,然后重新安装...
- 追剧神器免费下载(苹果手机免费追剧软件)
-
是正规网站茶杯狐官方网站是一款正规的视频播放软件。茶杯狐官方网站拥有各种电影、电视剧、综艺、动漫等视频任由用户们在线自由观看。茶杯狐官方网站所有的视频画质也非常的超清流畅,很安全。茶杯狐软件是很好用的...
- 平板电脑一键root工具(平板电脑 root)
-
1、下载APP:ROOT大师;2、打开小米平板USB调试模式;3、安装驱动,提示连接成功4、获取ROOT权限。5、显示更新成功后依次选择:返回-重新启动-系统6、重启后打开桌面的super...
- 手机u盘驱动器下载(手机u盘驱动器下载安装)
-
不能直接手机下载到u盘,可以下到手机,然后手机连电脑copy到u盘你只需把你心爱的U盘插到电脑上,然后它会有一个新硬安装向导当它要搜索驱动程序时,你只需将其浏览到你所下载的usb万能驱动文件夹点下...
- 旺旺下载app(旺旺下载链接是多少)
-
可以下载多换几个下载软件试试直接登录官方appstore里面下载就可以使用了。1、首先在手机中找到appstore。2、点击进入后,在上方的搜索条里输入“阿里旺旺”,选中官方的阿里旺旺。3、点击进...
- 主机显卡在哪个位置(主机显卡在哪个位置看)
-
1、集成显卡的位置一般在机箱后面的中部,插口是竖立的。独立显卡的位置一般在机箱后面的下部,插口是横平的。2、独立显卡,一般插在主板中部的插槽,用手可以直接拔出。如果分不清哪个是显卡的插口,就直接看显示...
- windows7ultimate下载(windows7 ultimate)
-
要下载澪Ultimate启动器,您可以按照以下步骤进行操作:1.打开您的网络浏览器,访问澪Ultimate启动器的官方网站或相关下载页面。2.在网站上找到适用于您的操作系统的下载选项。3.点击下...
- 苹果首次激活时间查询(iphone激活查询官网入口)
-
登陆苹果官网输入手机的序列号,查看手机的保修日期就可以了,一般保修到期的前一年就是手机的开机激活时间。查看第一次开机激活时间操作方法:1、首先打开设置2、点击通用3、点击关于本机...
- u盘启动哪个好用(u盘启动盘哪个最好)
-
大白菜超级U盘启动盘制作工具,打造你真正的万能U盘!安装后你在U盘里是看到什么文件的,是隐藏分区,这样你放存别的东西时也不会搞乱了。大白菜超级U盘启动盘制作工具,是纯傻瓜式制作U盘启动盘功能的超级万能...
- 一周热门
- 最近发表
- 标签列表
-
- 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)
