2017-10-24 2 views
1

Ich habe ein Datenframe mit etwa 1M Zeilen und 3 Spalten (sentence, eine Zeichenfolge im Bereich von 100 Zeichen, lang, eine 3-Zeichenkette und i_sent, ein int).Pandas Dataframe - Leistung von get_value in anwenden

Ich versuche, eine neue Serie zu erzeugen, eine Funktion compute_coverage genannt verwendet, die in einem Satz und die entsprechende Sprache nimmt und gibt einen float:

absolute_coverage = df.apply(lambda x: compute_coverage(x['sentence'], x['lang']), 
          axis=1) 

compute_coverage ist eine ziemlich einfache Funktion, sondern Erzeugen Die Serie dauert sehr lange (ca. 50s). Nach dem Profiling (Ergebnisse unten) stellt sich heraus, dass ein großer Teil der Zeit in der get_value-Funktion von Pandas verbracht wird, vermutlich um x['sentence'] und x['lang'] zu erhalten.

Mache ich das schrecklich falsch? Wird das erwartet? Gibt es eine bessere Möglichkeit, eine zeilenweise Operation auszuführen?

Danke!


Edit:

Ich denke, was ich komme an ist, ist es eine Möglichkeit, Aufruf get_value() zu vermeiden? Zum Beispiel, wenn ich

x = df.apply({'sentence': lambda x: compute_coverage(x, 'fra')}) 

tun (die offensichtlich falsche Ergebnisse liefert, sondern führt die gleiche Menge an Berechnung), Laufzeit sinkt um 90%.

Funktionskörper:

def compute_coverage(sentence, lang): 
    words = sentence.split() 
    return len(set(words))/(lang_vocab[lang] * len(words)) 

und lang_vocab ist ein 8-Element-Wörterbuch.


  120108317 function calls (114648864 primitive calls) in 150.379 seconds 

    Ordered by: internal time 
    List reduced from 141 to 10 due to restriction <10> 

    ncalls tottime percall cumtime percall filename:lineno(function) 
    2729722 13.090 0.000 83.294 0.000 base.py:2454(get_value) 
     1 11.105 11.105 150.064 150.064 {pandas._libs.lib.reduce} 
    1364861 10.287 0.000 16.268 0.000 <ipython-input-16-0ab58d43622d>:3(compute_coverage) 
    2729722 8.953 0.000 95.187 0.000 series.py:598(__getitem__) 
    2729722 7.476 0.000 7.476 0.000 {method 'get_value' of 'pandas._libs.index.IndexEngine' objects} 
    8189190 7.460 0.000 16.088 0.000 {built-in method builtins.getattr} 
13648677/8189224 6.484 0.000 9.794 0.000 {built-in method builtins.len} 
    5459444 6.244 0.000 20.539 0.000 {pandas._libs.lib.values_from_object} 
    1364864 5.801 0.000 17.845 0.000 series.py:284(_set_axis) 
    8189277 5.637 0.000 8.747 0.000 {built-in method builtins.isinstance} 
+0

1 Million Funktionsaufrufe wird eine Weile dauern, auch wenn Sie es konstante Zeit für einen Funktionsaufruf halten ... Kann man den Funktionskörper auch posten? –

+0

Ich habe meinen Beitrag bearbeitet, um ihn hinzuzufügen. – zale

Antwort

2

Dies wird Extrahieren (get_value) 2-mal mit einem Wert jedes

df.apply(lambda x: compute_coverage(x['sentence'], x['lang']), 
         axis=1) 

umgeschrieben werden kann als

df[['sentence', 'lang']].apply(lambda x: compute_coverage(*x)) 

ist es schneller sein, da beide Werte ausgewählt sind, in ein Versuch (dieser wird weiter entpackt und als Parameter an compute_coverage übergeben).

Mit 100.000 Zeilen Datenrahmen dieser erste Ansatz dauerte 7.77s, und für die gleichen Daten zweiten Ansatz dauerte 4.78s. Der zweite Ansatz scheint 40% schneller zu sein.


Für meine Datenrahmen mit 100.000 Datensätze

df = pd.DataFrame({'a':list('abcd')*100000, 
        'b':list(range(4))*100000, 
        'c': list(range(3,7))*100000 
        }) 
def f(x, y): 
    return str(x)+str(y) 

df.apply(lambda x: f(x['a'], x['b']), axis=1) nahm 7.66 s
df[['a', 'b']].apply(lambda x: f(*x), axis=1) 4 nahm.67 s
df.apply(lambda x: f(*x[['a', 'b']]), axis=1) nahm 1min 54s

Zeit gemessen läuft mit %%timeit in jupyter Notebook (python3)

+0

Ich hatte nicht daran gedacht, und es klingt wie eine gute Idee, aber in meinem eigenen Test verhält es sich sehr ähnlich wie das Original: '% time x = df [['sement', 'lang']]. Apply (Lambda x: compute_coverage (* x), axis = 1) 'gibt mir 56s, vs 53s für das Original. Ich weiß nicht, woher der Unterschied kommt. – zale

1

Nach etwas, es sieht aus wie

x = pd.Series(map(lambda x: compute_coverage(x[0], x[1]), 
        zip(df.sentence, df.lang))) 

9s nimmt, 7 davon sind innerhalb ausgegeben compute_coverage, so sieht es aus wie es viel besser erhalten kann, ohne diese Funktion zu optimieren.

Es ist wahrscheinlich nicht der beste Weg, es zu tun, aber es funktioniert inzwischen gut genug.

+1

Mit der Funktion 'apply' wird die Funktion unter Verwendung der zugrunde liegenden C/fortran-Bibliotheken parallel ausgeführt, und unter der Haube können weitere Optimierungen stattfinden. Die integrierte 'map'-Funktion ist eine reine Python-Generator-Funktion. Es ist merkwürdig, wie diese "Karte" schneller ist als "anwenden" – shanmuga

Verwandte Themen