2011-11-11 11 views
17

을 rebin : 평균하여 정수 계수를 2 차원 배열을 downsizes평균으로 크기를 조정하거나 내가 파이썬에서 IDL 기능을 재 구현하려고 오전 NumPy와 2 차원 배열

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

합니다. 예를 들어

:

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

내가 관련 샘플의 평균을 취함으로써 (2,3)로 크기를 조정하고 싶습니다 예상 출력은 다음과 같습니다

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

b[0,0] = np.mean(a[:2,:2]), b[0,1] = np.mean(a[:2,2:4]) 등등.

나는 4 차원 어레이로 변형 한 다음 정확한 슬라이스에서 평균을 취해야한다고 생각하지만 알고리즘을 파악할 수 없었습니다. 힌트 좀 줄래?

+1

은 그러나 나는이를 찾을 수 없습니다,이 http://stackoverflow.com/questions/4624112/grouping-2d-numpy-array-in-average의 중복 지금 발견 stackoverflow에서 검색 기능을 사용하기 전에. –

답변

29

여기 (명확성을 위해) the answer you've linked에 기초 예제 : 함수로서

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

:

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

덕분에, 나는 다른 사람들이 필요로하는 경우에 대비해 github에 대한 요지를 만들었습니다 : https://gist.github.com/1348792,'numpy-discussion'에'numpy 그러나 대답은 부정적이었다. –

+0

그들은 부정적인 대답에 대한 이유를 제시 했습니까? –

+0

[this] (http://mail.scipy.org/pipermail/numpy-discussion/2011-November/059208.html)가 토론이라고 생각합니다. 부정적인 것, 시간 부족 또는 관심 부족으로 보입니다. – Evert

7

J.F. 세바스찬 2D 비닝을위한 좋은 해답을 갖는다. 여기에 N 치수 작동 그의 "rebin"기능의 버전입니다 :

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
내가 래스터를 다운 스케일려고

- 대략 6000 2000 크기의 래스터을하고로 돌려 임의의 작은 래스터 크기는 이전의 저장소 크기를 통해 값을 올바르게 평균화했습니다. SciPy를 사용하여 솔루션을 찾았지만 SciPy를 공유 호스팅 서비스에 설치하지 못해 대신이 함수를 작성했습니다. 행과 열을 반복하는 것을 포함하지 않는 더 좋은 방법이있을 수 있지만 이것이 효과가있는 것 같습니다.

좋은 점은 이전 수의 행과 열을 새로운 행과 열 수로 나눌 필요가 없다는 것입니다.

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

다음은 이전 배열을 나눌 새 배열 차원이 필요하지 않은 행렬 곱하기를 사용하여 수행하는 방법입니다.

먼저 우리는 열 압축기 매트릭스와 열 압축기 매트릭스 (나는 어쩌면 혼자 NumPy와 작업을 사용하여,이 일을 깨끗한 방법이 확신) 생성 :

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

을 ...그래서, 예를 들면, get_row_compressor(5, 3) 당신에게 준다 :

[[ 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) 당신에게 준다 :

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

을이어서 간단히 전치 승산 열 압축기 행 압축기 postmultiply 의해 압축 된 매트릭스 얻을 :

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])) 
53,210

수율 :

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