2016-08-28 5 views
2

최근에 numpy 배열을 반복하는 여러 기법에 대해 많이 읽었으며 합의가 반복되지 않는 것으로 보입니다 (예 : a comment here 참조). 너무 비슷한 질문이 몇 가지 있지만, "반복"(또는 반복하지 않음)과 이전 값에 액세스해야하므로 내 경우가 약간 다릅니다.여러 개의 numpy 배열을 반복하고 현재 및 이전 요소를 처리하는 효율적인 방법은 무엇입니까?

목록에 X 인 N (N은 작음, 보통 4, 최대 7 일 수 있음) float128의 1-D numpy 배열이 있다고 가정 해 봅시다. 모든 배열의 크기는 같습니다. 당신에게 약간의 통찰력을주기 위해 PDE 통합에서 나온 데이터입니다. 각 배열은 하나의 함수를 나타내며 Poincare 섹션을 적용하고 싶습니다. 불행히도이 알고리즘은 각각 ~ 1Gb의 메모리와 시간에 효율적이기 때문에 메모리에 4Gb의 RAM 만 있습니다. (저는 memmap'ing에 대해 배웠습니다.).

이러한 배열 중 하나는 다른 필터를 필터링하는 데 사용되므로 secaxis = X.pop(idx)으로 시작합니다. 이제 인덱스 쌍을 찾고 (secaxis[i-1] > 0 and secaxis[i] < 0) or (secaxis[i-1] < 0 and secaxis[i] > 0)을 입력 한 다음 나머지 배열에 간단한 대수 변환을 적용한 후 X (및 결과 저장)을 수행해야합니다. 언급하면,이 작업 중에 데이터를 낭비해서는 안됩니다.

이렇게하는 방법은 여러 가지가 있지만 그 중 어느 것도 나에게 효율적이지는 않습니다. 하나는 당신이 단지에서 반복은 C와 같은 접근 방식 인을위한 루프 :

import array # better than lists 
res = [ array.array('d') for _ in X ] 
for i in xrange(1,secaxis.size): 
    if condition: # see above 
    co = -secaxis[i-1]/secaxis[i] 
    for j in xrange(N): 
     res[j].append((X[j][i-1] + co*X[j][i])/(1+co)) 

이것은 분명히 매우 비효율적 아닌 파이썬 방법 외에입니다.

또 다른 방법은 numpy.nditer을 사용하는 것입니다,하지만 하나는 이전 값에 액세스하는 방법은 한 번에 여러 배열을 통해 반복하는 수 있지만 나는 아직 파악되지 않은 :

# without secaxis = X.pop(idx) 
it = numpy.nditer(X) 
for vec in it: 
    # vec[idx] is current value, how do you get the previous (or next) one? 

세 번째 가능성은 처음이다 효율적인 numpy 조각으로 찾은 색인을 찾은 다음 대량 증분/추가에 사용합니다. 지금이 하나의 선호 :

res = [] 
inds, = numpy.where((secaxis[:-1] < 0) * (secaxis[1:] > 0) + 
        (secaxis[:-1] > 0) * (secaxis[1:] < 0)) 
coefs = -secaxis[inds]/secaxis[inds+1] # array of coefficients 
for f in X: # loop is done only N-1 times, that is, 3 to 6 
    res.append((f[inds] + coefs*f[inds+1])/(1+coefs)) 

을 그러나 이것은 겉으로 7 + 2 *에서 이루어집니다 (N - 1), 또한, 내가 (주소는 슬라이스 일반적으로하지 않습니다의 secaxis[inds] 유형에 대한 확실하지 않다 패스 첫 번째 방법과 마찬가지로 모든 요소를 ​​인덱스로 찾아야합니다.

마지막으로, 나는 또한 itertools를 사용하여 시도하고 그것이 내가 함수형 프로그래밍에 익숙하지 않다는 사실에서 줄기 수있는 괴물과 모호한 구조, 결과 :

def filt(x): 
    return (x[0] < 0 and x[1] > 0) or (x[0] > 0 and x[1] < 0) 
import array 
from itertools import izip, tee, ifilter 
res = [ array.array('d') for _ in X ] 
iters = [iter(x) for x in X] # N-1 iterators in a list 
prev, curr = tee(izip(*iters)) # 2 similar iterators, each of which 
           # consists of N-1 iterators 
next(curr, None) # one of them is now for current value 
seciter = tee(iter(secaxis)) 
next(seciter[1], None) 
for x in ifilter(filt, izip(seciter[0], seciter[1], prev, curr)): 
    co = - x[0]/x[1] 
    for r, p, c in zip(res, x[2], x[3]): 
    r.append((p+co*c)/(1+co)) 

뿐만 아니라이 모습 매우 못생긴, 그것은 또한 완료하는 데 엄청난 시간이 걸립니다.

그래서, 다음 한 질문 : 이러한 모든 방법의

  1. 는 세번째는 참으로 최고? 그렇다면, 마지막 것을 impove하기 위해 무엇을 할 수 있습니까?
  2. 아직 더 좋은 것들이 있습니까?
  3. 단순한 호기심에서 nditer를 사용하여 문제를 해결할 수있는 방법이 있습니까?
  4. 마지막으로 numpy 배열의 memmap 버전을 사용하는 것이 더 낫지 않습니까? 아니면 많은 것을 느리게할까요?어쩌면 내가 RAM에 secaxis 배열을로드해야합니다 디스크에 다른 사람을 유지하고 세 번째 방법을 사용합니까?
  5. (보너스 질문) 길이가 같은 1-D numpy 배열 목록은 크기가 미리 알 수없는 파일 (N은 있음)을로드하는 데서 발생합니다 (N .npy). 하나의 배열을 읽고, 그 다음에 2-D numpy 배열 (여기에 약간의 메모리 오버 헤드)을 할당하고 그 2-D 배열에 나머지를 읽는 것이 더 효율적입니까?

답변

3

numpy.where() 버전이 충분히 빠르면 method3()으로 조금 더 빨라질 수 있습니다. > 조건이 >=으로 변경 될 수있는 경우 method4()을 사용할 수도 있습니다.

import numpy as np 

a = np.random.randn(100000) 

def method1(a): 
    idx = [] 
    for i in range(1, len(a)): 
     if (a[i-1] > 0 and a[i] < 0) or (a[i-1] < 0 and a[i] > 0): 
      idx.append(i) 
    return idx 

def method2(a): 
    inds, = np.where((a[:-1] < 0) * (a[1:] > 0) + 
         (a[:-1] > 0) * (a[1:] < 0)) 
    return inds + 1 

def method3(a): 
    m = a < 0 
    p = a > 0 
    return np.where((m[:-1] & p[1:]) | (p[:-1] & m[1:]))[0] + 1 

def method4(a): 
    return np.where(np.diff(a >= 0))[0] + 1 

assert np.allclose(method1(a), method2(a)) 
assert np.allclose(method2(a), method3(a)) 
assert np.allclose(method3(a), method4(a)) 

%timeit method1(a) 
%timeit method2(a) 
%timeit method3(a) 
%timeit method4(a) 

%timeit 결과 :

1 loop, best of 3: 294 ms per loop 
1000 loops, best of 3: 1.52 ms per loop 
1000 loops, best of 3: 1.38 ms per loop 
1000 loops, best of 3: 1.39 ms per loop 
3

좀 더 자세히 귀하의 게시물을 읽을해야하지만 (이전 반복 질문에서) 몇 가지 일반적인 관측 시작됩니다.

Python에서 배열을 반복하는 효율적인 방법은 없지만 느려지는 것이 있습니다. 반복 메커니즘 (nditer, for x in A:)과 동작 (alist.append(...), x[i+1] += 1)을 구분하고 싶습니다. 큰 시간을 소비하는 소비자는 일반적으로 반복 메커니즘 자체가 아니라 여러 번 수행되는 동작입니다.

numpy을 컴파일 된 코드에서 반복하는 것이 가장 빠릅니다.

xdiff = x[1:] - x[:-1] 

np.nditer이 빨리되지 않습니다

xdiff = np.zeros(x.shape[0]-1) 
for i in range(x.shape[0]: 
    xdiff[i] = x[i+1] - x[i] 

보다 훨씬 빠릅니다.

nditer은 컴파일 된 코드의 일반적인 반복 도구로 권장됩니다. 그러나 그것의 주된 가치는 방송을 다루고 여러 배열 (입력/출력)에 걸쳐 반복을 조정하는 데 있습니다. 그리고 nditer에서 최상의 속도를 얻으려면 버퍼링을 사용하고 c 코드를 사용해야합니다 (최근의 질문을 살펴볼 것입니다).

https://stackoverflow.com/a/39058906/901925

는 관련 iteration 튜토리얼 페이지 (A cython 예로 끝나는 하나)를 공부하지 않고 nditer를 사용하지 마십시오.

그냥 경험에서 판단 =========================

,이 방법은 가장 빠른 것입니다. 그렇습니다. 숫자는 secaxis 번 이상 반복되지만 컴파일 된 코드로 완료되며 파이썬에서 반복되는 것보다 훨씬 빠릅니다. 그리고 for f in X: 반복은 단지 몇 번입니다.

res = [] 
inds, = numpy.where((secaxis[:-1] < 0) * (secaxis[1:] > 0) + 
        (secaxis[:-1] > 0) * (secaxis[1:] < 0)) 
coefs = -secaxis[inds]/secaxis[inds+1] # array of coefficients 
for f in X: 
    res.append((f[inds] + coefs*f[inds+1])/(1+coefs)) 

@HYRY

빠르게 where 단계를 만들기위한 대안을 모색하고있다. 그러나 당신이 볼 수 있듯이 그 차이는 그리 크지 않습니다. X가 배열 인 경우 다른 가능한 바뀌는

inds1 = inds+1 
coefs = -secaxis[inds]/secaxis[inds1] 
coefs1 = coefs+1 
for f in X: 
    res.append((f[inds] + coefs*f[inds1])/coefs1) 

res뿐만 아니라 배열이 될 수 있습니다.

res = (X[:,inds] + coefs*X[:,inds1])/coefs1 

는 그러나 작은 N 내가 목록 res 그냥 좋은 것으로 판단됩니다. 배열을 필요한 것보다 더 크게 만들 필요는 없습니다. 비틀기는 사소한 것으로, 재 계산을 피하려고합니다.

=================

np.where의 사용은 단지 np.nonzero입니다. 실제로 배열의 두 번을 만들고, 한 번 np.count_nonzero을 사용하여 반환 할 값의 수를 결정하고 반환 구조 (현재 알려진 길이의 배열 목록)를 만듭니다. 그리고 두 번째 루프는 이러한 지표를 채 웁니다. 따라서 반복 작업을 단순하게하면 여러 번 반복해도됩니다.