Cythonising Pandas:内容,索引和列的ctypes

Spm*_*pmP 6 cython pandas

我是新的,以用Cython,但我已经经历了非凡的加速只复制我.py.pyx(和cimport cython,numpy等等),并导入到ipython3pyximport.许多教程都是从这种方法开始的,下一步是cdef为每个数据类型添加声明,我可以为for循环中的迭代器做.但与大多数Pandas Cython教程或示例不同,我不应用函数,可以这么说,更多使用切片,求和和(等)来操纵数据.

所以问题是:我可以通过声明我的DataFrame只包含floats(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
Run Code Online (Sandbox Code Playgroud)

And*_*den 8

我的建议是尽可能多地做大熊猫.这是一种标准的建议"让它先工作,然后关注性能,如果真的很重要".所以让我们假设你已经完成了(希望你也写了一些测试),而且它太慢了:

描述您的代码.(请参阅此SO答案,或在ipython中使用%prun).

prun的输出应该驱动接下来要改进的位.

  1. 熊猫(让你的代码更加宽松,这可以帮助很多).
  2. numpy(不创建中间Series/DataFrames,小心dtypes)
  3. cython(最后的手段).

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


将整个代码放入cython实际上并没有多大帮助,你只想放置对性能敏感的特定行或函数调用.保持cython聚焦是获得美好时光的唯一方法.

阅读pandas docs*的增强性能部分!这个过程(prun - > cythonize - > type)通过一个真实的例子逐步完成.

*完全透露我写的那部分文档!:)