仅用3行Python代码即可让数据脚本提升4倍的速度!

2023-12-29 06:08

本文主要是介绍仅用3行Python代码即可让数据脚本提升4倍的速度!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Python是一门非常适合处理数据和自动化完成重复性工作的编程语言,我们在用数据训练机器学习模型之前,通常都需要对数据进行预处理,而Python就非常适合完成这项工作,比如需要重新调整几十万张图像的尺寸,用Python没问题!你几乎总是能找到一款可以轻松完成数据处理工作的Python库。

然而,虽然Python易于学习,使用方便,但它并非运行速度最快的语言。默认情况下,Python程序使用一个CPU以单个进程运行。不过如果你是在最近几年配置的电脑,通常都是四核处理器,也就是有4个CPU。这就意味着在你苦苦等待Python脚本完成数据处理工作时,你的电脑其实有75%甚至更多的计算资源就在那闲着没事干!

今天我(作者Adam Geitgey——译者注)就教大家怎样通过并行运行Python函数,充分利用你的电脑的全部处理能力。得益于Python的 concurrent.futures 模块,我们只需3行代码,就能将一个普通数据处理脚本变为能并行处理数据的脚本,提速4倍。

普通Python处理数据方法

比方说,我们有一个全是图像数据的文件夹,想用Python为每张图像创建缩略图。

下面是一个短暂的脚本,用Python的内置glob函数获取文件夹中所有JPEG图像的列表,然后用Pillow图像处理库为每张图像保存大小为128像素的缩略图:

import glob
import os
from PIL import Image
def make_image_thumbnail(filename):# 缩略图会被命名为"<original_filename>_thumbnail.jpg"base_filename, file_extension = os.path.splitext(filename)thumbnail_filename = f"{base_filename}_thumbnail{file_extension}"# 创建和保存缩略图image = Image.open(filename)image.thumbnail(size=(128, 128))image.save(thumbnail_filename, "JPEG")return thumbnail_filename
# 循环文件夹中所有JPEG图像,为每张图像创建缩略图
for image_file in glob.glob("*.jpg"):thumbnail_file = make_image_thumbnail(image_file)
print(f"A thumbnail for {image_file} was saved as {thumbnail_file}")

这段脚本沿用了一个简单的模式,你会在数据处理脚本中经常见到这种方法:

  • 首先获得你想处理的文件(或其它数据)的列表

  • 写一个辅助函数,能够处理上述文件的单个数据

  • 使用for循环调用辅助函数,处理每一个单个数据,一次一个。

咱们用一个包含1000张JPEG图像的文件夹测试一下这段脚本,看看运行完要花多长时间:

$ time python3 thumbnails_1.py
A thumbnail for 1430028941_4db9dedd10.jpg was saved as 1430028941_4db9dedd10_thumbnail.jpg
[... about 1000 more lines of output ...]
real 0m8.956s
user 0m7.086s
sys 0m0.743s

运行程序花了8.9秒,但是电脑的真实工作强度怎样呢?

我们再运行一遍程序,看看程序运行时的活动监视器情况:

 

电脑有75%的处理资源处于闲置状态!这是什么情况?

这个问题的原因就是我的电脑有4个CPU,但Python只使用了一个。所以程序只是卯足了劲用其中一个CPU,另外3个却无所事事。因此我需要一种方法能将工作量分成4个我能并行处理的单独部分。幸运的是,Python中有个方法很容易能让我们做到!

试试创建多进程

下面是一种可以让我们并行处理数据的方法:

1.将JPEG文件划分为4小块。 2.运行Python解释器的4个单独实例。 3.让每个Python实例处理这4块数据中的一块。 4.将这4部分的处理结果合并,获得结果的最终列表。

4个Python拷贝程序在4个单独的CPU上运行,处理的工作量应该能比一个CPU大约高出4倍,对吧?

最妙的是,Python已经替我们做完了最麻烦的那部分工作。我们只需告诉它想运行哪个函数以及使用多少实例就行了,剩下的工作它会完成。整个过程我们只需要改动3行代码。

首先,我们需要导入concurrent.futures库,这个库就内置在Python中:

import concurrent.futures

接着,我们需要告诉Python启动4个额外的Python实例。我们通过让Python创建一个Process Pool来完成这一步:

with concurrent.futures.ProcessPoolExecutor() as executor:

默认情况下,它会为你电脑上的每个CPU创建一个Python进程,所以如果你有4个CPU,就会启动4个Python进程。

最后一步是让创建的Process Pool用这4个进程在数据列表上执行我们的辅助函数。完成这一步,我们要将已有的for循环:

for image_file in glob.glob("*.jpg"):
thumbnail_file = make_image_thumbnail(image_file)

替换为新的调用executor.map():

image_files = glob.glob("*.jpg")
for image_file, thumbnail_file in zip(image_files, executor.map(make_image_thumbnail, image_files)):

该executor.map()函数调用时需要输入辅助函数和待处理的数据列表。这个函数能帮我完成所有麻烦的工作,包括将列表分为多个子列表、将子列表发送到每个子进程、运行子进程以及合并结果等。干得漂亮!

这也能为我们返回每个函数调用的结果。Executor.map()函数会按照和输入数据相同的顺序返回结果。所以我用了Python的zip()函数作为捷径,一步获取原始文件名和每一步中的匹配结果。

这里是经过这三步改动后的程序代码:

import glob
import os
from PIL import Image
import concurrent.futures
def make_image_thumbnail(filename):# 缩略图会被命名为 "<original_filename>_thumbnail.jpg"base_filename, file_extension = os.path.splitext(filename)thumbnail_filename = f"{base_filename}_thumbnail{file_extension}"# 创建和保存缩略图image = Image.open(filename)image.thumbnail(size=(128, 128))image.save(thumbnail_filename, "JPEG")return thumbnail_filename
# 创建Process Pool,默认为电脑的每个CPU创建一个
with concurrent.futures.ProcessPoolExecutor() as executor:# 获取需要处理的文件列表image_files = glob.glob("*.jpg")# 处理文件列表,但通过Process Pool划分工作,使用全部CPU!for image_file, thumbnail_file in zip(image_files, executor.map(make_image_thumbnail, image_files)):print(f"A thumbnail for {image_file} was saved as {thumbnail_file}")

我们来运行一下这段脚本,看看它是否以更快的速度完成数据处理:

$ time python3 thumbnails_2.py
A thumbnail for 1430028941_4db9dedd10.jpg was saved as 1430028941_4db9dedd10_thumbnail.jpg
[... about 1000 more lines of output ...]
real 0m2.274s
user 0m8.959s
sys 0m0.951s

脚本在2.2秒就处理完了数据!比原来的版本提速4倍!之所以能更快的处理数据,是因为我们使用了4个CPU而不是1个。

但是如果你仔细看看,会发现“用户”时间几乎为9秒。那为何程序处理时间为2.2秒,但不知怎么搞得运行时间还是9秒?这似乎不太可能啊?

这是因为“用户”时间是所有CPU时间的总和,我们最终完成工作的CPU时间总和一样,都是9秒,但我们使用4个CPU完成的,实际处理数据时间只有2.2秒!

注意:启用更多Python进程以及给子进程分配数据都会占用时间,因此靠这个方法并不能保证总是能大幅提高速度。如果你要处理非常大的数据集,这里有篇设置将数据集切分成多少小块的文章,可以读读,会对你帮助甚大.

更多Python视频、源码、资料加群683380553免费获取

 

这种方法总能帮我的数据处理脚本提速吗?

如果你有一列数据,并且每个数据都能单独处理时,使用我们这里所说的Process Pools是一个提速的好方法。下面是一些适合使用并行处理的例子:

  • 从一系列单独的网页服务器日志里抓取统计数据。

  • 从一堆XML,CSV和JSON文件中解析数据。

  • 对大量图片数据做预处理,建立机器学习数据集。

但也要记住,Process Pools并不是万能的。使用Process Pool需要在独立的Python处理进程之间来回传递数据。如果你要处理的数据不能在处理过程中被有效地传递,这种方法就行不通了。简而言之,你处理的数据必须是Python知道怎么应对的类型。

同时,也无法按照一个预想的顺序处理数据。如果你需要前一步的处理结果来进行下一步,这种方法也行不通。

那GIL的问题呢?

你可能知道Python有个叫全局解释器锁(Global Interpreter Lock)的东西,即GIL。这意味着即使你的程序是多线程的,每个线程也只能执行一个Python指令。GIL确保任何时候都只有一个Python线程执行。换句话说,多线程的Python代码并不能真正地并行运行,从而无法充分利用多核CPU。

但是Process Pool能解决这个问题!因为我们是运行单独的Python实例,每个实例都有自己的GIL。这样我们获得是真正能并行处理的Python代码!

不要害怕并行处理!

有了concurrent.futures库,Python就能让你简简单单地修改一下脚本后,立刻让你电脑上所有CPU投入到工作中。不要害怕尝试这种方法,一旦你掌握了,它就跟一个for循环一样简单,却能让你的数据处理脚本快到飞起。

这篇关于仅用3行Python代码即可让数据脚本提升4倍的速度!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/548553

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal