2013-07-26 36 views
7

Tôi gặp vấn đề về hiệu suất cụ thể tại đây. Tôi đang làm việc với chuỗi thời gian dự báo khí tượng, mà tôi biên soạn thành một mảng 2ngày NumPy màThay đổi mảng numpy thanh lịch và điền NaN?

  • dim0 = thời gian mà dự đoán loạt bắt đầu
  • dim1 = dự báo đường chân trời, ví dụ. 0 đến 120 giờ

Bây giờ, tôi muốn dim0 có khoảng thời gian theo giờ, nhưng một số nguồn chỉ dự báo mỗi N giờ. Ví dụ, giả sử N = 3 và bước thời gian trong dim1 là M = 1 giờ. Sau đó, tôi nhận được một cái gì đó như

12:00 11.2 12.2 14.0 15.0 11.3 12.0 
13:00 nan nan nan nan nan nan 
14:00 nan nan nan nan nan nan 
15:00 14.7 11.5 12.2 13.0 14.3 15.1 

Nhưng tất nhiên có thông tin lúc 13:00 và 14:00, vì nó có thể được điền từ 12:00 dự báo chạy. Vì vậy, tôi muốn kết thúc với một cái gì đó như thế này:

12:00 11.2 12.2 14.0 15.0 11.3 12.0 
13:00 12.2 14.0 15.0 11.3 12.0 nan 
14:00 14.0 15.0 11.3 12.0 nan nan 
15:00 14.7 11.5 12.2 13.0 14.3 15.1 

cách nhanh nhất để đạt được điều đó là gì, giả sử dim0 được theo thứ tự của 1e4 và dim1 theo thứ tự của 1e2? Hiện tại tôi đang thực hiện từng hàng nhưng điều đó rất chậm:

nRows, nCols = dat.shape 
if N >= M: 
    assert(N % M == 0) # must have whole numbers 
    for i in range(1, nRows): 
     k = np.array(np.where(np.isnan(self.dat[i, :]))) 
     k = k[k < nCols - N] # do not overstep 
     self.dat[i, k] = self.dat[i-1, k+N] 

Tôi chắc chắn phải có cách thanh lịch hơn để làm điều này? Bất kì gợi ý sẽ được đánh giá rất cao.

+2

Bạn có quan tâm giải thích nó một cách khác, tôi đã bị mất trong câu "Nhưng tất nhiên. .. ". Các nguồn khác nhau được thể hiện như thế nào trong mảng? Các hàng có nghĩa là dim0 và dim1 = dimension1 = columns? – elyase

+1

@elyase: Các con số đang được dịch chuyển xuống và sang trái, bởi vì, ví dụ, nếu dự báo là 12,2 giờ từ bây giờ (lúc 12:00), sau đó trong một giờ, dự báo sẽ là 12,2 giờ từ đó (lúc 13:00). – unutbu

Trả lời

2

Cắt dữ liệu của bạn bằng cách sử dụng a=yourdata[:,1:].

def shift_time(dat): 

    #Find number of required iterations 
    check=np.where(np.isnan(dat[:,0])==False)[0] 
    maxiters=np.max(np.diff(check))-1 

    #No sense in iterations where it just updates nans 
    cols=dat.shape[1] 
    if cols<maxiters: maxiters=cols-1 

    for iters in range(maxiters): 
     #Find nans 
     col_loc,row_loc=np.where(np.isnan(dat[:,:-1])) 

     dat[(col_loc,row_loc)]=dat[(col_loc-1,row_loc+1)] 


a=np.array([[11.2,12.2,14.0,15.0,11.3,12.0], 
[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan], 
[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan], 
[14.7,11.5,12.2,13.0,14.3,15.]]) 

shift_time(a) 
print a 

[[ 11.2 12.2 14. 15. 11.3 12. ] 
[ 12.2 14. 15. 11.3 12. nan] 
[ 14. 15. 11.3 12. nan nan] 
[ 14.7 11.5 12.2 13. 14.3 15. ]] 

Để sử dụng dữ liệu của bạn như là hoặc nó có thể được thay đổi một chút để mang nó trực tiếp, nhưng điều này có vẻ là một cách rõ ràng để hiển thị này:

shift_time(yourdata[:,1:]) #Updates in place, no need to return anything. 

Sử dụng thử nghiệm Tiago của:

tmp = np.random.uniform(-10, 20, (1e4, 1e2)) 
nan_idx = np.random.randint(30, 1e4 - 1,1e4) 
tmp[nan_idx] = np.nan 

t=time.time() 
shift_time(tmp,maxiter=1E5) 
print time.time()-t 

0.364198923111 (seconds) 

Nếu bạn thực sự thông minh, bạn sẽ có thể nhận được ngay với một đơn np.where.

0

Mỗi lần lặp này pad, cuộn, cuộn kết hợp chủ yếu làm những gì bạn đang tìm kiếm:

import numpy as np 
from numpy import nan as nan 

# Startup array 
A = np.array([[11.2, 12.2, 14.0, 15.0, 11.3, 12.0], 
       [nan, nan, nan, nan, nan, nan], 
       [nan, nan, nan, nan, nan, nan], 
       [14.7, 11.5, 12.2, 13.0, 14.3, 15.1]]) 

def pad_nan(v, pad_width, iaxis, kwargs): 
    v[:pad_width[0]] = nan 
    v[-pad_width[1]:] = nan 
    return v 

def roll_data(A): 
    idx = np.isnan(A) 
    A[idx] = np.roll(np.roll(np.pad(A,1, pad_nan),1,0), -1, 1)[1:-1,1:-1][idx] 
    return A 

print A 
print roll_data(A) 
print roll_data(A) 

Kết quả cho:

[[ 11.2 12.2 14. 15. 11.3 12. ] 
[ nan nan nan nan nan nan] 
[ nan nan nan nan nan nan] 
[ 14.7 11.5 12.2 13. 14.3 15.1]] 

[[ 11.2 12.2 14. 15. 11.3 12. ] 
[ 12.2 14. 15. 11.3 12. nan] 
[ nan nan nan nan nan nan] 
[ 14.7 11.5 12.2 13. 14.3 15.1]] 

[[ 11.2 12.2 14. 15. 11.3 12. ] 
[ 12.2 14. 15. 11.3 12. nan] 
[ 14. 15. 11.3 12. nan nan] 
[ 14.7 11.5 12.2 13. 14.3 15.1]] 

Mọi thứ đều NumPy tinh khiết vì thế nó phải cực nhanh mỗi lần lặp lại. Tuy nhiên tôi không chắc chắn về chi phí tạo ra một mảng đệm và chạy nhiều lần lặp lại, nếu bạn thử nó cho tôi biết kết quả!

+0

Tôi nghĩ rằng nhiều lần lặp lại điều này sẽ giết hiệu suất. Tôi đã thử nghiệm nó bằng cách thiết lập tương tự như câu trả lời của tôi (chạy NY lặp lại), và nó trong hệ thống của tôi mất 33,85s cho một hình dạng mảng (10000, 100), khoảng 20x chậm hơn so với giải pháp của tôi (tương tự như của Ophion). – tiago

1

Điều này dường như làm các trick:

import numpy as np 

def shift_time(dat): 
    NX, NY = dat.shape 
    for i in range(NY): 
     x, y = np.where(np.isnan(dat)) 
     xr = x - 1 
     yr = y + 1 
     idx = (xr >= 0) & (yr < NY) 
     dat[x[idx], y[idx]] = dat[xr[idx], yr[idx]] 
    return 

Bây giờ với một số dữ liệu thử nghiệm:

In [1]: test_data = array([[ 11.2, 12.2, 14. , 15. , 11.3, 12. ], 
          [ nan, nan, nan, nan, nan, nan], 
          [ nan, nan, nan, nan, nan, nan], 
          [ 14.7, 11.5, 12.2, 13. , 14.3, 15.1], 
          [ nan, nan, nan, nan, nan, nan], 
          [ 15.7, 16.5, 17.2, 18. , 14. , 12. ]]) 
In [2]: shift_time(test_data) 
In [3]: print test_data 
Out [3]: 
array([[ 11.2, 12.2, 14. , 15. , 11.3, 12. ], 
     [ 12.2, 14. , 15. , 11.3, 12. , nan], 
     [ 14. , 15. , 11.3, 12. , nan, nan], 
     [ 14.7, 11.5, 12.2, 13. , 14.3, 15.1], 
     [ 11.5, 12.2, 13. , 14.3, 15.1, nan], 
     [ 15.7, 16.5, 17.2, 18. , 14. , 12. ]]) 

Và thử nghiệm với một (1e4, 1e2) mảng:

In [1]: tmp = np.random.uniform(-10, 20, (1e4, 1e2)) 
In [2]: nan_idx = np.random.randint(30, 1e4 - 1,1e4) 
In [3]: tmp[nan_idx] = nan 
In [4]: time test3(tmp) 
CPU times: user 1.53 s, sys: 0.06 s, total: 1.59 s 
Wall time: 1.59 s 
5

Nầy , sức mạnh của lập chỉ mục boolean !!!

def shift_nans(arr) : 
    while True: 
     nan_mask = np.isnan(arr) 
     write_mask = nan_mask[1:, :-1] 
     read_mask = nan_mask[:-1, 1:] 
     write_mask &= ~read_mask 
     if not np.any(write_mask): 
      return arr 
     arr[1:, :-1][write_mask] = arr[:-1, 1:][write_mask] 

Tôi nghĩ rằng đặt tên là tự giải thích về những gì đang diễn ra. Lấy cắt đúng là một nỗi đau, nhưng có vẻ như được làm việc:

In [214]: shift_nans_bis(test_data) 
Out[214]: 
array([[ 11.2, 12.2, 14. , 15. , 11.3, 12. ], 
     [ 12.2, 14. , 15. , 11.3, 12. , nan], 
     [ 14. , 15. , 11.3, 12. , nan, nan], 
     [ 14.7, 11.5, 12.2, 13. , 14.3, 15.1], 
     [ 11.5, 12.2, 13. , 14.3, 15.1, nan], 
     [ 15.7, 16.5, 17.2, 18. , 14. , 12. ]]) 

Và đối với timings:

tmp1 = np.random.uniform(-10, 20, (1e4, 1e2)) 
nan_idx = np.random.randint(30, 1e4 - 1,1e4) 
tmp1[nan_idx] = np.nan 
tmp1 = tmp.copy() 

import timeit 

t1 = timeit.timeit(stmt='shift_nans(tmp)', 
        setup='from __main__ import tmp, shift_nans', 
        number=1) 
t2 = timeit.timeit(stmt='shift_time(tmp1)', # Ophion's code 
        setup='from __main__ import tmp1, shift_time', 
        number=1) 

In [242]: t1, t2 
Out[242]: (0.12696346416487359, 0.3427293070417363) 
+0

Bạn có thể cập nhật nan_mask bằng cách sử dụng 'nan_mask [1:,: - 1]^= write_mask' để bạn chỉ phải tính' np.isnan (arr) 'một lần. Nhược điểm là write_mask của bạn phải được sao chép sao cho nó không thay đổi các giá trị trong nan_mask. Có thể nhanh hơn hoặc chậm hơn nhiều tùy thuộc vào các lần lặp tối đa được yêu cầu. – Daniel

+0

Cảm ơn tất cả vì các giải pháp thông minh của bạn! Tôi biết phải có nhiều cách để làm điều này và có vẻ như bây giờ chúng tôi đã lấy mẫu một phần tốt của họ ... Tôi sẽ đi cho cái này, điều đó sẽ giải quyết vấn đề của tôi một cách độc đáo. – marfel

Các vấn đề liên quan