2011-11-11 11 views
17

Tôi cố gắng để reimplement trong python một chức năng IDL:thay đổi kích thước với trung bình hoặc rebin một mảng 2ngày NumPy

http://star.pst.qub.ac.uk/idl/REBIN.html

mà downsizes bởi một yếu tố nguyên một mảng 2ngày bằng trung bình.

Ví dụ:

>>> a=np.arange(24).reshape((4,6)) 
>>> a 
array([[ 0, 1, 2, 3, 4, 5], 
     [ 6, 7, 8, 9, 10, 11], 
     [12, 13, 14, 15, 16, 17], 
     [18, 19, 20, 21, 22, 23]]) 

Tôi muốn thay đổi kích thước (2,3) bằng cách lấy giá trị trung bình của các mẫu có liên quan, sản lượng dự kiến ​​sẽ là:

>>> b = rebin(a, (2, 3)) 
>>> b 
array([[ 3.5, 5.5, 7.5], 
     [ 15.5, 17.5, 19.5]]) 

tức b[0,0] = np.mean(a[:2,:2]), b[0,1] = np.mean(a[:2,2:4]) và vân vân.

Tôi tin rằng tôi nên định hình lại thành mảng 4 chiều và sau đó lấy giá trị trung bình trên đúng lát, nhưng không thể tìm ra thuật toán. Bạn có gợi ý gì không?

+1

chỉ thấy bây giờ điều này là một bản sao của http://stackoverflow.com/questions/4624112/grouping-2d-numpy-array-in-average, tuy nhiên tôi không thể tìm thấy điều này trước khi sử dụng chức năng tìm kiếm trong stackoverflow. –

Trả lời

29

Dưới đây là một ví dụ dựa trên the answer you've linked (cho rõ ràng):

>>> import numpy as np 
>>> a = np.arange(24).reshape((4,6)) 
>>> a 
array([[ 0, 1, 2, 3, 4, 5], 
     [ 6, 7, 8, 9, 10, 11], 
     [12, 13, 14, 15, 16, 17], 
     [18, 19, 20, 21, 22, 23]]) 
>>> a.reshape((2,a.shape[0]//2,3,-1)).mean(axis=3).mean(1) 
array([[ 3.5, 5.5, 7.5], 
     [ 15.5, 17.5, 19.5]]) 

Là một chức năng:

def rebin(a, shape): 
    sh = shape[0],a.shape[0]//shape[0],shape[1],a.shape[1]//shape[1] 
    return a.reshape(sh).mean(-1).mean(1) 
+0

cảm ơn, tôi đã tạo ra gist trên github với việc thực hiện chức năng này, trong trường hợp ai đó cần nó: https://gist.github.com/1348792, tôi cũng đề xuất về 'numpy-discussion' để thêm nó vào 'numpy 'nhưng câu trả lời là tiêu cực. –

+0

họ có đưa ra lý do cho câu trả lời tiêu cực không? –

+0

Tôi nghĩ [this] (http://mail.scipy.org/pipermail/numpy-discussion/2011-November/059208.html) là cuộc thảo luận. Dường như không tiêu cực, chỉ là thiếu thời gian hoặc không đủ quan tâm. – Evert

7

J.F. Sebastian có một câu trả lời tuyệt vời cho di chuyển chuột đã 2D. Đây là một phiên bản của chức năng "rebin" của mình mà làm việc cho kích thước N:

def bin_ndarray(ndarray, new_shape, operation='sum'): 
    """ 
    Bins an ndarray in all axes based on the target shape, by summing or 
     averaging. 

    Number of output dimensions must match number of input dimensions and 
     new axes must divide old ones. 

    Example 
    ------- 
    >>> m = np.arange(0,100,1).reshape((10,10)) 
    >>> n = bin_ndarray(m, new_shape=(5,5), operation='sum') 
    >>> print(n) 

    [[ 22 30 38 46 54] 
    [102 110 118 126 134] 
    [182 190 198 206 214] 
    [262 270 278 286 294] 
    [342 350 358 366 374]] 

    """ 
    operation = operation.lower() 
    if not operation in ['sum', 'mean']: 
     raise ValueError("Operation not supported.") 
    if ndarray.ndim != len(new_shape): 
     raise ValueError("Shape mismatch: {} -> {}".format(ndarray.shape, 
                  new_shape)) 
    compression_pairs = [(d, c//d) for d,c in zip(new_shape, 
                ndarray.shape)] 
    flattened = [l for p in compression_pairs for l in p] 
    ndarray = ndarray.reshape(flattened) 
    for i in range(len(new_shape)): 
     op = getattr(ndarray, operation) 
     ndarray = op(-1*(i+1)) 
    return ndarray 
1

Tôi đã cố gắng để thu hẹp một raster - mất một khoảng 6000 năm 2000 kích thước raster và biến nó thành một tùy tiện có kích thước nhỏ hơn mà raster trung bình các giá trị đúng cách trên các kích thước thùng trước đó. Tôi tìm thấy một giải pháp bằng cách sử dụng SciPy, nhưng sau đó tôi không thể có được SciPy để cài đặt trên các dịch vụ lưu trữ được chia sẻ tôi đã sử dụng, vì vậy tôi chỉ cần viết chức năng này để thay thế. Có khả năng là một cách tốt hơn để làm điều này không liên quan đến việc lặp qua các hàng và cột, nhưng điều này dường như hoạt động.

Phần hay nhất về điều này là số hàng và cột cũ không phải chia hết cho số hàng và cột mới.

def resize_array(a, new_rows, new_cols): 
    ''' 
    This function takes an 2D numpy array a and produces a smaller array 
    of size new_rows, new_cols. new_rows and new_cols must be less than 
    or equal to the number of rows and columns in a. 
    ''' 
    rows = len(a) 
    cols = len(a[0]) 
    yscale = float(rows)/new_rows 
    xscale = float(cols)/new_cols 

    # first average across the cols to shorten rows  
    new_a = np.zeros((rows, new_cols)) 
    for j in range(new_cols): 
     # get the indices of the original array we are going to average across 
     the_x_range = (j*xscale, (j+1)*xscale) 
     firstx = int(the_x_range[0]) 
     lastx = int(the_x_range[1]) 
     # figure out the portion of the first and last index that overlap 
     # with the new index, and thus the portion of those cells that 
     # we need to include in our average 
     x0_scale = 1 - (the_x_range[0]-int(the_x_range[0])) 
     xEnd_scale = (the_x_range[1]-int(the_x_range[1])) 
     # scale_line is a 1d array that corresponds to the portion of each old 
     # index in the_x_range that should be included in the new average 
     scale_line = np.ones((lastx-firstx+1)) 
     scale_line[0] = x0_scale 
     scale_line[-1] = xEnd_scale 
     # Make sure you don't screw up and include an index that is too large 
     # for the array. This isn't great, as there could be some floating 
     # point errors that mess up this comparison. 
     if scale_line[-1] == 0: 
      scale_line = scale_line[:-1] 
      lastx = lastx - 1 
     # Now it's linear algebra time. Take the dot product of a slice of 
     # the original array and the scale_line 
     new_a[:,j] = np.dot(a[:,firstx:lastx+1], scale_line)/scale_line.sum() 

    # Then average across the rows to shorten the cols. Same method as above. 
    # It is probably possible to simplify this code, as this is more or less 
    # the same procedure as the block of code above, but transposed. 
    # Here I'm reusing the variable a. Sorry if that's confusing. 
    a = np.zeros((new_rows, new_cols)) 
    for i in range(new_rows): 
     the_y_range = (i*yscale, (i+1)*yscale) 
     firsty = int(the_y_range[0]) 
     lasty = int(the_y_range[1]) 
     y0_scale = 1 - (the_y_range[0]-int(the_y_range[0])) 
     yEnd_scale = (the_y_range[1]-int(the_y_range[1])) 
     scale_line = np.ones((lasty-firsty+1)) 
     scale_line[0] = y0_scale 
     scale_line[-1] = yEnd_scale 
     if scale_line[-1] == 0: 
      scale_line = scale_line[:-1] 
      lasty = lasty - 1 
     a[i:,] = np.dot(scale_line, new_a[firsty:lasty+1,])/scale_line.sum() 

    return a 
2

Đây là cách thực hiện những gì bạn yêu cầu bằng phép nhân ma trận không yêu cầu thứ nguyên mảng mới để chia cũ.

Đầu tiên chúng ta tạo ra một ma trận nén hàng và một ma trận nén cột (Tôi chắc rằng có một cách sạch hơn để làm điều này, thậm chí có thể sử dụng các hoạt động NumPy một mình):

def get_row_compressor(old_dimension, new_dimension): 
    dim_compressor = np.zeros((new_dimension, old_dimension)) 
    bin_size = float(old_dimension)/new_dimension 
    next_bin_break = bin_size 
    which_row = 0 
    which_column = 0 
    while which_row < dim_compressor.shape[0] and which_column < dim_compressor.shape[1]: 
     if round(next_bin_break - which_column, 10) >= 1: 
      dim_compressor[which_row, which_column] = 1 
      which_column += 1 
     elif next_bin_break == which_column: 

      which_row += 1 
      next_bin_break += bin_size 
     else: 
      partial_credit = next_bin_break - which_column 
      dim_compressor[which_row, which_column] = partial_credit 
      which_row += 1 
      dim_compressor[which_row, which_column] = 1 - partial_credit 
      which_column += 1 
      next_bin_break += bin_size 
    dim_compressor /= bin_size 
    return dim_compressor 


def get_column_compressor(old_dimension, new_dimension): 
    return get_row_compressor(old_dimension, new_dimension).transpose() 

...như vậy, ví dụ, get_row_compressor(5, 3) mang đến cho bạn:

[[ 0.6 0.4 0. 0. 0. ] 
[ 0. 0.2 0.6 0.2 0. ] 
[ 0. 0. 0. 0.4 0.6]] 

get_column_compressor(3, 2) mang đến cho bạn:

[[ 0.66666667 0.  ] 
[ 0.33333333 0.33333333] 
[ 0.   0.66666667]] 

Sau đó chỉ cần premultiply bởi máy nén hàng và postmultiply bởi máy nén cột để có được ma trận nén:

def compress_and_average(array, new_shape): 
    # Note: new shape should be smaller in both dimensions than old shape 
    return np.mat(get_row_compressor(array.shape[0], new_shape[0])) * \ 
      np.mat(array) * \ 
      np.mat(get_column_compressor(array.shape[1], new_shape[1])) 

Sử dụng kỹ thuật này,

compress_and_average(np.array([[50, 7, 2, 0, 1], 
           [0, 0, 2, 8, 4], 
           [4, 1, 1, 0, 0]]), (2, 3)) 

sản lượng:

[[ 21.86666667 2.66666667 2.26666667] 
[ 1.86666667 1.46666667 1.86666667]]