Хороший способ узнать, почему что-то работает медленнее, — это профилировать его. Я буду использовать стороннюю библиотеку line_profiler
и команду IPython %lprun
(см., например, этот блог) здесь:
%load_ext line_profiler
import numpy as np
arr = np.full((3, 3), -9999, dtype=float)
%lprun -f np.ma.average np.ma.average(arr, axis=0)
Line # Hits Time Per Hit % Time Line Contents
==============================================================
519 def average(a, axis=None, weights=None, returned=False):
...
570 1 1810 1810.0 30.5 a = asarray(a)
571 1 15 15.0 0.3 m = getmask(a)
572
573 # inspired by 'average' in numpy/lib/function_base.py
574
575 1 5 5.0 0.1 if weights is None:
576 1 3500 3500.0 59.0 avg = a.mean(axis)
577 1 591 591.0 10.0 scl = avg.dtype.type(a.count(axis))
578 else:
...
608
609 1 7 7.0 0.1 if returned:
610 if scl.shape != avg.shape:
611 scl = np.broadcast_to(scl, avg.shape).copy()
612 return avg, scl
613 else:
614 1 5 5.0 0.1 return avg
Я удалил несколько ненужных строк.
Так что на самом деле 30% времени тратится на np.ma.asarray
(то, что arr.mean
делать не нужно!).
Однако относительное время резко меняется, если вы используете больший массив:
arr = np.full((1000, 1000), -9999, dtype=float)
%lprun -f np.ma.average np.ma.average(arr, axis=0)
Line # Hits Time Per Hit % Time Line Contents
==============================================================
519 def average(a, axis=None, weights=None, returned=False):
...
570 1 609 609.0 7.6 a = asarray(a)
571 1 14 14.0 0.2 m = getmask(a)
572
573 # inspired by 'average' in numpy/lib/function_base.py
574
575 1 7 7.0 0.1 if weights is None:
576 1 6924 6924.0 86.9 avg = a.mean(axis)
577 1 404 404.0 5.1 scl = avg.dtype.type(a.count(axis))
578 else:
...
609 1 6 6.0 0.1 if returned:
610 if scl.shape != avg.shape:
611 scl = np.broadcast_to(scl, avg.shape).copy()
612 return avg, scl
613 else:
614 1 6 6.0 0.1 return avg
На этот раз функция np.ma.MaskedArray.mean
занимает почти 90% времени.
Примечание. Вы также можете копнуть глубже и изучить np.ma.asarray
, np.ma.MaskedArray.count
или np.ma.MaskedArray.mean
и проверить профили их линий. Но я просто хотел показать, что есть много вызываемых функций, которые добавляют накладные расходы.
Итак, следующий вопрос: изменилось ли также относительное время между np.ndarray.mean
и np.ma.average
? И, по крайней мере, на моем компьютере разница теперь намного меньше:
%timeit np.ma.average(arr, axis=0)
# 2.96 ms ± 91 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit arr.mean(axis=0)
# 1.84 ms ± 23.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
На этот раз даже не в 2 раза медленнее. Я предполагаю, что для еще больших массивов разница станет еще меньше.
Это также то, что на самом деле довольно часто встречается в NumPy:
Постоянные коэффициенты довольно высоки даже для простых функций numpy (см., например, мой ответ на вопрос "Производительность в другом методе векторизации в numpy"< /а>). Для np.ma
эти постоянные факторы еще больше, особенно если вы не используете np.ma.MaskedArray
в качестве входных данных. Но даже несмотря на то, что постоянные коэффициенты могут быть высокими, эти функции превосходно работают с большими массивами.
person
MSeifert
schedule
08.08.2017
.ma.
?) работают медленно! - person sascha   schedule 08.08.2017np.mean
иnp.average
(две функции без маски) и используйте большие данные! - person sascha   schedule 08.08.2017ma.average
будет перенаправлять наnp.mean
(чего нет), даже перенаправление может иметь огромное влияние. - person Willem Van Onsem   schedule 08.08.2017