Saya telah menemukan sesuatu yang menarik di Python's numpy. ma.average jauh lebih lambat dari arr.mean (arr adalah array)

>>> arr = np.full((3, 3), -9999, dtype=float)
array([[-9999., -9999., -9999.],
       [-9999., -9999., -9999.],
       [-9999., -9999., -9999.]])

%timeit np.ma.average(arr, axis=0)
The slowest run took 49.32 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 191 µs per loop

%timeit arr.mean(axis=0)
The slowest run took 6.63 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 7.41 µs per loop

Dengan angka acak

arr = np.random.random((3,3))

%timeit arr.mean(axis=0)
The slowest run took 6.17 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 7.78 µs per loop

%timeit np.ma.average(arr, axis=0)
1000 loops, best of 3: 186 µs per loop

-> Itu hampir 24 kali lebih lambat.

Dokumentasi

numpy.ma.average(a, axis=None, weights=None, returned=False)

Kembalikan rata-rata weighted larik di atas sumbu yang diberikan.

numpy.mean(a, axis=None, dtype=None, out=None, keepdims)

Hitung rata-rata aritmatika di sepanjang sumbu yang ditentukan.


Mengapa ma.average jauh lebih lambat dari arr.mean? Secara matematis mereka sama (koreksi saya jika saya salah). Dugaan saya adalah itu ada hubungannya dengan opsi berbobot pada ma.average tetapi tidakkah ada fallback jika tidak ada bobot yang dilewatkan?

2
mumbala 8 Agustus 2017, 19:12

2 jawaban

Jawaban Terbaik

Cara yang baik untuk mengetahui mengapa sesuatu lebih lambat adalah dengan membuat profilnya. Saya akan menggunakan perpustakaan pihak ke-3 line_profiler dan perintah IPython %lprun (lihat misalnya blog ini) di sini:

%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

Saya menghapus beberapa baris yang tidak relevan.

Jadi sebenarnya 30% dari waktu dihabiskan di np.ma.asarray (sesuatu yang arr.mean tidak harus dilakukan!).

Namun waktu relatif berubah secara drastis jika Anda menggunakan array yang lebih besar:

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

Kali ini fungsi np.ma.MaskedArray.mean hampir menghabiskan 90% waktu.

Catatan: Anda juga dapat menggali lebih dalam dan melihat np.ma.asarray atau np.ma.MaskedArray.count atau np.ma.MaskedArray.mean dan memeriksa profil baris mereka. Tapi saya hanya ingin menunjukkan bahwa ada banyak fungsi yang dipanggil yang menambah overhead.

Jadi pertanyaan selanjutnya adalah: apakah waktu relatif antara np.ndarray.mean dan np.ma.average juga berubah? Dan setidaknya di komputer saya perbedaannya jauh lebih rendah sekarang:

%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)

Kali ini bahkan tidak 2 kali lebih lambat. Saya berasumsi untuk array yang lebih besar perbedaannya akan semakin kecil.


Ini juga sesuatu yang sebenarnya cukup umum dengan NumPy:

Faktor konstannya cukup tinggi bahkan untuk fungsi numpy biasa (lihat misalnya jawaban saya untuk pertanyaan "Kinerja dalam metode vektorisasi berbeda di numpy"< /a>). Untuk np.ma faktor konstan ini bahkan lebih besar, terutama jika Anda tidak menggunakan np.ma.MaskedArray sebagai input. Tetapi meskipun faktor konstan mungkin tinggi, fungsi-fungsi ini unggul dengan array besar.

2
MSeifert 8 Agustus 2017, 19:28

Terima kasih kepada @WillemVanOnsem dan @sascha di komentar di atas

edit: berlaku untuk array kecil, lihat jawaban yang diterima untuk informasi lebih lanjut

  • Operasi bertopeng adalah usaha yang lambat, untuk menghindarinya:

    mask = self.local_pos_history[:, 0] > -9
    local_pos_hist_masked = self.local_pos_history[mask]
    avg = local_pos_hist_masked.mean(axis=0)
    

    tua bertopeng

    mask = np.ma.masked_where(self.local_pos_history > -9, self.local_pos_history)
    local_pos_hist_mask = self.local_pos_history[mask].reshape(len(self.local_pos_history) // 3, 3)
    avg_pos = self.local_pos_history
    
  • np.average hampir sama dengan arr.mean:

    %timeit np.average(arr, axis=0)
    The slowest run took 5.81 times longer than the fastest. This could mean that an intermediate result is being cached.
    100000 loops, best of 3: 9.89 µs per loop
    
    %timeit np.mean(arr, axis=0)
    The slowest run took 6.44 times longer than the fastest. This could mean that an intermediate result is being cached.
    100000 loops, best of 3: 8.74 µs per loop
    

Hanya untuk klarifikasi masih tes pada batch kecil

0
mumbala 8 Agustus 2017, 20:22