2016-08-02 42 views
9

Ich habe ein paar Dutzend Bedingungen (z. B. foo > bar), die ich auf ~ 1MM Zeilen eines DataFrame bewerten muss, und das prägnanteste Um dies zu schreiben, speichern Sie diese Bedingungen als eine Liste von Strings und erstellen Sie eine DataFrame boolesche Ergebnisse (eine Zeile pro Datensatz x eine Spalte pro Bedingung). (Benutzereingabe ist nicht ausgewertet.)Wann DataFrame.eval() im Vergleich zu Pandas.eval() oder Python eval()

Auf der Suche nach vorzeitiger Optimierung, ich versuche, um zu bestimmen, ob ich diese Bedingungen für die Bewertung innerhalb DataFrame (zB schreiben soll, df.eval("foo > bar") oder es wie in eval("df.foo > df.bar") zu python verlassen

nach den documentation on enhancing eval performance.

Sie sollten nicht eval() verwenden, für einfache Ausdrücke oder Ausdrücke mit kleinem Datenrahmen In der Tat, eval() ist viele Aufträge von Größe langsamer für kleinere Ausdrücke/Objekte als plain ol ' Python. Eine gute Faustregel ist, dass Sie eval() nur verwenden, wenn Sie einen DataFrame mit mehr als 10.000 Zeilen haben.

Es wäre schön, Lage sein, die df.eval("foo > bar") Syntax zu verwenden, da meine Liste ein wenig mehr lesbar sein würde, aber ich kann nicht immer einen Fall, wo es nicht langsamer ist zu bewerten. Die Dokumentation zeigt Beispiele wo pandas.eval() ist schneller als Python eval() (die meiner Erfahrung entspricht) aber keine für DataFrame.eval() (die als 'Experimental' aufgeführt ist).

Zum Beispiel ist DataFrame.eval() noch ein klarer Verlierer in einem nicht einfachen Ausdruck auf eine groß ish DataFrame:

import pandas as pd 
import numpy as np 
import numexpr 
import timeit 

someDf = pd.DataFrame({'a':np.random.uniform(size=int(1e6)), 'b':np.random.uniform(size=int(1e6))}) 

%timeit -n100 someDf.eval("a**b - a*b > b**a - b/a") # DataFrame.eval() on notional expression 
%timeit -n100 eval("someDf['a']**someDf['b'] - someDf['a']*someDf['b'] > someDf['b']**someDf['a'] - someDf['b']/someDf['a']") 
%timeit -n100 pd.eval("someDf.a**someDf.b - someDf.a*someDf.b > someDf.b**someDf.a - someDf.b/someDf.a") 

100 loops, best of 3: 29.9 ms per loop 
100 loops, best of 3: 18.7 ms per loop 
100 loops, best of 3: 15.4 ms per loop 

So ist der Nutzen von DataFrame.eval() lediglich die Eingabe zu vereinfachen, oder können wir Umstände identifizieren wo ist die Verwendung dieser Methode tatsächlich schneller?

Gibt es noch andere Richtlinien für die Verwendung welche eval()? (Ich bin mir bewusst, dass pandas.eval() nicht den vollständigen Satz von Operationen nicht unterstützt.)

pd.show_versions() 

INSTALLED VERSIONS 
------------------ 
commit: None 
python: 3.5.1.final.0 
python-bits: 64 
OS: Windows 
OS-release: 7 
machine: AMD64 
processor: Intel64 Family 6 Model 63 Stepping 2, GenuineIntel 
byteorder: little 
LC_ALL: None 
LANG: en_US 

pandas: 0.18.0 
nose: 1.3.7 
pip: 8.1.2 
setuptools: 20.3 
Cython: 0.23.4 
numpy: 1.10.4 
scipy: 0.17.0 
statsmodels: None 
xarray: None 
IPython: 4.1.2 
sphinx: 1.3.1 
patsy: 0.4.0 
dateutil: 2.5.3 
pytz: 2016.2 
blosc: None 
bottleneck: 1.0.0 
tables: 3.2.2 
numexpr: 2.5 
matplotlib: 1.5.1 
openpyxl: 2.3.2 
xlrd: 0.9.4 
xlwt: 1.0.0 
xlsxwriter: 0.8.4 
lxml: 3.6.0 
bs4: 4.4.1 
html5lib: None 
httplib2: None 
apiclient: None 
sqlalchemy: 1.0.12 
pymysql: None 
psycopg2: None 
jinja2: 2.8 
boto: 2.39.0 
+0

danke für das Update - ich konnte keine Probleme in Ihrer "Infrastruktur" sehen. Ich denke, der beste Ansatz ist, die Geschwindigkeit zu testen, wie Sie es bei Ihren realen Daten getan haben. Sie könnten auch einen kleinen "Übersetzer" schreiben, der Ausdrücke von 'df.eval()' nach 'pd.eval()' Syntax – MaxU

+0

@MaxU übersetzt, das ist eine gute Idee, danke ... im Grunde würde ich nur Variablennamen erkennen und legen Sie "df." vor ihnen. – C8H10N4O2

+4

Ich würde empfehlen, 'df ['colname']' Syntax zu verwenden, da es sicherer ist - wenn Sie einen _risky_ Spaltennamen haben (reservierte Python-Attribute, wie 'id', Spaltennamen mit Leerzeichen usw.) – MaxU

Antwort

3

So ist der Nutzen von DataFrame.eval ist() lediglich in den Eingang zu vereinfachen, oder können wir Umstände identifizieren, wo dies mit Methode ist eigentlich schneller?

Die source code für DataFrame.eval() zeigt, dass es Argumente tatsächlich schafft nur(), um pd.eval weitergeben müssen:

def eval(self, expr, inplace=None, **kwargs): 

    inplace = validate_bool_kwarg(inplace, 'inplace') 
    resolvers = kwargs.pop('resolvers', None) 
    kwargs['level'] = kwargs.pop('level', 0) + 1 
    if resolvers is None: 
     index_resolvers = self._get_index_resolvers() 
     resolvers = dict(self.iteritems()), index_resolvers 
    if 'target' not in kwargs: 
     kwargs['target'] = self 
    kwargs['resolvers'] = kwargs.get('resolvers',()) + tuple(resolvers) 
    return _eval(expr, inplace=inplace, **kwargs) 

Wo _eval() für pd.eval nur ein Alias ​​ist (), die am Anfang des Moduls importiert wird:

from pandas.core.computation.eval import eval as _eval 

Also alles, was Sie mit df.eval() tun können, Sie mit pd.eval() tun könnte + ein paar zusätzliche Zeilen, um Dinge einzurichten. Wie die Dinge derzeit stehen, ist df.eval() niemals streng schneller als pd.eval().Aber das bedeutet nicht, dass es keine Fälle geben kann, in denen df.eval() genauso gut ist wie pd.eval(), aber noch bequemer zu schreiben.

Doch nach Herumspielen mit der %prun Magie scheint es, dass der Anruf von df.eval()-df._get_index_resolvers() zum df.eval() Verfahren auf einem fair bisschen Zeit hinzuzufügt. Letztendlich endet _get_index_resolvers() Aufruf der .copy() Methode von numpy.ndarray, was letztlich Dinge verlangsamt. Unterdessen ruft pd.eval() irgendwann numpy.ndarray.copy() auf, aber es dauert eine vernachlässigbare Zeit (zumindest auf meiner Maschine).

Lange Rede kurzer Sinn, es scheint, dass df.eval() langsamer als pd.eval() zu sein scheint, da unter der Haube ist es nur pd.eval() mit zusätzlichen Schritte, und diese Schritte sind nicht trivial.