Как я могу получить возвращаемое значение с помощью модуля Python timeit?

Я запускаю несколько алгоритмов машинного обучения со sklearn в цикле for и хочу посмотреть, сколько времени займет каждый из них. Проблема в том, что мне также нужно вернуть значение, и НЕ хочу запускать его более одного раза, потому что каждый алгоритм занимает так много времени. Есть ли способ зафиксировать возвращаемое значение 'clf' с помощью модуля timeit python или аналогичного с такой функцией...

def RandomForest(train_input, train_output):
    clf = ensemble.RandomForestClassifier(n_estimators=10)
    clf.fit(train_input, train_output)
    return clf

когда я вызываю такую ​​функцию

t = Timer(lambda : RandomForest(trainX,trainy))
print t.timeit(number=1)

P.S. Я также не хочу устанавливать глобальный «clf», потому что позже мне может понадобиться многопоточность или многопроцессорность.


person Leon    schedule 17.07.2014    source источник
comment
Почему вы вообще используете timeit, если вы заставляете number=1? timeit полезен для автоматической обработки повторяющихся хронометражей, когда вы не знаете, сколько времени вы должны запускать функцию, чтобы получить хороший хронометраж и т. д. В вашем случае простое использование time было бы хорошо, и вы бы не Не нужно никакого взлома, чтобы получить возвращаемое значение.   -  person Bakuriu    schedule 18.07.2014
comment
Можете ли вы предоставить пример ссылки, чтобы я мог увидеть, о чем вы говорите? Я гуглю время, и кажется, что модуль, о котором вы, возможно, говорите, связан только с форматированием дат, часовых поясов и т. д.   -  person Leon    schedule 18.07.2014
comment
Никогда не слышали о time.time()? Или time.clock()? Модуль timeit использует эти функции для выполнения таймингов. Если вам нужно выполнить только один тайминг, вы можете просто вызвать их напрямую, так же, как функция _timer используется в ответе unutbu (на самом деле это ссылка на time.time или time.clock в зависимости от ОС) .   -  person Bakuriu    schedule 18.07.2014
comment
@Bakuriu Я понял, что timeit также делает и другие вещи, например отключает сборку мусора, чтобы убедиться, что мы делаем честное сравнение. то есть, что мы смотрим на время выполнения, а не время стены.   -  person Joel    schedule 10.04.2018


Ответы (8)


Проблема сводится к тому, что timeit._template_func не возвращает возвращаемое функцией значение:

def _template_func(setup, func):
    """Create a timer function. Used if the "statement" is a callable."""
    def inner(_it, _timer, _func=func):
        setup()
        _t0 = _timer()
        for _i in _it:
            _func()
        _t1 = _timer()
        return _t1 - _t0
    return inner

Мы можем подчинить timeit нашей воле с помощью небольшого исправления:

import timeit
import time

def _template_func(setup, func):
    """Create a timer function. Used if the "statement" is a callable."""
    def inner(_it, _timer, _func=func):
        setup()
        _t0 = _timer()
        for _i in _it:
            retval = _func()
        _t1 = _timer()
        return _t1 - _t0, retval
    return inner

timeit._template_func = _template_func

def foo():
    time.sleep(1)
    return 42

t = timeit.Timer(foo)
print(t.timeit(number=1))

возвращается

(1.0010340213775635, 42)

Первое значение — результат timeit (в секундах), второе значение — возвращаемое значение функции.

Обратите внимание, что приведенный выше обезьяний патч влияет только на поведение timeit при передаче вызываемого timeit.Timer. Если вы передаете строковый оператор, вам придется (аналогично) исправить строку timeit.template.

person unutbu    schedule 17.07.2014
comment
Хммм, кажется, это возвращает мне функцию, а не возвращаемое значение функции. Но что мне нужно сделать, так это захватить его с помощью ret_val = t.timeit(number=1)[1](), чтобы фактически запустить функцию и вернуть мне значение. Разве это не запускает функцию дважды? - person Leon; 18.07.2014
comment
Учитывая код, который вы разместили, я не понимаю, почему t.timeit должен возвращать функцию. Получаете ли вы тот же результат, что и я, когда запускаете код, который я разместил? Если это так, вам нужно сравнить, чем отличается этот код от вашего кода (уделяя особое внимание типу передаваемых и возвращаемых объектов). - person unutbu; 18.07.2014
comment
Вы правы, я все еще использовал timeit.Timer(лямбда: пустышка) вместо просто timeit.Timer(пустышка). На StackOverflow есть несколько исключительно умных людей. Черт, я люблю этот сайт. - person Leon; 18.07.2014
comment
Глядя на источник для timeit; похоже, цель модуля - использовать его в командной строке в качестве инструмента тестирования для оптимизации вашего кода и самого Python. Если вы пишете приложение для тестирования чего-либо; скажем, скорость вызова API, возможно, вам лучше использовать time.perf_counter дважды и выполнять вычитание двух чисел. - person Chris Huang-Leaver; 14.12.2018

Для Python 3.5 вы можете переопределить значение timeit.template

timeit.template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        retval = {stmt}
    _t1 = _timer()
    return _t1 - _t0, retval
"""

ответ unutbu работает для python 3.4, но не 3.5, поскольку функция _template_func, похоже, была удалена в 3.5

person Brendan Cody-Kenny    schedule 02.11.2016

Как ни странно, я тоже занимаюсь машинным обучением, и у меня аналогичные требования ;-)

Я решил это следующим образом, написав функцию, которая:

  • выполняет вашу функцию
  • печатает время выполнения вместе с именем вашей функции
  • возвращает результаты

Допустим, вы хотите время:

clf = RandomForest(train_input, train_output)

Затем выполните:

clf = time_fn( RandomForest, train_input, train_output )

Stdout покажет что-то вроде:

mymodule.RandomForest: 0.421609s

Код для time_fn:

import time

def time_fn( fn, *args, **kwargs ):
    start = time.clock()
    results = fn( *args, **kwargs )
    end = time.clock()
    fn_name = fn.__module__ + "." + fn.__name__
    print fn_name + ": " + str(end-start) + "s"
    return results
person Hugh Perkins    schedule 20.12.2014

Если я хорошо это понимаю, после Python 3.5 вы можете определять глобальные переменные в каждом экземпляре Timer без необходимости определять их в своем блоке кода. Я не уверен, что у него будут такие же проблемы с распараллеливанием.

Мой подход будет примерно таким:

clf = ensemble.RandomForestClassifier(n_estimators=10)
myGlobals = globals()
myGlobals.update({'clf'=clf})
t = Timer(stmt='clf.fit(trainX,trainy)', globals=myGlobals)
print(t.timeit(number=1))
print(clf)
person Xavier    schedule 01.05.2018
comment
Хороший снимок, определенно более элегантное решение, оно также позволяет передать словарь timeit.Timer. Спасибо, что поделились - person jlandercy; 25.03.2019

По состоянию на 2020 год в ноутбуках ipython или jupyter это

t = %timeit -n1 -r1 -o RandomForest(trainX, trainy)
t.best
person Antony Hatchkins    schedule 26.11.2020
comment
Вы смешиваете результаты: OP хочет получить результат временной функции clf, чтобы не запускать эту функцию дважды (один раз, чтобы получить результат, один раз, чтобы получить время), а не результат магической функции timeit IPython (которая -o действительно обеспечивает). - person mins; 17.12.2020

Если вы не хотите исправлять timeit, вы можете попробовать использовать глобальный список, как показано ниже. Это также будет работать в python 2.7, у которого нет аргумента globals в timeit():

from timeit import timeit
import time

# Function to time - plaigiarised from answer above :-)
def foo():
    time.sleep(1)
    return 42

result = []
print timeit('result.append(foo())', setup='from __main__ import result, foo', number=1)
print result[0]

напечатает время, а затем результат.

person Jerzy    schedule 21.09.2020
comment
Меня устраивает - person namasikanam; 17.04.2021

Подход, который я использую, заключается в том, чтобы «добавить» время выполнения к результатам функции времени. Итак, я пишу очень простой декоратор, используя модуль «время»:

def timed(func):
    def func_wrapper(*args, **kwargs):
        import time
        s = time.clock()
        result = func(*args, **kwargs)
        e = time.clock()
        return result + (e-s,)
    return func_wrapper

А затем я использую декоратор для функции, которую хочу запрограммировать.

person ereynrs    schedule 13.03.2018

Для Python 3.X я использую этот подход:

# Redefining default Timer template to make 'timeit' return
#     test's execution timing and the function return value
new_template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        ret_val = {stmt}
    _t1 = _timer()
    return _t1 - _t0, ret_val
"""
timeit.template = new_template
person Andrii Marusiak    schedule 09.05.2019