【问题标题】:Cythonising Pandas: ctypes for content, index and columnsCythonising Pandas:内容、索引和列的 ctypes
【发布时间】:2015-04-25 23:05:34
【问题描述】:

非常是 Cython 的新手,但我已经体验了非凡的加速,只需将我的 .py 复制到 .pyx(以及 cimport cythonnumpy 等)并导入到 @987654325 @ 和pyximport。 许多教程都是从这种方法开始的,下一步是为每种数据类型添加 cdef 声明,我可以为我的 for 循环等中的迭代器做这些。 但与大多数 Pandas Cython 教程或示例不同,我不是应用函数,而是使用切片、求和和除法(等)更多地操作数据。

所以问题是:我可以通过声明我的 DataFrame 仅包含浮点数 (double) 来提高我的代码运行速度,其中列是 int,行是 int

如何定义嵌入列表的类型?即[[int,int],[int]]

这是一个为 DF 分区生成 AIC 分数的示例,抱歉它太冗长了:

    cimport cython
    import numpy as np
    cimport numpy as np
    import pandas as pd

    offcat = [
        "breakingPeace", 
        "damage", 
        "deception", 
        "kill", 
        "miscellaneous", 
        "royalOffences", 
        "sexual", 
        "theft", 
        "violentTheft"
        ]

    def partitionAIC(EmpFrame, part, OffenceEstimateFrame, ReturnDeathEstimate=False):
        """EmpFrame is DataFrame of ints, part is nested list of ints, OffenceEstimate frame is DF of float"""
        """partOf/block is a list of ints"""
        """ll, AIC,  is series/frame of floats"""
        ##Cython cdefs
        cdef int DFlen
        cdef int puns
        cdef int DeathPun    
        cdef int k
        cdef int pId
        cdef int punish

        DFlen = EmpFrame.shape[1]
        puns = 2
        DeathPun = 0
        PartitionModel = pd.DataFrame(index = EmpFrame.index, columns = EmpFrame.columns)

        for partOf in part:
            Grouping = [puns*x + y for x in partOf for y in list(range(0,puns))]
            PartGroupSum = EmpFrame.iloc[:,Grouping].sum(axis=1)

            for punish in range(0,puns):
                PunishGroup = [x*puns+punish for x in partOf]
                punishPunishment = ((EmpFrame.iloc[:,PunishGroup].sum(axis = 1) + 1/puns).div(PartGroupSum+1)).values[np.newaxis].T
                PartitionModel.iloc[:,PunishGroup] = punishPunishment
        PartitionModel = PartitionModel*OffenceEstimateFrame

        if ReturnDeathEstimate:
            DeathProbFrame = pd.DataFrame([[part]], index=EmpFrame.index, columns=['Partition'])
            for pId,block in enumerate(part):
                DeathProbFrame[pId] = PartitionModel.iloc[:,block[::puns]].sum(axis=1)
            DeathProbFrame = DeathProbFrame.apply(lambda row: sorted( [ [format("%6.5f"%row[idx])]+[offcat[X] for X in  x ] 
                for idx,x in enumerate(row['Partition'])],
                key=lambda x: x[0], reverse=True),axis=1)
        ll = (EmpFrame*np.log(PartitionModel.convert_objects(convert_numeric=True))).sum(axis=1)
        k = (len(part))*(puns-1)
        AIC = 2*k-2*ll

        if ReturnDeathEstimate:
            return AIC, DeathProbFrame
        else:
            return AIC

【问题讨论】:

    标签: pandas cython


    【解决方案1】:

    我的建议是在 pandas 中尽可能地。这是一种标准的建议,“先让它工作,然后再关心性能,如果它真的很重要”。所以让我们假设你已经这样做了(希望你也写了一些测试),而且太慢了:

    分析您的代码。(请参阅this SO answer,或在 ipython 中使用 %prun)。

    prun 的输出应该会驱动下一步改进的地方

    1. pandas(让您的代码更具可扩展性,这可以帮助很多)。
    2. numpy(不创建中间系列/数据帧,注意数据类型)
    3. cython(最后的手段)。

    现在,如果它与切片有关(可能不是)将 微小部分 放在 cython 中,我喜欢删除对 cython 函数的单个 python 函数调用。在这一点上,带有 cython 的东西应该使用 numpy 而不是 pandas,我认为 pandas 不会降低到 C(cython 无法推断类型)。


    将整个代码放入 cython 实际上并没有太大帮助,您只想放入对性能敏感的特定行或函数调用。保持 cython 专注是享受美好时光的唯一方法。

    阅读enhancing performance section of the pandas docs*!在这里,这个过程(prun -> cythonize -> type)通过一个真实的例子一步一步地完成。

    *完全披露是我在文档的那部分写的! :)

    【讨论】:

    • 将我的整个代码放入 Cython 的帮助令人难以置信!从通宵跑到 20 分钟!!观察 CPU 状态,在 Python 中运行时,CPU 在 C1+ 中花费了大量时间。因此,受此启发,问题更多的是“如何获得全面加速”而不是优化。感谢您所做的文档和其他工作,这是达到我所做的工作的基础 8)。 Pandas 是否处理所有细胞类型并将其传递给 cython?
    • 嗯,它可能会,但我认为你可以通过只对一小部分代码(性能敏感位)进行 cythonising 来获得更有效的加速。当您使用 pandas 方法时,Pandas 会处理不同的 dtypes(这些方法本身已经被 vecorized 或用 cython 编写)。
    • 也就是说,为了响应“'如何获得全面加速'而不是优化”,您应该能够通过全面 cythonizing 的优化获得 更多 加速。
    猜你喜欢
    • 2016-09-02
    • 2011-09-25
    • 2017-01-16
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-20
    相关资源
    最近更新 更多