Как сделать эквивалент block_reduce для маскированного массива? - PullRequest
0 голосов
/ 27 мая 2019

Я вычисляю совокупное значение по меньшим блокам в двумерном массиве.Я хотел бы исключить значения 0 из операции агрегации эффективным способом (а не для операторов if и if).

Я использую skimage.measure.block_reduce и numpy.ma.masked_equal, но это выглядит как block_reduceигнорирует маску.

import numpy as np
import skimage
a = np.array([[2,4,0,12,5,7],[6,0,8,4,3,9]])
zeros_included = skimage.measure.block_reduce(a,(2,2),np.mean)

включает в себя 0 и (правильно) выдает

zeros_included
array([[3., 6., 6.]])

Я надеялся, что

masked = np.ma.masked_equal(a,0)
zeros_excluded = skimage.measure.block_reduce(masked,(2,2),np.mean)

справится, но все равно выдаст

zeros_excluded
array([[3., 6., 6.]])

Желаемый результат будет:

array([[4., 8., 6.]])

Я ищу питонский способ для достижения правильного результата, использование лыжного мага необязательно.Конечно, мои фактические массивы и блоки намного больше, чем в этом примере, поэтому мы нуждаемся в эффективности.

Спасибо за ваш интерес.

Ответы [ 2 ]

1 голос
/ 27 мая 2019

Вы можете использовать np.nanmean, но вам придется изменить исходный массив или создать новый:

import numpy as np
import skimage

a = np.array([[2,4,0,12,5,7],[6,0,8,4,3,9]])


b = a.astype("float")
b[b==0] = np.nan
zeros_excluded = skimage.measure.block_reduce(b,(2,2), np.nanmean)
zeros_excluded

# array([[4., 8., 6.]])
0 голосов
/ 27 мая 2019

Код ядра block_reduce равен

blocked = view_as_blocks(image, block_size)
return func(blocked, axis=tuple(range(image.ndim, blocked.ndim)))

view_as_blocks использует as_strided для создания другого представления массива:

In [532]: skimage.util.view_as_blocks(a,(2,2))                                                        
Out[532]: 
array([[[[ 2,  4],
         [ 6,  0]],

        [[ 0, 12],
         [ 8,  4]],

        [[ 5,  7],
         [ 3,  9]]]])

При применении к замаскированному массиву он производит то же самое. По сути он работает с masked.data или np.asarray(masked). Некоторые действия сохраняют подклассы, это не так.

In [533]: skimage.util.view_as_blocks(masked,(2,2))  
Out[533]: 
array([[[[ 2,  4],
         [ 6,  0]],
         ...

Вот почему np.mean, примененный к осям (2,3), не реагирует на маскирование.

np.mean, примененный к маскированному массиву, делегирует действие собственному методу массива, поэтому чувствителен к маскированию:

In [544]: np.mean(masked[:,:2])                                                                       
Out[544]: 4.0
In [545]: masked[:,:2].mean()                                                                         
Out[545]: 4.0
In [547]: [masked[:,i:i+2].mean() for i in range(0,6,2)]                                              
Out[547]: [4.0, 8.0, 6.0]

np.nanmean работает с view_as_blocks, поскольку он не зависит от того, является ли массив специальным подклассом.

Я могу определить функцию, которая применяет маскирование к виду блока:

def foo(arr,axis):
    return np.ma.masked_equal(arr,0).mean(axis)

In [552]: skimage.measure.block_reduce(a,(2,2),foo)                                                   
Out[552]: 
masked_array(data=[[4.0, 8.0, 6.0]],
             mask=[[False, False, False]],
       fill_value=1e+20)

====

Поскольку ваши блоки не перекрываются, я создаю блоки с изменением формы и сменой осей.

In [554]: masked.reshape(2,3,2).transpose(1,0,2)                                                      
Out[554]: 
masked_array(
  data=[[[2, 4],
         [6, --]],

        [[--, 12],
         [8, 4]],

        [[5, 7],
         [3, 9]]],
  mask=[[[False, False],
         [False,  True]],

        [[ True, False],
         [False, False]],

        [[False, False],
         [False, False]]],
  fill_value=0)

и затем применить mean к последним 2 осям:

In [555]: masked.reshape(2,3,2).transpose(1,0,2).mean((1,2))                                          
Out[555]: 
masked_array(data=[4.0, 8.0, 6.0],
             mask=[False, False, False],
       fill_value=1e+20)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...