2016-08-02 40 views
9

Mam kilka warunków tuzin (np foo > bar), które muszę ocenić na 1mm ~ wiersze o DataFrame i najbardziej zwięzły sposobem zapisu jest zapisanie tych warunków jako listy łańcuchów i utworzenie DataFrame wyników boolowskich (jeden wiersz na rekord x kolumna na warunek). (Wejście użytkownika jest nie oceniany.)kiedy używać DataFrame.eval() kontra pandas.eval eval() lub() Pythona

W pogoni za przedwczesne optymalizacji Próbuję ustalić, czy powinienem napisać te warunki oceny w DataFrame (np df.eval("foo > bar") lub po prostu zostawić go python jak w eval("df.foo > df.bar")

Według documentation on enhancing eval performance.

nie należy używać eval() dla prostych wyrażeń lub do wyrażenia udziałem małych DataFrames W rzeczywistości, eval() jest wiele rzędów Siła wolniejsza dla mniejszych wyrażeń/obiektów niż zwykły ol ' Python. Dobrą zasadą jest używanie funkcji eval() tylko wtedy, gdy masz DataFrame z ponad 10 000 wierszy.

Byłoby miło, aby móc użyć składni df.eval("foo > bar"), bo moja lista będzie trochę bardziej czytelne, ale nigdy nie może znaleźć przypadek, w którym nie jest wolniejszy ocenić. W dokumentacji pokazano przykłady, gdzie pandas.eval() jest szybszy niż python eval() (który pasuje do mojego doświadczenia), ale żaden dla DataFrame.eval() (który jest wymieniony jako "Eksperymentalne").

Na przykład DataFrame.eval() jest jeszcze jasne, przegrany w nie proste wypowiedzi na duży-owski 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 

Tak jest zaletą DataFrame.eval() jedynie w uproszczeniu wejście, lub możemy zidentyfikować okoliczności gdzie używanie tej metody jest rzeczywiście szybsze?

Czy istnieją inne wytyczne dotyczące korzystania z których eval()? (Jestem świadomy, że pandas.eval() nie obsługuje kompletny zestaw operacji.)

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

dzięki za aktualizację - nie widziałem żadnych problemów w Twojej "infrastrukturze". Myślę, że najlepszym podejściem jest przetestowanie prędkości tak, jak robiłeś to na swoich prawdziwych danych. Mógłbyś też napisać mały "tłumacz", który tłumaczyłby wyrażenia z 'df.eval()' na 'pd.eval()' składnia – MaxU

+0

@MaxU to dobry pomysł, dziękuję ... właściwie po prostu wykryłem nazwy zmiennych i wstaw przed nimi "df". – C8H10N4O2

+4

Polecam użyć składni 'df ['colname']', ponieważ jest to bezpieczniejsze - jeśli posiadasz _risky_ nazwy kolumn (zastrzeżone atrybuty Pythona, takie jak 'id', nazwy kolumn ze spacjami itp.) – MaxU

Odpowiedz

3

Tak jest zaletą DataFrame.eval() tylko w uproszczeniu wejście, lub możemy zidentyfikować sytuację, w której za pomocą tego metoda jest rzeczywiście szybsza?

source code dla DataFrame.eval() pokazuje, że faktycznie tak tworzy argumentów aby przejść do pd.eval():

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) 

Gdzie _eval() jest tylko aliasem pd.eval (), który jest importowany na początku modułu:

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

więc coś, co można zrobić z df.eval(), ty mógłby zrobić z pd.eval() + kilka dodatkowych linii do ustawienia. W obecnej sytuacji, df.eval() nigdy nie jest ściśle szybszy niż pd.eval().Ale to nie znaczy, że nie może być przypadków, w których df.eval() jest tak dobry jak pd.eval(), ale wygodniejsze do napisania.

Jednak po zabawy z %prun magii wydaje się, że połączenie przez df.eval() do df._get_index_resolvers() dodaje w porządku trochę czasu do metody df.eval(). Ostatecznie, _get_index_resolvers() kończy się wywoływaniem metody .copy() z numpy.ndarray, co kończy się spowolnieniem działania. Tymczasem, pd.eval() w pewnym momencie wywołuje numpy.ndarray.copy(), ale zajmuje mało czasu (przynajmniej na moim komputerze).

Krótko mówiąc, wydaje się, że df.eval() wydaje się być wolniejszy niż pd.eval() bo pod maską to po prostu pd.eval() z dodatkowych kroków, a te kroki są nietrywialne.