2015-04-25 13 views
5

io sono molto nuovo a Cython, ma sto già sperimentando incrementi nella velocità straordinarie semplicemente copiando il mio .py-.pyx (e cimport cython, numpy ecc) e l'importazione in ipython3 con pyximport. Molti approcci iniziano con questo approccio e il passo successivo consiste nell'aggiungere le dichiarazioni cdef per ogni tipo di dati, che posso fare per gli iteratori nei miei cicli for ecc. Ma diversamente dalla maggior parte dei tutorial o degli esempi di Pandas Cython, non sono applicabile le funzioni per parla, manipola più dati usando slice, somme e divisioni (etc).Cythonising Pandas: ctypes per i contenuti, indice e colonne

Quindi la domanda è: Posso aumentare la velocità con cui il mio codice viene eseguito, affermando che la mia dataframe contiene solo carri (double), con colonne che sono int e le righe che sono int?

Come definire il tipo di un elenco incorporato? cioè [[int,int],[int]]

Ecco un esempio che genera il punteggio AIC per un partizionamento di un DF, mi dispiace è così dettagliata:

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 

risposta

6

Il mio consiglio è di fare il più possibile in panda. Questo è un consiglio standard "fallo funzionare prima, poi ti preoccupi delle prestazioni se è davvero importante". Supponiamo quindi di averlo fatto (speriamo che abbiate scritto anche alcuni test), ed è troppo lento:

Configura il tuo codice. (Vedere this SO answer oppure utilizzare% prun in ipython).

L'output di prun dovrebbe guidare quale bit per migliorare il prossimo.

  1. panda (rendere il codice più pandorable, questo può aiutare un sacco ).
  2. numpy (non creare serie intermediarie/DataFrames, facendo attenzione ai dtypes)
  3. cython (l'ultima risorsa).

Ora, se si tratta di una linea a che fare con affettare (probabilmente non è) mettere che piccola parte in Cython, mi piace per rimuovere singola funzione Python chiamate a Cython funzione. Su quel punto roba con cython dovrebbe usare numpy non panda, non penso che i panda non si abbassino a C (cython non può inferire tipi).


Mettere l'intero codice in Cython non sarà effettivamente aiutare più di tanto, si vuole solo mettere le linee specifiche, o chiamate di funzione, che sono le prestazioni sensibili. Mantenere cython focalizzato è l'unico modo per divertirsi.

Leggi il enhancing performance section of the pandas docs *! Qui questo processo (prun -> cythonize -> type) è andato su passo-passo con un esempio di vita reale.

* Full-disclose Ho scritto quella sezione dei documenti! :)

+0

Inserire tutto il codice in Cython ha aiutato incredibilmente!Da una corsa notturna a 20 minuti !! Guardando gli stati della CPU, quando si eseguiva in Python la CPU passava molto tempo in C1 +. Così motivato da questo, la domanda è più di un "come ottenere aumenti di velocità" piuttosto che l'ottimizzazione. Grazie per i documenti e altri lavori che stai facendo, è stata la base per arrivare fino a 8). Panda gestisce tutti i tipi di celle e lo trasmette a cython? – SpmP

+0

Beh, potrebbe darsi, ma penso che si possa ottenere una maggiore efficacia accelerando solo la cythonizzazione di una piccola parte del codice (il bit sensibile alle prestazioni). Pandas gestisce diversi tipi di dtype quando si usano i metodi pandas (questi sono già vecorizzati o scritti in cython). –

+1

Vale a dire, in risposta a "" come ottenere aumenti di velocità "piuttosto che ottimizzazione", dovresti essere in grado di ottenere * più * accelerazioni dall'ottimizzazione rispetto alla cythonizzazione globale. –

Problemi correlati