【Python】为Pandas加速(适合Pandas中级开发者)

非常好的一篇文章,解决问题的方式和思路层层递进,透彻深刻。

Pandas是个好工具,好工具要用正确高效的方式使用,才能发挥出万钧之力。

英文水平较高可直接阅读原文。Fast, Flexible, Easy and Intuitive: How to Speed Up Your pandas Projects – Real Python

逛了一下,这个原版国外网站内容很丰富,挺不错。

这里有个 Data Science 合集,点击可进入看更多文章:Python Data Science – Real Python

其核心观点是使用Pandas的矢量化操作(也可翻译为:向量化计算)特性,而非使用极其原始的for循环,可大幅提升Pandas操作数据的性能。

本博客翻译如下:(意译为主,省略废话,代码全部保留)


前言

本文是一个使用Pandas的指南,以充分利用其强大且易于使用的内置功能。此外,您将学习一些实用的加快处理的技巧。

Python风格代码可能不是最有效率的。和NumPy库一样,pandas被设计用来进行向量化操作,一次处理整列或者一整个数据集。不要再最开始的时候,就考虑如何处理每一个单元格或每一行,而应该再试过其他全部方法之后。

本文主要涉及以下三个内容:

  • 使用 datetime 类型处理时间序列的优势

  • 批量计算的最有效途径

  • 使用HDFStore存储数据来节省时间

使用Pandas,有很多种方法可以实现从A到B,但是并不是所有方法都能高效的扩大至更大的数据量。

阅读本文的前提是需熟练掌握Pandas库的数据选择和切片等操作。(译者注:若对某些Pandas方法不熟,可使用Kimi.ai)

案例

这个例子的目标是应用分时电价来计算一年的能源消耗总成本。也就是说,在一天中的不同时间,电价是不同的,所以任务是将每小时消耗的电量乘以消耗时的正确价格。

从CSV文件中读取数据,A列是时间,B列是耗电量(度)。

每一行包含每个小时使用的电量,因此全年有365 x 24=8760行。每行表示当时“起始小时”的使用情况,因此1/1/13 0:00表示2013年1月1日第一个小时的使用情况。

使用Datetime类型加速

>>> import pandas as pd
>>> pd.__version__
'0.23.1'

# Make sure that `demand_profile.csv` is in your
# current working directory.
>>> df = pd.read_csv('demand_profile.csv')
>>> df.head()
     date_time  energy_kwh
0  1/1/13 0:00       0.586
1  1/1/13 1:00       0.580
2  1/1/13 2:00       0.572
3  1/1/13 3:00       0.596
4  1/1/13 4:00       0.592

初看没有问题,但其实有个小毛病。Pandas和Numpy有数据类型(dtypes)的概念。如果没有指定类型, date_time 会使用  object 类型:

>>> df.dtypes
date_time      object
energy_kwh    float64
dtype: object

>>> type(df.iat[0, 0])
str

这不是最佳选择。 object 不仅是 str 类型的容器,任何没有合适数据类型的列都会被存放到 object 中。将日期数据作为字符串处理是非常低效的。(同时也很消耗内存)。

处理时间序列数据,更好的方法是将 date_time 列格式化为 datetime 对象的数组。(Pandas中称之为 Timestamp。)Pandas处理的更加简洁:

>>> df['date_time'] = pd.to_datetime(df['date_time'])
>>> df['date_time'].dtype
datetime64[ns]

(注意:本例中你还可以使用 Pandas 的 PeriodIndex 索引。)

df 如下:

>>> df.head()
               date_time    energy_kwh
0    2013-01-01 00:00:00         0.586
1    2013-01-01 01:00:00         0.580
2    2013-01-01 02:00:00         0.572
3    2013-01-01 03:00:00         0.596
4    2013-01-01 04:00:00         0.592

如何测试代码耗时?可以使用一个 timing decorator,这里我们称之为 @timeit。这个 decorator 最大程度的模仿了 Python 标准库中的 timeit.repeat() ,但是它能返回函数执行结果,并打印多次试验的平均运行时长。(Python标准库的 timeit.repeat() 只能返回时间结果,而不含函数结果)。

>>> @timeit(repeat=3, number=10)
... def convert(df, column_name):
...     return pd.to_datetime(df[column_name])

>>> # Read in again so that we have `object` dtype to start 
>>> df['date_time'] = convert(df, 'date_time')
Best of 3 trials with 10 function calls per trial:
Function `convert` ran in average of 1.610 seconds.

8760行数据耗费1.6秒。还不错,但是如果要处理更大的数据量,比如数据采集频率加快到每分钟采集一次数据,那么此时的全年用电量数据,将是现在数据量的60倍多,这个代码将运行1分钟30秒。

实际上,我最近分析了来自330个网站的共10年的每小时电量数据。如果只是用来转换时间数据的类型,就要等待88分钟,那难以想象!

那该如何加速呢?只需使用 format 参数告诉 Pandas 你的时间数据的具体格式即可。

>>> @timeit(repeat=3, number=100)
>>> def convert_with_format(df, column_name):
...     return pd.to_datetime(df[column_name],
...                           format='%d/%m/%y %H:%M')
Best of 3 trials with 100 function calls per trial:
Function `convert_with_format` ran in average of 0.032 seconds.

现在的耗时是0.032秒,效率是前面的50倍。如果你要从330个网页中处理数据,你就能节省86分钟,很大的进步。

还有一点需要说明,CSV中的时间格式不是 ISO 8601 格式:YYYY-MM-DD HH:MM。如果你不指定格式,Pandas 会使用 dateutil 包将字符串转为日期。

相反,如果时间数据已经是 ISO 8601 格式,Pandas 可以立即解析为日期。这就是为什么格式化时间后效率大幅提升的一个原因。另一个方式是传递 infer_datetime_format = True 参数。

注意:Pandas 的read_csv()允许在读写文件的同时解析日期。请参阅parse_dates、infer_datetime_format和date_parser参数。

简化对Pandas数据的循环

现在日期和时间已经转成正确的格式,现在可以开始计算电费了。请记住,电费因小时而异,因此您需要根据每个小时的不同电费单价计算总电费。在本例中,每小时的电费定义如下:

电费类型美分/度 时间段
高峰2817:00-24:00
平时207:00-17:00
低谷120:00-7:00

如果电价永远都是 28 美分/度,则代码就很简单:

>>> df['cost_cents'] = df['energy_kwh'] * 28

这样 df 中会产生一个新的列,代表每个小时的电费:

date_time    energy_kwh       cost_cents
0    2013-01-01 00:00:00         0.586           16.408
1    2013-01-01 01:00:00         0.580           16.240
2    2013-01-01 02:00:00         0.572           16.016
3    2013-01-01 03:00:00         0.596           16.688
4    2013-01-01 04:00:00         0.592           16.576
# ...

但我们要计算的电费因时间段不同而单价不同。我们会看到大多数人会这么思考如何写这段代码:写一个循环针对不同时间分别计算。

本文的后面,我们将从一个简陋的方案直到一个最能发挥 Pandas 特性的方案。

先看看循环方法,循环对于不熟悉 Pandas 设计原则的初学者时最喜欢的方案。

写一个普通的循环方法。

def apply_tariff(kwh, hour):
    """Calculates cost of electricity for given hour."""    
    if 0 <= hour < 7:
        rate = 12
    elif 7 <= hour < 17:
        rate = 20
    elif 17 <= hour < 24:
        rate = 28
    else:
        raise ValueError(f'Invalid hour: {hour}')
    return rate * kwh

应用到 df 中:

>>> # NOTE: Don't do this!
>>> @timeit(repeat=3, number=100)
... def apply_tariff_loop(df):
...     """Calculate costs in loop.  Modifies `df` inplace."""
...     energy_cost_list = []
...     for i in range(len(df)):
...         # Get electricity used and hour of day
...         energy_used = df.iloc[i]['energy_kwh']
...         hour = df.iloc[i]['date_time'].hour
...         energy_cost = apply_tariff(energy_used, hour)
...         energy_cost_list.append(energy_cost)
...     df['cost_cents'] = energy_cost_list
... 
>>> apply_tariff_loop(df)
Best of 3 trials with 100 function calls per trial:
Function `apply_tariff_loop` ran in average of 3.152 seconds.

上面这段代码看着似乎没有大问题,但是这个循环显得很呆。也可以说时反模式的,反“Pandas”的模式。它有几个问题。

首先,它需要初始化一个列表,这个列表用来存储结果。

其次,它使用不准确(opaque)的对象 range(0,len(df)) 来作为循环计算,然后在应用apply_tariff() 之后,它必须将结果附加到用于创建新DataFrame列的列表中。它还使用df.iloc[i]['date_time']进行所谓的链式索引,这通常会导致意想不到的结果。

(译者注:在循环遍历 df 的时候,又修改 df ,可能导致意外错误)

但是最大的问题还是费时,8760行数据花费了3秒钟。下面看改进后的方法。

使用itertuples() 和 iterrows()方法循环

未完待续。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902464.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux创建自定义服务部署项目

1.进入linux单元服务文件夹 cd /etc/systemd/system/ 2.创建一个文件以.service结尾的文件 C#(.Net Core)、 Java、Python等语言&#xff0c;都可以通过linux自定义服务来部署项目&#xff0c;实现守护进程、实现开机自启 2.1例如创建my.service文件 这里以部署python项目为…

新华三H3CNE网络工程师认证—OSPF路由协议

OSPF是典型的链路状态路由协议&#xff0c;是目前业内使用非常广泛的IGP协议之一。本博客将对OSPF路由协议进行总结。 OSPF目前针对IPv4协议使用的是OSPFVersion2(RFC2328)&#xff1b; 针对IPv6协议使用OSPFVersion3(RFC2740)。如无特殊说明本章后续所指的OSPF均为OSPF Versi…

HBase2.4.17 修改znode后master初始化失败

正常运行的hbase服务&#xff0c;修改zookeeper.znode.parent后&#xff0c;重启。hbase master服务可以启动成功&#xff0c;但是仅有meta表上线&#xff0c;且hbase:meta表中元数据丢失。仅残留table:state列的值&#xff0c;其他列的值全部丢失。 有大佬知道是怎么回事嘛

(二十四)、在 k8s 中部署自己的 jar 镜像(以 springcloud web 项目为例)

文章目录 1、环境陈述2、前期准备2.1、将一个 SpringCloud 微服务运行 以 jar 方式运行2.2、为 SpringCloud 项目生成 Docker 镜像2.3、推送镜像2.4、从宿主机访问 k8s(minikube) 发布的 redis 服务2.5、k8s(minikube) 部署mysql 3、本期关键3.1、打 jar 包需要修改的地方3.2、…

Anchor DETR:Transformer-Based目标检测的Query设计

写在前面 文中指出之前DETR-like算法存在以下问题&#xff1a; 之前DETR-liked检测算法里&#xff0c;object query是一组可学习的嵌入表示&#xff08;就是一组256-d的向量&#xff09;&#xff0c;缺乏明确的物理意义&#xff0c;不能解释它们会关注什么地方。每个object q…

禾川SV-X2E A伺服驱动器参数设置——脉冲型

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff01;人工智能学习网站 前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任…

PHPOK 4.8.338 后台任意文件上传漏洞(CVE-2018-12941)复现

PHPOK企业站(简称PHPOK&#xff09;建设系统是一套基于PHP和MySQL构建的高效企业网站建设方案之一&#xff0c;全面针对企业网&#xff08;以展示为中心&#xff09;进行合理的设计规划。 PHPOK是一套开源免费的建站系统&#xff0c;可以在遵守LGPL协议的基础上免费使用。系统具…

浅析正交投影矩阵和透视投影矩阵的推导

先上矩阵的内容。在opengl中&#xff0c;分别通过glOrtho函数和glFrustum函数得到正交投影矩阵和透视投影矩阵。 glOrtho 函数描述生成正交投影矩阵。 (左、 下、 近) 和 (右、 上、 近) 参数分别指定近剪裁平面上映射到窗口左下角和右上角的点&#xff0c;假定眼睛位于 (0、0…

Vue104 vue3 组合式API的优势 对比配置式API

笔记 1.Options API 存在的问题 使用传统OptionsAPI中&#xff0c;新增或者修改一个需求&#xff0c;就需要分别在data&#xff0c;methods&#xff0c;computed里修改 。 2.Composition API 的优势 我们可以更加优雅的组织我们的代码&#xff0c;函数。让相关功能的代码更…

三方接口调用设计方案

在为第三方系统提供接口的时候&#xff0c;肯定要考虑接口数据的安全问题&#xff0c;比如数据是否被篡改&#xff0c;数据是否已经过时&#xff0c;数据是否可以重复提交等问题 在设计三方接口调用的方案时&#xff0c;需要考虑到安全性和可用性。以下是一种设计方案的概述&am…

Jenkins + GitLab + Docker实现自动化部署(Java项目)

部署Jenkins 因为Jenkins需要用到git、jdk、maven&#xff0c;后两者我们采取挂载的方式&#xff0c;git采用直接在容器中安装的方式&#xff0c;所以我们要做一个增强版的Jenkins镜像&#xff0c;编写Dockerfile文件 #使用jenkins 镜像作为基础镜像 FROM jenkins/jenkins:lt…

Spring Boot框架下租房管理系统的设计与实现

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

springboot098基于web的网上摄影工作室的开发与实现(论文+源码)_kaic

网上摄影工作室 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了网上摄影工作室的开发全过程。通过分析网上摄影工作室管理的不足&#xff0c;创建了一个计算机管理网上摄影工作室的方案。文章介绍了网上摄影工…

Kubernetes实战——DevOps集成SpringBoot项目

目录 一、安装Gitlab 1、安装并配置Gitlab 1.1 、下载安装包 1.2、安装 1.3、修改配置文件 1.4、更新配置并重启 2、配置 2.1、修改密码 2.2、禁用注册功能 2.3、取消头像 2.4、修改中文配置 2.5、配置 webhook 3、卸载 二、安装镜像私服Harbor 1、下载安装包 2、…

UE5之5.4 第一人称示例代码阅读1 FirstPersonProjectile

既然如此&#xff0c;这几个文件都看看 先看看FirstPersonProjectile头文件 定义了几个函数 然后是两个component 这个projectilemovement应该是控制物理运动的 看看CPP文件 sphere那个就创建了一个subobject&#xff0c;初始化了一下&#xff0c;然后这里 CollisionComp-&g…

Maven 项目构建打包,如何引入本地 Jar 包?

上一篇讲到 Maven 离线仓库的使用&#xff0c;反响不错很多人收藏&#xff0c;这一篇还是继续聊 Maven 。假如你发现某开源项目有个 bug 影响到自己的系统&#xff0c;但官方还没修复&#xff0c;自己定位到了本地修改打了包先应急用&#xff0c;那么如何在其他项目上使用该包&…

985研一,转嵌入式好还是后端开发好?

有个老铁问&#xff0c;985研一&#xff0c;转嵌入式好还是后端开发好&#xff1f; 我认为&#xff0c;这学历&#xff0c;两个随便挑&#xff0c;我说的&#xff0c;从趋势来看&#xff0c;更建议嵌入式&#xff0c;走供应链上游&#xff0c;芯片原厂、新能源车企、军工或者搞…

Python画图|极坐标下的柱状图输出

【1】引言 前序学习了极坐标下的散点图输出&#xff0c;可通过下述链接直达&#xff1a; 西猫雷婶-CSDN博客 受此启发&#xff0c;我们继续自主探索极坐标下的柱状图输出。 【2】代码探索 其实柱状图和散点图画图的主要区别&#xff0c;可以理解为调用函数不同。 柱状图调…

Golang | Leetcode Golang题解之第515题在每个树行中找最大值

题目&#xff1a; 题解&#xff1a; func largestValues(root *TreeNode) (ans []int) {if root nil {return}q : []*TreeNode{root}for len(q) > 0 {maxVal : math.MinInt32tmp : qq nilfor _, node : range tmp {maxVal max(maxVal, node.Val)if node.Left ! nil {q …

stm32单片机个人学习笔记12(DMA直接存储器存取)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…