标签归档:开发工具

这个神奇的工具,能自动将.py转换为.exe

Auto-py-to-exe 能够基于简单的GUI图形界面和Python中的 PyInstaller,将.py转换为.exe,非常容易使用,适合那些需要在windows上直接执行py文件但又没有Python运行环境的情景。

1. 安装和使用

通过 PyPI 安装

你可以使用PyPI安装此项目:

pip install auto-py-to-exe

然后运行它,在终端中执行以下命令:

auto-py-to-exe

通过 GitHub 安装

git clone https://github.com/brentvollebregt/auto-py-to-exe.git
cd auto-py-to-exe
python setup.py install

然后运行它,在终端中执行以下命令:

auto-py-to-exe

在本地通过 Github 运行(无需安装)

你可以通过以下步骤在本地运行此项目:

1. 克隆/下载 https://github.com/brentvollebregt/auto-py-to-exe

2. 打开 cmd 或终端并 cd 到该项目

3. 执行以下命令

python -m pip install -r requirements.txt

现在运行应用程序,执行:

python -m auto_py_to_exe

将在应用程序模式下打开一个Chrome窗口,并在其中运行本项目。

2. 使用本程序

1.选择您的脚本文件的位置(粘贴或使用文件浏览器),文件存在时轮廓将变为蓝色:

2. 选择其他选项并添加图标或附加文件之类的内容

3. 点击底部的蓝色大按钮进行转换

完成后当前终端所处目录的 output 文件夹中找到转换后的文件:

非常简单,大家有需要可以试试看。

参数使用

如果你不想使用可视化的GUI,也可以通过参数创建:

auto-py-to-exe [-nc] [-c [CONFIG]] [-o [PATH]] [filename]
参数类型描述
filenamepositional在用户界面中预先填写“脚本位置”字段。
-nc, –no-chromeoptional使用默认浏览器打开用户界面。 不会尝试寻找Chrome。
-nu, –no-uioptional不要试图在浏览器中打开界面。
-c [CONFIG], –config [CONFIG]optional提供配置文件(json)以预填充UI。 这些可以在设置选项卡中生成。
-o [PATH], –output-dir [PATH]optional设置默认输出目录。

当然,我建议还是使用GUI的方式,用起来比命令行的形式方便许多。不过你如果需要批量创建exe,那么确实参数形式更适合你。

导出导入配置

“设置”里有“配置导入和导出”部分,它可以将配置作为JSON字符串导出到剪贴板或文件,从而导出UI的当前状态。然后可以使用该JSON再次将配置导入到UI中,以重新填充所有字段。

3. 使用上出现问题

1.输出可执行文件很大

有时 pyinstaller 会自动添加它在你的环境中看到的包,即使你没有在被打包的项目中使用它们。这可能导致输出可执行文件的大小为数十到数百兆字节。

为了解决这个问题,最简单的方法是:

  1. 创建一个新的/干净的虚拟环境
  2. 将 auto-py-to-exe 安装到其中
  3. 为你的项目安装所需的模块
  4. 在这个虚拟环境中使用 auto-py-to-exe 来打包你的脚本

这样做意味着 pyinstaller 看不到你不需要捆绑的软件包,文件会被尽可能减小。

2.命令“python setup.py egg_info”失败,错误代码为 1

安装最新版 setuptools:

pip install --upgrade setuptools.

3.PermissionError: [Errno 13] 权限被拒绝: …

发生这种情况是因为你试图修改无权访问的目录中的文件。解决此问题的一种方法是通过以管理员身份打开 cmd 来运行具有管理员权限的脚本,然后 cd 到你希望输出的脚本的所在目录运行 auto-py-to-exe

更多的问题,可以在这篇文章中尝试查找解决方案:https://nitratine.net/blog/post/issues-when-using-auto-py-to-exe/

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 高效流程编排引擎 bamboo-pipeline 实战教程

Bamboo-pipeline 是蓝鲸智云旗下SaaS标准运维的流程编排引擎。其具备以下特点:

  • 1. 多种流程模式:支持串行、并行,支持子流程,可以根据全局参数自动选择分支执行,节点失败处理机制可配置。
  • 2. 参数引擎:支持参数共享,支持参数替换。
  • 3. 可交互的任务执行:任务执行中可以随时暂停、继续、撤销,节点失败后可以重试、跳过。

本文需要涉及到 Django, Celery 的前置知识,如果你还不了解这两者,建议谷歌或百度搜索了解一下,或者阅读我之前的文章:

Django:

Python Django快速开发音乐高潮提取网(1)

Python Django快速开发音乐高潮提取网(2)

Python Django快速开发音乐高潮提取网(3)

Pycharm+Django 安装及配置指南

手把手Django+Vue前后端分离入门实战教程

Celery:

Python celery异步快速下载股票数据

Django Celery 异步与定时任务实战教程

网络IO谁更快?Python与Go请求速度对比

什么?Python Celery 也能调度Go worker?

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install bamboo-engine
pip install bamboo-pipeline
pip install django
pip install celery

2. 项目初始化

(选项一:无Django项目)如果你没有任何的现成Django项目,请按下面的流程初始化

由于 bamboo-pipeline 运行时基于 Django 实现,所以需要新建一个 Django 项目:

django-admin startproject easy_pipeline
cd easy_pipeline

在 easy_pipeline.settings.py 下添加如下配置:

from pipeline.eri.celery.queues import *
from celery import Celery

app = Celery("proj")

app.config_from_object("django.conf:settings")

INSTALLED_APPS = [
    ...
    "pipeline",
    "pipeline.engine",
    "pipeline.component_framework",
    "pipeline.eri",
    ...
]

在 easy_pipeline目录下初始化数据库:

python manage.py migrate

(选项二:有Django项目需要使用流程引擎)如果你有现成的PipeLine项目需要使用此流程引擎,请在项目的 settings.py 下添加如下配置:

from pipeline.eri.celery.queues import *
from celery import Celery

app = Celery("proj")

app.config_from_object("django.conf:settings")

INSTALLED_APPS = [
    ...
    "pipeline",
    "pipeline.engine",
    "pipeline.component_framework",
    "pipeline.eri",
    ...
]

然后重新执行migrate,生成pipeline相关的流程模型:

python manage.py migrate

migrate 执行完毕后会如下图所示:

由于是在原有项目上使用流程引擎,可能会遇到一些版本不匹配的问题,如果遇到报错,请排查解决或到蓝鲸官网上进行询问。

3. 简单的流程编排实战

首先在项目目录下启动 celery worker:

python manage.py celery worker -Q er_execute,er_schedule --pool=solo -l info

启动成功类似下图所示:

(注意)如果你是在你的原有Django项目上做改造,它并不一定能够顺利地启动成功,这是因为Pipeline使用了 Django 2.2.24,会存在许多版本不兼容的情况。如果遇到报错,请排查解决或到蓝鲸官网上进行询问。

在下面的例子中,我们将会创建并执行一个简单的流程:

3.1 创建流程APP

在 bamboo_pipeline 中,一个流程由多个组件组成,官方推荐使用APP统一管控组件:

python manage.py create_plugins_app big_calculator

该命令会在 Django 工程根目录下生成拥有以下目录结构的 APP:

big_calculator
├── __init__.py
├── components
│   ├── __init__.py
│   └── collections
│       ├── __init__.py
│       └── plugins.py
├── migrations
│   └── __init__.py
└── static
    └── big_calculator
        └── plugins.js

别忘了把新创建的这个插件添加到 Django 配置的 INSTALLED_APPS 中:

INSTALLED_APPS = (
    ...
    'big_calculator',
    ...
)

3.2 编写流程的Service原子

组件服务 Service 是组件的核心,Service 定义了组件被调用时执行的逻辑,下面让我们实现一个计算传入的参数 n 的阶乘,并把结果写到输出中的 Service,在 big_calculator/components/collections/plugins.py 中输入以下代码:

import math
from pipeline.core.flow.activity import Service


class FactorialCalculateService(Service):

    def execute(self, data, parent_data):
        """
        组件被调用时的执行逻辑
        :param data: 当前节点的数据对象
        :param parent_data: 该节点所属流程的数据对象
        :return:
        """
        n = data.get_one_of_inputs('n')
        if not isinstance(n, int):
            data.outputs.ex_data = 'n must be a integer!'
            return False

        data.outputs.factorial_of_n = math.factorial(n)
        return True

    def inputs_format(self):
        """
        组件所需的输入字段,每个字段都包含字段名、字段键、字段类型及是否必填的说明。
        :return:必须返回一个 InputItem 的数组,返回的这些信息能够用于确认该组件需要获取什么样的输入数据。
        """
        return [
            Service.InputItem(name='integer n', key='n', type='int', required=True)
        ]

    def outputs_format(self):
        """
        组件执行成功时输出的字段,每个字段都包含字段名、字段键及字段类型的说明
        :return: 必须返回一个 OutputItem 的数组, 便于在流程上下文或后续节点中进行引用
        """
        return [
            Service.OutputItem(name='factorial of n', key='factorial_of_n', type='int')
        ]

首先我们继承了 Service 基类,并实现了 execute() 和 outputs_format() 这两个方法,他们的作用如下:

  • execute:组件被调用时执行的逻辑。接收 data 和 parent_data 两个参数。
    其中,data 是当前节点的数据对象,这个数据对象存储了用户传递给当前节点的参数的值以及当前节点输出的值。parent_data 则是该节点所属流程的数据对象,通常会将一些全局使用的常量存储在该对象中,如当前流程的执行者、流程的开始时间等。
  • outputs_format:组件执行成功时输出的字段,每个字段都包含字段名、字段键及字段类型的说明。这个方法必须返回一个 OutputItem 的数组,返回的这些信息能够用于确认某个组件在执行成功时输出的数据,便于在流程上下文或后续节点中进行引用。
  • inputs_format:组件所需的输入字段,每个字段都包含字段名、字段键、字段类型及是否必填的说明。这个方法必须返回一个 InputItem 的数组,返回的这些信息能够用于确认某个组件需要获取什么样的输入数据。

下面我们来看一下 execute() 方法内部执行的逻辑,首先我们尝试从当前节点数据对象的输出中获取输入参数 n,如果获取到的参数不是一个 int 实例,那么我们会将异常信息写入到当前节点输出的 ex_data 字段中,这个字段是引擎内部的保留字段,节点执行失败时产生的异常信息都应该写入到该字段中。随后我们返回 False 代表组件本次执行失败,随后节点会进入失败状态:

n = data.get_one_of_inputs('n')
if not isinstance(n, int):
    data.outputs.ex_data = 'n must be a integer!'
    return False

若获取到的 n 是一个正常的 int,我们就调用 math.factorial() 函数来计算 n 的阶乘,计算完成后,我们会将结果写入到输出的 factorial_of_n 字段中,以供流程中的其他节点使用:

data.outputs.factorial_of_n = math.factorial(n)
return True

3.3 编写流程组件,绑定Service原子

完成 Service 的编写后,我们需要将其与一个 Component 绑定起来,才能够注册到组件库中,在 big_calculator\components\__init__.py 文件下添加如下的代码:

import logging
from pipeline.component_framework.component import Component
from big_calculator.components.collections.plugins import FactorialCalculateService

logger = logging.getLogger('celery')


class FactorialCalculateComponent(Component):
    name = 'FactorialCalculateComponent'
    code = 'fac_cal_comp'
    bound_service = FactorialCalculateService

我们定义了一个继承自基类 Component 的类 FactorialCalculateComponent,他拥有以下属性:

  • name:组件名。
  • code:组件代码,这个代码必须是全局唯一的。
  • bound_service:与该组件绑定的 Service

这样一来,我们就完成了一个流程原子的开发。

3.4 生成流程,测试刚编写的组件

在 big_calculator\test.py 写入以下内容,生成一个流程,测试刚刚编写的组件:

# Python 实用宝典
# 2021/06/20

import time

from bamboo_engine.builder import *
from big_calculator.components import FactorialCalculateComponent
from pipeline.eri.runtime import BambooDjangoRuntime
from bamboo_engine import api
from bamboo_engine import builder


def bamboo_playground():
    """
    测试流程引擎
    """
    # 使用 builder 构造出流程描述结构
    start = EmptyStartEvent()
    # 这里使用 我们刚创建好的n阶乘组件
    act = ServiceActivity(component_code=FactorialCalculateComponent.code)
    # 传入参数
    act.component.inputs.n = Var(type=Var.PLAIN, value=4)
    end = EmptyEndEvent()

    start.extend(act).extend(end)

    pipeline = builder.build_tree(start)
    api.run_pipeline(runtime=BambooDjangoRuntime(), pipeline=pipeline)

    # 等待 1s 后获取流程执行结果
    time.sleep(1)

    result = api.get_execution_data_outputs(BambooDjangoRuntime(), act.id).data

    print(result)

随后,在命令行输入

python manage.py shell

打开 django console, 输入以下命令,执行此流程:

from big_calculator.test import bamboo_playground
bamboo_playground()

流程运行完后,获取节点的执行结果,可以看到,该节点输出了 factorial_of_n,并且值为 24(4 * 3 * 2 *1),这正是我们需要的效果:

{'_loop': 0, '_result': True, 'factorial_of_n': 24}

恭喜你,你已经成功的创建了一个流程并把它运行起来了!在这期间你可能会遇到不少的坑,建议尝试先自行解决,如果实在无法解决,可以前往 标准运维 仓库提 issues,或者前往蓝鲸智云官网提问。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Pandas 性能优化全方位实战教程

本文讲解了Pandas性能优化的几种方法,比如第一篇文章讲到了transform函数的应用、Cython编写C扩展、减少类型转换、使用特殊的数组。第二篇Pandas基础优化讲到了尽量使用内置原生函数,写代码尽量避免循环,尽量写能够向量化计算的代码,最后别忘了按照自己业务需求进行算法优化。第三篇进阶版优化讲到了一些更高级的优化技巧。

1.Pandas 性能优化 40 倍 – DataFrame

1. 1 性能优化小试牛刀

大名鼎鼎的Pandas是数据分析的神器。有时候我们需要对上千万甚至上亿的数据进行非常复杂处理,那么运行效率就是一个不能忽视的问题。

比如下面这个简单例子,我们随机生成100万条数据,对val这一列进行处理:如果是偶数则减1,奇数则加1。实际的数据分析工作要比这个例子复杂的多,但考虑到我们没有那么多时间等待运行结果,所以就偷个懒吧。可以看到transform函数的平均运行时间是284ms:

import pandas as pd
import numpy as np

def gen_data(size):
    d = dict()
    d["genre"] = np.random.choice(["A""B""C""D"], size=size)
    d["val"] = np.random.randint(low=0, high=100, size=size)
    return pd.DataFrame(d)

data = gen_data(1000000)
data.head()
def transform(data):
    data.loc[:, "new_val"] = data.val.apply(lambda x: x + 1 if x % 2 else x - 1)

%timeit -n 1 transform(data)
284 ms ± 8.95 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

1.2. 用Cython编写C扩展

试试用我们的老朋友Cython来写一下 x + 1 if x % 2 else x - 1 这个函数。平均运行时间降低到了202ms,果然速度变快了。性能大约提升了1.4倍,离40倍的flag还差的好远。

%load_ext cython
%%cython
cpdef int _transform(int x):
    if x % 2:
        return x + 1
    return x - 1

def transform(data):
    data.loc[:, "new_val"] = data.val.apply(_transform)

%timeit -n 1 transform(data)
202 ms ± 13.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

1.3. 减少类型转换

为了减少C和Python之间的类型转换,我们直接把val这一列作为Numpy数组传递给Cython函数,注意区分cnpnp。平均运行时间直接降到10.8毫秒,性能大约提升了26倍,仿佛看到了一丝希望。

%%cython
import numpy as np
cimport numpy as cnp
ctypedef cnp.int_t DTYPE_t

cpdef cnp.ndarray[DTYPE_t] _transform(cnp.ndarray[DTYPE_t] arr):
    cdef:
        int i = 0
        int n = arr.shape[0]
        int x
        cnp.ndarray[DTYPE_t] new_arr = np.empty_like(arr)

    while i < n:
        x = arr[i]
        if x % 2:
            new_arr[i] = x + 1
        else:
            new_arr[i] = x - 1
        i += 1
    return new_arr

def transform(data):
    data.loc[:, "new_val"] = _transform(data.val.values)

%timeit -n 1 transform(data)
10.8 ms ± 512 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

1.4. 使用不安全的数组

利用@cython.boundscheck(False)@cython.wraparound(False)装饰器关闭数组的边界检查和负下标处理,平均运行时间变为5.9毫秒。性能提升了42倍左右,顺利完成任务。

%%cython
import cython
import numpy as np
cimport numpy as cnp
ctypedef cnp.int_t DTYPE_t

@cython.boundscheck(False)
@cython.wraparound(False)
cpdef cnp.ndarray[DTYPE_t] _transform(cnp.ndarray[DTYPE_t] arr):
    cdef:
        int i = 0
        int n = arr.shape[0]
        int x
        cnp.ndarray[DTYPE_t] new_arr = np.empty_like(arr)

    while i < n:
        x = arr[i]
        if x % 2:
            new_arr[i] = x + 1
        else:
            new_arr[i] = x - 1
        i += 1
    return new_arr

def transform(data):
    data.loc[:, "new_val"] = _transform(data.val.values)

%timeit -n 1 transform(data)
6.76 ms ± 545 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

来源:Python中文社区

作者:李小文,先后从事过数据分析、数据挖掘工作,主要开发语言是Python,现任一家小型互联网公司的算法工程师。https://github.com/tushushu

2.Pandas性能优化:基础篇

Pandas 号称“数据挖掘瑞士军刀”,是数据处理最常用的库。在数据挖掘或者kaggle比赛中,我们经常使用pandas进行数据提取、分析、构造特征。而如果数据量很大,操作算法复杂,那么pandas的运行速度可能非常慢。本文根据实际工作中的经验,总结了一些pandas的使用技巧,帮助提高运行速度或减少内存占用。

2.1 按行迭代优化

很多时候,我们会按行对dataframe进行迭代,一般我们会用iterrows这个函数。在新版的pandas中,提供了一个更快的itertuples函数。

我们测试一下速度:

import pandas as pd
import numpy as np
import time
df = pd.DataFrame({'a': np.random.randn(1000),
                     'b': np.random.randn(1000),
                    'N': np.random.randint(100, 1000, (1000)),
                   'x':  np.random.randint(1, 10, (1000))})

%%timeit
a2=[]
for index,row in df.iterrows():
    temp=row['a']
    a2.append(temp*temp)
df['a2']=a2    

67.6 ms ± 3.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
a2=[]
for row in df.itertuples():
    temp=getattr(row, 'a')
    a2.append(temp*temp)
df['a2']=a2

1.54 ms ± 168 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

可以看到直接循环,itertuples速度是iterrows的很多倍。

所以如果在必须要对dataframe进行遍历的话,直接用itertuples替换iterrows。

2.2 apply 优化

一般情况下,如果要对dataframe里的数据逐行处理,而不需要上下文信息,可以使用apply函数。
对于上面的例子,我们使用apply看下:

%%timeit
df['a2']=df.a.apply(lambda x: x*x)

360 µs ± 355 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

可以看到,效率又有提升,apply的速度是itertuples的5倍左右。

注意一下,apply不光能对单个列值做处理,也能对多个列的值做处理。

%%timeit
df['a3']=df.apply( lambda row: row['a']*row['b'],axis=1)

15 ms ± 1.61 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

这里看出来,多值处理的时候几乎等于iterrows。因此比单列值apply慢了许多,所以这里不推荐对整行进行apply。

我们可以简单的这样改写:

%%timeit
df['a3']=df['a']*df['b']

204 µs ± 8.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

如果在计算的时候,使用series.values 提取numpy 数组并使用numpy原生函数计算,效率可能更高

%%timeit
df['a3']=np.multiply(df['a'].values,df['b'].values)

93.3 µs ± 1.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

2.3 聚合agg效率优化

有的时候,我们会对一列数据按值进行分组(groupby),再分组后,依次对每一组数据中的其他列进行聚合(agg)。

还是上面的那个dataframe,我们看下:
采用自定义函数的agg函数:

%%timeit
df.groupby("x")['a'].agg(lambda x:x.sum())

1.27 ms ± 45.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

采用agg内置函数:

%%timeit
df.groupby("x")['a'].agg(sum)
#等价df.groupby("x")['a'].sum()

415 µs ± 20.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

刚才是单列聚合,我们看下多列聚合:
自定义函数:

%%timeit
df.groupby("x").agg({"a":lambda x:x.sum(),"b":lambda x:x.count()})

2.6 ms ± 8.17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

采用内置函数:

%%timeit
df.groupby("x").agg({"a":"sum","b":"count"})

1.33 ms ± 29.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

可以看出,对于多列聚合,内置函数仍然比自定义函数快1倍。所以再进行聚合操作时,尽量使用内置函数提高效率。

下面列出了一些内置函数:

内置函数描述
count计数
sum求和
mean平均值
median中位数
min,max最大值/最小值
std,var标准差/方差
prod求积

当我们需要统计的方法没有对于内置函数的情况下,在自定义函数的时候,优先选用pandas或numpy内置的其他高效函数,如

df.groupby("x")['a'].agg(lambda x:np.sum(np.abs(x)))

在数据量很大的时候要比非numpy函数效率高(数据量小的时候差不多)。

2.4 数据读取优化

如果我们需要用pandas读取较多次文件,或者读取的文件较大,那么这方面占用的时间也会较长,我们也需要对其进行优化。pandas最常见读取函数是read_csv函数,可以对csv文件进行读取。但是pandas read_csv对读取较大的、数据结构复杂的文件,效率不是很理想。

这里有几种方法可以优化:

1. 分块读取csv文件
如果文件过大,内存不够或者时间上等不及,可以分块进行读取。

chunksize = 10 ** 6for chunk in pd.read_csv(filename,  chunksize=chunksize):
     process(chunk)

这里也可以使用多进程方式,具体我们在后面进阶篇介绍。

  1. 过滤掉不需要的列

如果读取的文件列很多,可以使用usecols字段,只load需要的列,提高效率,节约内存。

df = pd.read_csv(filename,  usecols=["a","b","c"]):
  1. 为列指定类型
    panda在read_csv的时候,会自动匹配列的数值类型,这样会导致速度很慢,并且占用内存较大。我们可以为每个列指定类型。
df = pd.read_csv("f500.csv", dtype = {"revenues" : "float64","name":str})
  1. 保存为其他格式
    如果需要频繁的读取和写入,则可以将文件保存为其他格式的,如pickle或hdf。pickle和hdf读取速度是csv的数倍。这里注意一下,pickle比原csv略小,hdf比原csv略大。
 import pandas as pd
 #读取csv
 df = pd.read_csv('filename.csv')
 
 #pickle格式
 df.to_pickle('filename.pkl') 
 df = pd.read_pickle('filename.pkl') 
 
 #hdf格式
 df.to_hdf('filename.hdf','df') 
 df = pd.read_hdf('filename.hdf','df') 

file typetimespeed
csv1.93 s1
pickle415 m4.6
hdf808 ms2.3
  1. 用第三方的包读取
    如可以使用Dask DataFrame读取大文件。第三方包放到最后细讲。

2.5 优化数据处理逻辑

这点算是业务角度优化。如果我们能直接数据处理的步骤,那么处理时间就少了很多。

这需要具体问题具体分析,举个例子,假设我们有一个通讯记录数据集:

call_areacall_seconds
03364
23075
25847
12032

call_seconds 是拨打的时长,单位是秒。
call_area=1 是拨打国内电话,费率是0.1/min,call_area=0 是拨打国外电话,费率是0.7/min
call_area=2 是接听电话,不收费。

我们随机生成一批数据:

import pandas as pd
import numpy as np
import time
import math
row_number=10000
df = pd.DataFrame({'call_area':  np.random.randint(0, 3, (row_number)),
                   'call_seconds':  np.random.randint(0, 10000, (row_number))})

如果我们想计算每个电话的费用:

def get_cost(call_area,call_seconds):
    if call_area==1:
        rate=0.1
    elif call_area==0:
        rate=0.7
    else:
        return 0
    return math.ceil(call_seconds/60)*rate

方法1,采用按行迭代循环

%%timeit
cost_list = []
for i,row in df.iterrows():
    call_area=row['call_area']
    call_seconds=row['call_seconds']
    cost_list.append(get_cost(call_area,call_seconds))
df['cost'] = cost_list

方法2,采用apply行的方式

%%timeit
df['cost']=df.apply(
         lambda row: get_cost(
             row['call_area'],
             row['call_seconds']),
         axis=1)

方法3,采用mask+loc,分组计算

%%timeit
mask1=df.call_area==1
mask2=df.call_area==0
mask3=df.call_area==2
df['call_mins']=np.ceil(df['call_seconds']/60)
df.loc[mask1,'cost']=df.loc[mask1,'call_mins']*0.1
df.loc[mask2,'cost']=df.loc[mask2,'call_mins']*0.7
df.loc[mask3,'cost']=0

方法4,使用numpy的方式,可以使用index的方式找到对应的费用。

%%timeit
prices = np.array([0.7, 0.1, 0])
mins=np.ceil(df['call_seconds'].values/60)
df['cost']=prices[df.call_area]*mins

测试结果:

方法运行时间运行速度
iterrows513 ms1
apply181 ms2.8
loc6.44 ms79.6
numpy219 µs2342

方法4速度快是因为它采用了numpy向量化的数据处理方式。

总结

在优化尽量使用内置原生函数,写代码尽量避免循环,尽量写能够向量化计算的代码,最后别忘了按照自己业务需求进行算法优化。

3.Pandas性能优化:进阶篇

在这里介绍一些更高级的pandas优化方法。

3.1 numpy

我们先来回顾一下上节说过的一个例子

import pandas as pd
import numpy as np
import time
row_number=100000
df = pd.DataFrame({'a': np.random.randn(row_number),
                     'b': np.random.randn(row_number),
                    'N': np.random.randint(100, 1000, (row_number)),
                   'x':  np.random.randint(1, 10, (row_number))})

我们要计算a列与b列的乘积

方法1,采用apply

%timeit df.apply( lambda row: row['a']*row['b'],axis=1)

方法2,直接对series做乘法

%timeit df['a']*df['b']

方法3,使用numpy函数

%timeit  np.multiply(df['a'].values,df['b'].values)
方法运行时间运行速度
方法11.45s1
方法2254µs5708
方法341.2 µs3536

这提示我们,采用一些好的方法可以大幅度提高pandas的运行速度。

3.2 cython

我们还继续使用上面的dataframe,现在定义一个函数:

def f(x):
    return x * (x - 1)

def integrate_f(a, b, N):
    s = 0
    dx = (b - a) / N
    for i in range(N):
        s += f(a + i * dx)
    return s * dx

我们要计算每一行integrate_f的值,

方法1,还是apply:

%timeit df.apply(lambda x: integrate_f(x['a'], x['b'], x['N'].astype(int)), axis=1)

这个函数运行时间就较长了:

7.05 s ± 54.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

考虑可以用cython重写程序,提高效率。
在使用cython的时候,可能需要安装gcc环境或者mingw(windows)。

方法1,直接加头编译

%load_ext Cython
%%cython
def f_plain(x):
    return x * (x - 1)

def integrate_f_plain(a, b, N):
    s = 0
    dx = (b - a) / N
    for i in range(N):
        s += f_plain(a + i * dx)
    return s * dx

6.46 s ± 41.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

看来直接加头,效率提升不大。

方法2,使用c type

%%cython
cdef double f_typed(double x) except? -2:
     return x * (x - 1)
cpdef double integrate_f_typed(double a, double b, int N):
    cdef int i
    cdef double s, dx
    s = 0
    dx = (b - a) / N
    for i in range(N):
        s += f_typed(a + i * dx)
    return s * dx

345 ms ± 529 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

可以看到,使用cython的特定编程方法,效率提升较大。

3.3 numba

numba是一个动态JIT编译器,在一些数值计算中可以大幅度提高运行速度。
我们学cython,在python程序上直接加numba jit的头。

import numba

@numba.jitdef f_plain(x):
    return x * (x - 1)

@numba.jitdef integrate_f_numba(a, b, N):
    s = 0
    dx = (b - a) / N
    for i in range(N):
        s += f_plain(a + i * dx)
    return s * dx

@numba.jitdef apply_integrate_f_numba(col_a, col_b, col_N):
    n = len(col_N)
    result = np.empty(n, dtype='float64')
    assert len(col_a) == len(col_b) == n
    for i in range(n):
        result[i] = integrate_f_numba(col_a[i], col_b[i], col_N[i])
    return result

def compute_numba(df):
    result = apply_integrate_f_numba(df['a'].to_numpy(),
                                     df['b'].to_numpy(),
                                     df['N'].to_numpy())
    return pd.Series(result, index=df.index, name='result')

 %timeit compute_numba(df)

6.44 ms ± 440 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

我们看到,使用numba,需要做的代码改动较小,效率提升幅度却很大!

3.4 进阶 并行化处理

并行化读取数据

在基础篇讲分块读取时,简单提了一下并行化处理,这里详细说下代码。

第一种思路,分块读取,多进程处理。

import pandas as pd
from multiprocessing import Pool
 
def process(df):
    """
  数据处理
    """
    pass
 
# initialise the iterator object
iterator = pd.read_csv('train.csv', chunksize=200000, compression='gzip',
skipinitialspace=True, encoding='utf-8')
# depends on how many cores you want to utilise
max_processors = 4# Reserve 4 cores for our script
pool = Pool(processes=max_processors)
f_list = []
for df in iterator:
    # 异步处理每个分块
    f = pool.apply_async(process, [df])
    f_list.append(f)
    if len(f_list) >= max_processors:
        for f in f_list:
            f.get()
            del f_list[:]

第二种思路,把大文件拆分成多份,多进程读取。

利用linux中的split命令,将csv切分成p个文件。

!split -l 200000 -d train.csv train_split
#将文件train.csv按每200000行分割,前缀名为train_split,并设置文件命名为数字

代码部分

from multiprocessing import Pool
import pandas as pd
import os
 
def read_func(file_path):
    df = pd.read_csv(file_path, header=None)
    return df
 
def read_file():
    file_list=["train_split%02d"%i for i in range(66)]
    p = Pool(4)
    res = p.map(read_func, file_list)
    p.close()
    p.join()
    df = pd.concat(res, axis=0, ignore_index=True)
    return df
 
df = read_file()

并行化apply

apply的func如果在用了我们之前说的技术优化了速度之后仍然很慢,或者func遇到网络阻塞,那么我们需要去并行化执行apply。这里提供一种处理思路:

import multiprocessing as mp
import time
def slow_func(s):
    time.sleep(1)
    return "done"with mp.Pool(mp.cpu_count()) as pool:
    df['newcol'] = pool.map(slow_func, df['qid'])

3.5 进阶 第三方pandas库

由于padans的操作如apply,都是单线程的,直接调用效率不高。我可以使用第三方库进行并行操作。
当然第三方库会带来新的代码不兼容问题。我们有时候会考虑像上一章一样,手写并行化处理。这个权衡需要我们在编程之初就要规划好,避免后期因为bug需要重构。

dask库

pip install dask

类pandas库,可以并行读取、运行。

import pandas as pd
import dask.dataframe as dd
from dask.multiprocessing import getand the syntax isdata = <your_pandas_dataframe>
ddata = dd.from_pandas(data, npartitions=30)

def some_function(x,y,z):
    return x+y+z

res = ddata.map_partitions(lambda df: df.apply((lambda row: myfunc(*row)), axis=1))
.compute(get=get)  

swifter

pip install swifter

pandas的插件,可以直接在pandas上操作:

import swifter

def some_function(data):
    return data * 10

data['out'] = data['in'].swifter.apply(some_function)

Modin库

Modin后端使用dask或者ray,是个支持分布式运行的类pandas库,当然功能异常强大。具体请看官网,这里就不具体介绍了。

https://modin.readthedocs.io/en/latest/using_modin.html

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Jupyter 中 IPython 可以用的12个方便命令

我在日常编程中一般都会用到两个工具——Pycharm和Jupyter,在刷算法、写爬虫时会用到前者,因为我习惯用Pycharm里的Debug功能调试,很容易找出代码中的Bug。而进行数据分析、机器学习时就会用到后者,因为Jupyter编译器利用的IPython是一种交互式计算和开发环境,而且有许多方便命令。
Jupyter对数据的可视化十分友好,这类单元格的形式每一步都有运行结果,便于整理自己思路,并且很大程度上节约了运行时间,在调试的时候只需要运行出错的部分代码,而不是全部。

IPython中有一些特有的魔法命令,如果能合理的利用这些魔法命令,会省去很多不必要的操作,为编程带来很大程度的便利,下面就来安利十二个常用的魔法命令。

方便命令的基础常识

  • ?和?? ->例:%matplotlib?、%matplotlib??

后缀为?可以获取一个对象的相关信息,比如描述一个方法该怎么用;后缀为??可以获取该对象更加详细的信息,比如源码。这个对象可以是IPython中自带的、也可以是导入的、也可以是自己定义的。

  • %和%% ->例:%time、%%time

前缀为%被称作行魔法命令(line magics),只能在单个输入行上运行;前缀为%%被称作单元格魔法命令(cell magics),可以在多个输入行上运行。

1.%Ismagic和%magic

如果你还不了解IPython的魔法命令,那这两个魔法命令一定是最重要的,记牢这两个命令之后慢慢了解剩下的。%lsmagic的作用就是列出所有存在的行魔法命令和单元格魔法命令,部分截图如下:

%magic的作用就是给出所有魔法命令的详细介绍,比如介绍、样例等等,比较考验英语功底,耐下心慢慢了解。

2.%pdb

输入这个命令并且运行之后,如果后面的代码出现了异常,这个指令就会主动进入调试器,几十行几百行代码难免会有几个或一堆Bug。可能比较笨的方法就是找断点然后print,最后还要把print删掉,而%pdb调试找到Bug后直接退出就好,相对前者更方便些。

比如两个数相加,不小心把一个整数定义成字符型,在调用函数计算时会发生报错,然后就可以进入调试器进行调试,切记最后要通过exit()退出,不能直接终止单元格运行。

3.%debug

%debug的作用与%pdb几乎是一样的,不同之处就是%pdb在遇到异常自动进入调试器,而%debug是人遇到报错主动输入指令进入调试器,仍然是上面那个例子,调试界面如下:
主动和被动两种调试方式大家可以靠自己喜好选择,我个人比较喜欢%debug。

4.%who和%whos

代码一多变量可能就会变多,变量一多可能就会混淆,或者在删除单元格的时候不小心把变量定义的单元格也删掉了,%who和%whos这两条命令就起到大作用了。
%who给出的信息只有全局变量的名称,而%whos给出的信息更加详细,包括变量名称、类型、和数据。

5.%time和%timeit

这两条命令都是用来输出代码的执行时间,比如可以用来粗略的比较两种算法在相同的问题上执行时间哪一个更少,不同点在于%time只执行一次就输出执行时间,而%timeit是执行多次然后计算平均时间再输出。
比如这里%timeit命令输出中有7 runs代表共执行7次,这两个命令都为行命令,%%time和%%timeit为单元格命令,区别同上。

6.%store

如果你在一个文件中花了很长的时间清理了一些数据,比如对原始数据缺失值填充呀、降维呀、转换呀等等,然后在另一个文件中需要用到同样的数据,笨一点方法就是将数据保存然后在新文件中调用,但这种操作一条%store命令就能完成,我们先在一个文件中利用%store保存一个变量。
然后在另一个文件中调用这个变量:
可以看到直接调用是会报错的,但利用了%store -r命令之后就可以成功调用被%store保存的变量,所以%store用来保存,%store -r用来读取。

7.%xdel和%reset

这条命令的作用就是删除变量,并且删除其在IPython中的对象上的一切引用。平时在数据清洗时,从原始数据到清洗后的数据中间要经过很多步骤,我们不可能全程用一个变量名称,所以中间步骤很容易为数据起一些类似的名称,而利用%xdel就可以将无用的单个变量名称删掉,防止混淆。
%reset的作用就是删除所有变量名。

8.%cls

在数据清洗时候,通常都是做一步然后输出一次数据集,观察一下变化,我们都知道展示数据集是很占网页的,久而久之,这个notebook就特别长,再想查看文件前面的内容不仅需要滚动很长时间滑轮,而且数据间很容易混淆,所以每当输出一次数据集后可以利用%cls命令清除一次,使notebook看起来更整洁。
可以看到正常的话data之后会打印数据集,但利用%cls之后数据集的输出被清除了。

9.%%writefile

如果我们想写一个函数,例如去除中文符号的函数,这样的函数在很多情景下都可以利用,所以我们可以将这个函数写入一个单独文件,想用的时候直接调用,这个操作可以利用%%writefile命令进行写入。

10.%run

%run命令的作用就是运行脚本文件,不仅可以直接使用脚本文件中的代码,脚本文件也可以使用IPython环境中的变量,仍用上面的例子,可以用%run命令直接运行。

11.%psource

如果你在notebook定义了一个函数,但隔了比较久需要用到这个函数,但是可能忘记了这个函数需要传入哪些参数、或者传入参数的类型应该是什么,这种情况下就不得不往前翻寻找这个函数的代码,但利用%psource可以偷懒,这个命令就是输出源代码。
前面提及的??也有相同的作用,但是输出的形式没有%psource直观,还混有其它的信息在里面。

12.%hist

%hist的作用就是打印所有命令行输入的历史记录,方便查看之前输入的代码信息。
这个命令允许设置查询的区间,也就是命令行输入对应的序号。
这些魔法命令有一部分能被常用的代码语句代替,但是却没有魔法命令简单明了,只是个人习惯的问题,如果可能尽量改掉自己的思维定式,用更加便捷的代码处理问题。
转自Python编程时光。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Birdseye 极其强大的Python调试工具

Birdseye是一个Python调试器,它在函数调用中记录表达式的值,并让你在函数**退出**后轻松查看它们。例如:

无论你如何运行或编辑代码,都可以使用Birdseye。只需要你安装好依赖:

pip install birdseye

并在代码函数上方添加 @eye 装饰器(如上所示),即可根据需要运行函数,并在浏览器中查看结果。

它还可以与一些常用工具集成在一起,如 Pycharm 和 Vscode,以提供更流畅的体验,后续我们会介绍如何将其与这些工具结合使用。

它不仅仅能够单步执行,还能在循环迭代中来回移动,并查看所选表达式的值如何变化:

通过 birdseye 你能很容易地知道哪些表达式引发了异常:

你也能够展开具体的数据结构和对象以查看其内容:

调用会按功能组织(文件组织)并进行时间排序进行显示,让你一目了然地看到发生了什么:

1.快速上手

首先,使用 pip 安装 birdseye :

pip install birdseye

然后,对需要进行调试的函数使用eye装饰器:

from birdseye import eye

@eye
def foo():

在你调用该函数完成后,在终端运行命令打开Birdseye的Web服务:

python -m birdseye

在浏览器打开 http://localhost:7777 就能看到需要调试的函数执行流程了。点击下图的按钮即可跳转到最新的函数调用。

2.Birdseye在Pycharm中集成调试

在 Pycharm 的 Settings 中,点击 Plugins 插件市场搜索 birdseye 点击 install 安装。

安装完成后重启Pycharm,就可以在 Pycharm 中使用 birdseye了:

默认情况下,该插件还可以为你自动运行Birdseye服务器,因此就不需要输入 python -m birdseye 那行命令了。

3.Birdseye在VSCode中集成调试

在VSCode中继承调试Birdseye也非常方便,点击左侧的扩展商店,在弹出框中输入搜索 birdseye,并点击 install 安装:

安装完成后,点击 F1 输入Birdseye,就能显示调试界面:

如果无法正常显示右侧调试界面,并提示未安装birdseye,但实际上你已经安装成功了,这一般是路径错误导致的,请在扩展设置中手动更改python路径为你安装了Birdseye的Python。

4.美中不足

Birdseye 是一个非常强大的调试工具,但我认为这还是有缺点可以改善的:

1.为了防止堆栈过大,每个迭代它最多只保留6个(前三、末三)元素:

因此如果你想看一些特殊元素值的执行情况,它可能不会如你所愿。

不过,不需要担心某些分支你调试不到,因为 birdseye 有个保险机制:如果一个表达式仅在某种特定情况下会被执行,那么执行时的元素也会被加入到可调试元素中

2.由于需要记录堆栈,程序会大大减慢速度,因此它绝对不适合上到生产环境。

3.每个函数调用,Birdseye 都需要收集许多数据,对于某些极其复杂的函数调用,可能会引发内存问题。

如果你不担心这三个缺点,而且希望能快速方便地看到函数中不同分支的执行情况,那么Birdseye就是你的不二之选。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Pyforest !自动导入代码所需的 Python 库

今天给大家介绍一个懒人 Python 库:Pyforest,只用一行代码,就能导入所有的 Python 库(本地已经安装的)。

项目地址:https://github.com/8080labs/pyforest

/ 01 / 介绍

Python 因为有着成千上万个功能强大开源库,备受大家的欢迎

目前,通过 PyPl 可以导入超过 23.5 万个 Python 库,数量庞大

在大家平常的实践当中,一般都是需要导入多个库或者框架来执行任务

而且每当新建一个程序文件时,都需要根据自己的需求导入相关的库

如果是相同类型的任务,比如想做一个数据可视化的小项目,可能会一直使用到某个库

如此,反复编写同一条 import 语句,就算是复制粘贴,也会感觉到麻烦,这时 Pyforest 库就可以上场了

Pyforest 是一个开源的 Python 库,可以自动导入代码中使用到的 Python 库

在进行数据可视化的时候,一般都需要导入多个库,比如 pandas、numpy、matplotlib 等等

使用了 Pyforest,每个程序文件中就不需要导入相同的 Python 库,而且也不必使用确切的导入语句

比如下面这行代码,就可以省略掉

from sklearn.ensemble import RandomForestClassifier

在你使用 import 语句导入Pyforest 库后,你就可以直接使用所有的 Python 库

import pyforest

df = pd.read_csv(‘test.csv’)
print(df)

你使用的任何库都不需要使用 import 语句导入,Pyforest 会为你自动导入。

只有在代码中调用库或创建库的对象后,才会导入库。如果一个库没有被使用或调用,Pyforest 将不会导入它。

/ 02 / 使用

安装,使用以下命令安装 Pyforest

pip install pyforest -i https://pypi.tuna.tsinghua.edu.cn/simple

安装成功后,使用 import 语句导入它

现在,你可以直接使用相关的 Python 库,无需编写 import 导入

先以 jupiter notebook 为例,我们没有导入 pandas、seaborn 和 matplotlib 库,但是我们可以通过导入 Pyforest 库直接使用它们

读取数据,这个是国内棉花产量排行前三的省份,新疆全国第一(数据来源:国家统计局)

那么 Pyforest 可以导入所有库吗?

目前这个包包含了大部分流行的 Python 库,比如

pandas as pd
NumPy as np
matplotlob.pyplot as plt
seaborn as sns 

除了这些库之外,它还提供了一些辅助的 Python 库,如 os、tqdm、re 等

如果你想查看库列表,可以使用 dir(pyforest) 进行查看,内置的是 68 个库

import pyforest

print(len(dir(pyforest)))
for i in dir(pyforest):
    print(i)

————————-
68
GradientBoostingClassifier
GradientBoostingRegressor
LazyImport
OneHotEncoder
Path
RandomForestClassifier
RandomForestRegressor
SparkContext
TSNE
TfidfVectorizer

如果没有的话,可以进行自定义添加,在主目录中的文件写入 import 语句

示例如下

vim ~/.pyforest/user_imports.py

添加语句,此处便能在代码中使用 requests 这个库

# Add your imports here, line by line
# e.g
# import pandas as pd
# from pathlib import Path
# import re

import requests as req
~                                                                               
~                                                                                                                                                                                                      
“~/.pyforest/user_imports.py” 7L129C

这回我们在 PyCharm 中来实验一下。

发现 PyCharm 的自动补全的功能失效了,看来这个库还是比较适合 jupyter notebook(自动补全代码还可以使用)

除了上面这个地方可以自定义添加,还可以在库的 _import.py 文件中添加

此处以 Pyechars 为例,缩写为 chart

可视化代码如下

新疆棉花产量年年上升,其它省份年年下降…

最后 Pyforest 还提供了一些函数来了解库的使用情况

# 返回已导入并且正在使用的库列表
print(pyforest.active_imports())
——————————–
[‘import pandas as pd’‘import requests as req’‘import pyg2plot’]


# 返回pyforest中所有Python库的列表
print(pyforest.lazy_imports())
——————————–
[‘import glob’‘import numpy as np’‘import matplotlib.pyplot as plt’…]

只有代码中有使用到的库,程序才会 import 进去,否则不会导入的哦!

/ 03 / 总结

好了,到此本期的分享就结束了

使用 Pyforest 库有时候确实是可以节省一些时间,不过也是有弊端存在的。比如调试的时候(大型项目),可能会很痛苦,不知道是哪里来的库

所以建议大家,在一些独立的脚本程序中使用,效果应该还是不错的

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

数据分析实战教程|Pandas处理数据太慢,来试试Polars吧!

很多人在学习数据分析的时候,肯定都会用到Pandas这个库,非常的实用!

从创建数据到读取各种格式的文件(text、csv、json),或者对数据进行切片和分割组合多个数据源,Pandas都能够很好的满足。

Pandas最初发布于2008年,使用Python、Cython和C编写的。是一个超级强大、快速易于使用的Python库,用于数据分析和处理。

当然Pandas也是有不足之处的,比如不具备多处理器,处理较大的数据集速度很慢。

今天,小F就给大家介绍一个新兴的Python库——Polars。

使用语法和Pandas差不多,处理数据的速度却比Pandas快了不少。

一个是大熊猫,一个是北极熊~

GitHub地址:https://github.com/ritchie46/polars

使用文档:https://ritchie46.github.io/polars-book/

Polars是通过Rust编写的一个库,Polars的内存模型是基于Apache Arrow。

Polars存在两种API,一种是Eager API,另一种则是Lazy API。

其中Eager API和Pandas的使用类似,语法差不太多,立即执行就能产生结果。

而Lazy API就像Spark,首先将查询转换为逻辑计划,然后对计划进行重组优化,以减少执行时间和内存使用。

安装Polars,使用百度pip源。

# 安装polars
pip install polars -i https://mirror.baidu.com/pypi/simple/

安装成功后,开始测试,比较Pandas和Polars处理数据的情况。

使用某网站注册用户的用户名数据进行分析,包含约2600万个用户名的CSV文件。

import pandas as pd

df = pd.read_csv(‘users.csv’)
print(df)

数据情况如下。

此外还使用了一个自己创建的CSV文件,用以数据整合测试。

import pandas as pd

df = pd.read_csv(‘fake_user.csv’)
print(df)

得到结果如下。

首先比较一下两个库的排序算法耗时。

import timeit
import pandas as pd

start = timeit.default_timer()

df = pd.read_csv(‘users.csv’)
df.sort_values(‘n’, ascending=False)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

————————-
Time:  27.555776743218303

可以看到使用Pandas对数据进行排序,花费了大约28s。

import timeit
import polars as pl

start = timeit.default_timer()

df = pl.read_csv(‘users.csv’)
df.sort(by_column=‘n’, reverse=True)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

———————–
Time:  9.924110282212496

Polars只花费了约10s,这意味着Polars比Pandas快了2.7倍。

下面,我们来试试数据整合的效果,纵向连接。

import timeit
import pandas as pd

start = timeit.default_timer()

df_users = pd.read_csv(‘users.csv’)
df_fake = pd.read_csv(‘fake_user.csv’)
df_users.append(df_fake, ignore_index=True)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

————————
Time:  15.556222308427095

使用Pandas耗时15s。

import timeit
import polars as pl

start = timeit.default_timer()

df_users = pl.read_csv(‘users.csv’)
df_fake = pl.read_csv(‘fake_user.csv’)
df_users.vstack(df_fake)
stop = timeit.default_timer()

print(‘Time: ‘, stop – start)

———————–
Time:  3.475433263927698

Polars居然最使用了约3.5s,这里Polars比Pandas快了4.5倍。

通过上面的比较,Polars在处理速度上表现得相当不错。

可以是大家在未来处理数据时,另一种选择~

当然,Pandas目前历时12年,已经形成了很成熟的生态,支持很多其它的数据分析库。

Polars则是一个较新的库,不足的地方还有很多。

如果你的数据集对于Pandas来说太大,对于Spark来说太小,那么Polars便是你可以考虑的一个选择。

本文转载自公众号【法纳斯特】

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!


​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典