2013-09-03 3 views
28

Я регулярно выполняю операции pandas с кадрами данных, превышающими 15 миллионов или около того, и мне бы хотелось иметь доступ к индикатору прогресса для определенных операций.Индикатор выполнения во время операций pandas (python)

Имеется ли индикатор прогресса на основе текста для операций pandas split-apply-comb?

Например, в чем-то вроде:

df_users.groupby(['userID', 'requestDate']).apply(feature_rollup) 

где feature_rollup является то запутанная функцией, которая займет много столбцов DF и создает новые столбцы пользователя с помощью различных методов. Эти операции могут занять некоторое время для больших кадров данных, поэтому я хотел бы знать, возможно ли иметь текстовый вывод в ноутбуке iPython, который обновляет меня в ходе выполнения.

До сих пор я пробовал индикаторы хода канонического цикла для Python, но они не взаимодействуют с пандами каким-либо значимым образом.

Я надеюсь, что в библиотеке/документации pandas есть что-то, что я пропустил, чтобы узнать о ходе объединения split-apply-comb. Простая реализация могла бы рассмотреть общее количество подмножеств фреймов данных, на которых работает функция apply, и сообщать о ходе выполнения как завершенной части этих подмножеств.

Возможно, это что-то, что нужно добавить в библиотеку?

+0

У вас есть% prun (профиль) на код? иногда вы можете делать операции на всем кадре, прежде чем применять их для устранения узких мест. – Jeff

+0

@Jeff: вы делаете ставку, я сделал это раньше, чтобы выжать каждый последний бит производительности из него. Проблема действительно сводится к границе сокращения псевдо-карты, над которой я работаю, поскольку строки находятся в десятках миллионов, поэтому я не ожидаю, что увеличение скорости ускорит, просто хочу получить обратную связь о прогрессе. – cwharland

+0

Рассмотрите cythonising: http://pandas.pydata.org/pandas-docs/dev/enhancingperf.html#cython-writing-c-extensions-for-pandas –

ответ

52

Из-за популярного спроса tqdm добавил поддержку pandas. В отличие от других ответов, это будет не заметно медленно панды вниз - вот пример для DataFrameGroupBy.progress_apply:

import pandas as pd 
import numpy as np 
from tqdm import tqdm, tqdm_pandas 

df = pd.DataFrame(np.random.randint(0, int(1e8), (10000, 1000))) 

# Create and register a new `tqdm` instance with `pandas` 
# (can use tqdm_gui, optional kwargs, etc.) 
tqdm_pandas(tqdm()) 

# Now you can use `progress_apply` instead of `apply` 
df.groupby(0).progress_apply(lambda x: x**2) 

В случае, если вы заинтересованы в том, как это работает (и как изменить его для своих собственных обратных вызовов) , см. examples on github, full documentation on pypi или импортируйте модуль и запустите help(tqdm).

EDIT


Для прямого ответа на исходный вопрос, заменить:

df_users.groupby(['userID', 'requestDate']).apply(feature_rollup) 

с:

from tqdm import tqdm, tqdm_pandas 
tqdm_pandas(tqdm()) 
df_users.groupby(['userID', 'requestDate']).progress_apply(feature_rollup) 
+0

Это выглядит великолепно! Зачем вам нужно создать «Создать и зарегистрировать новый экземпляр« tqdm »с« pandas »? Может ли tqdm вывести то, что он вызывает (и переопределить это только в случае необходимости/необычного случая)? –

+1

'tqdm' фактически был создан только для простых итераций:' from tqdm import tqdm; для i в tqdm (диапазон (int (1e8))): pass' Поддержка pandas была недавно взломанной. – abcdaa

+2

Кстати, если вы используете ноутбуки Jupyter, вы также можете использовать tqdm_notebooks, чтобы получить более красивый бар. Вместе с пандами вам в настоящее время нужно создать экземпляр, как 'from tqdm import tqdm_notebook; tqdm_notebook(). pandas (* args, ** kwargs) ' [см. здесь] (http://stackoverflow.com/questions/40476680/how-to-use-tqdm-with-pandas-in-a-jupyter- ноутбук) – grinsbaeckchen

2

Вы можете легко сделать это с помощью декоратора

from functools import wraps 

def logging_decorator(func): 

    @wraps 
    def wrapper(*args, **kwargs): 
     wrapper.count += 1 
     print "The function I modify has been called {0} times(s).".format(
       wrapper.count) 
     func(*args, **kwargs) 
    wrapper.count = 0 
    return wrapper 

modified_function = logging_decorator(feature_rollup) 

затем просто использовать modified_function (и изменить, когда вы хотите напечатать)

+0

Очевидное предупреждение, что это замедлит вашу функцию! Вы даже можете обновить его с помощью http://stackoverflow.com/questions/5426546/in-python-how-to-change-text-after-its-printed, например. count/len в процентах. –

+0

yep - у вас будет заказ (количество групп), поэтому в зависимости от того, что может быть вашим узким местом, может быть, что это может сделать разницу – Jeff

+0

, возможно, интуитивно понятное дело - обернуть это в функцию 'logged_apply (g, func)', d имеют доступ к заказу и могут регистрироваться с самого начала. –

12

Чтобы настроить ответ Джеффа (и есть это в качестве многоразовых функции).

def logged_apply(g, func, *args, **kwargs): 
    step_percentage = 100./len(g) 
    import sys 
    sys.stdout.write('apply progress: 0%') 
    sys.stdout.flush() 

    def logging_decorator(func): 
     def wrapper(*args, **kwargs): 
      progress = wrapper.count * step_percentage 
      sys.stdout.write('\033[D \033[D' * 4 + format(progress, '3.0f') + '%') 
      sys.stdout.flush() 
      wrapper.count += 1 
      return func(*args, **kwargs) 
     wrapper.count = 0 
     return wrapper 

    logged_func = logging_decorator(func) 
    res = g.apply(logged_func, *args, **kwargs) 
    sys.stdout.write('\033[D \033[D' * 4 + format(100., '3.0f') + '%' + '\n') 
    sys.stdout.flush() 
    return res 

Примечание: применять прогресс процент updates inline. Если ваша функция stdout, это не сработает.

In [11]: g = df_users.groupby(['userID', 'requestDate']) 

In [12]: f = feature_rollup 

In [13]: logged_apply(g, f) 
apply progress: 100% 
Out[13]: 
... 

Как обычно вы можете добавить это к вашим объектам GroupBy как метод:

from pandas.core.groupby import DataFrameGroupBy 
DataFrameGroupBy.logged_apply = logged_apply 

In [21]: g.logged_apply(f) 
apply progress: 100% 
Out[21]: 
... 

Как уже упоминалось в комментариях, это не особенность, что основные панды будут заинтересованы в реализации , Но python позволяет создавать их для многих объектов/методов панд (сделать это будет довольно немного работы ... хотя вы должны иметь возможность обобщить этот подход).

+0

Я говорю «довольно много работы», но вы, вероятно, могли бы переписать всю эту функцию как (более общий) декоратор. –

+0

Спасибо за расширение на сообщение Джеффа. Я реализовал оба варианта, и замедление для каждого из них довольно минимальное (добавлено в общей сложности 1,1 минуты для выполнения операции, которая заняла 27 минут). Таким образом, я могу видеть прогресс и учитывая пристойность этих операций, я думаю, что это приемлемое замедление. – cwharland

+0

Отлично, рад, что это помогло. Я был действительно удивлен замедлением (когда я попробовал пример), я ожидал, что это будет намного хуже. –

0

Я изменил Jeff's answer, чтобы включить в общей сложности, так что вы можете отслеживать прогресс и переменную до Функция печати каждые X итераций (это на самом деле повышает производительность за счет много, если «print_at» является достаточно высоким)

def count_wrapper(func,total, print_at): 

    def wrapper(*args): 
     wrapper.count += 1 
     if wrapper.count % wrapper.print_at == 0: 
      clear_output() 
      sys.stdout.write("%d/%d"%(calc_time.count,calc_time.total)) 
      sys.stdout.flush() 
     return func(*args) 
    wrapper.count = 0 
    wrapper.total = total 
    wrapper.print_at = print_at 

    return wrapper 

clear_output() от

from IPython.core.display import clear_output 

если не на IPython Энди Хайдена ответ делает это без него

1

в случае, если нужна поддержка для того, как использовать это в Jupyter/IPython ноутбук, как и я, вот полезный путеводитель и источник relevant article:

from tqdm._tqdm_notebook import tqdm_notebook 
import pandas as pd 
tqdm_notebook.pandas() 
df = pd.DataFrame(np.random.randint(0, int(1e8), (10000, 1000))) 
df.groupby(0).progress_apply(lambda x: x**2) 

Обратите внимание на подчеркивание в заявке на импорт для _tqdm_notebook. Как упоминается в статье, развитие происходит на поздней стадии бета-тестирования.

Смежные вопросы