Я пытался измерить время, затраченное на оценку математической функции, как простым, так и питоническим способом. Во-первых, я измерил, сколько времени требуется для оценки функции.
import numpy as np
import scipy as sp
import scipy.integrate as si
def func1(x):
return np.piecewise(x, [x<=2, x>2],
[lambda x: 2*x + pow(2, x),
lambda x: -(pow(x, 2) + 2)]
)
def func2(x):
if (x<=2): return 2*x + pow(2,x)
else: return -(pow(x, 2) + 2)
data = np.linspace(-10, 10, 1000)
%timeit data1 = func1(data)
data2 = []
%timeit for i in range(0, np.size(data)): data2.append(func2(data[i]))
На самом деле, результат выше был таким, как я и ожидал, numpy способ был намного быстрее, чем основной способ.
35.2 µs ± 110 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
771 µs ± 10.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Однако в scipy.integrate.quad происходит странная вещь. Скорость интеграции была намного выше в базовом питоническом стиле. Почему так?
%timeit si.quad(func1, -10, 10)
%timeit si.quad(func2, -10, 10)
5.59 ms ± 25.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
187 µs ± 39.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
np.piecewise
работает быстро только при использовании векторизации. Когда входные данные являются скалярными, он намного медленнее, чем эквивалент Python, предположительно из-за накладных расходов:
from timeit import timeit
kwds = dict(globals=globals(), number=1000)
print(timeit("data1 = func1(data)", **kwds))
data2 = []
print(timeit("for i in range(0, np.size(data)): data2.append(func2(data[i]))", **kwds))
data3 = []
print(timeit("for i in range(0, np.size(data)): data3.append(func1(data[i]))", **kwds))
0.06953751016408205
0.957529284991324
15.591511018108577
Сам факт того, что он работает с func2
, документация и то, как работают популярные схемы адаптивной интеграции, предполагает, что quad
вызывает подынтегральную функцию со скалярными аргументами один за другим, что объясняет ваше наблюдение.
Если производительность имеет значение, используйте низкоуровневый вызываемый объект для интеграции. Это превзойдет стандартный способ Python как минимум на порядок. например. stackoverflow.com/a/50097776/4045774