2014-09-24 8 views
7

Ben (1000, 800, 1024) bir dizi (sırayla) 3D dizi diziler var çalışmak istiyorum. Eksen = 0 boyunca ortalamayı hesaplamam gerekiyor ama bunu yapmadan önce, veriyi 'doğru yerde' olana kadar eksen 2 boyunca yuvarlamak zorundayım.numpy.sum() veya mean() 0'dan önce verimli numpy.roll()

Bu garip geliyor, bu yüzden açıklamaya çalışacağım. Şekil lD-alt dizisi (1024,), bir fiziksel halka tamponundan elde edilen verilerdir. Halka tampon, bildiğim farklı alanlarda okunur. Bu yüzden pos şekil (1000, 800) dizim var. Ring buffer'ın hangi pozisyonda olduğunu bana anlattı. Ve benim 3D dizilerim'a göre rulo ihtiyacım olan data şeklindedir (1000, 800, 1024).

Sadece yuvarlanmadan sonra 3D diziler benim için anlamlı ve bunları analiz etmeye başlayabilirim. C de, bu oldukça basit bir kod yazabilirim, bu yüzden ben, farklı indekslerde başlamalı 'numpy ortalama() veya sum() rutinleri' ve 'etrafında' etrafında 'etrafında' rulo 'diyebilirim 1D-alt dizini.

Ne şu anda yapmak şudur:

rolled = np.zeros_like(data) # shape (1000, 800, 1024) 
for a in range(rolled.shape[0]): 
    for b in range(rolled.shape[1]): 
     rolled[a,b] = np.roll(data[a,b], pos[a,b]) 

Bu 60sec ~ alır Ve sonra örneğin yapın:

sadece 15sn kadar sürer
m = rolled.mean(axis=0) 
s = rolled.std(axis=0) 

.

Bulunduğum nokta, bu nedenle, bu kopyayı uygulamak için bir yol (C) olmasına rağmen, aktarılan kopyayı çok fazla yer ve zaman harcayan (tamamıyla rulo maddeyi data'a yazarak alandan tasarruf edebilirdim) ve bir döngüde yuvarlanıyor, çok fazla zaman tasarrufu sağlıyor. Sorum şu: eğer numpy ile benzer bir şey yapmanın bir yolu varsa?

+2

zaten bir ['numpy.roll'] 'dir (http://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html). hesaplarınıza uymuyor mu? –

+0

@ behzad.nouri Sorumu düzenledim. Zaten np.roll kullanıyorum, ama aslında haddelenmiş veriye ihtiyacım yok, ama biraz 'farklı' davranmak için mean() ve std() istiyorum .. :-) Sanırım bunu yapmanın bir yolu yok Python ile daha hızlı ... belki inline C kullanarak cevaptır ... –

+0

Tek bir döngüde ortalamanın yapıldığından şüphe etmem çok daha hızlıdır çünkü önbellek tutarlılığınızla uğraşır; Verileri yeniden düzenlemek muhtemelen en hızlıdır. Gerçekten de bellek yükü gereksizdir, ancak haddelemeyi optimize etmek için bir C-uzantısı yazarak ve python döngülerini ortadan kaldırarak biraz tasarruf edebilirsiniz. Her iki durumda da terrabayt veriyle uğraşırsınız, bu yüzden beklemeye hazır olun veya python için birçok C uzantı yönteminden birini nasıl kullanacağınızı öğrenin. –

cevap

10

Sıkıldım ve işlevinizi Cython'da yazdım. Bir ara dizi tahsis etmeden, gönderdiğiniz koddan yaklaşık 10 kat daha hızlı çalışır. Eğer kayan nokta doğruluk hataları girmek olabilir bu nedenle bu Naive mean+stdv algorithm kullandığı

import numpy as np 
cimport numpy as np 
cimport cython 
from libc.math cimport sqrt 

@cython.boundscheck(False) 
@cython.wraparound(False) 
@cython.nonecheck(False) 
@cython.cdivision(True) 
def rolled_mean_std(double[:,:,::1] data, int[:,::1] pos): 
    cdef Py_ssize_t s1,s2,s3,a,b,c 
    s1 = data.shape[0] 
    s2 = data.shape[1] 
    s3 = data.shape[2] 
    cdef double[:,::1] sums = np.zeros((s2,s3)) 
    cdef double[:,::1] sumsq = np.zeros((s2,s3)) 
    cdef double d 
    cdef int p 
    # Compute sums and sum-of-squares. 
    for a in range(s1): 
    for b in range(s2): 
     p = pos[a,b] 
     for c in range(s3): 
     d = data[a,b,(c+s3-p)%s3] 
     sums[b,c] += d 
     sumsq[b,c] += d * d 
    # Calculate mean + std in place. 
    for b in range(s2): 
    for c in range(s3): 
     d = sums[b,c] 
     sums[b,c] /= s1 
     sumsq[b,c] = sqrt((s1*sumsq[b,c] - (d*d)))/s1 
    return sums, sumsq 

Not. Testlerim bir etki göstermedi.

+0

Vay! teşekkürler @perimosocordiae. Çok naziksiniz. Ben kesinlikle Cython'a daha çok bakmalıyım.Ancak, cevabınızı yanıt olarak işaretleyemem, çünkü gerçekten bu noktaya gelmiyor ... –

+0

Anladım. :-) Maalesef, doğru cevabın "yapamazsın" olduğunu düşünüyorum. – perimosocordiae