2015-04-03 6 views
4

У меня есть большая 2D-матрица, которая должна быть меньше (например: конвертировать от 100x100 до 10x10).Упростить матрицу, усредняя несколько ячеек

Моя цель по существу: преодолей NxN матрицу на более мелкие МХМ матриц, усреднить клетки в этих МХМ ломтиками, а затем построить новый (меньший) матрицу из этих МХМ ломтиками.

Я думаю об использовании чего-то вроде matrix[a::b, c::d] для извлечения меньших матриц, а затем усреднения этих значений, но это кажется слишком сложным. Есть ли лучший способ сделать это?

ответ

5

Вы можете разделить свой массив на блоки с помощью функции view_as_blocks (в scikit-изображении).

Для 2D массива, это возвращает 4D массив с блоками упорядоченных по строкам:

>>> import skimage.util as ski 
>>> import numpy as np 
>>> a = np.arange(16).reshape(4,4) # 4x4 array 
>>> ski.view_as_blocks(a, (2,2)) 
array([[[[ 0, 1], 
     [ 4, 5]], 

     [[ 2, 3], 
     [ 6, 7]]], 


     [[[ 8, 9], 
     [12, 13]], 

     [[10, 11], 
     [14, 15]]]]) 

Принимая среднее вдоль последних двух осей возвращает 2D массив со средним значением в каждом блоке:

>>> ski.view_as_blocks(a, (2,2)).mean(axis=(2,3)) 
array([[ 2.5, 4.5], 
     [ 10.5, 12.5]]) 

Примечание: view_as_blocks возвращает представление массива путем изменения успехов (он также работает с массивами с более чем двух измерениях). Он реализован исключительно в NumPy с использованием as_strided, поэтому, если у вас нет доступа к библиотеке scikit-image, вы можете copy the code from here.

+0

какая ось = (2,3) здесь делать? – Teknophilia

+0

@Teknophilia: Он применяет «среднюю» функцию вдоль обеих этих осей для вычисления среднего значения в каждом отдельном блоке. В массиве 4D ось 3 указывает вдоль каждой строки блока, ось 2 указывает вдоль каждого столбца блока –

1

Без ski-learn вы можете просто изменить форму и принять соответствующее среднее значение.

M=np.arange(10000).reshape(100,100) 
M1=M.reshape(10,10,10,10) 
M2=M1.mean(axis=(1,3)) 

быстро проверить, чтобы увидеть, если я получил право топоры

In [127]: M2[0,0] 
Out[127]: 454.5 

In [128]: M[:10,:10].mean() 
Out[128]: 454.5 

In [131]: M[-10:,-10:].mean() 
Out[131]: 9544.5 

In [132]: M2[-1,-1] 
Out[132]: 9544.5 

.transpose([0,2,1,3]) Добавление складывает 2 размеры усреднения в конце концов, как view_as_blocks делает.

Для этого случая (100,100), подход с изменением высоты в 2 раза быстрее, чем приближение as_strided, но оба они довольно быстры.

Однако прямолинейное решение не намного медленнее, чем изменение формы.

as_strided(M,shape=(10,10,10,10),strides=(8000,80,800,8)).mean((2,3)) 
as_strided(M,shape=(10,10,10,10),strides=(8000,800,80,8)).mean((1,3)) 
0

Я прихожу поздно, но я бы рекомендовал scipy.ndimage.zoom() как вне готовое решение для этого. Он уменьшает размер (или увеличивает) с использованием сплайновых интерполяций произвольного порядка от 0 до 5. Звучит так, как порядок 0 будет достаточным для вас по вашему вопросу.

from scipy import ndimage as ndi 
import numpy as np 

M=np.arange(1000000).reshape(1000,1000) 

shrinkby=10 

Mfilt = ndi.filters.uniform_filter(input=M, size=shrinkby) 
Msmall = ndi.interpolation.zoom(input=Mfilt, zoom=1./shrinkby, order=0) 

Это все, что вам нужно. Возможно, немного менее удобно указать масштаб, а не желаемый выходной размер, но по крайней мере для order=0 этот метод очень быстрый.

Размер выходного сигнала составляет 10% от входного сигнала в каждом измерении, т.е.

print M.shape, Msmall.shape 

дает (1000, 1000) (100, 100) и скорость вы можете получить от

%timeit Mfilt = ndi.filters.uniform_filter(input=M, size=shrinkby) 
%timeit Msmall = ndi.interpolation.zoom(input=Mfilt, zoom=1./shrinkby, order=0) 

, который на моей машине дал 10 loops, best of 3: 20.5 ms per loop для uniform_filter вызова и 1000 loops, best of 3: 1.67 ms per loop для zoom вызова.

Смежные вопросы